authentik.api.v3.schema.cleanup
Error Response schema, from https://github.com/axnsan12/drf-yasg/issues/224
1"""Error Response schema, from https://github.com/axnsan12/drf-yasg/issues/224""" 2 3from collections.abc import Callable 4from typing import Any 5 6from drf_spectacular.contrib.django_filters import ( 7 DjangoFilterExtension as BaseDjangoFilterExtension, 8) 9from drf_spectacular.generators import SchemaGenerator 10from drf_spectacular.plumbing import ( 11 ResolvedComponent, 12 follow_field_source, 13) 14from drf_spectacular.renderers import OpenApiJsonRenderer 15from drf_spectacular.settings import spectacular_settings 16from structlog.stdlib import get_logger 17 18from authentik.api.apps import AuthentikAPIConfig 19 20LOGGER = get_logger() 21 22 23def preprocess_schema_exclude_non_api(endpoints: list[tuple[str, Any, Any, Callable]], **kwargs): 24 """Filter out all API Views which are not mounted under /api""" 25 return [ 26 (path, path_regex, method, callback) 27 for path, path_regex, method, callback in endpoints 28 if path.startswith("/" + AuthentikAPIConfig.mountpoint) 29 ] 30 31 32def postprocess_schema_remove_unused( 33 result: dict[str, Any], generator: SchemaGenerator, **kwargs 34) -> dict[str, Any]: 35 """Remove unused components""" 36 # To check if the schema is used, render it to JSON and then substring check that 37 # less efficient than walking through the tree but a lot simpler and no 38 # possibility that we miss something 39 raw = OpenApiJsonRenderer().render(result, renderer_context={}).decode() 40 count = 0 41 for key in result["components"][ResolvedComponent.SCHEMA].keys(): 42 schema_usages = raw.count(f"#/components/{ResolvedComponent.SCHEMA}/{key}") 43 if schema_usages >= 1: 44 continue 45 del generator.registry[(key, ResolvedComponent.SCHEMA)] 46 count += 1 47 LOGGER.debug("Removing unused components", count=count) 48 result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS) 49 return result 50 51 52class DjangoFilterExtension(BaseDjangoFilterExtension): 53 """ 54 From https://github.com/netbox-community/netbox/pull/21521: 55 56 Overrides drf-spectacular's DjangoFilterExtension to fix a regression in v0.29.0 where 57 _get_model_field() incorrectly double-appends to_field_name when field_name already ends 58 with that value (e.g. field_name='tags__slug', to_field_name='slug' produces the invalid 59 path ['tags', 'slug', 'slug']). This caused hundreds of spurious warnings during schema 60 generation for filters such as TagFilter, TenancyFilterSet.tenant, and OwnerFilterMixin.owner. 61 62 See: https://github.com/netbox-community/netbox/issues/20787 63 https://github.com/tfranzel/drf-spectacular/issues/1475 64 """ 65 66 priority = 1 67 68 def _get_model_field(self, filter_field, model): 69 if not filter_field.field_name: 70 return None 71 path = filter_field.field_name.split("__") 72 to_field_name = filter_field.extra.get("to_field_name") 73 if to_field_name is not None and path[-1] != to_field_name: 74 path.append(to_field_name) 75 return follow_field_source(model, path, emit_warnings=False)
24def preprocess_schema_exclude_non_api(endpoints: list[tuple[str, Any, Any, Callable]], **kwargs): 25 """Filter out all API Views which are not mounted under /api""" 26 return [ 27 (path, path_regex, method, callback) 28 for path, path_regex, method, callback in endpoints 29 if path.startswith("/" + AuthentikAPIConfig.mountpoint) 30 ]
Filter out all API Views which are not mounted under /api
33def postprocess_schema_remove_unused( 34 result: dict[str, Any], generator: SchemaGenerator, **kwargs 35) -> dict[str, Any]: 36 """Remove unused components""" 37 # To check if the schema is used, render it to JSON and then substring check that 38 # less efficient than walking through the tree but a lot simpler and no 39 # possibility that we miss something 40 raw = OpenApiJsonRenderer().render(result, renderer_context={}).decode() 41 count = 0 42 for key in result["components"][ResolvedComponent.SCHEMA].keys(): 43 schema_usages = raw.count(f"#/components/{ResolvedComponent.SCHEMA}/{key}") 44 if schema_usages >= 1: 45 continue 46 del generator.registry[(key, ResolvedComponent.SCHEMA)] 47 count += 1 48 LOGGER.debug("Removing unused components", count=count) 49 result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS) 50 return result
Remove unused components
53class DjangoFilterExtension(BaseDjangoFilterExtension): 54 """ 55 From https://github.com/netbox-community/netbox/pull/21521: 56 57 Overrides drf-spectacular's DjangoFilterExtension to fix a regression in v0.29.0 where 58 _get_model_field() incorrectly double-appends to_field_name when field_name already ends 59 with that value (e.g. field_name='tags__slug', to_field_name='slug' produces the invalid 60 path ['tags', 'slug', 'slug']). This caused hundreds of spurious warnings during schema 61 generation for filters such as TagFilter, TenancyFilterSet.tenant, and OwnerFilterMixin.owner. 62 63 See: https://github.com/netbox-community/netbox/issues/20787 64 https://github.com/tfranzel/drf-spectacular/issues/1475 65 """ 66 67 priority = 1 68 69 def _get_model_field(self, filter_field, model): 70 if not filter_field.field_name: 71 return None 72 path = filter_field.field_name.split("__") 73 to_field_name = filter_field.extra.get("to_field_name") 74 if to_field_name is not None and path[-1] != to_field_name: 75 path.append(to_field_name) 76 return follow_field_source(model, path, emit_warnings=False)
From https://github.com/netbox-community/netbox/pull/21521:
Overrides drf-spectacular's DjangoFilterExtension to fix a regression in v0.29.0 where _get_model_field() incorrectly double-appends to_field_name when field_name already ends with that value (e.g. field_name='tags__slug', to_field_name='slug' produces the invalid path ['tags', 'slug', 'slug']). This caused hundreds of spurious warnings during schema generation for filters such as TagFilter, TenancyFilterSet.tenant, and OwnerFilterMixin.owner.
See: https://github.com/netbox-community/netbox/issues/20787 https://github.com/tfranzel/drf-spectacular/issues/1475