def wrapper(*args, **kwargs): headers = {"Content-Type": "application/vnd.api+json"} try: return func(*args, **kwargs) except JsonApiException as e: return make_response(jsonify(jsonapi_errors([e.to_dict()])), e.status, headers) except Exception as e: if current_app.config["DEBUG"] is True: raise e if "sentry" in current_app.extensions: current_app.extensions["sentry"].captureException() exc = JsonApiException( getattr( e, "detail", current_app.config.get("GLOBAL_ERROR_MESSAGE") or str(e), ), source=getattr(e, "source", ""), title=getattr(e, "title", None), status=getattr(e, "status", None), code=getattr(e, "code", None), id_=getattr(e, "id", None), links=getattr(e, "links", None), meta=getattr(e, "meta", None), ) return make_response(jsonify(jsonapi_errors([exc.to_dict()])), exc.status, headers)
def user_wrap_function(*args, **kwargs): extra = None if not request.headers.has_key("access_token"): perms = Permission.NONUSER user = None extra = "No access token provided. Please add a header with the name 'access_token'." else: user = User.query.filter_by( api_token=request.headers.get("access_token") ).first() if not user: perms = Permission.NONUSER extra = "The provided access token was invalid." elif user.is_anonymous: perms = Permission.NONUSER elif user.is_admin: perms = Permission.ADMIN elif not user.is_active(): perms = Permission.NONUSER extra = "User is not active." else: perms = Permission.USER if perms < min_level: title = "Insufficient permissions to access this resource" raise JsonApiException( title=title, detail=extra, status=403, ) kwargs["user"] = user kwargs["permission"] = perms return function(*args, **kwargs)
def create_object(self, data, view_kwargs): """Create an object through sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return DeclarativeMeta: an object from sqlalchemy """ self.before_create_object(data, view_kwargs) relationship_fields = get_relationships(self.resource.schema, model_field=True) nested_fields = get_nested_fields(self.resource.schema, model_field=True) join_fields = relationship_fields + nested_fields obj = self.model( **{key: value for (key, value) in data.items() if key not in join_fields} ) self.apply_relationships(data, obj) self.apply_nested_fields(data, obj) self.session.add(obj) try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException( "Object creation error: " + str(e), source={"pointer": "/data"} ) self.after_create_object(obj, data, view_kwargs) return obj
def delete_object(self, obj, view_kwargs): """Delete an object through sqlalchemy :param DeclarativeMeta item: an item from sqlalchemy :param dict view_kwargs: kwargs from the resource view """ if obj is None: url_field = getattr(self, "url_field", "id") filter_value = view_kwargs[url_field] raise ObjectNotFound( "{}: {} not found".format(self.model.__name__, filter_value), source={"parameter": url_field}, ) self.before_delete_object(obj, view_kwargs) self.session.delete(obj) try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException("Delete object error: " + str(e)) self.after_delete_object(obj, view_kwargs)
def wrapper(*args, **kwargs): headers = {"Content-Type": "application/vnd.api+json"} try: return func(*args, **kwargs) except JsonApiException as e: if isinstance(e.status, str) and e.status and e.status[0] == '5': logger.exception("Exception while processing request") elif isinstance(e.status, int) and 500 <= e.status <= 599: logger.exception("Exception while processing request") return make_response(jsonify(jsonapi_errors([e.to_dict()])), e.status, headers) except Exception as e: if current_app.config["DEBUG"] is True: raise e if "sentry" in current_app.extensions: current_app.extensions["sentry"].captureException() logger.exception('Unhandled exception while processing request') exc = JsonApiException( getattr( e, "detail", current_app.config.get("GLOBAL_ERROR_MESSAGE") or str(e), ), source=getattr(e, "source", ""), title=getattr(e, "title", None), status=getattr(e, "status", None), code=getattr(e, "code", None), id_=getattr(e, "id", None), links=getattr(e, "links", None), meta=getattr(e, "meta", None), ) return make_response(jsonify(jsonapi_errors([exc.to_dict()])), exc.status, headers)
def update_object(self, obj, data, view_kwargs): """Update an object through sqlalchemy :param DeclarativeMeta obj: an object from sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return boolean: True if object have changed else False """ if obj is None: url_field = getattr(self, "url_field", "id") filter_value = view_kwargs[url_field] raise ObjectNotFound( "{}: {} not found".format(self.model.__name__, filter_value), source={"parameter": url_field}, ) self.before_update_object(obj, data, view_kwargs) relationship_fields = get_relationships(self.resource.schema, model_field=True) nested_fields = get_nested_fields(self.resource.schema, model_field=True) join_fields = relationship_fields + nested_fields for key, value in data.items(): if hasattr(obj, key) and key not in join_fields: setattr(obj, key, value) self.apply_relationships(data, obj) self.apply_nested_fields(data, obj) try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException( "Update object error: " + str(e), source={"pointer": "/data"} ) self.after_update_object(obj, data, view_kwargs)
def delete_relationship( self, json_data, relationship_field, related_id_field, view_kwargs ): """Delete a relationship :param dict json_data: the request params :param str relationship_field: the model attribute used for relationship :param str related_id_field: the identifier field of the related model :param dict view_kwargs: kwargs from the resource view """ self.before_delete_relationship( json_data, relationship_field, related_id_field, view_kwargs ) obj = self.get_object(view_kwargs) if obj is None: url_field = getattr(self, "url_field", "id") filter_value = view_kwargs[url_field] raise ObjectNotFound( "{}: {} not found".format(self.model.__name__, filter_value), source={"parameter": url_field}, ) if not hasattr(obj, relationship_field): raise RelationNotFound( "{} has no attribute {}".format( obj.__class__.__name__, relationship_field ) ) related_model = getattr( obj.__class__, relationship_field ).property.mapper.class_ updated = False if isinstance(json_data["data"], list): obj_ids = { str(getattr(obj__, related_id_field)) for obj__ in getattr(obj, relationship_field) } for obj_ in json_data["data"]: if obj_["id"] in obj_ids: getattr(obj, relationship_field).remove( self.get_related_object(related_model, related_id_field, obj_) ) updated = True else: setattr(obj, relationship_field, None) updated = True try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException("Delete relationship error: " + str(e)) self.after_delete_relationship( obj, updated, json_data, relationship_field, related_id_field, view_kwargs ) return obj, updated
def update_relationship( self, json_data, relationship_field, related_id_field, view_kwargs ): """Update a relationship :param dict json_data: the request params :param str relationship_field: the model attribute used for relationship :param str related_id_field: the identifier field of the related model :param dict view_kwargs: kwargs from the resource view :return boolean: True if relationship have changed else False """ self.before_update_relationship( json_data, relationship_field, related_id_field, view_kwargs ) obj = self.get_object(view_kwargs) if obj is None: url_field = getattr(self, "url_field", "id") filter_value = view_kwargs[url_field] raise ObjectNotFound( "{}: {} not found".format(self.model.__name__, filter_value), source={"parameter": url_field}, ) if not hasattr(obj, relationship_field): raise RelationNotFound( "{} has no attribute {}".format( obj.__class__.__name__, relationship_field ) ) related_model = getattr( obj.__class__, relationship_field ).property.mapper.class_ updated = False if isinstance(json_data["data"], list): related_objects = [] for obj_ in json_data["data"]: related_objects.append( self.get_related_object(related_model, related_id_field, obj_) ) obj_ids = { getattr(obj__, related_id_field) for obj__ in getattr(obj, relationship_field) } new_obj_ids = { getattr(related_object, related_id_field) for related_object in related_objects } if obj_ids != new_obj_ids: setattr(obj, relationship_field, related_objects) updated = True else: related_object = None if json_data["data"] is not None: related_object = self.get_related_object( related_model, related_id_field, json_data["data"] ) obj_id = getattr(getattr(obj, relationship_field), related_id_field, None) new_obj_id = getattr(related_object, related_id_field, None) if obj_id != new_obj_id: setattr(obj, relationship_field, related_object) updated = True try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException("Update relationship error: " + str(e)) self.after_update_relationship( obj, updated, json_data, relationship_field, related_id_field, view_kwargs ) return obj, updated