-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathexamples.py
More file actions
167 lines (123 loc) · 4.43 KB
/
examples.py
File metadata and controls
167 lines (123 loc) · 4.43 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
"""
This module contains classes that are specific to the OpenAPI Documentation
Specification V3, to create examples of values from schemas. These are used to produce
basic examples of request and response bodies from schemas, when specific examples are
not included in documentation files.
"""
import random
from abc import ABC, abstractmethod
from typing import Any, Callable, ClassVar, Dict, Iterable, List, Type
from uuid import uuid4
class SchemaExampleHandler(ABC):
"""
Base type for classes that can generate basic example values
from schema types.
"""
type_name: ClassVar[str]
@abstractmethod
def get_example(self, schema) -> Any:
"""
Returns an example value for a property with the given name and schema.
"""
# TODO: allOf, anyOf, oneOf, not (?)
# https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/
class ScalarExampleHandler(SchemaExampleHandler):
type_name = ""
default: Any
formats: Dict[str, Callable[[], Any]]
def get_example(self, schema) -> str:
enum = schema.get("enum")
if isinstance(enum, list) and enum:
return enum[0]
format = schema.get("format")
if format and format in self.formats:
return self.formats[format]()
return self.default
class StringExampleHandler(ScalarExampleHandler):
type_name = "string"
default = "string"
formats = {
"email": lambda: "derp@meme.org",
"uuid": lambda: str(uuid4()),
"date": lambda: "2022-04-13",
"date-time": lambda: "2022-04-13T15:42:05.901Z",
"password": lambda: "*" * 12,
"byte": lambda: "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=",
"binary": lambda: "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=",
}
class IntegerExampleHandler(ScalarExampleHandler):
type_name = "integer"
default = 0
formats = {
"int32": lambda: random.randint(0, 300),
"int64": lambda: random.randint(0, 300),
}
class BooleanExampleHandler(ScalarExampleHandler):
type_name = "boolean"
default = True
formats = {}
class NumberExampleHandler(ScalarExampleHandler):
type_name = "number"
default = 10.12
formats = {
"float": lambda: 10.12,
"double": lambda: 10.12,
}
class ObjectExampleHandler(SchemaExampleHandler):
type_name = "object"
def get_example(self, schema) -> Any:
"""
Returns an example value for a property with the given name and schema.
"""
properties = schema.get("properties") or {}
example = {}
for key in properties:
example[key] = get_example_from_schema(properties[key])
return example
class ArrayExampleHandler(SchemaExampleHandler):
type_name = "array"
def get_example(self, schema) -> Any:
"""
Returns an example value for a property with the given name and schema.
Example:
type: array
items:
$ref: '#/components/schemas/ReleaseNodeDownload'
nullable: true
"""
items = schema.get("items", [])
if not isinstance(items, list):
items = [items]
return [get_example_from_schema(item) for item in items]
def get_subclasses(cls) -> Iterable[Type]:
for subclass in cls.__subclasses__():
yield from get_subclasses(subclass)
yield subclass
def get_example_from_schema(schema) -> Any:
if schema is None:
return None
if "example" in schema:
return schema["example"]
examples = schema.get("examples")
if isinstance(examples, list) and examples:
return examples[0]
# does it have a type?
handlers_types: List[Type[SchemaExampleHandler]] = list(
get_subclasses(SchemaExampleHandler)
)
schema_type = schema.get("type")
# OAS 3.1: type can be a list (e.g. ["string", "null"]). Use the first non-null type.
if isinstance(schema_type, list):
non_null = [t for t in schema_type if t != "null"]
schema_type = non_null[0] if non_null else None
if schema_type:
handler_type = next(
(_type for _type in handlers_types if _type.type_name == schema_type), None
)
if handler_type is None: # pragma: nocover
# fallback to returning the raw schema;
return schema
handler = handler_type()
return handler.get_example(schema)
# TODO: handle special cases (allOf, anyOf, etc.)
return None