File: marshmallow_apispec.py

package info (click to toggle)
python-flasgger 0.9.5%2Bdfsg.2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 13,152 kB
  • sloc: javascript: 6,403; python: 3,665; makefile: 9; sh: 1
file content (139 lines) | stat: -rw-r--r-- 4,174 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
# coding: utf-8
import inspect

from flask.views import MethodView

import flasgger

try:
    from marshmallow import Schema, fields
    from apispec.ext.marshmallow import openapi
    from apispec import APISpec as BaseAPISpec

    # Note that openapi_converter is initialized with trivial
    #   schema_name_resolver. Resolving circular reference is not
    #   supported for now. See issue #314 .
    # Also see: https://github.com/marshmallow-code/apispec/pull/447
    openapi_converter = openapi.OpenAPIConverter(
        openapi_version='2.0',
        schema_name_resolver=lambda schema: None,
        spec=None
    )
    schema2jsonschema = openapi_converter.schema2jsonschema
    schema2parameters = openapi_converter.schema2parameters
except ImportError:
    Schema = None
    fields = None
    schema2jsonschema = lambda schema: {}  # noqa
    schema2parameters = lambda schema: []  # noqa
    BaseAPISpec = object


class APISpec(BaseAPISpec):
    """
    Wrapper around APISpec to add `to_flasgger` method
    """
    def to_flasgger(self, app=None, definitions=None, paths=None):
        """
        Converts APISpec dict to flasgger suitable dict
        also adds definitions and paths (optional)
        """
        if Schema is None:
            raise RuntimeError('Please install marshmallow and apispec')

        return flasgger.utils.apispec_to_template(
            app,
            self,
            definitions=definitions,
            paths=paths
        )


class SwaggerView(MethodView):
    """
    A Swagger view
    """
    parameters = []
    responses = {}
    definitions = {}
    tags = []
    consumes = ['application/json']
    produces = ['application/json']
    schemes = []
    security = []
    deprecated = False
    operationId = None
    externalDocs = {}
    summary = None
    description = None
    validation = False
    validation_function = None
    validation_error_handler = None

    def dispatch_request(self, *args, **kwargs):
        """
        If validation=True perform validation
        """
        if self.validation:
            specs = {}
            attrs = flasgger.constants.OPTIONAL_FIELDS + [
                'parameters', 'definitions', 'responses',
                'summary', 'description'
            ]
            for attr in attrs:
                specs[attr] = getattr(self, attr)
            definitions = {}
            specs.update(convert_schemas(specs, definitions))
            specs['definitions'] = definitions
            flasgger.utils.validate(
                specs=specs, validation_function=self.validation_function,
                validation_error_handler=self.validation_error_handler
            )
        return super(SwaggerView, self).dispatch_request(*args, **kwargs)


def convert_schemas(d, definitions=None):
    """
    Convert Marshmallow schemas to dict definitions

    Also updates the optional definitions argument with any definitions
    entries contained within the schema.
    """
    if definitions is None:
        definitions = {}
    definitions.update(d.get('definitions', {}))

    new = {}
    for k, v in d.items():
        if isinstance(v, dict):
            v = convert_schemas(v, definitions)
        if isinstance(v, (list, tuple)):
            new_v = []
            for item in v:
                if isinstance(item, dict):
                    new_v.append(convert_schemas(item, definitions))
                else:
                    new_v.append(item)
            v = new_v
        if inspect.isclass(v) and issubclass(v, Schema):

            if Schema is None:
                raise RuntimeError('Please install marshmallow and apispec')

            definitions[v.__name__] = schema2jsonschema(v)
            ref = {
                "$ref": "#/definitions/{0}".format(v.__name__)
            }
            if k == 'parameters':
                new[k] = schema2parameters(v)
                new[k][0]['schema'] = ref
            else:
                new[k] = ref
        else:
            new[k] = v

    # This key is not permitted anywhere except the very top level.
    if 'definitions' in new:
        del new['definitions']

    return new