Пример #1
0
def code_samples(endpoint, header_params, path_params,
                 query_params) -> List[CodeSample]:
    """Create a list of rendered code sample Objects

    These are not specified by OpenAPI but are specific to ReDoc."""
    env = _jinja_environment()

    return [{
        'label':
        example.label,
        'lang':
        example.lang,
        'source':
        env.get_template(example.label).render(
            hostname='localhost',
            site=omd_site(),
            username='******',
            password='******',
            endpoint=endpoint,
            path_params=to_openapi(path_params, 'path'),
            query_params=to_openapi(query_params, 'query'),
            header_params=to_openapi(header_params, 'header'),
            request_endpoint=endpoint.path,
            request_method=endpoint.method,
            request_schema=_get_schema(endpoint.request_schema),
            request_schema_multiple=_schema_is_multiple(
                endpoint.request_schema),
        ).strip(),
    } for example in CODE_EXAMPLES]
Пример #2
0
def code_samples(
    endpoint,
    header_params,
    path_params,
    query_params,
) -> List[CodeSample]:
    """Create a list of rendered code sample Objects

    These are not specified by OpenAPI but are specific to ReDoc.

    Examples:

        >>> class Endpoint:
        ...     path = 'foo'
        ...     method = 'get'
        ...     content_type = 'application/json'
        ...     request_schema = _get_schema('CreateHost')
        ...     does_redirects = False

        >>> _endpoint = Endpoint()
        >>> import os
        >>> from unittest import mock
        >>> with mock.patch.dict(os.environ, {"OMD_SITE": "NO_SITE"}):
        ...     samples = code_samples(_endpoint, [], [], [])

        >>> assert len(samples)

    """
    env = _jinja_environment()

    return [{
        'label':
        example.label,
        'lang':
        example.lang,
        'source':
        env.get_template(example.label).render(
            hostname='localhost',
            site=omd_site(),
            username='******',
            password='******',
            endpoint=endpoint,
            path_params=to_openapi(path_params, 'path'),
            query_params=to_openapi(query_params, 'query'),
            header_params=to_openapi(header_params, 'header'),
            request_endpoint=endpoint.path,
            request_method=endpoint.method,
            request_schema=_get_schema(endpoint.request_schema),
            request_schema_multiple=_schema_is_multiple(
                endpoint.request_schema),
        ).strip(),
    } for example in CODE_EXAMPLES]
Пример #3
0
def code_samples(
    endpoint,
    header_params,
    path_params,
    query_params,
) -> List[CodeSample]:
    """Create a list of rendered code sample Objects

    These are not specified by OpenAPI but are specific to ReDoc.

    Examples:

        >>> class Endpoint:  # doctest: +SKIP
        ...     path = 'foo'
        ...     method = 'get'
        ...     content_type = 'application/json'
        ...     request_schema = _get_schema('CreateHost')
        ...     does_redirects = False

        >>> endpoint = Endpoint()  # doctest: +SKIP
        >>> samples = code_samples(endpoint, [], [], [])  # doctest: +SKIP


    """
    env = _jinja_environment()

    return [
        {
            "label": example.label,
            "lang": example.lang,
            "source": env.get_template(example.label)
            .render(
                hostname="localhost",
                site=omd_site(),
                username="******",
                password="******",
                endpoint=endpoint,
                path_params=to_openapi(path_params, "path"),
                query_params=to_openapi(query_params, "query"),
                header_params=to_openapi(header_params, "header"),
                request_endpoint=endpoint.path,
                request_method=endpoint.method,
                request_schema=_get_schema(endpoint.request_schema),
                request_schema_multiple=_schema_is_multiple(endpoint.request_schema),
            )
            .strip(),
        }
        for example in CODE_EXAMPLES
    ]
Пример #4
0
 def default_path(self):
     replace = {}
     if self.path_params is not None:
         parameters = to_openapi(self.path_params, 'path')
         for param in parameters:
             name = param['name']
             replace[name] = f"<string:{name}>"
     try:
         path = self.path.format(**replace)
     except KeyError:
         raise AttributeError(f"Endpoint {self.path} has unspecified path parameters. "
                              f"Specified: {replace}")
     return path
