fi(api): improve auth/preferences schema generation

Applying recommendation from https://github.com/tfranzel/drf-spectacular/issues/874
spectacular_schema
jo 2023-02-04 21:55:47 +01:00 zatwierdzone przez Georg Krause
rodzic 9bfc83f0e4
commit 8f65318d32
1 zmienionych plików z 84 dodań i 22 usunięć

Wyświetl plik

@ -1,35 +1,97 @@
import os
from drf_spectacular.contrib.django_oauth_toolkit import OpenApiAuthenticationExtension
from drf_spectacular.plumbing import build_bearer_security_scheme_object
from drf_spectacular.contrib.django_oauth_toolkit import (
DjangoOAuthToolkitScheme,
OpenApiAuthenticationExtension,
)
from drf_spectacular.extensions import (
OpenApiSerializerExtension,
OpenApiSerializerFieldExtension,
)
from drf_spectacular.plumbing import (
OpenApiTypes,
build_basic_type,
build_bearer_security_scheme_object,
)
class CustomOAuthExt(OpenApiAuthenticationExtension):
class CustomRelatedFieldScheme(OpenApiSerializerFieldExtension):
target_class = "funkwhale_api.common.serializers.RelatedField"
match_subclasses = True
def map_serializer_field(self, auto_schema, direction):
if direction == "request":
return build_basic_type(OpenApiTypes.UUID)
elif direction == "response" and self.target.serializer:
component = auto_schema.resolve_serializer(
self.target.serializer, direction
)
return component.ref
else:
# happens for
# UserViewSet: UserWriteSerializer: not sure how this works for: avatar
# AlbumViewSet: AlbumCreateSerializer: not sure how this works for: artist
return build_basic_type(OpenApiTypes.UUID)
class PreferenceSerializerScheme(OpenApiSerializerExtension):
target_class = "dynamic_preferences.api.serializers.PreferenceSerializer"
match_subclasses = True
def map_serializer(self, auto_schema, direction):
from dynamic_preferences.api.serializers import PreferenceSerializer
class Fix(PreferenceSerializer):
def get_default(self, o) -> str:
pass
def get_verbose_name(self, o) -> str:
pass
def get_identifier(self, o) -> str:
pass
def get_help_text(self, o) -> str:
pass
def get_additional_data(self, o) -> dict:
pass
def get_field(self, o) -> dict:
pass
return auto_schema._map_serializer(Fix, direction, bypass_extensions=True)
class CustomOAuthScheme(DjangoOAuthToolkitScheme):
target_class = "funkwhale_api.common.authentication.OAuth2Authentication"
name = "oauth2"
def get_security_definition(self, auto_schema):
from drf_spectacular.settings import spectacular_settings
from oauth2_provider.scopes import get_scopes_backend
def get_security_requirement(self, auto_schema):
from funkwhale_api.users.oauth.permissions import (
METHOD_SCOPE_MAPPING,
ScopePermission,
)
flows = {}
for flow_type in spectacular_settings.OAUTH2_FLOWS:
flows[flow_type] = {}
if flow_type in ("implicit", "authorizationCode"):
flows[flow_type][
"authorizationUrl"
] = spectacular_settings.OAUTH2_AUTHORIZATION_URL
if flow_type in ("password", "clientCredentials", "authorizationCode"):
flows[flow_type]["tokenUrl"] = spectacular_settings.OAUTH2_TOKEN_URL
if spectacular_settings.OAUTH2_REFRESH_URL:
flows[flow_type]["refreshUrl"] = spectacular_settings.OAUTH2_REFRESH_URL
scope_backend = get_scopes_backend()
flows[flow_type]["scopes"] = scope_backend.get_all_scopes()
for permission in auto_schema.view.get_permissions():
if isinstance(permission, ScopePermission):
scope_config = getattr(auto_schema.view, "required_scope", "noopscope")
return {"type": "oauth2", "flows": flows}
if isinstance(scope_config, str):
scope_config = {
"read": f"read:{scope_config}",
"write": f"write:{scope_config}",
}
action = METHOD_SCOPE_MAPPING[
auto_schema.view.request.method.lower()
]
required_scope = scope_config[action]
else:
required_scope = scope_config[auto_schema.view.action]
return {self.name: [required_scope]}
class CustomApplicationTokenExt(OpenApiAuthenticationExtension):
class CustomApplicationTokenScheme(OpenApiAuthenticationExtension):
target_class = "funkwhale_api.common.authentication.ApplicationTokenAuthentication"
name = "ApplicationToken"