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)
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)
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)
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)
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)
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)
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)
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)