def test_json_deserializer():
    data = {"a": "test"}
    assert JSONDeserializer().deserialize(json.dumps(data)) == data

    data = {}
    assert JSONDeserializer().deserialize(json.dumps(data)) == {}

    data = None
    assert JSONDeserializer().deserialize(json.dumps(data)) is None
예제 #2
0
class RecordResourceConfig(ResourceConfig):
    """Record resource config."""

    # Blueprint configuration
    blueprint_name = None
    url_prefix = "/records"
    routes = {
        "list": "",
        "item": "/<pid_value>",
    }

    # Request parsing
    request_read_args = {}
    request_view_args = {"pid_value": ma.fields.Str()}
    request_search_args = SearchRequestArgsSchema
    request_headers = {"if_match": ma.fields.Int()}
    request_body_parsers = {
        "application/json": RequestBodyParser(JSONDeserializer())
    }
    default_content_type = "application/json"

    # Response handling
    response_handlers = {
        "application/json": ResponseHandler(JSONSerializer(),
                                            headers=etag_headers)
    }
    default_accept_mimetype = "application/json"
class Marc21RecordResourceConfig(RecordResourceConfig):
    """Marc21 Record resource configuration."""

    blueprint_name = "marc21_records"
    url_prefix = url_prefix

    default_accept_mimetype = "application/json"

    response_handlers = record_serializer

    request_view_args = {
        "pid_value": ma.fields.Str(),
        "pid_type": ma.fields.Str(),
    }
    links_config = {}

    routes = record_ui_routes

    # Request parsing
    request_args = SearchRequestArgsSchema
    request_view_args = {"pid_value": ma.fields.Str()}
    request_headers = {"if_match": ma.fields.Int()}
    request_body_parsers = {
        "application/json": RequestBodyParser(JSONDeserializer())
    }

    request_view_args = {
        "pid_value": ma.fields.Str(),
        "pid_type": ma.fields.Str(),
    }
예제 #4
0
from ..errors import ErrorHandlersMixin
from .parser import RequestStreamParser

#
# Decorator helpers
#
request_view_args = request_parser(
    {
        'pid_value': ma.fields.Str(required=True),
        'key': ma.fields.Str()
    },
    location='view_args')

request_data = request_body_parser(
    parsers={"application/json": RequestBodyParser(JSONDeserializer())},
    default_content_type="application/json",
)

request_stream = request_body_parser(
    parsers={"application/octet-stream": RequestStreamParser()},
    default_content_type="application/octet-stream",
)


#
# Resource
#
class FileResource(ErrorHandlersMixin, Resource):
    """File resource."""
    def __init__(self, config, service):
예제 #5
0
class TodoResource(Resource):
    def __init__(self, config, service):
        super().__init__(config)
        # The service layer is injected into the resource, so that the resource
        # have a service instance to perform it's task with.
        self.service = service

    #
    # Resource API
    #
    error_handlers = {
        # Here we map data and service level exceptions into errors for the
        # user. This dictionary is passed directly to
        # Blueprint.register_error_handler().
        NoResultError: create_error_handler(
            # The HTTPJSONException is responsible for creating an HTTP
            # response with a JSON-formatted body. We do not do content
            # negotiation on errors.
            HTTPJSONException(code=404, description="Not found"),
        ),
        ma.exceptions.ValidationError: create_error_handler(
            HTTPJSONException(code=400, description="Bad request"),
        ),
        PermissionDenied: create_error_handler(
            HTTPJSONException(code=403, description="Forbidden"),
        ),
    }

    def create_url_rules(self):
        # Here we define the RESTful routes. The return value is passed
        # directly to Blueprint.add_url_rule().
        return [
            # The "route()" does a couple of things:
            # - it enforces one HTTP method = one class method (similar to
            #   flask.MethodView, however it allows many methods)
            # - it puts more emphasis on the HTTP method
            # - it wraps the resource method (e.g. self.create) in a >>resource
            #   request context<<. More on that below.
            # You are not required to use the "route()".
            route("POST", "", self.create),
            route("GET", "", self.search),
            route("GET", "/<item_id>", self.read),
        ]

    #
    # Internals
    #
    def _make_identity(self, user_id):
        # This method is a replacement for having proper login system etc.
        if user_id is not None:
            i = Identity(user_id)
            i.provides.add(UserNeed(user_id))
            i.provides.add(RoleNeed("authenticated_user"))
            return i
        else:
            return AnonymousIdentity()

    #
    # View methods
    #
    # Most view methods looks like below:
    # - A couple of decorators to extract arguments from the HTTP request,
    # - A single call to a service method
    # - Returns a simple dict representation of their object with a HTTP status
    #   code

    # The user request parser is a decorator defined above (because we use it
    # multiple times DRY).
    @user_request_parser
    # The request body parser allows the client to send data in many different
    # data formats by sending the Content-Type header (e.g.
    # "Content-Type: application/json"). This can e.g be used for versioning
    # the REST API.
    @request_body_parser(parsers={
        "application/json": RequestBodyParser(JSONDeserializer())
    })
    # The response handler, is the decorator which allows the view to return
    # an dict object instead of a HTTP response. The response handler works in
    # conjunction with the HTTP content negotiation. That is, if a client
    # sends a "Accept: application/json" header, the response handler will
    # choose the appropriate serializer (e.g. return XML, JSON, plain text, ..)
    @response_handler()
    def create(self):
        # The view method itself, does not take any arguments. This is because
        # we ensure that all data is validated and passed through the resource
        # request context (i.e. anything that ends up in "resource_requestctx"
        # has been validated according to the rules defined).
        identity = self._make_identity(resource_requestctx.args['user'])

        item = self.service.create(
            identity,
            resource_requestctx.data,
        )
        # A view may return a dict if the @response_handler decorator was used.
        # Alternatively, the view can also simply return a normal
        # Flask.Response.
        return item.to_dict(), 201

    @user_request_parser
    @request_parser(
        # This request parser extracts the item id from the URL. The name
        # "item_id" is the one we used in create_url_rules().
        {'item_id': ma.fields.Int(required=True)},
        location='view_args',
        unknown=ma.RAISE,
    )
    @response_handler()
    def read(self):
        identity = self._make_identity(resource_requestctx.args['user'])

        item = self.service.read(
            identity,
            resource_requestctx.view_args['item_id'],
        )

        return item.to_dict(), 200

    @user_request_parser
    @request_parser(
        {
            'page': ma.fields.Int(
                missing=1,
                validate=ma.validate.Range(min=1),
            ),
            'size': ma.fields.Int(
                missing=10,
                validate=ma.validate.Range(min=1)
            ),
        },
        location='args',
        unknown=ma.EXCLUDE,
    )
    # When return a list results, we add the "many=True". This is because the
    # serializer may need special handling for lists - e.g. enveloping the
    # results. For the default JSONSerializer which we use in this example,
    # there's no difference be between many=True/many=False.
    @response_handler(many=True)
    def search(self):
        identity = self._make_identity(resource_requestctx.args['user'])

        item_list = self.service.search(
            identity,
            page=resource_requestctx.args['page'],
            size=resource_requestctx.args['size'],
        )

        return item_list.to_dict(), 200