|
|
|
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"
|