File: extensions.py

package info (click to toggle)
python-drf-spectacular 0.28.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,748 kB
  • sloc: python: 14,174; javascript: 114; sh: 61; makefile: 30
file content (143 lines) | stat: -rw-r--r-- 6,325 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union

from drf_spectacular.plumbing import OpenApiGeneratorExtension
from drf_spectacular.utils import Direction

if TYPE_CHECKING:
    from rest_framework.views import APIView

    from drf_spectacular.openapi import AutoSchema


_SchemaType = Dict[str, Any]


class OpenApiAuthenticationExtension(OpenApiGeneratorExtension['OpenApiAuthenticationExtension']):
    """
    Extension for specifying authentication schemes.

    The common use-case usually consists of setting a ``name`` string and returning a dict from
    ``get_security_definition``. To model a group of headers that go together, set a list
    of names and return a corresponding list of definitions from ``get_security_definition``.

    The view class is available via ``auto_schema.view``, while the original authentication class
    can be accessed via ``self.target``. If you want to override an included extension, be sure to
    set a higher matching priority by setting the class attribute ``priority = 1`` or higher.

    get_security_requirement is expected to return a dict with security object names as keys and a
    scope list as value (usually just []). More than one key in the dict means that each entry is
    required (AND). If you need alternate variations (OR), return a list of those dicts instead.

    ``get_security_definition()`` is expected to return a valid `OpenAPI security scheme object
    <https://spec.openapis.org/oas/v3.0.3#security-scheme-object>`_
    """
    _registry: List[Type['OpenApiAuthenticationExtension']] = []

    name: Union[str, List[str]]

    def get_security_requirement(
            self, auto_schema: 'AutoSchema'
    ) -> Union[Dict[str, List[Any]], List[Dict[str, List[Any]]]]:
        assert self.name, 'name(s) must be specified'
        if isinstance(self.name, str):
            return {self.name: []}
        else:
            return {name: [] for name in self.name}

    @abstractmethod
    def get_security_definition(self, auto_schema: 'AutoSchema') -> Union[_SchemaType, List[_SchemaType]]:
        pass  # pragma: no cover


class OpenApiSerializerExtension(OpenApiGeneratorExtension['OpenApiSerializerExtension']):
    """
    Extension for replacing an insufficient or specifying an unknown Serializer schema.

    The existing implementation of ``map_serializer()`` will generate the same result
    as *drf-spectacular* would. Either augment or replace the generated schema. The
    view instance is available via ``auto_schema.view``, while the original serializer
    can be accessed via ``self.target``.

    ``map_serializer()`` is expected to return a valid `OpenAPI schema object
    <https://spec.openapis.org/oas/v3.0.3#schema-object>`_.
    """
    _registry: List[Type['OpenApiSerializerExtension']] = []

    def get_name(self, auto_schema: 'AutoSchema', direction: Direction) -> Optional[str]:
        """ return str for overriding default name extraction """
        return None

    def get_identity(self, auto_schema: 'AutoSchema', direction: Direction) -> Any:
        """ return anything to compare instances of target. Target will be used by default. """
        return None

    def map_serializer(self, auto_schema: 'AutoSchema', direction: Direction) -> _SchemaType:
        """ override for customized serializer mapping """
        return auto_schema._map_serializer(self.target_class, direction, bypass_extensions=True)


class OpenApiSerializerFieldExtension(OpenApiGeneratorExtension['OpenApiSerializerFieldExtension']):
    """
    Extension for replacing an insufficient or specifying an unknown SerializerField schema.

    To augment the default schema, you can get what *drf-spectacular* would generate with
    ``auto_schema._map_serializer_field(self.target, direction, bypass_extensions=True)``.
    and edit the returned schema at your discretion. Beware that this may still emit
    warnings, in which case manual construction is advisable.

    ``map_serializer_field()`` is expected to return a valid `OpenAPI schema object
    <https://spec.openapis.org/oas/v3.0.3#schema-object>`_.
    """
    _registry: List[Type['OpenApiSerializerFieldExtension']] = []

    def get_name(self) -> Optional[str]:
        """ return str for breaking out field schema into separate named component """
        return None

    @abstractmethod
    def map_serializer_field(self, auto_schema: 'AutoSchema', direction: Direction) -> _SchemaType:
        """ override for customized serializer field mapping """
        pass  # pragma: no cover


class OpenApiViewExtension(OpenApiGeneratorExtension['OpenApiViewExtension']):
    """
    Extension for replacing discovered views with a more schema-appropriate/annotated version.

    ``view_replacement()`` is expected to return a subclass of ``APIView`` (which includes
    ``ViewSet`` et al.). The discovered original view instance can be accessed with
    ``self.target`` and be subclassed if desired.
    """
    _registry: List[Type['OpenApiViewExtension']] = []

    @classmethod
    def _load_class(cls):
        super()._load_class()
        # special case @api_view: view class is nested in the cls attr of the function object
        if hasattr(cls.target_class, 'cls'):
            cls.target_class = cls.target_class.cls

    @abstractmethod
    def view_replacement(self) -> 'Type[APIView]':
        pass  # pragma: no cover


class OpenApiFilterExtension(OpenApiGeneratorExtension['OpenApiFilterExtension']):
    """
    Extension for specifying a list of filter parameters for a given ``FilterBackend``.

    The original filter class object can be accessed via ``self.target``. The attached view
    is accessible via ``auto_schema.view``.

    ``get_schema_operation_parameters()`` is expected to return either an empty list or a list
    of valid raw `OpenAPI parameter objects
    <https://spec.openapis.org/oas/v3.0.3#parameter-object>`_.
    Using ``drf_spectacular.plumbing.build_parameter_type`` is recommended to generate
    the appropriate raw dict objects.
    """
    _registry: List[Type['OpenApiFilterExtension']] = []

    @abstractmethod
    def get_schema_operation_parameters(self, auto_schema: 'AutoSchema', *args, **kwargs) -> List[_SchemaType]:
        pass  # pragma: no cover