예제 #1
0
    def add_path(self, path=None, operations=None, **kwargs):
        """Add a new path object to the spec.

        https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#pathsObject

        :param str|None path: URL path component
        :param dict|None operations: describes the http methods and options for `path`
        :param dict kwargs: parameters used by any path helpers see :meth:`register_path_helper`
        """
        def normalize_path(path):
            if path and 'basePath' in self.options:
                pattern = '^{0}'.format(re.escape(self.options['basePath']))
                path = re.sub(pattern, '', path)
            return path

        path = normalize_path(path)
        operations = operations or OrderedDict()

        # Execute path helpers
        for plugin in self.plugins:
            try:
                ret = plugin.path_helper(path=path,
                                         operations=operations,
                                         **kwargs)
            except PluginMethodNotImplementedError:
                continue
            if ret is not None:
                path = normalize_path(ret)
        if not path:
            raise APISpecError('Path template is not specified')

        # Execute operation helpers
        for plugin in self.plugins:
            try:
                plugin.operation_helper(path=path,
                                        operations=operations,
                                        **kwargs)
            except PluginMethodNotImplementedError:
                continue

        # Execute response helpers
        # TODO: cache response helpers output for each (method, status_code) couple
        for method, operation in iteritems(operations):
            if method in VALID_METHODS and 'responses' in operation:
                for status_code, response in iteritems(operation['responses']):
                    for plugin in self.plugins:
                        try:
                            response.update(
                                plugin.response_helper(method, status_code, **
                                                       kwargs) or {})
                        except PluginMethodNotImplementedError:
                            continue

        clean_operations(operations, self.openapi_version.major)

        self._paths.setdefault(path, operations).update(operations)
예제 #2
0
def path_from_view(spec, app, view, **kwargs):
    """Path helper that allows passing a Chalice view function."""
    kwarg_ops = kwargs.get('operations')
    kwarg_ops = set() if not kwarg_ops else set(kwarg_ops)

    uri, methods = _route_for_view(app,
                                   view,
                                   path=kwargs.get('path', Path()),
                                   operations=kwarg_ops)
    operations = load_operations_from_docstring(view.__doc__)
    if not operations:
        operations = {}

    # check that the operations in the docstring match those of the actual route decorator
    path = Path(path=uri,
                operations={
                    method: op
                    for method, op in iteritems(operations)
                    if method in methods
                })

    # add methods from route decorator that were not in docstring
    for op in methods:
        path.operations.setdefault(op, {})

    return path
예제 #3
0
파일: openapi.py 프로젝트: mkipcak/apispec
    def fields2jsonschema(self, fields, ordered=False, partial=None):
        """Return the JSON Schema Object given a mapping between field names and
        :class:`Field <marshmallow.Field>` objects.

        :param dict fields: A dictionary of field name field object pairs
        :param bool ordered: Whether to preserve the order in which fields were declared
        :param bool|tuple partial: Whether to override a field's required flag.
            If `True` no fields will be set as required. If an iterable fields
            in the iterable will not be marked as required.
        :rtype: dict, a JSON Schema Object
        """
        jsonschema = {"type": "object", "properties": OrderedDict() if ordered else {}}

        for field_name, field_obj in iteritems(fields):
            observed_field_name = self._observed_name(field_obj, field_name)
            property = self.field2property(field_obj)
            jsonschema["properties"][observed_field_name] = property

            if field_obj.required:
                if not partial or (
                    is_collection(partial) and field_name not in partial
                ):
                    jsonschema.setdefault("required", []).append(observed_field_name)

        if "required" in jsonschema:
            jsonschema["required"].sort()

        return jsonschema