Пример #5
0
    def to_operation_dict(self) -> OperationSpecType:
        """Generate the openapi spec part of this endpoint.

        The result needs to be added to the `apispec` instance manually.
        """
        assert self.func is not None, "This object must be used in a decorator environment."
        assert self.operation_id is not None, "This object must be used in a decorator environment."

        module_obj = import_string(self.func.__module__)

        response_headers: Dict[str, OpenAPIParameter] = {}
        content_type_header = to_openapi([CONTENT_TYPE], "header")[0]
        del content_type_header["in"]
        response_headers[content_type_header.pop("name")] = content_type_header

        if self.etag in ("output", "both"):
            etag_header = to_openapi([ETAG_HEADER_PARAM], "header")[0]
            del etag_header["in"]
            response_headers[etag_header.pop("name")] = etag_header

        responses: ResponseType = {}

        responses["406"] = self._path_item(
            406, "The requests accept headers can not be satisfied.")

        if 401 in self._expected_status_codes:
            responses["401"] = self._path_item(
                401, "The user is not authorized to do this request.")

        if self.tag_group == "Setup":
            responses["403"] = self._path_item(
                403, "Configuration via WATO is disabled.")
        if self.tag_group == "Checkmk Internal" and 403 in self._expected_status_codes:
            responses["403"] = self._path_item(
                403,
                "You have insufficient permissions for this operation.",
            )

        if 404 in self._expected_status_codes:
            responses["404"] = self._path_item(
                404, "The requested object has not been found.")

        if 422 in self._expected_status_codes:
            responses["422"] = self._path_item(
                422, "The request could not be processed.")

        if 423 in self._expected_status_codes:
            responses["423"] = self._path_item(
                423, "This resource is currently locked.")

        if 405 in self._expected_status_codes:
            responses["405"] = self._path_item(
                405,
                "Method not allowed: This request is only allowed with other HTTP methods"
            )

        if 409 in self._expected_status_codes:
            responses["409"] = self._path_item(
                409,
                "The request is in conflict with the stored resource.",
            )

        if 415 in self._expected_status_codes:
            responses["415"] = self._path_item(
                415, "The submitted content-type is not supported.")

        if 302 in self._expected_status_codes:
            responses["302"] = self._path_item(
                302,
                "Either the resource has moved or has not yet completed. Please see this "
                "resource for further information.",
            )

        if 400 in self._expected_status_codes:
            responses["400"] = self._path_item(
                400, "Parameter or validation failure.")

        # We don't(!) support any endpoint without an output schema.
        # Just define one!
        if 200 in self._expected_status_codes:
            if self.response_schema:
                content: ContentObject
                content = {self.content_type: {"schema": self.response_schema}}
            elif self.content_type == "application/octet-stream" or self.content_type.startswith(
                    "image/"):
                content = {
                    self.content_type: {
                        "schema": {
                            "type": "string",
                            "format": "binary",
                        }
                    }
                }
            else:
                raise ValueError(
                    f"Unknown content-type: {self.content_type} Please add condition."
                )
            responses["200"] = self._path_item(
                200,
                "The operation was done successfully.",
                content=content,
                headers=response_headers,
            )

        if 204 in self._expected_status_codes:
            responses["204"] = self._path_item(
                204, "Operation done successfully. No further output.")

        if 412 in self._expected_status_codes:
            responses["412"] = self._path_item(
                412,
                "The value of the If-Match header doesn't match the object's ETag.",
            )

        if 428 in self._expected_status_codes:
            responses["428"] = self._path_item(
                428, "The required If-Match header is missing.")

        docstring_name = _docstring_name(module_obj.__doc__)
        tag_obj: OpenAPITag = {
            "name": docstring_name,
            "x-displayName": docstring_name,
        }
        docstring_desc = _docstring_description(module_obj.__doc__)
        if docstring_desc:
            tag_obj["description"] = docstring_desc
        _add_tag(tag_obj, tag_group=self.tag_group)

        operation_spec: OperationSpecType = {
            "operationId": self.operation_id,
            "tags": [docstring_name],
            "description": "",
        }

        header_params: List[RawParameter] = []
        query_params: Sequence[RawParameter] = (
            self.query_params if self.query_params is not None else [])
        path_params: Sequence[RawParameter] = (
            self.path_params if self.path_params is not None else [])

        if active_config.rest_api_etag_locking and self.etag in ("input",
                                                                 "both"):
            header_params.append(ETAG_IF_MATCH_HEADER)

        if self.request_schema:
            header_params.append(CONTENT_TYPE)

        # While we define the parameters separately to be able to use them for validation, the
        # OpenAPI spec expects them to be listed in on place, so here we bunch them together.
        operation_spec["parameters"] = coalesce_schemas([
            ("header", header_params),
            ("query", query_params),
            ("path", path_params),
        ])

        operation_spec["responses"] = responses

        if self.request_schema is not None:
            operation_spec["requestBody"] = {
                "required": True,
                "content": {
                    "application/json": {
                        "schema": self.request_schema,
                    }
                },
            }

        operation_spec["x-codeSamples"] = code_samples(
            self,
            header_params=header_params,
            path_params=path_params,
            query_params=query_params,
        )

        # If we don't have any parameters we remove the empty list, so the spec will not have it.
        if not operation_spec["parameters"]:
            del operation_spec["parameters"]

        try:
            docstring_name = _docstring_name(self.func.__doc__)
        except ValueError as exc:
            raise ValueError(
                f"Function {module_obj.__name__}:{self.func.__name__} has no docstring."
            ) from exc

        if docstring_name:
            operation_spec["summary"] = docstring_name
        else:
            raise RuntimeError(
                f"Please put a docstring onto {self.operation_id}")

        docstring_desc = _docstring_description(self.func.__doc__)
        if docstring_desc:
            operation_spec["description"] = docstring_desc

        if self.permissions_required is not None:
            # Check that all the names are known to the system.
            for perm in self.permissions_required.iter_perms():
                if perm not in permission_registry:
                    # NOTE:
                    #   See rest_api.py. dynamic_permission() have to be loaded before request
                    #   for this to work reliably.
                    raise RuntimeError(
                        f'Permission "{perm}" is not registered in the permission_registry.'
                    )

            # Write permission documentation in openapi spec.
            if description := _permission_descriptions(
                    self.permissions_required, self.permissions_description):
                operation_spec.setdefault("description", "")
                if not operation_spec["description"]:
                    operation_spec["description"] += "\n\n"
                operation_spec["description"] += description
