Exemplo n.º 1
0
def marshal(data, fields, envelope=None):
    """Takes raw data (in the form of a dict, list, object) and a dict of
    fields to output and filters the data based on those fields.

    :param data: the actual object(s) from which the fields are taken from
    :param fields: a dict of whose keys will make up the final serialized
                   response output
    :param envelope: optional key that will be used to envelop the serialized
                     response


    >>> from flask_restful import fields, marshal
    >>> data = { 'a': 100, 'b': 'foo' }
    >>> mfields = { 'a': fields.Raw }

    >>> marshal(data, mfields)
    OrderedDict([('a', 100)])

    >>> marshal(data, mfields, envelope='data')
    OrderedDict([('data', OrderedDict([('a', 100)]))])

    """
    def make(cls):
        if isinstance(cls, type):
            return cls()
        return cls

    if isinstance(data, (list, tuple)):
        return (OrderedDict([(envelope, [marshal(d, fields) for d in data])])
                if envelope else [marshal(d, fields) for d in data])

    items = ((k, marshal(data, v) if isinstance(v, dict) else make(v).output(
        k, data)) for k, v in fields.items())
    return OrderedDict([(envelope, OrderedDict(items))
                        ]) if envelope else OrderedDict(items)
Exemplo n.º 2
0
 def test_list_of_nested(self):
     obj = {'list': [{'a': 1, 'b': 1}, {'a': 2, 'b': 1}, {'a': 3, 'b': 1}]}
     field = fields.List(fields.Nested({'a': fields.Integer}))
     self.assertEqual([
         OrderedDict([('a', 1)]),
         OrderedDict([('a', 2)]),
         OrderedDict([('a', 3)])
     ], field.output('list', obj))
Exemplo n.º 3
0
    def test_list_of_raw(self):
        obj = {'list': [{'a': 1, 'b': 1}, {'a': 2, 'b': 1}, {'a': 3, 'b': 1}]}
        field = fields.List(fields.Raw)
        self.assertEquals([OrderedDict([('a', 1), ('b', 1), ]),
                           OrderedDict([('a', 2), ('b', 1), ]),
                           OrderedDict([('a', 3), ('b', 1), ])],
                          field.output('list', obj))

        obj = {'list': [1, 2, 'a']}
        field = fields.List(fields.Raw)
        self.assertEquals([1, 2, 'a'], field.output('list', obj))
Exemplo n.º 4
0
    def __init__(self,
                 app=None,
                 prefix='',
                 default_mediatype='application/json',
                 decorators=None,
                 catch_all_404s=False,
                 serve_challenge_on_401=False,
                 url_part_order='bae',
                 errors=None):
        self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
        self.urls = {}
        self.prefix = prefix
        self.default_mediatype = default_mediatype
        self.decorators = decorators if decorators else []
        self.catch_all_404s = catch_all_404s
        self.serve_challenge_on_401 = serve_challenge_on_401
        self.url_part_order = url_part_order
        self.errors = errors or {}
        self.blueprint_setup = None
        self.endpoints = set()
        self.resources = []
        self.app = None
        self.blueprint = None

        if app is not None:
            self.app = app
            self.init_app(app)
Exemplo n.º 5
0
    def dispatch_request(self, *args, **kwargs):

        # Taken from flask
        #noinspection PyUnresolvedReferences
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method

        if isinstance(self.method_decorators, Mapping):
            decorators = self.method_decorators.get(request.method.lower(), [])
        else:
            decorators = self.method_decorators

        for decorator in decorators:
            meth = decorator(meth)

        resp = meth(*args, **kwargs)

        if isinstance(resp, ResponseBase):  # There may be a better way to test
            return resp

        representations = self.representations or OrderedDict()

        #noinspection PyUnresolvedReferences
        mediatype = request.accept_mimetypes.best_match(representations,
                                                        default=None)
        if mediatype in representations:
            data, code, headers = unpack(resp)
            resp = representations[mediatype](data, code, headers)
            resp.headers['Content-Type'] = mediatype
            return resp

        return resp
Exemplo n.º 6
0
    def dispatch_request(self, *args, **kwargs):
        """Overridden to support list and create method names, and improved
        decorator handling for methods
        """
        method = self._get_method_for_request()

        try:
            resp = method(*args, **kwargs)
        except Exception:
            if apm.is_initialized:
                apm.capture_exception()
            resp = ({
                'message': 'Server Error'
            }, HTTPStatus.INTERNAL_SERVER_ERROR, {})
            traceback.print_exc()
        if isinstance(resp, Response):
            return resp

        representations = self.representations or OrderedDict()
        mediatype = request.accept_mimetypes.best_match(representations,
                                                        default=None)
        if mediatype in representations:
            data, code, headers = unpack(resp)
            resp = representations[mediatype](data, code, headers)
            resp.headers['Content-Type'] = mediatype
            return resp

        return resp
