예제 #1
0
    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)
예제 #2
0
        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)
예제 #3
0
    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
예제 #4
0
    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)
예제 #5
0
    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)
예제 #6
0
    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)
예제 #7
0
    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
예제 #8
0
    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