コード例 #1
0
ファイル: _api.py プロジェクト: testeddoughnut/safrs
    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)
        swagger_doc = self.get_swagger_doc()
        safrs.dict_merge(swagger_doc, custom_swagger)
        self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
コード例 #2
0
    def swagger_doc_gen(func, instance=False):
        """
            Decorator used to document SAFRSRestAPI HTTP methods exposed in the API
        """
        default_id = cls._s_sample_id()
        class_name = cls.__name__
        collection_name = cls._s_collection_name
        http_method = func.__name__.lower()
        parameters = [
            {"name": cls.object_id, "in": "path", "type": "string", "default": default_id, "required": True},  # parameter id, e.g. UserId
            {
                "name": "Content-Type",  # parameter id, e.g. UserId
                "in": "header",
                "type": "string",
                "default": "application/vnd.api+json",
                "enum": ["application/vnd.api+json", "application/json"],
                "required": True,
            },
        ]
        if tags is None:
            doc_tags = [collection_name]
        else:
            doc_tags = tags

        doc = {"tags": doc_tags}

        responses = {}
        # adhere to open api
        # the model_name will hold the OAS "$ref" schema reference
        coll_model_name = "{}_{}_coll".format(class_name, http_method)  # collection model name
        inst_model_name = "{}_{}_inst".format(class_name, http_method)  # instance model name

        sample_dict = cls._s_sample_dict()

        # Samples with "id" are used for GET and PATCH
        coll_sample_data = schema_from_object(
            coll_model_name, {"data": [{"attributes": sample_dict, "type": cls._s_type, "id": cls._s_sample_id()}]}
        )

        inst_sample_data = schema_from_object(
            inst_model_name, {"data": {"attributes": sample_dict, "type": cls._s_type, "id": cls._s_sample_id()}}
        )

        cls.swagger_models["instance"] = inst_sample_data
        cls.swagger_models["collection"] = coll_sample_data

        if http_method == "get":
            doc["summary"] = "Retrieve a {} object".format(class_name)
            doc["collection_summary"] = "Retrieve a collection of {} objects".format(class_name)
            body, responses = cls._s_get_swagger_doc(http_method)

            responses[HTTPStatus.OK.value] = {"schema": coll_sample_data}

        elif http_method == "patch":
            post_model, responses = cls._s_get_swagger_doc(http_method)

            parameters.append(
                {
                    "name": "PATCH body",
                    "in": "body",
                    "description": "{} attributes".format(class_name),
                    "schema": inst_sample_data,
                    "required": True,
                }
            )
        elif http_method == "post":
            _, responses = cls._s_get_swagger_doc(http_method)
            doc["summary"] = "Create a {} object".format(class_name)

            # Create the default POST body schema
            sample_dict = cls._s_sample_dict()
            # The POST sample doesn't contain an "id"
            sample_data = schema_from_object(inst_model_name, {"data": {"attributes": sample_dict, "type": cls._s_type}})
            parameters.append(
                {
                    "name": "POST body",
                    "in": "body",
                    "description": "{} attributes".format(class_name),
                    "schema": sample_data,
                    "required": True,
                }
            )

        elif http_method == "delete":
            pass
        else:
            # one of 'options', 'head', 'delete'
            safrs.log.debug('no additional documentation for "%s" ', func)

        doc["parameters"] = parameters
        doc["responses"] = responses
        doc["produces"] = ["application/vnd.api+json"]

        method_doc = parse_object_doc(func)
        safrs.dict_merge(doc, method_doc)
        apply_fstring(doc, locals())
        return swagger.doc(doc)(func)
コード例 #3
0
 def update_spec(self, custom_swagger):
     """
         :param custom_swagger: swagger spec to be added to the swagger.json
     """
     _swagger_doc = self.get_swagger_doc()
     safrs.dict_merge(_swagger_doc, custom_swagger)
