Eve-Swagger 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eve_swagger/__init__.py +11 -0
- eve_swagger/definitions.py +283 -0
- eve_swagger/objects.py +313 -0
- eve_swagger/paths.py +468 -0
- eve_swagger/swagger.py +163 -0
- eve_swagger/templates/index.html +9 -0
- eve_swagger/tests/__init__.py +69 -0
- eve_swagger/tests/test_settings.py +93 -0
- eve_swagger/tests/tests.py +339 -0
- eve_swagger/validation.py +57 -0
- eve_swagger-0.2.1.dist-info/METADATA +344 -0
- eve_swagger-0.2.1.dist-info/RECORD +16 -0
- eve_swagger-0.2.1.dist-info/WHEEL +5 -0
- eve_swagger-0.2.1.dist-info/licenses/AUTHORS +35 -0
- eve_swagger-0.2.1.dist-info/licenses/LICENSE +33 -0
- eve_swagger-0.2.1.dist-info/top_level.txt +1 -0
eve_swagger/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
eve-swagger
|
|
3
|
+
~~~~~~~~~~~
|
|
4
|
+
swagger.io extension for Eve-powered REST APIs.
|
|
5
|
+
|
|
6
|
+
:copyright: (c) 2015 by Nicola Iarocci.
|
|
7
|
+
:license: BSD, see LICENSE for more details.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .swagger import get_swagger_blueprint, add_documentation # noqa
|
|
11
|
+
from .definitions import INFO, HOST # noqa
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
eve-swagger.definitions
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
swagger.io extension for Eve-powered REST APIs.
|
|
5
|
+
|
|
6
|
+
:copyright: (c) 2016 by Nicola Iarocci.
|
|
7
|
+
:license: BSD, see LICENSE for more details.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections import OrderedDict
|
|
11
|
+
from flask import current_app as app
|
|
12
|
+
|
|
13
|
+
INFO = "SWAGGER_INFO"
|
|
14
|
+
HOST = "SWAGGER_HOST"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def definitions():
|
|
18
|
+
definitions = OrderedDict()
|
|
19
|
+
dr_sources = {}
|
|
20
|
+
for rd in app.config["DOMAIN"].values():
|
|
21
|
+
dr_sources.update(_get_dr_sources(rd["schema"]))
|
|
22
|
+
|
|
23
|
+
for rd in app.config["DOMAIN"].values():
|
|
24
|
+
if rd.get("disable_documentation"):
|
|
25
|
+
continue
|
|
26
|
+
title = rd["item_title"]
|
|
27
|
+
definitions[title] = _object(rd, dr_sources)
|
|
28
|
+
if "description" in rd:
|
|
29
|
+
definitions[title]["description"] = rd["description"]
|
|
30
|
+
|
|
31
|
+
# add data_relation source fields to #/components/schemas/
|
|
32
|
+
definitions.update(dr_sources)
|
|
33
|
+
# add default error response eve schema
|
|
34
|
+
error_schema = {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"_status": {"type": "string", "example": "OK"},
|
|
38
|
+
"_error": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"properties": {
|
|
41
|
+
"code": {"type": "integer"},
|
|
42
|
+
"message": {"type": "string"},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
"required": ["_status", "_error"],
|
|
47
|
+
}
|
|
48
|
+
definitions["Error"] = error_schema
|
|
49
|
+
# add default meta response eve schema
|
|
50
|
+
response_metadata_schema = {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"properties": {
|
|
53
|
+
"page": {"type": "string"},
|
|
54
|
+
"total": {"type": "integer"},
|
|
55
|
+
"max_results": {"type": "integer"},
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
definitions["response_metadata"] = response_metadata_schema
|
|
59
|
+
|
|
60
|
+
response_links_schema = {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"parent": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"title": {"type": "string", "format": "uri"},
|
|
67
|
+
"href": {"type": "string", "format": "uri"},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"self": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"properties": {
|
|
73
|
+
"title": {"type": "string", "format": "uri"},
|
|
74
|
+
"href": {"type": "string", "format": "uri"},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
definitions["response_links"] = response_links_schema
|
|
81
|
+
|
|
82
|
+
return definitions
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _object(rd, dr_sources):
|
|
86
|
+
props = {}
|
|
87
|
+
required = []
|
|
88
|
+
for field, rules in rd["schema"].items():
|
|
89
|
+
if rules.get("required") is True:
|
|
90
|
+
required.append(field)
|
|
91
|
+
|
|
92
|
+
def_name = rd["item_title"] + "_" + field
|
|
93
|
+
props[field] = _field_props(rules, dr_sources, def_name)
|
|
94
|
+
|
|
95
|
+
if def_name in dr_sources:
|
|
96
|
+
# the current field is a source of a data_relation
|
|
97
|
+
|
|
98
|
+
# replace None in dr_sources with the field properties
|
|
99
|
+
dr_sources[def_name] = OrderedDict(props[field])
|
|
100
|
+
props[field] = {"$ref": "#/components/schemas/{}".format(def_name)}
|
|
101
|
+
|
|
102
|
+
if "data_relation" in rules:
|
|
103
|
+
# the current field is a copy of another field
|
|
104
|
+
dr = rules["data_relation"]
|
|
105
|
+
if dr["resource"] not in app.config["DOMAIN"]:
|
|
106
|
+
# source of data_relation does not exist
|
|
107
|
+
continue
|
|
108
|
+
title = app.config["DOMAIN"][dr["resource"]]["item_title"]
|
|
109
|
+
source_def_name = title + "_" + dr["field"]
|
|
110
|
+
props[field] = {"$ref": "#/components/schemas/{}".format(source_def_name)}
|
|
111
|
+
|
|
112
|
+
field_def = {}
|
|
113
|
+
field_def["type"] = "object"
|
|
114
|
+
field_def["properties"] = props
|
|
115
|
+
if len(required):
|
|
116
|
+
field_def["required"] = required
|
|
117
|
+
return field_def
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _field_props(rules, dr_sources, prefix):
|
|
121
|
+
resp = {}
|
|
122
|
+
map = {
|
|
123
|
+
"dict": ("object",),
|
|
124
|
+
"list": ("array",),
|
|
125
|
+
"objectid": ("string", "objectid"),
|
|
126
|
+
"datetime": ("string", "date-time"),
|
|
127
|
+
"float": ("number", "float"),
|
|
128
|
+
"point": ("geometry", "Point"),
|
|
129
|
+
"multipoint": ("geometry", "MultiPoint"),
|
|
130
|
+
"linestring": ("geometry", "LineString"),
|
|
131
|
+
"multilinestring": ("geometry", "MultiLineString"),
|
|
132
|
+
"polygon": ("geometry", "Polygon"),
|
|
133
|
+
"multipolygon": ("geometry", "MultiPolygon"),
|
|
134
|
+
"geometrycollection": ("geometry", "GeometryCollection"),
|
|
135
|
+
}
|
|
136
|
+
if app.config["RETURN_MEDIA_AS_URL"]:
|
|
137
|
+
map["media"] = ("string",)
|
|
138
|
+
else:
|
|
139
|
+
map["media"] = ("string", "base64")
|
|
140
|
+
|
|
141
|
+
eve_type = rules.get("type")
|
|
142
|
+
if eve_type is None:
|
|
143
|
+
return resp
|
|
144
|
+
|
|
145
|
+
if "description" in rules:
|
|
146
|
+
resp["description"] = rules["description"]
|
|
147
|
+
|
|
148
|
+
if "example" in rules:
|
|
149
|
+
resp["example"] = rules["example"]
|
|
150
|
+
|
|
151
|
+
if "allowed" in rules:
|
|
152
|
+
resp["enum"] = rules["allowed"]
|
|
153
|
+
|
|
154
|
+
if "default" in rules:
|
|
155
|
+
resp["default"] = rules["default"]
|
|
156
|
+
|
|
157
|
+
if "minlength" in rules:
|
|
158
|
+
if eve_type == "string":
|
|
159
|
+
resp["minLength"] = rules["minlength"]
|
|
160
|
+
elif eve_type == "list":
|
|
161
|
+
resp["minItems"] = rules["minlength"]
|
|
162
|
+
|
|
163
|
+
if "maxlength" in rules:
|
|
164
|
+
if eve_type == "string":
|
|
165
|
+
resp["maxLength"] = rules["maxlength"]
|
|
166
|
+
elif eve_type == "list":
|
|
167
|
+
resp["maxItems"] = rules["maxlength"]
|
|
168
|
+
|
|
169
|
+
if "min" in rules:
|
|
170
|
+
if eve_type in ["number", "integer", "float"]:
|
|
171
|
+
resp["minimum"] = rules["min"]
|
|
172
|
+
|
|
173
|
+
if "max" in rules:
|
|
174
|
+
if eve_type in ["number", "integer", "float"]:
|
|
175
|
+
resp["maximum"] = rules["max"]
|
|
176
|
+
|
|
177
|
+
if "readonly" in rules:
|
|
178
|
+
resp["readOnly"] = rules["readonly"]
|
|
179
|
+
|
|
180
|
+
if "regex" in rules:
|
|
181
|
+
resp["pattern"] = rules["regex"]
|
|
182
|
+
|
|
183
|
+
if "nullable" in rules:
|
|
184
|
+
resp["nullable"] = rules["nullable"]
|
|
185
|
+
|
|
186
|
+
type = map.get(eve_type, (eve_type,))
|
|
187
|
+
|
|
188
|
+
resp["type"] = type[0]
|
|
189
|
+
if type[0] == "object":
|
|
190
|
+
if "schema" in rules:
|
|
191
|
+
# set prefix as item_title to avoid name collisions of nested
|
|
192
|
+
# fields with higher up fields
|
|
193
|
+
pseudo_rd = {"item_title": prefix, "schema": rules["schema"]}
|
|
194
|
+
resp.update(_object(pseudo_rd, dr_sources))
|
|
195
|
+
# TODO add support for more elaborate cases using additionalProperties
|
|
196
|
+
elif "valueschema" in rules:
|
|
197
|
+
keytype = {"type": "string"}
|
|
198
|
+
if "keyschema" in rules:
|
|
199
|
+
keytype = rules["keyschema"]
|
|
200
|
+
|
|
201
|
+
pseudo_rd = {
|
|
202
|
+
"item_title": prefix,
|
|
203
|
+
"properties": {"keys": keytype, "values": rules["valueschema"]},
|
|
204
|
+
}
|
|
205
|
+
resp.update(pseudo_rd)
|
|
206
|
+
elif type[0] == "array":
|
|
207
|
+
type = "array"
|
|
208
|
+
if "schema" in rules:
|
|
209
|
+
resp["items"] = _field_props(rules["schema"], dr_sources, prefix)
|
|
210
|
+
else:
|
|
211
|
+
# 'items' is mandatory for swagger, we assume it's a list of
|
|
212
|
+
# strings
|
|
213
|
+
resp["items"] = {"type": "string"}
|
|
214
|
+
elif type[0] == "geometry":
|
|
215
|
+
# create a pseudo object
|
|
216
|
+
pseudo_rd = {
|
|
217
|
+
"item_title": prefix,
|
|
218
|
+
"schema": {
|
|
219
|
+
"type": {"type": "string", "allowed": [type[1]], "required": True},
|
|
220
|
+
"coordinates": {"type": "list", "required": True},
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
point2D_rd = {"schema": {"type": "integer"}, "minlength": 2, "maxlength": 2}
|
|
224
|
+
array_rd = {"schema": {"type": "list"}}
|
|
225
|
+
s = "schema"
|
|
226
|
+
c = "coordinates"
|
|
227
|
+
if type[1] == "Point":
|
|
228
|
+
# 1st level array of point 2D
|
|
229
|
+
pseudo_rd[s][c].update(point2D_rd)
|
|
230
|
+
elif type[1] in ["LineString", "MultiPoint"]:
|
|
231
|
+
# 2nd level array of point 2D
|
|
232
|
+
pseudo_rd[s][c].update(array_rd)
|
|
233
|
+
pseudo_rd[s][c][s].update(point2D_rd)
|
|
234
|
+
elif type[1] in ["MultiLineString", "Polygon"]:
|
|
235
|
+
# 3rd level array of point 2D
|
|
236
|
+
pseudo_rd[s][c].update(array_rd)
|
|
237
|
+
pseudo_rd[s][c][s].update(array_rd)
|
|
238
|
+
pseudo_rd[s][c][s][s].update(point2D_rd)
|
|
239
|
+
elif type[1] in ["MultiPolygon"]:
|
|
240
|
+
# 4th level array of point 2D
|
|
241
|
+
pseudo_rd[s][c].update(array_rd)
|
|
242
|
+
pseudo_rd[s][c][s].update(array_rd)
|
|
243
|
+
pseudo_rd[s][c][s][s].update(array_rd)
|
|
244
|
+
pseudo_rd[s][c][s][s][s].update(point2D_rd)
|
|
245
|
+
elif type[1] in ["GeometryCollection"]:
|
|
246
|
+
# GeometryCollection require external defs
|
|
247
|
+
pass
|
|
248
|
+
resp.update(_object(pseudo_rd, dr_sources))
|
|
249
|
+
else:
|
|
250
|
+
try:
|
|
251
|
+
resp["format"] = type[1]
|
|
252
|
+
except IndexError:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
return resp
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _get_dr_sources(schema):
|
|
259
|
+
"""
|
|
260
|
+
Returns a dict of names for sources of data_relations:
|
|
261
|
+
|
|
262
|
+
e.g. the name field in the user resource relates to (is a copy of) the
|
|
263
|
+
name field of the person resource
|
|
264
|
+
=> return {'person_name': None}
|
|
265
|
+
"""
|
|
266
|
+
dr_sources = {}
|
|
267
|
+
for rules in schema.values():
|
|
268
|
+
if "data_relation" in rules:
|
|
269
|
+
dr = rules["data_relation"]
|
|
270
|
+
if dr["resource"] not in app.config["DOMAIN"]:
|
|
271
|
+
# source of data_relation does not exist
|
|
272
|
+
continue
|
|
273
|
+
title = app.config["DOMAIN"][dr["resource"]]["item_title"]
|
|
274
|
+
def_name = title + "_" + dr["field"]
|
|
275
|
+
dr_sources[def_name] = None
|
|
276
|
+
elif "schema" in rules:
|
|
277
|
+
if rules.get("type") in "dict":
|
|
278
|
+
# recursively handle data_relations in subdicts
|
|
279
|
+
dr_sources.update(_get_dr_sources(rules["schema"]))
|
|
280
|
+
elif rules.get("type") == "list" and "schema" in rules["schema"]:
|
|
281
|
+
# recursively handle data_relations in lists
|
|
282
|
+
dr_sources.update(_get_dr_sources(rules["schema"]["schema"]))
|
|
283
|
+
return dr_sources
|
eve_swagger/objects.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
eve-swagger.objects
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
swagger.io extension for Eve-powered REST APIs.
|
|
5
|
+
|
|
6
|
+
:copyright: (c) 2015 by Nicola Iarocci.
|
|
7
|
+
:license: BSD, see LICENSE for more details.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from collections import OrderedDict
|
|
12
|
+
from flask import request, current_app as app
|
|
13
|
+
from eve.auth import BasicAuth, TokenAuth
|
|
14
|
+
|
|
15
|
+
from .validation import validate_info
|
|
16
|
+
from .paths import get_ref_schema
|
|
17
|
+
from .definitions import INFO, HOST
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def info():
|
|
21
|
+
validate_info()
|
|
22
|
+
|
|
23
|
+
cfg = app.config[INFO]
|
|
24
|
+
|
|
25
|
+
def node(parent, cfg, key):
|
|
26
|
+
value = cfg.get(key)
|
|
27
|
+
if value:
|
|
28
|
+
parent[key] = cfg[key]
|
|
29
|
+
|
|
30
|
+
info = OrderedDict()
|
|
31
|
+
node(info, cfg, "title")
|
|
32
|
+
node(info, cfg, "description")
|
|
33
|
+
node(info, cfg, "termsOfService")
|
|
34
|
+
node(info, cfg, "contact")
|
|
35
|
+
node(info, cfg, "license")
|
|
36
|
+
node(info, cfg, "version")
|
|
37
|
+
|
|
38
|
+
return info
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def servers():
|
|
42
|
+
url = app.config.get(HOST) or "%s://%s" % (request.scheme, request.host)
|
|
43
|
+
if app.config["URL_PREFIX"]:
|
|
44
|
+
url = url + "/" + app.config["URL_PREFIX"]
|
|
45
|
+
if app.config["API_VERSION"]:
|
|
46
|
+
url = url + "/" + app.config["API_VERSION"]
|
|
47
|
+
return [{"url": url}]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def responses():
|
|
51
|
+
return {
|
|
52
|
+
"error": {
|
|
53
|
+
"description": "An error message",
|
|
54
|
+
"content": {
|
|
55
|
+
"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parameters():
|
|
62
|
+
parameters = OrderedDict()
|
|
63
|
+
# resource parameters
|
|
64
|
+
for resource_name, rd in app.config["DOMAIN"].items():
|
|
65
|
+
if resource_name.endswith("_versions") or rd.get("disable_documentation"):
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
title = rd["item_title"]
|
|
69
|
+
lookup_field = rd["item_lookup_field"]
|
|
70
|
+
if lookup_field not in rd["schema"]:
|
|
71
|
+
rd["schema"][lookup_field] = {"type": "objectid"}
|
|
72
|
+
eve_type = rd["schema"][lookup_field]["type"]
|
|
73
|
+
descr = rd["schema"][lookup_field].get("description") or ""
|
|
74
|
+
example = rd["schema"][lookup_field].get("example") or ""
|
|
75
|
+
if "data_relation" in rd["schema"][lookup_field]:
|
|
76
|
+
# the lookup field is a copy of another field
|
|
77
|
+
dr = rd["schema"][lookup_field]["data_relation"]
|
|
78
|
+
|
|
79
|
+
# resource definition of the data relation source
|
|
80
|
+
source_rd = app.config["DOMAIN"][dr["resource"]]
|
|
81
|
+
|
|
82
|
+
# schema of the data relation source field
|
|
83
|
+
source_def = source_rd["schema"][dr["field"]]
|
|
84
|
+
|
|
85
|
+
# key in #/definitions/...
|
|
86
|
+
source_def_name = source_rd["item_title"] + "_" + dr["field"]
|
|
87
|
+
|
|
88
|
+
# copy description if necessary
|
|
89
|
+
descr = descr or source_def.get("description")
|
|
90
|
+
descr = descr + " (links to {})".format(source_def_name)
|
|
91
|
+
|
|
92
|
+
p = OrderedDict()
|
|
93
|
+
p["in"] = "path"
|
|
94
|
+
p["name"] = title.lower() + "Id"
|
|
95
|
+
p["required"] = True
|
|
96
|
+
p["description"] = descr
|
|
97
|
+
if "SWAGGER_EXAMPLE_FIELD_REMOVE" not in app.config:
|
|
98
|
+
p["example"] = example
|
|
99
|
+
ptype = ""
|
|
100
|
+
if eve_type == "objectid" or eve_type == "datetime":
|
|
101
|
+
ptype = "string"
|
|
102
|
+
elif eve_type == "float":
|
|
103
|
+
ptype = "number"
|
|
104
|
+
else:
|
|
105
|
+
# TODO define default
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
p["schema"] = {"type": ptype}
|
|
109
|
+
parameters[title + "_" + lookup_field] = p
|
|
110
|
+
|
|
111
|
+
# additional_lookup
|
|
112
|
+
if "additional_lookup" in rd:
|
|
113
|
+
lookup_field = rd["additional_lookup"]["field"]
|
|
114
|
+
descr = rd["schema"][lookup_field].get("description") or ""
|
|
115
|
+
example = rd["schema"][lookup_field].get("example") or ""
|
|
116
|
+
|
|
117
|
+
p = OrderedDict()
|
|
118
|
+
p["in"] = "path"
|
|
119
|
+
p["name"] = rd["additional_lookup"]["field"].title()
|
|
120
|
+
p["required"] = True
|
|
121
|
+
p["description"] = descr
|
|
122
|
+
p["example"] = example
|
|
123
|
+
p["schema"] = {"type": "string"}
|
|
124
|
+
|
|
125
|
+
parameters[title + "_" + lookup_field] = p
|
|
126
|
+
|
|
127
|
+
# add header parameters
|
|
128
|
+
parameters.update(_header_parameters())
|
|
129
|
+
# add query parameters
|
|
130
|
+
parameters.update(_query_parameters())
|
|
131
|
+
return parameters
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _query_parameters():
|
|
135
|
+
params = {}
|
|
136
|
+
|
|
137
|
+
r = OrderedDict()
|
|
138
|
+
r["in"] = "query"
|
|
139
|
+
r["name"] = app.config["QUERY_WHERE"]
|
|
140
|
+
r["description"] = 'the filters query parameter (ex.: {"number": 10})'
|
|
141
|
+
r["schema"] = {"type": "string"}
|
|
142
|
+
params["query__where"] = r
|
|
143
|
+
|
|
144
|
+
r = OrderedDict()
|
|
145
|
+
r["in"] = "query"
|
|
146
|
+
r["name"] = app.config["QUERY_PROJECTION"]
|
|
147
|
+
r["description"] = 'the projections query parameter (ex.: {"name": 1})'
|
|
148
|
+
r["schema"] = {"type": "string"}
|
|
149
|
+
params["query__projections"] = r
|
|
150
|
+
|
|
151
|
+
r = OrderedDict()
|
|
152
|
+
r["in"] = "query"
|
|
153
|
+
r["name"] = app.config["QUERY_SORT"]
|
|
154
|
+
r["description"] = 'the sort query parameter (ex.: "city,-lastname")'
|
|
155
|
+
r["schema"] = {"type": "string"}
|
|
156
|
+
params["query__sort"] = r
|
|
157
|
+
|
|
158
|
+
r = OrderedDict()
|
|
159
|
+
r["in"] = "query"
|
|
160
|
+
r["name"] = app.config["QUERY_PAGE"]
|
|
161
|
+
r["description"] = "the pages query parameter"
|
|
162
|
+
r["schema"] = {"type": "integer", "example": 1}
|
|
163
|
+
params["query__page"] = r
|
|
164
|
+
|
|
165
|
+
r = OrderedDict()
|
|
166
|
+
r["in"] = "query"
|
|
167
|
+
r["name"] = app.config["QUERY_MAX_RESULTS"]
|
|
168
|
+
r["description"] = "the max results query parameter"
|
|
169
|
+
r["schema"] = {"type": "integer", "example": 25}
|
|
170
|
+
params["query__max_results"] = r
|
|
171
|
+
|
|
172
|
+
return params
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _header_parameters():
|
|
176
|
+
r = OrderedDict()
|
|
177
|
+
r["in"] = "header"
|
|
178
|
+
r["name"] = "If-Match"
|
|
179
|
+
r["description"] = "Current value of the _etag field"
|
|
180
|
+
r["required"] = app.config["IF_MATCH"] and app.config["ENFORCE_IF_MATCH"]
|
|
181
|
+
r["schema"] = {"type": "string"}
|
|
182
|
+
return {"If-Match": r}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def examples():
|
|
186
|
+
examples = OrderedDict()
|
|
187
|
+
|
|
188
|
+
for resource_name, rd in app.config["DOMAIN"].items():
|
|
189
|
+
if resource_name.endswith("_versions") or rd.get("disable_documentation"):
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
title = rd["item_title"]
|
|
193
|
+
ex = OrderedDict()
|
|
194
|
+
ex["summary"] = "An example {0} document."
|
|
195
|
+
ex["description"] = (
|
|
196
|
+
"An example for {0} documents request bodies."
|
|
197
|
+
" Used in POST, PUT, PATCH methods."
|
|
198
|
+
).format(title)
|
|
199
|
+
if "example" in rd:
|
|
200
|
+
ex["value"] = rd["example"]
|
|
201
|
+
|
|
202
|
+
examples[title] = ex
|
|
203
|
+
|
|
204
|
+
return examples
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def request_bodies():
|
|
208
|
+
def _get_ref_examples(rd):
|
|
209
|
+
return {"$ref": "#/components/examples/%s" % rd["item_title"]}
|
|
210
|
+
|
|
211
|
+
rbodies = OrderedDict()
|
|
212
|
+
|
|
213
|
+
for resource_name, rd in app.config["DOMAIN"].items():
|
|
214
|
+
if resource_name.endswith("_versions") or rd.get("disable_documentation"):
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
title = rd["item_title"]
|
|
218
|
+
rb = OrderedDict()
|
|
219
|
+
description = "A {} document.".format(title)
|
|
220
|
+
if rd["bulk_enabled"]:
|
|
221
|
+
description = "A {0} or list of {0} documents".format(title)
|
|
222
|
+
|
|
223
|
+
rb["description"] = description
|
|
224
|
+
rb["required"] = True
|
|
225
|
+
rb["content"] = {
|
|
226
|
+
# TODO what about other methods
|
|
227
|
+
"application/json": {
|
|
228
|
+
"schema": get_ref_schema(rd),
|
|
229
|
+
"examples": {title: _get_ref_examples(rd)},
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
rbodies[title] = rb
|
|
233
|
+
|
|
234
|
+
return rbodies
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def headers():
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def security_schemes():
|
|
242
|
+
if "flask_oauthlib.provider" in sys.modules.keys():
|
|
243
|
+
url = app.config.get(HOST) or request.host
|
|
244
|
+
return {
|
|
245
|
+
"oAuth2": {
|
|
246
|
+
"type": "oauth2",
|
|
247
|
+
"description": "oAuth2 password credentials.",
|
|
248
|
+
"flows": {
|
|
249
|
+
"password": {
|
|
250
|
+
# TODO why does this not work with a relative path?
|
|
251
|
+
"tokenUrl": url
|
|
252
|
+
+ app.config["SENTINEL_ROUTE_PREFIX"]
|
|
253
|
+
+ app.config["SENTINEL_TOKEN_URL"],
|
|
254
|
+
"scopes": {},
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if "eve_auth_jwt" in sys.modules.keys():
|
|
260
|
+
return {
|
|
261
|
+
"BearerAuth": {
|
|
262
|
+
"type": "http",
|
|
263
|
+
"scheme": "bearer",
|
|
264
|
+
"bearerFormat": "JWT",
|
|
265
|
+
"description": "Bearer authentication scheme.",
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
elif isinstance(app.auth, TokenAuth):
|
|
269
|
+
return {"BearerAuth": {"type": "http", "scheme": "bearer"}}
|
|
270
|
+
elif isinstance(app.auth, BasicAuth):
|
|
271
|
+
return {"BasicAuth": {"type": "http", "scheme": "basic"}}
|
|
272
|
+
else:
|
|
273
|
+
pass # FIXME
|
|
274
|
+
# TODO use app.auth to build the security scheme
|
|
275
|
+
# can not auto generate oauth, maybe should use add_documentation({...})
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def links():
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def callbacks():
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def security():
|
|
287
|
+
if "flask_oauthlib.provider" in sys.modules.keys():
|
|
288
|
+
return [{"oAuth2": []}]
|
|
289
|
+
elif "eve_auth_jwt" in sys.modules.keys():
|
|
290
|
+
return [{"BearerAuth": []}]
|
|
291
|
+
elif isinstance(app.auth, TokenAuth):
|
|
292
|
+
return [{"BearerAuth": []}]
|
|
293
|
+
elif isinstance(app.auth, BasicAuth):
|
|
294
|
+
return [{"BasicAuth": []}]
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def tags():
|
|
298
|
+
tags = []
|
|
299
|
+
for resource_name, rd in app.config["DOMAIN"].items():
|
|
300
|
+
if resource_name.endswith("_versions") or rd.get("disable_documentation"):
|
|
301
|
+
continue
|
|
302
|
+
|
|
303
|
+
tagInfo = {"name": rd["item_title"]}
|
|
304
|
+
|
|
305
|
+
if "description" in rd:
|
|
306
|
+
tagInfo["description"] = rd["description"]
|
|
307
|
+
|
|
308
|
+
tags.append(tagInfo)
|
|
309
|
+
return tags
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def external_docs():
|
|
313
|
+
pass
|