예제 #4
0
파일: openapi.py 프로젝트: mkipcak/apispec
    def metadata2properties(self, field):
        """Return a dictionary of properties extracted from field Metadata

        Will include field metadata that are valid properties of `OpenAPI schema
        objects
        <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject>`_
        (e.g. “description”, “enum”, “example”).

        In addition, `specification extensions
        <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions>`_
        are supported.  Prefix `x_` to the desired extension when passing the
        keyword argument to the field constructor. apispec will convert `x_` to
        `x-` to comply with OpenAPI.

        :param Field field: A marshmallow field.
        :rtype: dict
        """
        # Dasherize metadata that starts with x_
        metadata = {
            key.replace("_", "-") if key.startswith("x_") else key: value
            for key, value in iteritems(field.metadata)
        }

        # Avoid validation error with "Additional properties not allowed"
        ret = {
            key: value
            for key, value in metadata.items()
            if key in _VALID_PROPERTIES or key.startswith(_VALID_PREFIX)
        }
        return ret
예제 #5
0
파일: core.py 프로젝트: YasmimLira/Python
def clean_operations(operations, openapi_major_version):
    """Ensure that all parameters with "in" equal to "path" are also required
    as required by the OpenAPI specification, as well as normalizing any
    references to global parameters. Also checks for invalid HTTP methods.

    See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject.

    :param dict operations: Dict mapping status codes to operations
    :param int openapi_major_version: The major version of the OpenAPI standard
        to use. Supported values are 2 and 3.
    """
    invalid = {
        key
        for key in set(iterkeys(operations)) -
        set(VALID_METHODS[openapi_major_version]) if not key.startswith("x-")
    }
    if invalid:
        raise APISpecError("One or more HTTP methods are invalid: {}".format(
            ", ".join(invalid)))

    def get_ref(obj_type, obj, openapi_major_version):
        """Return object or rererence

        If obj is a dict, it is assumed to be a complete description and it is returned as is.
        Otherwise, it is assumed to be a reference name as string and the corresponding $ref
        string is returned.

        :param str obj_type: "parameter" or "response"
        :param dict|str obj: parameter or response in dict form or as ref_id string
        :param int openapi_major_version: The major version of the OpenAPI standard
        """
        if isinstance(obj, dict):
            return obj
        return build_reference(obj_type, openapi_major_version, obj)

    for operation in (operations or {}).values():
        if "parameters" in operation:
            parameters = operation["parameters"]
            for parameter in parameters:
                if (isinstance(parameter, dict) and "in" in parameter
                        and parameter["in"] == "path"):
                    parameter["required"] = True
            operation["parameters"] = [
                get_ref("parameter", p, openapi_major_version)
                for p in parameters
            ]
        if "responses" in operation:
            responses = OrderedDict()
            for code, response in iteritems(operation["responses"]):
                try:
                    code = int(code)  # handles IntEnums like http.HTTPStatus
                except (TypeError, ValueError):
                    if openapi_major_version < 3:
                        warnings.warn(
                            "Non-integer code not allowed in OpenAPI < 3")

                responses[str(code)] = get_ref("response", response,
                                               openapi_major_version)
            operation["responses"] = responses
예제 #6
0
def load_operations_from_docstring(docstring):
    """Return a dictionary of OpenAPI operations parsed from a
    a docstring.
    """
    doc_data = load_yaml_from_docstring(docstring)
    return {
        key: val for key, val in iteritems(doc_data)
        if key in PATH_KEYS or key.startswith('x-')
    }
예제 #7
0
파일: core.py 프로젝트: YasmimLira/Python
 def to_dict(self):
     subsections = {
         "schema": self._schemas,
         "parameter": self._parameters,
         "response": self._responses,
         "security_scheme": self._security_schemes,
     }
     return {
         COMPONENT_SUBSECTIONS[self.openapi_version.major][k]: v
         for k, v in iteritems(subsections) if v != {}
     }
예제 #8
0
def views():
    ret = ['<ul>']
    view_funcs = APP.view_functions
    for ep, view_func in iteritems(view_funcs):
        ret.append('<li>View Function = ' + view_func.__name__ + ', ep = ' +
                   ep + '<ul>')
        for rule in APP.url_map._rules_by_endpoint[ep]:
            ret.append('<li>' + rule.rule + '</li>')
        ret.append('</ul></li>')
    ret.append('</ul>')
    return "".join(ret)