コード例 #4
0
    def swagger_doc_gen(func, instance=False):
        """
        Decorator used to document SAFRSRestAPI HTTP methods exposed in the API
        """
        default_id = cls._s_sample_id()
        class_name = cls.__name__
        collection_name = cls._s_collection_name
        http_method = func.__name__.lower()
        parameters = [
            {
                "name": cls._s_object_id,
                "in": "path",
                "type": "string",
                "default": default_id,
                "required": True,
            },  # parameter id, e.g. UserId
            {
                "name": "Content-Type",  # parameter id, e.g. UserId
                "in": "header",
                "type": "string",
                "default": "application/vnd.api+json",
                "enum": ["application/vnd.api+json", "application/json"],
                "required": True,
            },
        ]
        if tags is None:
            doc_tags = [collection_name]
        else:
            doc_tags = tags

        doc = {"tags": doc_tags}

        responses = {}
        # adhere to open api
        # the model_name will hold the OAS "$ref" schema reference
        coll_model_name = "{}_coll".format(class_name)  # collection model name
        inst_model_name = "{}_inst".format(class_name)  # instance model name

        sample_dict = cls._s_sample_dict()

        # Samples with "id" are used for GET and PATCH
        sample_instance = {
            "attributes": sample_dict,
            "type": cls._s_type,
            "id": cls._s_sample_id()
        }

        if http_method == "get":
            sample_rels = {}
            for rel_name, val in cls._s_relationships.items():
                sample_rels[rel_name] = {
                    "data": None if val.direction is MANYTOONE else [],
                    "links": {
                        "self": None
                    }
                }
            sample_instance["relationships"] = sample_rels

        coll_sample_data = schema_from_object(coll_model_name,
                                              {"data": [sample_instance]})
        coll_sample_data.description += "{} {};".format(
            class_name, http_method)

        inst_sample_data = schema_from_object(inst_model_name,
                                              {"data": sample_instance})
        inst_sample_data.description += "{} {};".format(
            class_name, http_method)

        cls.swagger_models["instance"] = inst_sample_data
        cls.swagger_models["collection"] = coll_sample_data

        if http_method == "get":
            doc["summary"] = "Retrieve a {} object".format(class_name)
            doc["collection_summary"] = "Retrieve a collection of {} objects".format(
                class_name)
            body, responses = cls._s_get_swagger_doc(http_method)
            responses[HTTPStatus.OK.value] = {
                "schema": coll_sample_data,
                "description": HTTPStatus.OK.description
            }

        elif http_method == "patch":
            post_model, responses = cls._s_get_swagger_doc(http_method)
            parameters.append({
                "name": "PATCH body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": inst_sample_data,
                "required": True,
            })
            responses[HTTPStatus.OK.value] = {
                "schema": inst_sample_data,
                "description": HTTPStatus.OK.description
            }

        elif http_method == "post":
            _, responses = cls._s_get_swagger_doc(http_method)
            doc["summary"] = "Create a {} object".format(class_name)
            # Create the default POST body schema
            sample_dict = cls._s_sample_dict()
            # The POST sample doesn't contain an "id", unless cls.allow_client_generated_ids is True
            if cls.allow_client_generated_ids:
                sample_data = schema_from_object(
                    inst_model_name, {
                        "data": {
                            "attributes": sample_dict,
                            "type": cls._s_type,
                            "id": "client_generated"
                        }
                    })
            else:
                sample_data = schema_from_object(
                    inst_model_name,
                    {"data": {
                        "attributes": sample_dict,
                        "type": cls._s_type
                    }})

            sample_data.description += "{} {};".format(class_name, http_method)

            parameters.append({
                "name": "POST body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": sample_data,
                "required": True,
            })
            responses[HTTPStatus.CREATED.value] = {
                "schema": inst_sample_data,
                "description": HTTPStatus.CREATED.description
            }

        elif http_method == "delete":
            _, responses = cls._s_get_swagger_doc(http_method)
        elif http_method != "options":
            # one of 'options', 'head', 'delete'
            safrs.log.debug(
                'no additional documentation for "{}" '.format(func))

        if is_debug():
            responses.update(debug_responses)

        doc["parameters"] = parameters
        doc["responses"] = responses
        doc["produces"] = ["application/vnd.api+json"]

        method_doc = parse_object_doc(func)
        safrs.dict_merge(doc, method_doc)
        apply_fstring(doc, locals())
        update_response_schema(doc["responses"])
        return swagger.doc(doc)(func)