Пример #6
0
            plugins.ValueTypedDictMarshmallowPlugin(),
            apispec_oneofschema.MarshmallowPlugin(),
        ],
        **options,
    )


SPEC = make_spec(options=OPTIONS)
for sec_scheme_name, sec_scheme_spec in SECURITY_SCHEMES.items():
    SPEC.components.security_scheme(sec_scheme_name, sec_scheme_spec)

# All the supported response headers by the spec.

# response_headers = {
#     'Allow',
#     'Cache-Control',
#     'Last-Modified',
#     'Warning',
#     'Content-Type',
# }
for header_name, field in ACCEPT_HEADER.items():
    SPEC.components.parameter(
        header_name,
        'header',
        to_openapi([{
            header_name: field
        }], 'header')[0],
    )

ErrorType = Literal['ignore', 'raise']
Пример #7
0
    def to_operation_dict(self) -> OperationSpecType:
        """Generate the openapi spec part of this endpoint.

        The result needs to be added to the `apispec` instance manually.
        """
        assert self.func is not None, "This object must be used in a decorator environment."
        assert self.operation_id is not None, "This object must be used in a decorator environment."

        module_obj = import_string(self.func.__module__)

        response_headers: Dict[str, OpenAPIParameter] = {}
        content_type_header = to_openapi([CONTENT_TYPE], 'header')[0]
        del content_type_header['in']
        response_headers[content_type_header.pop('name')] = content_type_header

        if self.etag in ('output', 'both'):
            etag_header = to_openapi([ETAG_HEADER_PARAM], 'header')[0]
            del etag_header['in']
            response_headers[etag_header.pop('name')] = etag_header

        responses: ResponseType = {}

        if 404 in self._expected_status_codes:
            responses['404'] = self._path_item(
                404, 'The requested object has not been found.')

        if 422 in self._expected_status_codes:
            responses['422'] = self._path_item(
                422, 'The request could not be processed.')

        if 405 in self._expected_status_codes:
            responses['405'] = _path_item(
                405, 'Method not allowed: This request is only allowed '
                'with other HTTP methods')

        if 409 in self._expected_status_codes:
            responses['409'] = self._path_item(
                409,
                'The request is in conflict with the stored resource',
            )

        if 415 in self._expected_status_codes:
            responses['415'] = self._path_item(
                415, 'The submitted content-type is not supported.')

        if 302 in self._expected_status_codes:
            responses['302'] = self._path_item(
                302,
                'Either the resource has moved or has not yet completed. Please see this '
                'resource for further information.',
            )

        if 400 in self._expected_status_codes:
            responses['400'] = self._path_item(
                400, 'Parameter or validation failure')

        # We don't(!) support any endpoint without an output schema.
        # Just define one!
        if 200 in self._expected_status_codes:
            responses['200'] = self._path_item(
                200,
                'The operation was done successfully.',
                content={self.content_type: {
                    'schema': self.response_schema
                }},
                headers=response_headers,
            )

        if 204 in self._expected_status_codes:
            responses['204'] = self._path_item(
                204, 'Operation done successfully. No further output.')

        if 412 in self._expected_status_codes:
            responses['412'] = self._path_item(
                412,
                "The value of the If-Match header doesn't match the object's ETag.",
            )

        if 428 in self._expected_status_codes:
            responses['428'] = self._path_item(
                428, 'The required If-Match header is missing.')

        docstring_name = _docstring_name(module_obj.__doc__)
        tag_obj: OpenAPITag = {
            'name': docstring_name,
            'x-displayName': docstring_name,
        }
        docstring_desc = _docstring_description(module_obj.__doc__)
        if docstring_desc:
            tag_obj['description'] = docstring_desc
        _add_tag(tag_obj, tag_group=self.tag_group)

        operation_spec: OperationSpecType = {
            'operationId': self.operation_id,
            'tags': [docstring_name],
            'description': '',
        }

        header_params: List[RawParameter] = []
        query_params: Sequence[
            RawParameter] = self.query_params if self.query_params is not None else []
        path_params: Sequence[
            RawParameter] = self.path_params if self.path_params is not None else []

        if self.etag in ('input', 'both'):
            header_params.append(ETAG_IF_MATCH_HEADER)

        if self.request_schema:
            header_params.append(CONTENT_TYPE)

        # While we define the parameters separately to be able to use them for validation, the
        # OpenAPI spec expects them to be listed in on place, so here we bunch them together.
        operation_spec['parameters'] = coalesce_schemas([
            ('header', header_params),
            ('query', query_params),
            ('path', path_params),
        ])

        operation_spec['responses'] = responses

        if self.request_schema is not None:
            operation_spec['requestBody'] = {
                'required': True,
                'content': {
                    'application/json': {
                        'schema': self.request_schema,
                    }
                }
            }

        operation_spec['x-codeSamples'] = code_samples(
            self,
            header_params=header_params,
            path_params=path_params,
            query_params=query_params,
        )

        # If we don't have any parameters we remove the empty list, so the spec will not have it.
        if not operation_spec['parameters']:
            del operation_spec['parameters']

        docstring_name = _docstring_name(self.func.__doc__)
        if docstring_name:
            operation_spec['summary'] = docstring_name
        else:
            raise RuntimeError(
                f"Please put a docstring onto {self.operation_id}")
        docstring_desc = _docstring_description(self.func.__doc__)
        if docstring_desc:
            operation_spec['description'] = docstring_desc

        apispec.utils.deepupdate(operation_spec, self.options)

        return {self.method: operation_spec}  # type: ignore[misc]
