Skip to content

Commit

Permalink
[#222] add tags to spec's root
Browse files Browse the repository at this point in the history
  • Loading branch information
Sonny Bakker committed Sep 29, 2022
1 parent be48c29 commit e05840d
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 49 deletions.
12 changes: 6 additions & 6 deletions tests/test_schema_root_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ def test_schema_root_tags():
request = APIView().initialize_request(request)
request._request.jwt_auth = mock.Mock()

generator = OpenAPISchemaGenerator(info=mock.Mock())
generator = OpenAPISchemaGenerator()

schema = generator.get_schema(request)
assert hasattr(schema, "tags")
assert "tags" in schema

# Convert list of ordereddicts to simple dict.
tags = dict([dict(od).values() for od in schema.tags])
assert "persons" in tags
assert tags["persons"] == "Summary\n\nMore summary"
summary = next(
tag["description"] for tag in schema["tags"] if tag["name"] == "persons"
)
assert summary == "Summary\n\nMore summary"


def test_view_summary():
Expand Down
3 changes: 3 additions & 0 deletions vng_api_common/conf/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"BASE_REST_FRAMEWORK",
"BASE_SPECTACULAR_SETTINGS",
"COMMON_SPEC",
"DOCUMENTATION_INFO_MODULE",
"DRF_EXCLUDED_ENDPOINTS",
"LINK_FETCHER",
"ZDS_CLIENT_CLASS",
Expand Down Expand Up @@ -90,6 +91,8 @@
],
}

DOCUMENTATION_INFO_MODULE = None

DRF_EXCLUDED_ENDPOINTS = ["callbacks", "jwtsecret/", "openapi.yaml", "openapi{var}"]

REDOC_SETTINGS = {"EXPAND_RESPONSES": "200,201", "SPEC_URL": "openapi.json"}
Expand Down
86 changes: 43 additions & 43 deletions vng_api_common/generators.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import importlib
import re
from importlib import import_module

from django.conf import settings

from drf_spectacular.drainage import reset_generator_stats
from drf_spectacular.generators import (
EndpointEnumerator as _EndpointEnumerator,
SchemaGenerator as _OpenAPISchemaGenerator,
)
from drf_spectacular.plumbing import (
build_root_object,
normalize_result_object,
sanitize_result_object,
)
from drf_spectacular.settings import spectacular_settings


class EndpointEnumerator(_EndpointEnumerator):
Expand All @@ -33,18 +27,47 @@ class OpenAPISchemaGenerator(_OpenAPISchemaGenerator):
endpoint_inspector_cls = EndpointEnumerator

def get_schema(self, request=None, public=False):
"""Generate a OpenAPI schema."""
reset_generator_stats()
result = build_root_object(
paths=self.parse(request, public),
components=self.registry.build(spectacular_settings.APPEND_COMPONENTS),
version=self.api_version or getattr(request, "version", None),
)
result = self.restructure_root_object(result)
for hook in spectacular_settings.POSTPROCESSING_HOOKS:
result = hook(result=result, generator=self, request=request, public=public)
schema = super().get_schema(request=request, public=public)
schema["tags"] = self.get_tags()

try:
info_module = import_module(settings.DOCUMENTATION_INFO_MODULE)
except (ImportError, AttributeError):
return schema

info_kwargs = {
variable.lower(): getattr(info_module, variable)
for variable in info_module.__all__
}

schema["info"].update(info_kwargs)
return schema

def get_tags(self):
tags = []

endpoints = self._get_paths_and_endpoints()
for path, path_regex, method, view in endpoints:
path_fragments = path.split("/api/v{version")
endpoint_path = path_fragments[-1]

return sanitize_result_object(normalize_result_object(result))
if "{" in endpoint_path:
continue

tag = endpoint_path.rsplit("/", 1)[-1]

# exclude special non-rest actions
if tag.startswith("_") or not tag or tag in [tag["name"] for tag in tags]:
continue

tags.append(
{
"name": tag,
"description": view.schema.get_summary(),
}
)

return tags

def create_view(self, callback, method, request=None):
view = super(_OpenAPISchemaGenerator, self).create_view(
Expand All @@ -55,26 +78,3 @@ def create_view(self, callback, method, request=None):
return view

return super().create_view(callback, method, request=request)

def restructure_root_object(self, root):
settings = spectacular_settings
if settings.DESCRIPTION:
schema_module = importlib.import_module(settings.DESCRIPTION)
root["info"]["title"] = schema_module.TITLE
root["info"]["version"] = schema_module.VERSION
root["info"]["description"] = (
schema_module.DESCRIPTION if schema_module.DESCRIPTION else ""
)
root["info"]["contact"] = schema_module.CONTACT
root["info"]["license"] = schema_module.LICENSE

if settings.TAGS:
TAGS = []
for tag in settings.TAGS:
schema_module = importlib.import_module(tag["path"])
doc_string = schema_module.__dict__[tag["view"]].__doc__
doc_string = re.sub(r" +", " ", doc_string)

TAGS.append({"name": tag["name"], "description": doc_string})
root["tags"] = TAGS
return root
5 changes: 5 additions & 0 deletions vng_api_common/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from rest_framework import exceptions, serializers, status, viewsets

from vng_api_common.search import is_search_view
from vng_api_common.utils import get_view_summary

from .caching.introspection import has_cache_header
from .constants import HEADER_AUDIT, HEADER_LOGRECORD_ID, VERSION_HEADER
Expand Down Expand Up @@ -209,6 +210,10 @@ def get_operation_id(self):

return super().get_operation_id()

def get_summary(self):
summary = get_view_summary(self.view)
return summary or super().get_summary() or ""

def get_description(self):
if self.method == "HEAD":
return _("Vraag de headers op die je bij een GET request zou krijgen.")
Expand Down

0 comments on commit e05840d

Please sign in to comment.