コード例 #5
0
ファイル: swagger_doc.py プロジェクト: macleodbroad-wf/safrs
    def swagger_doc_gen(func):
        """
            Decorator used to document (SAFRSBase) class methods exposed in the API
        """
        default_id = cls._s_sample_id()
        class_name = cls.__name__
        table_name = cls.__tablename__
        http_method = func.__name__.lower()
        parameters = [
            {
                "name": cls.object_id,  # parameter id, e.g. UserId
                "in": "path",
                "type": "string",
                "default": default_id,
                "required": True,
            },
            {
                "name": "Content-Type",  # parameter id, e.g. UserId
                "in": "header",
                "type": "string",
                "default": "application/vnd.api+json",
                "required": True,
            },
        ]
        if tags is None:
            doc_tags = [table_name]
        else:
            doc_tags = tags

        doc = {
            "tags": doc_tags,
            "description": "Returns a {}".format(class_name)
        }

        responses = {}
        # adhere to open api
        model_name = "{}_{}".format(class_name, http_method)
        if http_method == "get":
            doc["summary"] = "Retrieve a {} object".format(class_name)
            _, responses = cls.get_swagger_doc(http_method)

        elif http_method == "post":
            _, responses = cls.get_swagger_doc(http_method)
            doc["summary"] = "Create a {} object".format(class_name)

            #
            # Create the default POST body schema
            #
            sample_dict = cls._s_sample_dict()
            sample_data = schema_from_object(
                model_name,
                {"data": {
                    "attributes": sample_dict,
                    "type": table_name
                }})
            parameters.append({
                "name": "POST body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": sample_data,
                "required": True,
            })

        elif http_method == "delete":
            doc["summary"] = doc["description"] = "Delete a {} object".format(
                class_name)
            responses = {
                "204": {
                    "description": "Object Deleted"
                },
                "404": {
                    "description": "Object Not Found"
                }
            }

        elif http_method == "patch":
            doc["summary"] = "Update a {} object".format(class_name)
            post_model, responses = cls.get_swagger_doc("patch")
            sample = cls._s_sample_dict()
            sample_dict = cls._s_sample_dict()
            if sample:
                sample_data = schema_from_object(
                    model_name, {
                        "data": {
                            "attributes": sample_dict,
                            "id": cls._s_sample_id(),
                            "type": table_name
                        }
                    })
            else:
                sample_data = schema_from_object(
                    model_name, {
                        "data": {
                            "attributes": sample_dict,
                            "id": cls._s_sample_id(),
                            "type": table_name
                        }
                    })
            parameters.append({
                "name": "POST body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": sample_data,
                "required": True,
            })
        else:
            # one of 'options', 'head', 'patch'
            safrs.log.debug('no documentation for "%s" ', http_method)

        responses_str = {}
        for k, v in responses.items():
            # convert int response code to string
            responses_str[str(k)] = v

        doc["parameters"] = parameters
        doc["responses"] = responses_str
        doc["produces"] = ["application/json"]

        method_doc = parse_object_doc(func)
        safrs.dict_merge(doc, method_doc)

        return swagger.doc(doc)(func)
