import json import time import mongoengine from flask import jsonify, request from flask_views.base import View from mongoengine import ValidationError, DoesNotExist, InvalidQueryError from werkzeug.exceptions import Unauthorized, NotFound, Forbidden from restapi.resource import Resource class ApiView(View): model = None authentication_methods = [] def __init__(self, model, authentication_methods): self.start = time.time() self.model = model self.resource = Resource(self.model) self.authentication_methods = authentication_methods def dispatch_request(self, *args, **kwargs): # keep all the logic in a helper method (_dispatch_request) so that # it's easy for subclasses to override this method without them also having to copy/paste all the # authentication logic, etc. return self._dispatch_request(*args, **kwargs) def _dispatch_request(self, *args, **kwargs): authorized = True if len(self.authentication_methods) == 0 else False autherror = None for authentication_method in self.authentication_methods: authclass = authentication_method() if authclass.authorized(): authorized = True else: autherror = authclass.get_error() if not authorized: return {'error': 'Unauthorized', "message": str(autherror), "status": False}, '401 Unauthorized' try: return super(ApiView, self).dispatch_request(*args, **kwargs) except mongoengine.queryset.DoesNotExist as e: return {'error': 'empty_query', "message": str(e), "status": False}, '404 Not Found' except ValidationError as e: return {"error": "bad_request", "message": e.args, "status": False}, '400 Bad Request' except Unauthorized: return {'error': 'Unauthorized', "status": False}, '401 Unauthorized' except Forbidden: return {'error': 'Access_Denied', "status": False}, '403 Access Denied' except NotFound as e: return {'error': str(e), "status": False}, '404 Not Found' def get(self, *args, **kwargs): """ TODO: check permissions :param args: :param kwargs: :return: """ if 'pk' in kwargs: try: qs = self.resource.to_qs(pk=kwargs.get('pk')) qs = self.has_read_permission(qs) count, data = self.resource.to_json(pk=kwargs.get('pk'), qs=qs) response = { 'response': data, 'status': True, 'info': { 'took': time.time() - self.start }, } except (ValidationError, DoesNotExist) as e: kwargs.update({'code': 404}) response = { "status": False, "errors": "Object not found" } else: if "query" in request.args and self.model._meta.get('can_query'): self.resource.external_query(json.loads(request.args.get('query'))) qs = self.resource.to_qs() qs = self.has_read_permission(qs) count, data = self.resource.to_json(qs=qs) response = { 'response': data, 'info': { 'offset': self.resource.offset, 'limit': self.resource.limit, 'status': True, 'total': count, 'took': time.time() - self.start }, } return jsonify(response), kwargs.get('code', 200) def post(self, *args, **kwargs): """ :param args: :param kwargs: :return: """ try: item = self.model(**request.json) item.validate() except ValidationError as v: return jsonify({ 'status': False, 'errors': [{"field": field, "error": str(error)} for field, error in v.__dict__.get('errors').items() if isinstance(error, ValidationError) and field[0] != "_"] }), 400 except Exception as e: return jsonify({ 'status': False, 'errors': str(e) }), 400 resp, obj = self.has_add_permission(item) from flask import current_app if resp: try: item = obj data = item.save() except Exception as e: current_app.logger.error(e) current_app.logger.info(item.pk) return self.get(pk=item.pk, code=201) else: raise Forbidden() def put(self, *args, **kwargs): """ TODO: check permissions :param args: :param kwargs: :return: """ if "pk" not in kwargs: return jsonify({ 'status': False, "error": "Method not allowed" }), 403 else: try: item = self.model.objects.get(id=kwargs.get('pk')) updata = request.json resp, obj = self.has_change_permission(item, updata) from flask import current_app if resp: updata = obj item.update(**updata) else: raise Forbidden() return self.get(pk=kwargs.get('pk')) except ValidationError as v: print(v.__dict__) return jsonify({ 'status': False, 'errors': [{"field": v.__dict__.get('field_name'), "error": v.__dict__.get('_message')}] }), 400 except InvalidQueryError as e: return jsonify({ 'status': False, 'errors': str(e) }), 400 def has_read_permission(self, qs): for authentication_method in self.authentication_methods: authclass = authentication_method() qs = authclass.has_model_read_permission(qs) return qs def has_add_permission(self, obj): has_ret = False for authentication_method in self.authentication_methods: authclass = authentication_method() resp, obj = authclass.has_model_write_permission(obj) if resp: has_ret = True return has_ret, obj def has_change_permission(self, obj, update): has_ret = False for authentication_method in self.authentication_methods: authclass = authentication_method() resp, obj = authclass.has_model_update_permission(obj, update) if resp: has_ret = True return has_ret, update def has_delete_permission(self, obj): return True def delete(self, *args, **kwargs): "delete method"