예제 #9
0
def _route_for_view(current_app, view, path=Path(), operations=set()):
    view_funcs = current_app.routes

    for uri, endpoint in iteritems(view_funcs):
        methods = set()
        for method, route_entry in iteritems(endpoint):
            method = method.lower()
            if route_entry.view_function == view and (not operations
                                                      or method in operations):
                if path.path and not path.path == uri:
                    break
                else:
                    methods.add(method)
        else:
            if methods:
                return uri, methods

    raise APISpecError(
        'Could not find endpoint for view {0} and path {1}'.format(
            view, getattr(path, 'path', None)))
예제 #10
0
파일: utils.py 프로젝트: rmoorman/apispec
def load_operations_from_docstring(docstring):
    """Return a dictionary of Swagger operations parsed from a
    a docstring.
    """
    doc_data = load_yaml_from_docstring(docstring)
    if doc_data:
        return {
            key: val
            for key, val in iteritems(doc_data) if key in PATH_KEYS
        }
    else:
        return None
예제 #11
0
def _rule_for_view(view):
    view_funcs = current_app.view_functions
    endpoint = None
    for ep, view_func in iteritems(view_funcs):
        if view_func == view:
            endpoint = ep
    if not endpoint:
        raise APISpecError('Could not find endpoint for view {0}'.format(view))

    # WARNING: Assume 1 rule per view function for now
    rule = current_app.url_map._rules_by_endpoint[endpoint][0]
    return rule
예제 #12
0
    def _rule_for_view(view, app=None):
        if app is None:
            app = current_app

        view_funcs = app.view_functions
        endpoint = None
        for ept, view_func in iteritems(view_funcs):
            if view_func == view:
                endpoint = ept
        if not endpoint:
            raise APISpecError(
                "Could not find endpoint for view {}".format(view))

        # WARNING: Assume 1 rule per view function for now
        rule = app.url_map._rules_by_endpoint[endpoint][0]
        return rule
예제 #13
0
파일: openapi.py 프로젝트: mkipcak/apispec
    def fields2parameters(self, fields, default_in="body"):
        """Return an array of OpenAPI parameters given a mapping between field names and
        :class:`Field <marshmallow.Field>` objects. If `default_in` is "body", then return an array
        of a single parameter; else return an array of a parameter for each included field in
        the :class:`Schema <marshmallow.Schema>`.

        In OpenAPI3, only "query", "header", "path" or "cookie" are allowed for the location
        of parameters. In OpenAPI 3, "requestBody" is used when fields are in the body.

        This function always returns a list, with a parameter
        for each included field in the :class:`Schema <marshmallow.Schema>`.

        https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject
        """
        parameters = []
        body_param = None
        for field_name, field_obj in iteritems(fields):
            if field_obj.dump_only:
                continue
            param = self.field2parameter(
                field_obj,
                name=self._observed_name(field_obj, field_name),
                default_in=default_in,
            )
            if (
                self.openapi_version.major < 3
                and param["in"] == "body"
                and body_param is not None
            ):
                body_param["schema"]["properties"].update(param["schema"]["properties"])
                required_fields = param["schema"].get("required", [])
                if required_fields:
                    body_param["schema"].setdefault("required", []).extend(
                        required_fields
                    )
            else:
                if self.openapi_version.major < 3 and param["in"] == "body":
                    body_param = param
                parameters.append(param)
        return parameters