コード例 #6
0
ファイル: swagger_doc.py プロジェクト: testeddoughnut/safrs
    def swagger_doc_gen(func):
        '''
            Decorator used to document (SAFRSBase) class methods exposed in the API
        '''
        default_id = cls._s_sample_id()
        class_name = cls.__name__
        table_name = cls.__tablename__
        http_method = func.__name__.lower()
        parameters = [
            {
                'name': cls.object_id,  # parameter id, e.g. UserId
                'in': 'path',
                'type': 'string',
                'default': default_id,
                'required': True
            },
            {
                'name': 'Content-Type',  # parameter id, e.g. UserId
                'in': 'header',
                'type': 'string',
                'default': 'application/vnd.api+json',
                'required': True
            },
        ]
        if tags is None:
            doc_tags = [table_name]
        else:
            doc_tags = tags

        doc = {
            'tags': doc_tags,
            'description': 'Returns a {}'.format(class_name)
        }

        responses = {}
        # adhere to open api
        model_name = '{}_{}'.format(class_name, http_method)
        if http_method == 'get':
            doc['summary'] = 'Retrieve a {} object'.format(class_name)
            _, responses = cls.get_swagger_doc(http_method)

        elif http_method == 'post':
            _, responses = cls.get_swagger_doc(http_method)
            doc['summary'] = 'Create a {} object'.format(class_name)

            #
            # Create the default POST body schema
            #
            sample_dict = cls._s_sample_dict()
            sample_data = schema_from_object(
                model_name,
                {'data': {
                    'attributes': sample_dict,
                    'type': table_name
                }})
            parameters.append({
                'name': 'POST body',
                'in': 'body',
                'description': '{} attributes'.format(class_name),
                'schema': sample_data,
                'required': True
            })

        elif http_method == 'delete':
            doc['summary'] = doc['description'] = 'Delete a {} object'.format(
                class_name)
            responses = {
                '204': {
                    'description': 'Object Deleted'
                },
                '404': {
                    'description': 'Object Not Found'
                }
            }

        elif http_method == 'patch':
            doc['summary'] = 'Update a {} object'.format(class_name)
            post_model, responses = cls.get_swagger_doc('patch')
            sample = cls._s_sample_dict()
            sample_dict = cls._s_sample_dict()
            if sample:
                sample_data = schema_from_object(
                    model_name, {
                        'data': {
                            'attributes': sample_dict,
                            'id': cls._s_sample_id(),
                            'type': table_name
                        }
                    })
            else:
                sample_data = schema_from_object(
                    model_name, {
                        'data': {
                            'attributes': sample_dict,
                            'id': cls._s_sample_id(),
                            'type': table_name
                        }
                    })
            parameters.append({
                'name': 'POST body',
                'in': 'body',
                'description': '{} attributes'.format(class_name),
                'schema': sample_data,
                'required': True
            })
        else:
            # one of 'options', 'head', 'patch'
            safrs.LOGGER.debug('no documentation for "%s" ', http_method)

        responses_str = {}
        for k, v in responses.items():
            # convert int response code to string
            responses_str[str(k)] = v

        doc['parameters'] = parameters
        doc['responses'] = responses_str
        doc['produces'] = ["application/json"]

        method_doc = parse_object_doc(func)
        safrs.dict_merge(doc, method_doc)

        return swagger.doc(doc)(func)
コード例 #7
0
def expose_object(self, safrs_object, url_prefix="", **properties):
    """Eine eigene expose_object Funktion um swagger doc zu erzeugen.

    Wird bei Klassen ohne Datanbankanbindung verwendet

    .. code::

        paths: {
            <__qualname__> : {
                <__http_method> : <__rest_doc>
            }
        }

        In <__rest_doc>.tags wird wenn nicht angegeben __qualname__ abgelegt
        In <__rest_doc>.type wird wenn nicht angegeben "string" abgelegt

         creates a class of the form

        @api_decorator
        class Class_API(SAFRSRestAPI):
            SAFRSObject = safrs_object

        add the class as an api resource to /SAFRSObject and /SAFRSObject/{id}

        tablename/collectionname: safrs_object._s_collection_name, e.g. "Users"
        classname: safrs_object.__name__, e.g. "User"

        Möglichkeiten:
            a) /class/ : api_list in class aufrufen
            b) /class/{objectId} : keine Funktion objectId vorhanden also api_get aufrufen
            c) /class/test : Vorhandene Funktion test in class aufrufen

    Parameters
    ----------
    safrs_object : SAFSBase
        FSBase subclass that we would like to expose.
    url_prefix : str, optional
        url prefix. The default is "".
    **properties :
        additional flask-restful properties.

    Returns
    -------
    None.

    """
    # alle methoden der klasse durchgehen und nach __rest_doc suchen

    docs = {}
    # alle methoden von safrs_object durchsuchen und bei eigenen methoden mit __rest_doc merken
    for method_name in dir(safrs_object):
        # die method selbst bestimmen
        try:
            method = getattr(safrs_object, method_name, None)
        except Exception as exc:
            # method_name query gibt gibt einen fehler
            # SQL expression, column, or mapped entity expected - got '<class 'xxxxx'>'
            #print( "expose_object - error beim bestimmen von", method_name, exc)
            pass

        if method and hasattr(method, '__qualname__') and hasattr(
                method, '__rest_doc'):
            # full_name bestimmt die eigentliche Funktion
            full_name = "{}.{}".format(safrs_object.__qualname__, method_name)
            if method_name == "api_list":
                # variante a)
                path_name = "/{}/".format(safrs_object.__qualname__)
            elif method_name == "api_get":
                # variante b)
                path_name = "/{}/{}/".format(
                    safrs_object.__qualname__,
                    "{" + safrs_object._s_object_id + "}")
            else:
                # variante c)
                path_name = "/{}".format(full_name)

            if method and method.__qualname__ == full_name:
                # für swagger . durch / ersetzen
                path_name = path_name.replace(".", "/")

                docs[path_name] = {}
                for hm in getattr(method, "__http_method", []):
                    method_doc = getattr(method, '__rest_doc', {})
                    if not "tags" in method_doc:
                        method_doc["tags"] = [safrs_object.__qualname__]
                    if not "type" in method_doc:
                        method_doc["type"] = "string"
                    # in docs ablegen
                    docs[path_name][hm.lower()] = method_doc

    # wenn in docs was ist dann die Klasse selbst in _swagger_object einfügen
    if len(docs) > 0:
        object_doc = parse_object_doc(safrs_object)
        object_doc["name"] = safrs_object.__qualname__
        self._swagger_object["tags"].append(object_doc)
        custom_swagger = {"paths": docs}
        # doc im object selbst in _swagger_paths merken
        safrs_object._swagger_paths = docs

        _swagger_doc = self.get_swagger_doc()
        safrs.dict_merge(_swagger_doc, custom_swagger)