Exemplo n.º 7
0
def dispatch_request(self, *args, **kwargs):
    meth = getattr(type(self), request.method.lower(), None)
    if meth is None and request.method == "HEAD":
        meth = getattr(type(self), "get", None)
    assert meth is not None, "Unimplemented method %r" % request.method

    if isinstance(self.method_decorators, Mapping):
        decorators = self.method_decorators.get(
            "*", []) + self.method_decorators.get(request.method.lower(), [])
    else:
        decorators = self.method_decorators

    for decorator in decorators:
        meth = decorator(meth)

    resp = meth(self, *args, **kwargs)

    if isinstance(resp, Response):
        return resp

    representations = self.representations or OrderedDict()

    mediatype = request.accept_mimetypes.best_match(representations,
                                                    default=None)
    if mediatype in representations:
        data, code, headers = unpack(resp)
        resp = representations[mediatype](data, code, headers)
        resp.headers["Content-Type"] = mediatype
        return resp

    return resp
Exemplo n.º 8
0
    def __init__(
        self,
        app,
        host="localhost",
        port=5000,
        prefix="",
        description="SAFRSAPI",
        json_encoder=SAFRSJSONEncoder,
        swaggerui_blueprint=True,
        **kwargs,
    ):
        """
        http://jsonapi.org/format/#content-negotiation-servers
        Servers MUST send all JSON:API data in response documents with
        the header Content-Type: application/vnd.api+json without any media type parameters.

        Servers MUST respond with a 415 Unsupported Media Type status code if
        a request specifies the header Content-Type: application/vnd.api+json with any media type parameters.

        Servers MUST respond with a 406 Not Acceptable status code if
        a request’s Accept header contains the JSON:API media type and
        all instances of that media type are modified with media type parameters.
        """

        self._custom_swagger = kwargs.pop("custom_swagger", {})
        kwargs["default_mediatype"] = "application/vnd.api+json"
        safrs.SAFRS(app,
                    prefix=prefix,
                    json_encoder=json_encoder,
                    swaggerui_blueprint=swaggerui_blueprint,
                    **kwargs)
        # the host shown in the swagger ui
        # this host may be different from the hostname of the server and
        # sometimes we don't want to show the port (eg when proxied)
        # in that case the port may be None
        if port:
            host = f"{host}:{port}"

        super().__init__(
            app,
            api_spec_url=kwargs.pop("api_spec_url", "/swagger"),
            host=host,
            description=description,
            prefix=prefix,
            base_path=prefix,
            **kwargs,
        )
        self.init_app(app)
        self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
        self.update_spec()
Exemplo n.º 9
0
def marshal(data, fields, envelope=None, strip_none=False):
    """Takes raw data (in the form of a dict, list, object) and a dict of
    fields to output and filters the data based on those fields.

    :param data: the actual object(s) from which the fields are taken from
    :param fields: a dict of whose keys will make up the final serialized
                   response output
    :param envelope: optional key that will be used to envelop the serialized
                     response
    :param strip_none: optional key that will strip serialized None data. Default: False


    >>> from flask_restful import fields, marshal
    >>> data = { 'a': 100, 'b': 'foo' }
    >>> mfields = { 'a': fields.Raw }

    >>> marshal(data, mfields)
    OrderedDict([('a', 100)])

    >>> marshal(data, mfields, envelope='data')
    OrderedDict([('data', OrderedDict([('a', 100)]))])

    """

    def make(cls):
        if isinstance(cls, type):
            return cls()
        return cls

    if isinstance(data, (list, tuple)):
        return (OrderedDict([(envelope, [marshal(d, fields, strip_none=strip_none) for d in data])])
                if envelope else [marshal(d, fields, strip_none=strip_none) for d in data])

    if isinstance(data, (float, int, str)):
        return (OrderedDict([(envelope, data)])
                if envelope else data)

    items = ((k, marshal(data, v, strip_none=strip_none) if isinstance(v, dict)
              else make(v).output(k, data))
             for k, v in fields.items())

    if strip_none == True or (current_app and current_app.config.get("RESTFUL_MARSHAL_STRIP_NONE", False) == True):
        # strip None values
        newItems = OrderedDict()
        allItems = OrderedDict(items)
        for k in allItems:
            if allItems[k] is not None:
                newItems[k] = allItems[k]
        items = newItems
    else:
        items = OrderedDict(items)

    return (OrderedDict([(envelope, items)])
            if envelope else items)
