120 lines
4.9 KiB
Python
120 lines
4.9 KiB
Python
"""
|
|
OpenAPI Schema Extensions for drf-spectacular
|
|
Custom extensions for JWT authentication and unified response format
|
|
"""
|
|
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
|
from drf_spectacular.plumbing import build_bearer_security_scheme_object
|
|
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
|
from rest_framework import status
|
|
|
|
# Explicit tags we want to keep (from SPECTACULAR_SETTINGS)
|
|
EXPLICIT_TAGS = {
|
|
'Authentication',
|
|
'Planner',
|
|
'Writer',
|
|
'System',
|
|
'Billing',
|
|
'Account',
|
|
'Automation',
|
|
'Linker',
|
|
'Optimizer',
|
|
'Publisher',
|
|
'Integration',
|
|
'Admin Billing',
|
|
}
|
|
|
|
|
|
def postprocess_schema_filter_tags(result, generator, request, public):
|
|
"""
|
|
Postprocessing hook to remove auto-generated tags and keep only explicit tags.
|
|
This prevents duplicate tags from URL patterns (auth, planner, etc.)
|
|
"""
|
|
# First, filter tags from all paths (operations)
|
|
if 'paths' in result:
|
|
for path, methods in result['paths'].items():
|
|
for method, operation in methods.items():
|
|
if isinstance(operation, dict) and 'tags' in operation:
|
|
# Explicitly exclude system webhook from tagging/docs grouping
|
|
if '/system/webhook' in path:
|
|
operation['tags'] = []
|
|
continue
|
|
|
|
# Keep only explicit tags from the operation
|
|
filtered_tags = [
|
|
tag for tag in operation['tags']
|
|
if tag in EXPLICIT_TAGS
|
|
]
|
|
|
|
# If no explicit tags found, infer from path
|
|
if not filtered_tags:
|
|
if '/ping' in path or '/system/ping/' in path:
|
|
filtered_tags = ['System'] # Health check endpoint
|
|
elif '/auth/' in path or '/api/v1/auth/' in path:
|
|
filtered_tags = ['Authentication']
|
|
elif '/planner/' in path or '/api/v1/planner/' in path:
|
|
filtered_tags = ['Planner']
|
|
elif '/writer/' in path or '/api/v1/writer/' in path:
|
|
filtered_tags = ['Writer']
|
|
elif '/system/' in path or '/api/v1/system/' in path:
|
|
filtered_tags = ['System']
|
|
elif '/billing/' in path or '/api/v1/billing/' in path:
|
|
filtered_tags = ['Billing']
|
|
elif '/account/' in path or '/api/v1/account/' in path:
|
|
filtered_tags = ['Account']
|
|
elif '/automation/' in path or '/api/v1/automation/' in path:
|
|
filtered_tags = ['Automation']
|
|
elif '/linker/' in path or '/api/v1/linker/' in path:
|
|
filtered_tags = ['Linker']
|
|
elif '/optimizer/' in path or '/api/v1/optimizer/' in path:
|
|
filtered_tags = ['Optimizer']
|
|
elif '/publisher/' in path or '/api/v1/publisher/' in path:
|
|
filtered_tags = ['Publisher']
|
|
elif '/integration/' in path or '/api/v1/integration/' in path:
|
|
filtered_tags = ['Integration']
|
|
elif '/admin/' in path or '/api/v1/admin/' in path:
|
|
filtered_tags = ['Admin Billing']
|
|
|
|
operation['tags'] = filtered_tags
|
|
|
|
# Now filter the tags list - keep only explicit tag definitions
|
|
# The tags list contains tag definitions with 'name' and 'description'
|
|
if 'tags' in result and isinstance(result['tags'], list):
|
|
# Keep only tags that match our explicit tag names
|
|
result['tags'] = [
|
|
tag for tag in result['tags']
|
|
if isinstance(tag, dict) and tag.get('name') in EXPLICIT_TAGS
|
|
]
|
|
|
|
return result
|
|
|
|
|
|
class JWTAuthenticationExtension(OpenApiAuthenticationExtension):
|
|
"""
|
|
OpenAPI extension for JWT Bearer Token authentication
|
|
"""
|
|
target_class = 'igny8_core.api.authentication.JWTAuthentication'
|
|
name = 'JWTAuthentication'
|
|
|
|
def get_security_definition(self, auto_schema):
|
|
return build_bearer_security_scheme_object(
|
|
header_name='Authorization',
|
|
token_prefix='Bearer',
|
|
bearer_format='JWT'
|
|
)
|
|
|
|
|
|
class CSRFExemptSessionAuthenticationExtension(OpenApiAuthenticationExtension):
|
|
"""
|
|
OpenAPI extension for CSRF-exempt session authentication
|
|
"""
|
|
target_class = 'igny8_core.api.authentication.CSRFExemptSessionAuthentication'
|
|
name = 'SessionAuthentication'
|
|
|
|
def get_security_definition(self, auto_schema):
|
|
return {
|
|
'type': 'apiKey',
|
|
'in': 'cookie',
|
|
'name': 'sessionid'
|
|
}
|
|
|