예제 #14
0
def clean_operations(operations, openapi_major_version):
    """Ensure that all parameters with "in" equal to "path" are also required
    as required by the OpenAPI specification, as well as normalizing any
    references to global parameters. Also checks for invalid HTTP methods.

    See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject.

    :param dict operations: Dict mapping status codes to operations
    :param int openapi_major_version: The major version of the OpenAPI standard
        to use. Supported values are 2 and 3.
    """
    invalid = {
        key
        for key in set(iterkeys(operations)) -
        set(VALID_METHODS[openapi_major_version]) if not key.startswith("x-")
    }
    if invalid:
        raise APISpecError("One or more HTTP methods are invalid: {}".format(
            ", ".join(invalid)))

    for operation in (operations or {}).values():
        if "parameters" in operation:
            operation["parameters"] = clean_parameters(operation["parameters"],
                                                       openapi_major_version)
        if "responses" in operation:
            responses = OrderedDict()
            for code, response in iteritems(operation["responses"]):
                try:
                    code = int(code)  # handles IntEnums like http.HTTPStatus
                except (TypeError, ValueError):
                    if openapi_major_version < 3:
                        warnings.warn(
                            "Non-integer code not allowed in OpenAPI < 3")

                responses[str(code)] = get_ref("response", response,
                                               openapi_major_version)
            operation["responses"] = responses
예제 #15
0
    def add_path(self, path=None, operations=None, **kwargs):
        """Add a new path object to the spec.

        https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#pathsObject

        :param str|Path|None path: URL Path component or Path instance
        :param dict|None operations: describes the http methods and options for `path`
        :param dict kwargs: parameters used by any path helpers see :meth:`register_path_helper`
        """
        def normalize_path(path):
            if path and 'basePath' in self.options:
                pattern = '^{0}'.format(re.escape(self.options['basePath']))
                path = re.sub(pattern, '', path)
            return path

        if isinstance(path, Path):
            path.path = normalize_path(path.path)
            if operations:
                path.operations.update(operations)
        else:
            path = Path(
                path=normalize_path(path),
                operations=operations,
                openapi_version=self.openapi_version,
            )

        # Execute path helpers
        for plugin in self.plugins:
            try:
                ret = plugin.path_helper(path=path,
                                         operations=path.operations,
                                         **kwargs)
            except PluginMethodNotImplementedError:
                continue
            if isinstance(ret, Path):
                ret.path = normalize_path(ret.path)
                path.update(ret)
        # Deprecated interface
        for func in self._path_helpers:
            try:
                ret = func(self,
                           path=path,
                           operations=path.operations,
                           **kwargs)
            except TypeError:
                continue
            if isinstance(ret, Path):
                ret.path = normalize_path(ret.path)
                path.update(ret)
        if not path.path:
            raise APISpecError('Path template is not specified')

        # Execute operation helpers
        for plugin in self.plugins:
            try:
                plugin.operation_helper(path=path,
                                        operations=path.operations,
                                        **kwargs)
            except PluginMethodNotImplementedError:
                continue
        # Deprecated interface
        for func in self._operation_helpers:
            func(self, path=path, operations=path.operations, **kwargs)

        # Execute response helpers
        # TODO: cache response helpers output for each (method, status_code) couple
        for method, operation in iteritems(path.operations):
            if method in VALID_METHODS and 'responses' in operation:
                for status_code, response in iteritems(operation['responses']):
                    for plugin in self.plugins:
                        try:
                            response.update(
                                plugin.response_helper(method, status_code, **
                                                       kwargs) or {})
                        except PluginMethodNotImplementedError:
                            continue
        # Deprecated interface
        # Rule is that method + http status exist in both operations and helpers
        methods = set(iterkeys(path.operations)) & set(
            iterkeys(self._response_helpers))
        for method in methods:
            responses = path.operations[method]['responses']
            statuses = set(iterkeys(responses)) & set(
                iterkeys(self._response_helpers[method]))
            for status_code in statuses:
                for func in self._response_helpers[method][status_code]:
                    responses[status_code].update(func(self, **kwargs), )

        self._paths.setdefault(path.path,
                               path.operations).update(path.operations)