Exemplo n.º 10
0
    def __init__(self, *args, **kwargs):
        """ 
            http://jsonapi.org/format/#content-negotiation-servers
            Servers MUST send all JSON:API data in response documents with
            the header Content-Type: application/vnd.api+json without any media type parameters.

            Servers MUST respond with a 415 Unsupported Media Type status code if
            a request specifies the header Content-Type: application/vnd.api+json with any media type parameters.

            Servers MUST respond with a 406 Not Acceptable status code if
            a request’s Accept header contains the JSON:API media type and
            all instances of that media type are modified with media type parameters.
        """

        custom_swagger = kwargs.pop("custom_swagger", {})
        kwargs["default_mediatype"] = "application/vnd.api+json"
        super(Api, self).__init__(*args, **kwargs)
        self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
        self.update_spec(custom_swagger)
Exemplo n.º 11
0
    def dispatch_request(self, *args, **kwargs):
        """Overridden to support list and create method names, and improved
        decorator handling for methods
        """
        method = self._get_method_for_request()

        resp = method(*args, **kwargs)
        if isinstance(resp, Response):
            return resp

        representations = self.representations or OrderedDict()
        mediatype = request.accept_mimetypes.best_match(representations,
                                                        default=None)
        if mediatype in representations:
            data, code, headers = unpack(resp)
            resp = representations[mediatype](data, code, headers)
            resp.headers['Content-Type'] = mediatype
            return resp

        return resp
Exemplo n.º 12
0
    def _add_oas_req_params(self, resource, path_item, method,
                            exposing_instance, is_jsonapi_rpc, swagger_url):
        """
        Add the request parameters to the swagger (filter, sort)
        """
        method_doc = path_item[method]
        parameters = []
        for parameter in method_doc.get("parameters", []):
            object_id = "{%s}" % parameter.get("name")
            if method == "get":
                # Get the jsonapi included resources, ie the exposed relationships
                param = resource.get_swagger_include()
                parameters.append(param)

                # Get the jsonapi fields[], ie the exposed attributes/columns
                # only required for collections though
                param = resource.get_swagger_fields()
                parameters.append(param)

            #
            # Add the sort, filter parameters to the swagger doc when retrieving a collection
            #
            if method == "get" and not (exposing_instance or is_jsonapi_rpc):
                # limit parameter specifies the number of items to return
                parameters += default_paging_parameters()
                param = resource.get_swagger_sort()
                parameters.append(param)
                parameters += list(resource.get_swagger_filters())

            if not (parameter.get("in") == "path" and object_id
                    not in swagger_url) and parameter not in parameters:
                # Only if a path param is in path url then we add the param
                parameters.append(parameter)

        unique_params = OrderedDict()  # rm duplicates
        for param in parameters:
            unique_params[param["name"]] = param
        method_doc["parameters"] = list(unique_params.values())
        path_item[method] = method_doc
Exemplo n.º 13
0
    def dispatch_request(self, *args, **kwargs):
        # Taken from flask

        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == 'HEAD':
            meth = getattr(self, 'get', None)
        assert meth is not None, 'Unimplemented method %r' % request.method

        for decorator in self.method_decorators:
            meth = decorator(meth)

        try:
            increment(total_requests)
        except Exception as exc:
            pass

        # this is where actual method starts

        resp = meth(*args, **kwargs)

        # this is where actual method ends

        if isinstance(resp, ResponseBase):  # There may be a better way to test
            return resp

        representations = self.representations or OrderedDict()

        mediatype = request.accept_mimetypes.best_match(representations,
                                                        default=None)
        if mediatype in representations:
            data, code, headers = unpack(resp)
            resp = representations[mediatype](data, code, headers)
            resp.headers['Content-Type'] = mediatype
            return resp

        return resp
Exemplo n.º 14
0
# -*- coding:utf-8 -*-
from app.utils.api import RestfulApi
from . import app
from .controllers import Case
from flask_restful.utils import OrderedDict
from flask_restful.representations.json import output_json

RESOURCES = ([Case, '/api/success_case',
              '/api/success_case/<string:action>'], )

api = RestfulApi(app, default_mediatype='application/json; charset=UTF-8')
api.representations = OrderedDict([('application/json; charset=UTF-8',
                                    output_json)])
