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