Exemple #1
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
Exemple #2
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)