api.add_resources(RESOURCES)
Exemplo n.º 15
0
    def add_resource(self, resource, *urls, **kwargs):
        """
            This method is partly copied from flask_restful_swagger_2/__init__.py

            I changed it because we don't need path id examples when
            there's no {id} in the path.
            We also have to filter out the unwanted parameters
        """
        #
        # This function has grown out of proportion and should be refactored, disable lint warning for now
        #
        # pylint: disable=too-many-nested-blocks,too-many-statements, too-many-locals
        #
        kwargs.pop("relationship", False)  # relationship object
        SAFRS_INSTANCE_SUFFIX = get_config("OBJECT_ID_SUFFIX") + "}"

        path_item = collections.OrderedDict()
        definitions = {}
        resource_methods = kwargs.get("methods", HTTP_METHODS)
        kwargs.pop("safrs_object", None)
        is_jsonapi_rpc = kwargs.pop(
            "jsonapi_rpc",
            False)  # check if the exposed method is a jsonapi_rpc method
        deprecated = kwargs.pop("deprecated", False)  # TBD!!
        for method in self.get_resource_methods(resource):
            if deprecated:
                continue
            if not method.upper() in resource_methods:
                continue
            f = getattr(resource, method, None)
            if not f:
                continue

            operation = getattr(f, "__swagger_operation_object", None)
            if operation:
                # operation, definitions_ = self._extract_schemas(operation)
                operation, definitions_ = Extractor.extract(operation)
                path_item[method] = operation
                definitions.update(definitions_)
                summary = parse_method_doc(f, operation)
                if summary:
                    operation["summary"] = summary.split("<br/>")[0]

        try:
            validate_definitions_object(definitions)
        except FRSValidationError:
            safrs.log.critical("Validation failed for {}".format(definitions))
            exit()

        self._swagger_object["definitions"].update(definitions)

        if path_item:
            for url in urls:
                if not url.startswith("/"):
                    raise ValidationError("paths must start with a /")
                swagger_url = extract_swagger_path(url)

                # exposing_instance tells us whether we're exposing an instance (as opposed to a collection)
                exposing_instance = swagger_url.strip("/").endswith(
                    SAFRS_INSTANCE_SUFFIX)

                for method in self.get_resource_methods(resource):
                    if method == "post" and exposing_instance:
                        # POSTing to an instance isn't jsonapi-compliant (https://jsonapi.org/format/#crud-creating-client-ids)
                        # "A server MUST return 403 Forbidden in response to an
                        # unsupported request to create a resource with a client-generated ID"
                        # the method has already been added before, remove it & continue
                        path_item.pop(method, None)
                        continue

                    method_doc = copy.deepcopy(path_item.get(method))
                    if not method_doc:
                        continue

                    collection_summary = method_doc.pop(
                        "collection_summary", method_doc.get("summary", None))
                    if not exposing_instance and collection_summary:
                        method_doc["summary"] = collection_summary

                    parameters = []
                    for parameter in method_doc.get("parameters", []):
                        object_id = "{%s}" % parameter.get("name")
                        if method == "get":
                            # Get the jsonapi included resources, ie the exposed relationships
                            param = resource.get_swagger_include()
                            parameters.append(param)

                            # Get the jsonapi fields[], ie the exposed attributes/columns
                            param = resource.get_swagger_fields()
                            parameters.append(param)

                        #
                        # Add the sort, filter parameters to the swagger doc when retrieving a collection
                        #
                        if method == "get" and not (exposing_instance
                                                    or is_jsonapi_rpc):
                            # limit parameter specifies the number of items to return
                            parameters += default_paging_parameters()

                            param = resource.get_swagger_sort()
                            parameters.append(param)

                            parameters += list(resource.get_swagger_filters())

                        if not (parameter.get("in") == "path"
                                and object_id not in swagger_url
                                ) and parameter not in parameters:
                            # Only if a path param is in path url then we add the param
                            parameters.append(parameter)

                    unique_params = OrderedDict()  # rm duplicates
                    for param in parameters:
                        unique_params[param["name"]] = param
                    method_doc["parameters"] = list(unique_params.values())
                    method_doc["operationId"] = self.get_operation_id(
                        path_item.get(method).get("summary", ""))
                    path_item[method] = method_doc

                    instance_schema = method_doc.get("responses",
                                                     {}).get("200", {})
                    if instance_schema and exposing_instance and method_doc[
                            "responses"]["200"].get("schema", None):
                        method_doc["responses"]["200"][
                            "schema"] = resource.SAFRSObject.swagger_models[
                                "instance"].reference()
                        # add this later
                        method_doc["responses"]["200"]["schema"] = {}

                    try:
                        validate_path_item_object(path_item)
                    except FRSValidationError as exc:
                        safrs.log.exception(exc)
                        safrs.log.critical(
                            "Validation failed for {}".format(path_item))
                        exit()

                self._swagger_object["paths"][swagger_url] = path_item
                # Check whether we manage to convert to json
                try:
                    json.dumps(self._swagger_object)
                except Exception:
                    safrs.log.critical("Json encoding failed for")
                    # safrs.log.debug(self._swagger_object)

        # disable API methods that were not set by the SAFRSObject
        for http_method in HTTP_METHODS:
            hm = http_method.lower()
            if hm not in self.get_resource_methods(resource):
                setattr(resource, hm, lambda x:
                        ({}, HTTPStatus.METHOD_NOT_ALLOWED))

        # pylint: disable=bad-super-call
        super(FRSApiBase, self).add_resource(resource, *urls, **kwargs)