Пример #8
0
    def to_operation_dict(self) -> OperationSpecType:
        """Generate the openapi spec part of this endpoint.

        The result needs to be added to the `apispec` instance manually.
        """
        assert self.func is not None, "This object must be used in a decorator environment."
        assert self.operation_id is not None, "This object must be used in a decorator environment."

        module_obj = import_string(self.func.__module__)

        response_headers: Dict[str, OpenAPIParameter] = {}
        content_type_header = to_openapi([CONTENT_TYPE], "header")[0]
        del content_type_header["in"]
        response_headers[content_type_header.pop("name")] = content_type_header

        if self.etag in ("output", "both"):
            etag_header = to_openapi([ETAG_HEADER_PARAM], "header")[0]
            del etag_header["in"]
            response_headers[etag_header.pop("name")] = etag_header

        responses: ResponseType = {}

        if self.tag_group == "Setup":
            responses["403"] = self._path_item(
                403, "Configuration via WATO is disabled")

        if 404 in self._expected_status_codes:
            responses["404"] = self._path_item(
                404, "The requested object has not been found.")

        if 422 in self._expected_status_codes:
            responses["422"] = self._path_item(
                422, "The request could not be processed.")

        if 405 in self._expected_status_codes:
            responses["405"] = _path_item(
                405, "Method not allowed: This request is only allowed "
                "with other HTTP methods")

        if 409 in self._expected_status_codes:
            responses["409"] = self._path_item(
                409,
                "The request is in conflict with the stored resource.",
            )

        if 415 in self._expected_status_codes:
            responses["415"] = self._path_item(
                415, "The submitted content-type is not supported.")

        if 302 in self._expected_status_codes:
            responses["302"] = self._path_item(
                302,
                "Either the resource has moved or has not yet completed. Please see this "
                "resource for further information.",
            )

        if 400 in self._expected_status_codes:
            responses["400"] = self._path_item(
                400, "Parameter or validation failure.")

        # We don't(!) support any endpoint without an output schema.
        # Just define one!
        if 200 in self._expected_status_codes:
            if self.response_schema:
                content: ContentObject
                content = {self.content_type: {"schema": self.response_schema}}
            elif self.content_type == "application/octet-stream" or self.content_type.startswith(
                    "image/"):
                content = {
                    self.content_type: {
                        "schema": {
                            "type": "string",
                            "format": "binary",
                        }
                    }
                }
            else:
                raise ValueError(
                    f"Unknown content-type: {self.content_type} Please add condition."
                )
            responses["200"] = self._path_item(
                200,
                "The operation was done successfully.",
                content=content,
                headers=response_headers,
            )

        if 204 in self._expected_status_codes:
            responses["204"] = self._path_item(
                204, "Operation done successfully. No further output.")

        if 412 in self._expected_status_codes:
            responses["412"] = self._path_item(
                412,
                "The value of the If-Match header doesn't match the object's ETag.",
            )

        if 428 in self._expected_status_codes:
            responses["428"] = self._path_item(
                428, "The required If-Match header is missing.")

        docstring_name = _docstring_name(module_obj.__doc__)
        tag_obj: OpenAPITag = {
            "name": docstring_name,
            "x-displayName": docstring_name,
        }
        docstring_desc = _docstring_description(module_obj.__doc__)
        if docstring_desc:
            tag_obj["description"] = docstring_desc
        _add_tag(tag_obj, tag_group=self.tag_group)

        operation_spec: OperationSpecType = {
            "operationId": self.operation_id,
            "tags": [docstring_name],
            "description": "",
        }

        header_params: List[RawParameter] = []
        query_params: Sequence[RawParameter] = (
            self.query_params if self.query_params is not None else [])
        path_params: Sequence[RawParameter] = (
            self.path_params if self.path_params is not None else [])

        if config.rest_api_etag_locking and self.etag in ("input", "both"):
            header_params.append(ETAG_IF_MATCH_HEADER)

        if self.request_schema:
            header_params.append(CONTENT_TYPE)

        # While we define the parameters separately to be able to use them for validation, the
        # OpenAPI spec expects them to be listed in on place, so here we bunch them together.
        operation_spec["parameters"] = coalesce_schemas([
            ("header", header_params),
            ("query", query_params),
            ("path", path_params),
        ])

        operation_spec["responses"] = responses

        if self.request_schema is not None:
            operation_spec["requestBody"] = {
                "required": True,
                "content": {
                    "application/json": {
                        "schema": self.request_schema,
                    }
                },
            }

        operation_spec["x-codeSamples"] = code_samples(
            self,
            header_params=header_params,
            path_params=path_params,
            query_params=query_params,
        )

        # If we don't have any parameters we remove the empty list, so the spec will not have it.
        if not operation_spec["parameters"]:
            del operation_spec["parameters"]

        docstring_name = _docstring_name(self.func.__doc__)
        if docstring_name:
            operation_spec["summary"] = docstring_name
        else:
            raise RuntimeError(
                f"Please put a docstring onto {self.operation_id}")
        docstring_desc = _docstring_description(self.func.__doc__)
        if docstring_desc:
            operation_spec["description"] = docstring_desc

        apispec.utils.deepupdate(operation_spec, self.options)

        return {self.method: operation_spec}  # type: ignore[misc]