コード例 #8
0
ファイル: swagger_doc.py プロジェクト: Faaab/safrs
    def swagger_doc_gen(func):
        """
            Decorator used to document SAFRSRestAPI HTTP methods exposed in the API
        """
        default_id = cls._s_sample_id()
        class_name = cls.__name__
        collection_name = cls._s_collection_name
        http_method = func.__name__.lower()
        parameters = [
            {
                "name": cls.object_id,
                "in": "path",
                "type": "string",
                "default": default_id,
                "required": True
            },  # parameter id, e.g. UserId
            {
                "name": "Content-Type",  # parameter id, e.g. UserId
                "in": "header",
                "type": "string",
                "default": "application/vnd.api+json",
                "enum": ["application/vnd.api+json", "application/json"],
                "required": True,
            },
        ]
        if tags is None:
            doc_tags = [collection_name]
        else:
            doc_tags = tags

        doc = {"tags": doc_tags}

        responses = {}
        # adhere to open api
        model_name = "{}_{}".format(class_name, http_method)
        if http_method == "get":
            doc["summary"] = "Retrieve a {} object".format(class_name)
            doc["collection_summary"] = "Retrieve a collection of {} objects".format(
                class_name)
            _, responses = cls.get_swagger_doc(http_method)

        elif http_method == "post":
            _, responses = cls.get_swagger_doc(http_method)
            doc["summary"] = "Create a {} object".format(class_name)

            # Create the default POST body schema
            sample_dict = cls._s_sample_dict()
            sample_data = schema_from_object(
                model_name,
                {"data": {
                    "attributes": sample_dict,
                    "type": cls._s_type
                }})
            parameters.append({
                "name": "POST body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": sample_data,
                "required": True,
            })

        elif http_method == "patch":
            post_model, responses = cls.get_swagger_doc("patch")
            sample = cls._s_sample_dict()
            sample_dict = cls._s_sample_dict()
            if sample:
                sample_data = schema_from_object(
                    model_name, {
                        "data": [{
                            "attributes": sample_dict,
                            "id": cls._s_sample_id(),
                            "type": cls._s_type
                        }]
                    })
            else:
                sample_data = schema_from_object(
                    model_name, {
                        "data": {
                            "attributes": sample_dict,
                            "id": cls._s_sample_id(),
                            "type": cls._s_type
                        }
                    })
            parameters.append({
                "name": "PATCH body",
                "in": "body",
                "description": "{} attributes".format(class_name),
                "schema": sample_data,
                "required": True,
            })
        elif http_method == "delete":
            pass
        else:
            # one of 'options', 'head', 'delete'
            safrs.log.debug('no additional documentation for "%s" ', func)

        doc["parameters"] = parameters
        doc["responses"] = responses
        doc["produces"] = ["application/vnd.api+json"]

        method_doc = parse_object_doc(func)
        safrs.dict_merge(doc, method_doc)
        apply_fstring(doc, locals())
        return swagger.doc(doc)(func)