def get(self, path: str) -> Response: """ Retrieve a collection of items from the database. """ auth_response = check_authentication_response() if isinstance(auth_response, Response): return auth_response endpoint_ = checkEndpoint("GET", path) if endpoint_['method']: # If endpoint and GET method is supported in the API if path in get_doc().collections: # If collection name in document's collections collection = get_doc().collections[path]["collection"] try: # Get collection details from the database response = crud.get_collection(get_api_name(), collection.class_.title, session=get_session(), path=path) return set_response_headers( jsonify(hydrafy(response, path=path))) except ClassNotFound as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) # If class is supported elif path in get_doc().parsed_classes and "{}Collection".format( path) not in get_doc().collections: try: class_type = get_doc().parsed_classes[path]['class'].title response = crud.get_single(class_type, api_name=get_api_name(), session=get_session(), path=path) return set_response_headers( jsonify(hydrafy(response, path=path))) except (ClassNotFound, InstanceNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) abort(endpoint_['status'])
def post(self, path: str) -> Response: """ Method executed for POST requests. Used to update a non-collection class. :param path - Path for Item type ( Specified in APIDoc @id) """ auth_response = check_authentication_response() if isinstance(auth_response, Response): return auth_response endpoint_ = checkEndpoint("POST", path) if endpoint_['method']: object_ = json.loads(request.data.decode('utf-8')) if path in get_doc().parsed_classes and path + \ "Collection" not in get_doc().collections: obj_type = getType(path, "POST") if validObject(object_): if object_["@type"] == obj_type: try: crud.update_single(object_=object_, session=get_session(), api_name=get_api_name(), path=path) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + path + "/" }] response = { "message": "Object successfully updated" } return set_response_headers(jsonify(response), headers=headers_) except (ClassNotFound, InstanceNotFound, InstanceExists, PropertyNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers( jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(endpoint_['status'])
def put(self, path, int_list="") -> Response: """ To insert multiple objects into the database :param path: endpoint :param int_list: Optional String containing ',' separated ID's :return: """ auth_response = check_authentication_response() if isinstance(auth_response, Response): return auth_response endpoint_ = checkEndpoint("PUT", path) if endpoint_['method']: # If endpoint and PUT method is supported in the API object_ = json.loads(request.data.decode('utf-8')) object_ = object_["data"] if path in get_doc().collections: # If collection name in document's collections collection = get_doc().collections[path]["collection"] # title of HydraClass object corresponding to collection obj_type = collection.class_.title if validObjectList(object_): type_result = type_match(object_, obj_type) # If Item in request's JSON is a valid object # ie. @type is one of the keys in object_ if type_result: # If the right Item type is being added to the # collection try: # Insert object and return location in Header object_id = crud.insert_multiple( objects_=object_, session=get_session(), id_=int_list) headers_ = [{"Location": "{}{}/{}/{}".format( get_hydrus_server_url(), get_api_name(), path, object_id)}] response = { "message": "Object with ID {} successfully added".format(object_id)} return set_response_headers( jsonify(response), headers=headers_, status_code=201) except (ClassNotFound, InstanceExists, PropertyNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(endpoint_['status'])
def on_post(self, req, resp, id_: int, type_: str): """Update object of type<type_> at ID<id_> with new object_ using HTTP POST. :param id_ - ID of Item to be updated :param type_ - Type(Class name) of Item to be updated """ if get_authentication(resp): if req.auth is None: return failed_authentication(resp) else: try: auth = check_authorization(req, get_session(resp)) if auth is False: return failed_authentication(resp) except Exception as e: status_code, message = e.get_HTTP() # type: ignore resp.media = message return set_response_headers(resp, status_code=status_code) class_type = get_doc( resp).collections[type_]["collection"].class_.title if checkClassOp(resp, class_type, "POST"): # Check if class_type supports POST operation object_ = req.media obj_type = getType(resp, class_type, "POST") # Load new object and type if validObject(object_): if object_["@type"] == obj_type: try: # Update the right ID if the object is valid and matches # type of Item object_id = crud.update(object_=object_, id_=id_, type_=object_["@type"], session=get_session(resp), api_name=get_api_name(resp)) headers_ = [{ "Location": get_hydrus_server_url(resp) + get_api_name(resp) + "/" + type_ + "/" + str(object_id) }] response = { "message": "Object with ID %s successfully updated" % (object_id) } resp.media = response return set_response_headers(resp, headers=headers_[0]) except Exception as e: status_code, message = e.get_HTTP() resp.media = message return set_response_headers(resp, status_code) return set_response_headers(resp, status_code=falcon.HTTP_400) resp.status = falcon.HTTP_405
def on_get(self, req, resp, category: str) -> falcon.Response: """Return the context for the specified class.""" if "Collection" in category: if category in get_doc(resp).collections: resp.media = {"@context": get_doc(resp).collections[category]["context"].generate()} # type: Union[Dict[str,Any],Dict[int,str]] return set_response_headers(resp) else: return set_response_headers(resp, status_code= falcon.HTTP_404) else: if category in get_doc(resp).parsed_classes: resp.media = {"@context": get_doc(resp).parsed_classes[category]["context"].generate()} return set_response_headers(resp) else: return set_response_headers(resp, status_code=falcon.HTTP_404)
def checkClassOp(path: str, method: str) -> bool: """ Check if the Class supports the operation. :param path: Path of the collection or non-collection class. :param method: Method name. :return: True if the method is defined, false otherwise. """ collections, parsed_classes = get_collections_and_parsed_classes() if path in collections: supported_operations = get_doc( ).collections[path]["collection"].supportedOperation else: supported_operations = get_doc( ).parsed_classes[path]["class"].supportedOperation for supportedOp in supported_operations: if supportedOp.method == method: return True return False
def finalize_response(path: str, obj: Dict[str, Any]) -> Dict[str, Any]: """ finalize response objects by removing properties which are not readable and correcting path of nested objects. :param path: Path of the collection or non-collection class. :param obj: object being finalized :return: An object not containing any `readable=False` properties and having proper path of any nested object's url. """ collections, parsed_classes = get_collections_and_parsed_classes() expanded_base_url = DocUrl.doc_url if path in collections: members = list() for member in obj["members"]: member_id = member[0] member_type = member[1] member_path = get_path_from_type(member_type) member = { "@type": "hydra:Link", "@id": f"/{get_api_name()}/{member_path}/{member_id}", } members.append(member) obj['members'] = members return obj else: # path is of a non-collection class supported_properties = get_doc( ).parsed_classes[path]["class"].supportedProperty expanded_base_url = DocUrl.doc_url for prop in supported_properties: # Skip not required properties which are not inserted yet. if not prop.required and prop.title not in obj: continue # if prop.read is False: # obj.pop(prop.title, None) elif isinstance(prop.prop, HydraLink): hydra_link = prop.prop range_class = hydra_link.range.split(expanded_base_url)[1] nested_path, is_collection = get_nested_class_path(range_class) if is_collection: id = obj[prop.title] obj[prop.title] = f"/{get_api_name()}/{nested_path}/{id}" else: obj[prop.title] = f"/{get_api_name()}/{nested_path}" elif expanded_base_url in prop.prop: prop_class = prop.prop.split(expanded_base_url)[1] prop_class_path = parsed_classes[prop_class]['class'].path id = obj[prop.title] class_resp = crud.get(id, prop_class, get_api_name(), get_session(), path=prop_class_path) obj[prop.title] = finalize_response(prop_class_path, class_resp) return obj
def get(self, type_): """Retrieve a collection of items from the database.""" if get_authentication(): if request.authorization is None: return failed_authentication() else: auth = check_authorization(request, get_session()) if auth is False: return failed_authentication() if checkEndpoint("GET", type_): # Collections if type_ in get_doc().collections: collection = get_doc().collections[type_]["collection"] try: response = crud.get_collection(get_api_name(), collection.class_.title, session=get_session()) return set_response_headers(jsonify(hydrafy(response))) except Exception as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) # Non Collection classes elif type_ in get_doc( ).parsed_classes and type_ + "Collection" not in get_doc( ).collections: try: response = crud.get_single(type_, api_name=get_api_name(), session=get_session()) return set_response_headers(jsonify(hydrafy(response))) except Exception as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) abort(405)
def post(self, type_): """Update Non Collection class item.""" if get_authentication(): if request.authorization is None: return failed_authentication() else: auth = check_authorization(request, get_session()) if auth is False: return failed_authentication() if checkEndpoint("POST", type_): object_ = json.loads(request.data.decode('utf-8')) if type_ in get_doc( ).parsed_classes and type_ + "Collection" not in get_doc( ).collections: obj_type = getType(type_, "POST") if validObject(object_): if object_["@type"] == obj_type: # try: crud.update_single(object_=object_, session=get_session(), api_name=get_api_name()) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + type_ + "/" }] response = {"message": "Object successfully updated"} return set_response_headers(jsonify(response), headers=headers_) # except Exception as e: # status_code, message = e.get_HTTP() # return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(405)
def get(self, category: str) -> Response: """Return the context for the specified class.""" # Check for collection if category in get_doc().collections: # type: Union[Dict[str,Any],Dict[int,str]] response = { "@context": get_doc().collections[category]["context"].generate() } return set_response_headers(jsonify(response)) # Check for non collection class elif category in get_doc().parsed_classes: response = { "@context": get_doc().parsed_classes[category]["context"].generate() } return set_response_headers(jsonify(response)) else: response = {404: "NOT FOUND"} return set_response_headers(jsonify(response), status_code=404)
def checkClassOp(path: str, method: str) -> bool: """Check if the Class supports the operation. :param path: Path of the collection or non-collection class. :param method: Method name. :return: True if the method is defined, false otherwise. """ for supportedOp in get_doc( ).parsed_classes[path]["class"].supportedOperation: if supportedOp.method == method: return True return False
def on_post(self, req, resp, type_: str): """ Method executed for POST requests. Used to update a non-collection class. :param type_ - Item type """ if get_authentication(resp): if req.auth is None: return failed_authentication(resp) else: try: auth = check_authorization(req, get_session(resp)) if auth is False: return failed_authentication(resp) except Exception as e: status_code, message = e.get_HTTP() # type: ignore resp.media = message return set_response_headers(resp, status_code=status_code) endpoint_ = checkEndpoint(resp, "POST", type_) if endpoint_['method']: object_ = req.media if type_ in get_doc(resp).parsed_classes and type_ + "Collection" not in get_doc(resp).collections: obj_type = getType(resp, type_, "POST") if validObject(object_): if object_["@type"] == obj_type: try: crud.update_single( object_=object_, session=get_session(resp), api_name=get_api_name(resp)) headers_ = [{"Location": get_hydrus_server_url(resp ) + get_api_name(resp) + "/" + type_ + "/"}] response = {"message": "Object successfully updated"} resp.media = response return set_response_headers(resp, headers=headers_[0]) except Exception as e: status_code, message = e.get_HTTP() resp.media = message return set_response_headers(resp, status_code=status_code) return set_response_headers(resp, status_code=falcon.HTTP_400)
def post(self, type_: str) -> Response: """ Method executed for POST requests. Used to update a non-collection class. :param type_ - Item type """ auth_response = check_authentication_response() if type(auth_response) == Response: return auth_response endpoint_ = checkEndpoint("POST", type_) if endpoint_['method']: object_ = json.loads(request.data.decode('utf-8')) if type_ in get_doc( ).parsed_classes and type_ + "Collection" not in get_doc( ).collections: obj_type = getType(type_, "POST") if validObject(object_): if object_["@type"] == obj_type: # try: crud.update_single(object_=object_, session=get_session(), api_name=get_api_name()) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + type_ + "/" }] response = {"message": "Object successfully updated"} return set_response_headers(jsonify(response), headers=headers_) # except Exception as e: # status_code, message = e.get_HTTP() # return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(endpoint_['status'])
def get(self, type_: str) -> Response: """ Retrieve a collection of items from the database. """ auth_response = check_authentication_response() if type(auth_response) == Response: return auth_response endpoint_ = checkEndpoint("GET", type_) if endpoint_['method']: # If endpoint and GET method is supported in the API if type_ in get_doc().collections: # If collection name in document's collections collection = get_doc().collections[type_]["collection"] try: # Get collection details from the database response = crud.get_collection(get_api_name(), collection.class_.title, session=get_session()) return set_response_headers(jsonify(hydrafy(response))) except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) # If class is supported elif type_ in get_doc( ).parsed_classes and type_ + "Collection" not in get_doc( ).collections: try: response = crud.get_single(type_, api_name=get_api_name(), session=get_session()) return set_response_headers(jsonify(hydrafy(response))) except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) abort(endpoint_['status'])
def get(self, category): """Return the context for the specified class.""" if "Collection" in category: if category in get_doc().collections: response = {"@context": get_doc().collections[category]["context"].generate()} return set_response_headers(jsonify(response)) else: response = {404: "NOT FOUND"} return set_response_headers(jsonify(response), status_code=404) else: if category in get_doc().parsed_classes: response = {"@context": get_doc().parsed_classes[category]["context"].generate()} return set_response_headers(jsonify(response)) else: response = {404: "NOT FOUND"} return set_response_headers(jsonify(response), status_code=404)
def post(self, id_: int, type_: str) -> Response: """Update object of type<type_> at ID<id_> with new object_ using HTTP POST.""" if get_authentication(): if request.authorization is None: return failed_authentication() else: try: auth = check_authorization(request, get_session()) if auth is False: return failed_authentication() except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) class_type = get_doc().collections[type_]["collection"].class_.title if checkClassOp(class_type, "POST"): object_ = json.loads(request.data.decode('utf-8')) obj_type = getType(class_type, "POST") if validObject(object_): if object_["@type"] == obj_type: try: object_id = crud.update(object_=object_, id_=id_, type_=object_["@type"], session=get_session(), api_name=get_api_name()) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + type_ + "/" + str(object_id) }] response = { "message": "Object with ID %s successfully updated" % (object_id) } return set_response_headers(jsonify(response), headers=headers_) except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(405)
def getType(class_path: str, method: str) -> Any: """ Return the @type of object allowed for POST/PUT. :param class_path: path for the class :param method: Method name """ expanded_base_url = DocUrl.doc_url for supportedOp in get_doc( ).parsed_classes[class_path]["class"].supportedOperation: if supportedOp.method == method: class_type = supportedOp.expects.split(expanded_base_url)[1] return class_type
def check_required_props(class_type: str, obj: Dict[str, Any]) -> bool: """ Check if the object contains all required properties. :param class_type: class name of the object :param obj: object under check :return: True if the object contains all required properties False otherwise. """ for prop in get_doc().parsed_classes[class_type]["class"].supportedProperty: if prop.required: if prop.title not in obj: return False return True
def checkEndpoint(method: str, path: str) -> Dict[str, Union[bool, int]]: """ Check if endpoint and method is supported in the API. :param method: Method name :param path: Path of the collection or non-collection class :return : Dict with 'method' and 'status' key """ status_val = 404 vocab_route = get_doc().doc_name if path == vocab_route: return {'method': False, 'status': 405} expanded_base_url = DocUrl.doc_url for endpoint in get_doc().entrypoint.entrypoint.supportedProperty: expanded_endpoint_id = endpoint.id_.replace("EntryPoint/", "") endpoint_id = expanded_endpoint_id.split(expanded_base_url)[1] if path == endpoint_id: status_val = 405 for operation in endpoint.supportedOperation: if operation.method == method: status_val = 200 return {'method': True, 'status': status_val} return {'method': False, 'status': status_val}
def check_writeable_props(path: str, obj: Dict[str, Any]) -> bool: """ Check that the object only contains writeable fields(properties). :param path: Path of the collection or non-collection class. :param obj: object under check :return: True if the object only contains writeable properties False otherwise. """ collections, parsed_classes = get_collections_and_parsed_classes() if path in collections: # path is of a collection class supported_properties = get_doc( ).collections[path]["collection"].supportedProperty else: # path is of a non-collection class supported_properties = get_doc( ).parsed_classes[path]["class"].supportedProperty for prop in supported_properties: if prop.write is False: if prop.title in obj: return False return True
def check_required_props(path: str, obj: Dict[str, Any]) -> bool: """ Check if the object contains all required properties. :param path: Path of the collection or non-collection class. :param obj: object under check :return: True if the object contains all required properties False otherwise. """ collections, parsed_classes = get_collections_and_parsed_classes() if path in collections: # path is of a collection class supported_properties = get_doc( ).collections[path]["collection"].supportedProperty else: # path is of a non-collection class supported_properties = get_doc( ).parsed_classes[path]["class"].supportedProperty for prop in supported_properties: if prop.required: if prop.title not in obj: return False return True
def check_read_only_props(class_type: str, obj: Dict[str, Any]) -> bool: """ Check that the object does not contain any read-only properties. :param class_type: class name of the object :param obj: object under check :return: True if the object doesn't contain any read-only properties False otherwise. """ for prop in get_doc().parsed_classes[class_type]["class"].supportedProperty: if prop.read: if prop.title in obj: return False return True
def on_get(self, req, resp, type_): """Retrieve a collection of items from the database.""" if get_authentication(resp): if req.auth is None: return failed_authentication(resp) else: try: auth = check_authorization(req, get_session(resp)) if auth is False: return failed_authentication(resp) except Exception as e: status_code, message = e.get_HTTP() # type: ignore resp.media = message return set_response_headers(resp, status_code=status_code) if checkEndpoint(resp, "GET", type_): # Collections if type_ in get_doc(resp).collections: collection = get_doc(resp).collections[type_]["collection"] try: resp.media = hydrafy(resp, crud.get_collection(get_api_name(resp), collection.class_.title, session=get_session(resp))) return set_response_headers(resp) except Exception as e: status_code, message = e.get_HTTP() resp.media = message return set_response_headers(resp, status_code=status_code) # Non Collection classes elif type_ in get_doc(resp).parsed_classes and type_+"Collection" not in get_doc(resp).collections: try: resp.media = hydrafy(resp, crud.get_single(type_, api_name=get_api_name(resp), session=get_session(resp))) return set_response_headers(resp) except Exception as e: status_code, message = e.get_HTTP() resp.media = message return set_response_headers(resp, status_code=status_code)
def check_required_props(path: str, obj: Dict[str, Any]) -> bool: """ Check if the object contains all required properties. :param path: Path of the collection or non-collection class. :param obj: object under check :return: True if the object contains all required properties False otherwise. """ for prop in get_doc().parsed_classes[path]["class"].supportedProperty: if prop.required: if prop.title not in obj: return False return True
def post(self, id_: str, path: str) -> Response: """Update object of type<path> at ID<id_> with new object_ using HTTP POST. :param id_ - ID of Item to be updated :param path - Path for Item type( Specified in APIDoc @id) """ id_ = str(id_) auth_response = check_authentication_response() if isinstance(auth_response, Response): return auth_response class_type = get_doc().collections[path]["collection"].class_.title if checkClassOp(class_type, "POST"): # Check if class_type supports POST operation object_ = json.loads(request.data.decode('utf-8')) obj_type = getType(class_type, "POST") # Load new object and type if validObject(object_): if object_["@type"] == obj_type: try: # Update the right ID if the object is valid and matches # type of Item object_id = crud.update(object_=object_, id_=id_, type_=object_["@type"], session=get_session(), api_name=get_api_name()) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + path + "/" + str(object_id) }] response = { "message": "Object with ID %s successfully updated" % (object_id) } return set_response_headers(jsonify(response), headers=headers_) except (ClassNotFound, InstanceNotFound, InstanceExists, PropertyNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(405)
def checkEndpoint(method: str, path: str) -> Dict[str, Union[bool, int]]: """Check if endpoint and method is supported in the API.""" status_val = 404 if path == 'vocab': return {'method': False, 'status': 405} for endpoint in get_doc().entrypoint.entrypoint.supportedProperty: if path == "/".join(endpoint.id_.split("/")[1:]): status_val = 405 for operation in endpoint.supportedOperation: if operation.method == method: status_val = 200 return {'method': True, 'status': status_val} return {'method': False, 'status': status_val}
def checkEndpoint(resp: falcon.Response, method: str, type_: str) -> Dict[str, Union[bool, falcon.HTTPStatus]]: """Check if endpoint and method is supported in the API.""" status_code = falcon.HTTP_404 if type_ == 'vocab': return {'method': False, 'status': falcon.HTTP_405} for endpoint in get_doc(resp).entrypoint.entrypoint.supportedProperty: if type_ == endpoint.name: status_code = falcon.HTTP_405 for operation in endpoint.supportedOperation: if operation.method == method: status_code = falcon.HTTP_200 return {'method': True, 'status': status_code} return {'method': False, 'status': status_code}
def checkEndpoint(method: str, type_: str) -> Dict[str, Union[bool, int]]: """Check if endpoint and method is supported in the API.""" status_val = 404 if type_ == 'vocab': return {'method': False, 'status': 405} for endpoint in get_doc().entrypoint.entrypoint.supportedProperty: if type_ == endpoint.name: status_val = 405 for operation in endpoint.supportedOperation: if operation.method == method: status_val = 200 return {'method': True, 'status': status_val} return {'method': False, 'status': status_val}
def post(self, id_: int, type_: str) -> Response: """Update object of type<type_> at ID<id_> with new object_ using HTTP POST. :param id_ - ID of Item to be updated :param type_ - Type(Class name) of Item to be updated """ auth_response = check_authentication_response() if type(auth_response) == Response: return auth_response class_type = get_doc().collections[type_]["collection"].class_.title if checkClassOp(class_type, "POST"): # Check if class_type supports POST operation object_ = json.loads(request.data.decode('utf-8')) obj_type = getType(class_type, "POST") # Load new object and type if validObject(object_): if object_["@type"] == obj_type: try: # Update the right ID if the object is valid and matches # type of Item object_id = crud.update(object_=object_, id_=id_, type_=object_["@type"], session=get_session(), api_name=get_api_name()) headers_ = [{ "Location": get_hydrus_server_url() + get_api_name() + "/" + type_ + "/" + str(object_id) }] response = { "message": "Object with ID %s successfully updated" % (object_id) } return set_response_headers(jsonify(response), headers=headers_) except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) abort(405)
def delete(self, type_): """Delete a non Collection class item.""" if get_authentication(): if request.authorization is None: return failed_authentication() else: auth = check_authorization(request, get_session()) if auth is False: return failed_authentication() if checkEndpoint("DELETE", type_): # No Delete Operation for collections if type_ in get_doc( ).parsed_classes and type_ + "Collection" not in get_doc( ).collections: try: crud.delete_single(type_, session=get_session()) response = {"message": "Object successfully deleted"} return set_response_headers(jsonify(response)) except Exception as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) abort(405)