Пример #9
0
    def to_operation_dict(self) -> OperationSpecType:
        """Generate the openapi spec part of this endpoint.

        The result needs to be added to the `apispec` instance manually.
        """
        assert self.func is not None, "This object must be used in a decorator environment."
        assert self.operation_id is not None, "This object must be used in a decorator environment."

        module_obj = import_string(self.func.__module__)
        module_name = module_obj.__name__

        headers: Dict[str, OpenAPIParameter] = {}
        if self.etag in ('output', 'both'):
            etag_header = to_openapi([ETAG_HEADER_PARAM], 'header')[0]
            del etag_header['in']
            headers[etag_header.pop('name')] = etag_header

        responses: ResponseType = {}

        # We don't(!) support any endpoint without an output schema.
        # Just define one!
        if self.response_schema is not None:
            responses['200'] = {
                'content': {
                    self.content_type: {
                        'schema': self.response_schema
                    },
                },
                'description': apispec.utils.dedent(self.response_schema.__doc__ or ''),
                'headers': headers,
            }

        if self.will_do_redirects:
            responses['302'] = {
                'description':
                    ('Either the resource has moved or has not yet completed. Please see this '
                     'resource for further information.')
            }

        # Actually, iff you don't want to give out anything, then we don't need a schema.
        if self.output_empty:
            responses['204'] = {
                'description': 'Operation done successfully. No further output.',
                'headers': headers,
            }

        tag_obj: OpenAPITag = {
            'name': module_name,
        }
        docstring_name = _docstring_name(module_obj.__doc__)
        if docstring_name:
            tag_obj['x-displayName'] = docstring_name
        docstring_desc = _docstring_description(module_obj.__doc__)
        if docstring_desc:
            tag_obj['description'] = docstring_desc
        _add_tag(tag_obj, tag_group=self.tag_group)

        operation_spec: OperationSpecType = {
            'operationId': self.operation_id,
            'tags': [module_name],
            'description': '',
            'responses': {
                'default': {
                    'description': 'Any unsuccessful or unexpected result.',
                    'content': {
                        'application/problem+json': {
                            'schema': self.error_schema,
                        }
                    }
                }
            },
        }

        header_params: List[RawParameter] = []
        query_params: Sequence[
            RawParameter] = self.query_params if self.query_params is not None else []
        path_params: Sequence[
            RawParameter] = self.path_params if self.path_params is not None else []

        if self.etag in ('input', 'both'):
            header_params.append(ETAG_IF_MATCH_HEADER)

        operation_spec['parameters'] = coalesce_schemas([
            ('header', header_params),
            ('query', query_params),
            ('path', path_params),
        ])

        operation_spec['responses'].update(responses)

        if self.request_schema is not None:
            operation_spec['requestBody'] = {
                'required': self.request_body_required,
                'content': {
                    'application/json': {
                        'schema': self.request_schema,
                    }
                }
            }

        operation_spec['x-codeSamples'] = code_samples(self)

        # If we don't have any parameters we remove the empty list, so the spec will not have it.
        if not operation_spec['parameters']:
            del operation_spec['parameters']

        docstring_name = _docstring_name(self.func.__doc__)
        if docstring_name:
            operation_spec['summary'] = docstring_name
        else:
            raise RuntimeError(f"Please put a docstring onto {self.operation_id}")
        docstring_desc = _docstring_description(self.func.__doc__)
        if docstring_desc:
            operation_spec['description'] = docstring_desc

        apispec.utils.deepupdate(operation_spec, self.options)

        return {self.method: operation_spec}  # type: ignore[misc]
Пример #10
0
        plugins=[
            marshmallow.MarshmallowPlugin(),
            plugins.ValueTypedDictMarshmallowPlugin(),
            apispec_oneofschema.MarshmallowPlugin(),
        ],
        **options,
    )


SPEC = make_spec(options=OPTIONS)
for sec_scheme_name, sec_scheme_spec in SECURITY_SCHEMES.items():
    SPEC.components.security_scheme(sec_scheme_name, sec_scheme_spec)

# All the supported response headers by the spec.

# response_headers = {
#     'Allow',
#     'Cache-Control',
#     'Last-Modified',
#     'Warning',
#     'Content-Type',
# }
for header_name, field in ACCEPT_HEADER.items():
    SPEC.components.parameter(
        header_name,
        "header",
        to_openapi([{header_name: field}], "header")[0],
    )

ErrorType = Literal["ignore", "raise"]