예제 #16
0
def clean_operations(operations, openapi_major_version):
    """Ensure that all parameters with "in" equal to "path" are also required
    as required by the OpenAPI specification, as well as normalizing any
    references to global parameters. Also checks for invalid HTTP methods.

    See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject.

    :param dict operations: Dict mapping status codes to operations
    :param int openapi_major_version: The major version of the OpenAPI standard
        to use. Supported values are 2 and 3.
    """
    invalid = {
        key
        for key in set(iterkeys(operations)) - set(VALID_METHODS)
        if not key.startswith('x-')
    }
    if invalid:
        raise APISpecError(
            'One or more HTTP methods are invalid: {0}'.format(
                ', '.join(invalid)), )

    def get_ref(obj_type, obj, openapi_major_version):
        """Return object or rererence

        If obj is a dict, it is assumed to be a complete description and it is returned as is.
        Otherwise, it is assumed to be a reference name as string and the corresponding $ref
        string is returned.

        :param str obj_type: 'parameter' or 'response'
        :param dict|str obj: parameter or response in dict form or as ref_id string
        :param int openapi_major_version: The major version of the OpenAPI standard
        """
        if isinstance(obj, dict):
            return obj

        ref_paths = {
            'parameter': {
                2: 'parameters',
                3: 'components/parameters',
            },
            'response': {
                2: 'responses',
                3: 'components/responses',
            },
        }
        ref_path = ref_paths[obj_type][openapi_major_version]
        return {'$ref': '#/{0}/{1}'.format(ref_path, obj)}

    for operation in (operations or {}).values():
        if 'parameters' in operation:
            parameters = operation['parameters']
            for parameter in parameters:
                if (isinstance(parameter, dict) and 'in' in parameter
                        and parameter['in'] == 'path'):
                    parameter['required'] = True
            operation['parameters'] = [
                get_ref('parameter', p, openapi_major_version)
                for p in parameters
            ]
        if 'responses' in operation:
            for code, response in iteritems(operation['responses']):
                operation['responses'][code] = get_ref('response', response,
                                                       openapi_major_version)
예제 #17
0
def clean_operations(operations, openapi_major_version):
    """Ensure that all parameters with "in" equal to "path" are also required
    as required by the OpenAPI specification, as well as normalizing any
    references to global parameters. Also checks for invalid HTTP methods.

    See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject.

    :param dict operations: Dict mapping status codes to operations
    :param int openapi_major_version: The major version of the OpenAPI standard
        to use. Supported values are 2 and 3.
    """
    invalid = {
        key
        for key in set(iterkeys(operations)) -
        set(VALID_METHODS[openapi_major_version]) if not key.startswith("x-")
    }
    if invalid:
        raise APISpecError("One or more HTTP methods are invalid: {}".format(
            ", ".join(invalid)))

    def get_ref(obj_type, obj, openapi_major_version):
        """Return object or rererence

        If obj is a dict, it is assumed to be a complete description and it is returned as is.
        Otherwise, it is assumed to be a reference name as string and the corresponding $ref
        string is returned.

        :param str obj_type: 'parameter' or 'response'
        :param dict|str obj: parameter or response in dict form or as ref_id string
        :param int openapi_major_version: The major version of the OpenAPI standard
        """
        if isinstance(obj, dict):
            return obj

        ref_paths = {
            "parameter": {
                2: "parameters",
                3: "components/parameters"
            },
            "response": {
                2: "responses",
                3: "components/responses"
            },
        }
        ref_path = ref_paths[obj_type][openapi_major_version]
        return {"$ref": "#/{}/{}".format(ref_path, obj)}

    for operation in (operations or {}).values():
        if "parameters" in operation:
            parameters = operation["parameters"]
            for parameter in parameters:
                if (isinstance(parameter, dict) and "in" in parameter
                        and parameter["in"] == "path"):
                    parameter["required"] = True
            operation["parameters"] = [
                get_ref("parameter", p, openapi_major_version)
                for p in parameters
            ]
        if "responses" in operation:
            for code, response in iteritems(operation["responses"]):
                operation["responses"][code] = get_ref("response", response,
                                                       openapi_major_version)