-
-
Notifications
You must be signed in to change notification settings - Fork 546
Description
Hi Vitalik et al!
First off: thank you for creating Django Ninja. We're using this at the internet.nl internet standards compliance checker to describe our API endpoints and it's working like a charm.
Being a governmental funded project, our API should comply to government standards. Most requirements can directly be translated to Django Ninja, except for one. One of these is a bit odd: it requires to document a header being sent on every response, so in every schema. While sending the actual header can easily be done with a decorator (as documented and seen below), the header must be documented on every response also, or the openapi.json file will not pass their linter.
How would i be able to document an "API-Version" header in every response schema under the "header" section?
Background
The specification for doing so is here: https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/version-header
{
"headers": {
"API-Version": {
"description": "Semver van de API",
"schema": {
"type": "string",
"example": "1.0.0"
}
}
}
}I'm now using two workarounds: specifying a decorator and adding a "headers" section in the openapi_extra field.
Decorator:
def version_header(func):
def wrapper(request, *args, **kwargs):
response = func(request, *args, **kwargs)
response["API-Version"] = django_settings.OPEN_API_VERSION
return response
return wrapperOpenAPI Extra:
api = NinjaAPI(
title=django_settings.OPEN_API_TITLE,
openapi_extra={
"info": {
"contact": {
"name": django_settings.OPEN_API_CONTACT_ORGANIZATION,
"email": django_settings.OPEN_API_CONTACT_EMAIL,
"url": django_settings.OPEN_API_CONTACT_URL
},
},
# Makeshift alternative for specifying headers everywhere.
"headers": {
"API-Version": django_settings.OPEN_API_VERSION
}
},
version=django_settings.OPEN_API_VERSION,
renderer=ORJSONRenderer()
)Current workaround
I've now hacked it using some workaround proposed by an AI tool, but it looks messy and might break in a next version perhaps:
Workaround fix:
# Inject API-Version header into the generated OpenAPI schema for all responses.
_orig_get_openapi_schema = api.get_openapi_schema
def _get_openapi_schema_with_version_header(**kwargs):
schema = _orig_get_openapi_schema()
components = schema.setdefault("components", {})
headers = components.setdefault("headers", {})
headers["API-Version"] = {
"description": f"Semantic version of the {django_settings.OPEN_API_TITLE}",
"schema": {"type": "string", "example": django_settings.OPEN_API_VERSION},
}
for path_item in schema.get("paths", {}).values():
for operation in path_item.values():
responses = operation.get("responses", {})
for response in responses.values():
response_headers = response.setdefault("headers", {})
response_headers["API-Version"] = {"$ref": "#/components/headers/API-Version"}
return schema
api.get_openapi_schema = _get_openapi_schema_with_version_header