ul-api-utils 9.3.0__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.
Files changed (156) hide show
  1. example/__init__.py +0 -0
  2. example/conf.py +35 -0
  3. example/main.py +24 -0
  4. example/models/__init__.py +0 -0
  5. example/permissions.py +6 -0
  6. example/pure_flask_example.py +65 -0
  7. example/rate_limit_load.py +10 -0
  8. example/redis_repository.py +22 -0
  9. example/routes/__init__.py +0 -0
  10. example/routes/api_some.py +335 -0
  11. example/sockets/__init__.py +0 -0
  12. example/sockets/on_connect.py +16 -0
  13. example/sockets/on_disconnect.py +14 -0
  14. example/sockets/on_json.py +10 -0
  15. example/sockets/on_message.py +13 -0
  16. example/sockets/on_open.py +16 -0
  17. example/workers/__init__.py +0 -0
  18. example/workers/worker.py +28 -0
  19. ul_api_utils/__init__.py +0 -0
  20. ul_api_utils/access/__init__.py +122 -0
  21. ul_api_utils/api_resource/__init__.py +0 -0
  22. ul_api_utils/api_resource/api_request.py +105 -0
  23. ul_api_utils/api_resource/api_resource.py +414 -0
  24. ul_api_utils/api_resource/api_resource_config.py +20 -0
  25. ul_api_utils/api_resource/api_resource_error_handling.py +21 -0
  26. ul_api_utils/api_resource/api_resource_fn_typing.py +356 -0
  27. ul_api_utils/api_resource/api_resource_type.py +16 -0
  28. ul_api_utils/api_resource/api_response.py +300 -0
  29. ul_api_utils/api_resource/api_response_db.py +26 -0
  30. ul_api_utils/api_resource/api_response_payload_alias.py +25 -0
  31. ul_api_utils/api_resource/db_types.py +9 -0
  32. ul_api_utils/api_resource/signature_check.py +41 -0
  33. ul_api_utils/commands/__init__.py +0 -0
  34. ul_api_utils/commands/cmd_enc_keys.py +172 -0
  35. ul_api_utils/commands/cmd_gen_api_user_token.py +77 -0
  36. ul_api_utils/commands/cmd_gen_new_api_user.py +106 -0
  37. ul_api_utils/commands/cmd_generate_api_docs.py +181 -0
  38. ul_api_utils/commands/cmd_start.py +110 -0
  39. ul_api_utils/commands/cmd_worker_start.py +76 -0
  40. ul_api_utils/commands/start/__init__.py +0 -0
  41. ul_api_utils/commands/start/gunicorn.conf.local.py +0 -0
  42. ul_api_utils/commands/start/gunicorn.conf.py +26 -0
  43. ul_api_utils/commands/start/wsgi.py +22 -0
  44. ul_api_utils/conf/ul-debugger-main.js +1 -0
  45. ul_api_utils/conf/ul-debugger-ui.js +1 -0
  46. ul_api_utils/conf.py +70 -0
  47. ul_api_utils/const.py +78 -0
  48. ul_api_utils/debug/__init__.py +0 -0
  49. ul_api_utils/debug/debugger.py +119 -0
  50. ul_api_utils/debug/malloc.py +93 -0
  51. ul_api_utils/debug/stat.py +444 -0
  52. ul_api_utils/encrypt/__init__.py +0 -0
  53. ul_api_utils/encrypt/encrypt_decrypt_abstract.py +15 -0
  54. ul_api_utils/encrypt/encrypt_decrypt_aes_xtea.py +59 -0
  55. ul_api_utils/errors.py +200 -0
  56. ul_api_utils/internal_api/__init__.py +0 -0
  57. ul_api_utils/internal_api/__tests__/__init__.py +0 -0
  58. ul_api_utils/internal_api/__tests__/internal_api.py +29 -0
  59. ul_api_utils/internal_api/__tests__/internal_api_content_type.py +22 -0
  60. ul_api_utils/internal_api/internal_api.py +369 -0
  61. ul_api_utils/internal_api/internal_api_check_context.py +42 -0
  62. ul_api_utils/internal_api/internal_api_error.py +17 -0
  63. ul_api_utils/internal_api/internal_api_response.py +296 -0
  64. ul_api_utils/main.py +29 -0
  65. ul_api_utils/modules/__init__.py +0 -0
  66. ul_api_utils/modules/__tests__/__init__.py +0 -0
  67. ul_api_utils/modules/__tests__/test_api_sdk_jwt.py +195 -0
  68. ul_api_utils/modules/api_sdk.py +555 -0
  69. ul_api_utils/modules/api_sdk_config.py +63 -0
  70. ul_api_utils/modules/api_sdk_jwt.py +377 -0
  71. ul_api_utils/modules/intermediate_state.py +34 -0
  72. ul_api_utils/modules/worker_context.py +35 -0
  73. ul_api_utils/modules/worker_sdk.py +109 -0
  74. ul_api_utils/modules/worker_sdk_config.py +13 -0
  75. ul_api_utils/py.typed +0 -0
  76. ul_api_utils/resources/__init__.py +0 -0
  77. ul_api_utils/resources/caching.py +196 -0
  78. ul_api_utils/resources/debugger_scripts.py +97 -0
  79. ul_api_utils/resources/health_check/__init__.py +0 -0
  80. ul_api_utils/resources/health_check/const.py +2 -0
  81. ul_api_utils/resources/health_check/health_check.py +439 -0
  82. ul_api_utils/resources/health_check/health_check_template.py +64 -0
  83. ul_api_utils/resources/health_check/resource.py +97 -0
  84. ul_api_utils/resources/not_implemented.py +25 -0
  85. ul_api_utils/resources/permissions.py +29 -0
  86. ul_api_utils/resources/rate_limitter.py +84 -0
  87. ul_api_utils/resources/socketio.py +55 -0
  88. ul_api_utils/resources/swagger.py +119 -0
  89. ul_api_utils/resources/web_forms/__init__.py +0 -0
  90. ul_api_utils/resources/web_forms/custom_fields/__init__.py +0 -0
  91. ul_api_utils/resources/web_forms/custom_fields/custom_checkbox_select.py +5 -0
  92. ul_api_utils/resources/web_forms/custom_widgets/__init__.py +0 -0
  93. ul_api_utils/resources/web_forms/custom_widgets/custom_select_widget.py +86 -0
  94. ul_api_utils/resources/web_forms/custom_widgets/custom_text_input_widget.py +42 -0
  95. ul_api_utils/resources/web_forms/uni_form.py +75 -0
  96. ul_api_utils/sentry.py +52 -0
  97. ul_api_utils/utils/__init__.py +0 -0
  98. ul_api_utils/utils/__tests__/__init__.py +0 -0
  99. ul_api_utils/utils/__tests__/api_path_version.py +16 -0
  100. ul_api_utils/utils/__tests__/unwrap_typing.py +67 -0
  101. ul_api_utils/utils/api_encoding.py +51 -0
  102. ul_api_utils/utils/api_format.py +61 -0
  103. ul_api_utils/utils/api_method.py +55 -0
  104. ul_api_utils/utils/api_pagination.py +58 -0
  105. ul_api_utils/utils/api_path_version.py +60 -0
  106. ul_api_utils/utils/api_request_info.py +6 -0
  107. ul_api_utils/utils/avro.py +131 -0
  108. ul_api_utils/utils/broker_topics_message_count.py +47 -0
  109. ul_api_utils/utils/cached_per_request.py +23 -0
  110. ul_api_utils/utils/colors.py +31 -0
  111. ul_api_utils/utils/constants.py +7 -0
  112. ul_api_utils/utils/decode_base64.py +9 -0
  113. ul_api_utils/utils/deprecated.py +19 -0
  114. ul_api_utils/utils/flags.py +29 -0
  115. ul_api_utils/utils/flask_swagger_generator/__init__.py +0 -0
  116. ul_api_utils/utils/flask_swagger_generator/conf.py +4 -0
  117. ul_api_utils/utils/flask_swagger_generator/exceptions.py +7 -0
  118. ul_api_utils/utils/flask_swagger_generator/specifiers/__init__.py +0 -0
  119. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +57 -0
  120. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_specifier.py +48 -0
  121. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +777 -0
  122. ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_version.py +40 -0
  123. ul_api_utils/utils/flask_swagger_generator/utils/__init__.py +0 -0
  124. ul_api_utils/utils/flask_swagger_generator/utils/input_type.py +77 -0
  125. ul_api_utils/utils/flask_swagger_generator/utils/parameter_type.py +51 -0
  126. ul_api_utils/utils/flask_swagger_generator/utils/replace_in_dict.py +18 -0
  127. ul_api_utils/utils/flask_swagger_generator/utils/request_type.py +52 -0
  128. ul_api_utils/utils/flask_swagger_generator/utils/schema_type.py +15 -0
  129. ul_api_utils/utils/flask_swagger_generator/utils/security_type.py +39 -0
  130. ul_api_utils/utils/imports.py +16 -0
  131. ul_api_utils/utils/instance_checks.py +16 -0
  132. ul_api_utils/utils/jinja/__init__.py +0 -0
  133. ul_api_utils/utils/jinja/t_url_for.py +19 -0
  134. ul_api_utils/utils/jinja/to_pretty_json.py +11 -0
  135. ul_api_utils/utils/json_encoder.py +126 -0
  136. ul_api_utils/utils/load_modules.py +15 -0
  137. ul_api_utils/utils/memory_db/__init__.py +0 -0
  138. ul_api_utils/utils/memory_db/__tests__/__init__.py +0 -0
  139. ul_api_utils/utils/memory_db/errors.py +8 -0
  140. ul_api_utils/utils/memory_db/repository.py +102 -0
  141. ul_api_utils/utils/token_check.py +14 -0
  142. ul_api_utils/utils/token_check_through_request.py +16 -0
  143. ul_api_utils/utils/unwrap_typing.py +117 -0
  144. ul_api_utils/utils/uuid_converter.py +22 -0
  145. ul_api_utils/validators/__init__.py +0 -0
  146. ul_api_utils/validators/__tests__/__init__.py +0 -0
  147. ul_api_utils/validators/__tests__/test_custom_fields.py +32 -0
  148. ul_api_utils/validators/custom_fields.py +66 -0
  149. ul_api_utils/validators/validate_empty_object.py +10 -0
  150. ul_api_utils/validators/validate_uuid.py +11 -0
  151. ul_api_utils-9.3.0.dist-info/LICENSE +21 -0
  152. ul_api_utils-9.3.0.dist-info/METADATA +279 -0
  153. ul_api_utils-9.3.0.dist-info/RECORD +156 -0
  154. ul_api_utils-9.3.0.dist-info/WHEEL +5 -0
  155. ul_api_utils-9.3.0.dist-info/entry_points.txt +2 -0
  156. ul_api_utils-9.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,777 @@
1
+ import inspect
2
+ import io
3
+ import json
4
+ import re
5
+ from datetime import datetime
6
+ from typing import Callable, List, Union, Any, Optional
7
+
8
+ from pydantic import BaseModel
9
+ from pydantic.v1.fields import FieldInfo
10
+
11
+ from ul_api_utils.access import PermissionDefinition
12
+ from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
13
+ from ul_api_utils.utils.flask_swagger_generator.specifiers.swagger_models import SwaggerModel
14
+ from ul_api_utils.utils.flask_swagger_generator.specifiers.swagger_specifier import SwaggerSpecifier
15
+ from ul_api_utils.utils.flask_swagger_generator.utils.input_type import InputType
16
+ from ul_api_utils.utils.flask_swagger_generator.utils.replace_in_dict import replace_value_in_dict
17
+ from ul_api_utils.utils.flask_swagger_generator.utils.request_type import RequestType
18
+ from ul_api_utils.utils.flask_swagger_generator.utils.security_type import SecurityType
19
+
20
+
21
+ class SwaggerSecurity(SwaggerModel):
22
+
23
+ def __init__(self, function_names: List[str], security_type: SecurityType) -> None:
24
+ super(SwaggerSecurity, self).__init__()
25
+ self.security_type = security_type
26
+ self.function_names = function_names
27
+
28
+ def perform_write(self, file) -> None: # type: ignore
29
+ if self.security_type.equals(SecurityType.BEARER_AUTH):
30
+ security_entry = inspect.cleandoc(
31
+ """
32
+ security:
33
+ - bearerAuth: []
34
+ """,
35
+ )
36
+
37
+ security_entry = self.indent(security_entry, 3 * self.TAB)
38
+ file.write(security_entry)
39
+ file.write('\n')
40
+
41
+ def perform_component_write(self, file) -> None: # type: ignore
42
+ if SecurityType.BEARER_AUTH.equals(self.security_type):
43
+ security_entry = inspect.cleandoc(
44
+ """
45
+ bearerAuth:
46
+ type: http
47
+ scheme: bearer
48
+ bearerFormat: JWT
49
+ """,
50
+ )
51
+ security_entry = self.indent(security_entry, 2 * self.TAB)
52
+ file.write(security_entry)
53
+ file.write('\n')
54
+
55
+
56
+ class SwaggerSchema(SwaggerModel):
57
+
58
+ def __init__(self, reference_name: str, schema) -> None: # type: ignore
59
+ super(SwaggerSchema, self).__init__()
60
+ self.reference_name = reference_name
61
+ self.schema = schema
62
+ self.properties = {}
63
+ self.items = {}
64
+ self.type = 'object'
65
+
66
+ if isinstance(schema, type) and issubclass(schema, BaseModel):
67
+ json_schema = schema.model_json_schema()
68
+ if '$ref' in json.dumps(json_schema):
69
+ while '$ref' in json.dumps(json_schema):
70
+ json_schema = replace_value_in_dict(json_schema.copy(), json_schema.copy())
71
+ if '$defs' in json_schema:
72
+ del json_schema['$defs']
73
+ self.type = json_schema['type']
74
+ if json_schema['type'] == 'object':
75
+ self.properties = json_schema.get('properties', dict())
76
+ elif json_schema['type'] == 'array':
77
+ self.items = json_schema['items']
78
+
79
+ @staticmethod
80
+ def get_type(value: Union[int, str, float, List[Any]]) -> InputType:
81
+ if isinstance(value, int):
82
+ return InputType.INTEGER
83
+ elif isinstance(value, str):
84
+ return InputType.STRING
85
+ elif isinstance(value, float):
86
+ return InputType.NUMBER
87
+ elif isinstance(value, list):
88
+ return InputType.ARRAY
89
+ else:
90
+ SwaggerGeneratorError("Type {} is not supported".format(type(value))) # type: ignore
91
+
92
+ def perform_write(self, file) -> None: # type: ignore
93
+ if self.type == 'object':
94
+ schema_entry = inspect.cleandoc(
95
+ """
96
+ {}:
97
+ type: object
98
+ properties: {}
99
+ """.format(self.reference_name, self.properties),
100
+ )
101
+
102
+ if self.type == 'array':
103
+ schema_entry = inspect.cleandoc(
104
+ """
105
+ {}:
106
+ type: array
107
+ items: {}
108
+ """.format(self.reference_name, self.items),
109
+ )
110
+ if self.type == 'null':
111
+ schema_entry = inspect.cleandoc(
112
+ """
113
+ {}:
114
+ type: object
115
+ items: {}
116
+ """.format(self.reference_name, self.items),
117
+ )
118
+ if self.type == 'string':
119
+ schema_entry = inspect.cleandoc(
120
+ """
121
+ {}:
122
+ type: string
123
+ items: {}
124
+ """.format(self.reference_name, self.items),
125
+ )
126
+ schema_entry = self.indent(schema_entry, 2 * self.TAB)
127
+ file.write(schema_entry)
128
+ file.write('\n')
129
+
130
+
131
+ class SwaggerResponses(SwaggerModel):
132
+ def perform_write(self, file) -> None: # type: ignore
133
+ responses_entry = 'responses:'
134
+ responses_entry = self.indent(responses_entry, 3 * self.TAB)
135
+ file.write(responses_entry)
136
+ file.write('\n')
137
+
138
+
139
+ class SwaggerResponse(SwaggerModel):
140
+ def __init__(
141
+ self,
142
+ function_name: str,
143
+ schema_reference: str,
144
+ status_code: int = 200,
145
+ description: Optional[str] = None,
146
+ ) -> None:
147
+ super(SwaggerResponse, self).__init__()
148
+ self.function_name = function_name
149
+ self.description = description
150
+ self.status_code = status_code
151
+ self.schema_reference = schema_reference
152
+ self.response_reference = function_name + '_response'
153
+
154
+ def perform_write(self, file) -> None: # type: ignore
155
+ response_entry = inspect.cleandoc(
156
+ """
157
+ '{}':
158
+ $ref: '#/components/responses/{}'
159
+ """.format(self.status_code, self.response_reference),
160
+ )
161
+ response_entry = self.indent(response_entry, 4 * self.TAB)
162
+ file.write(response_entry)
163
+ file.write('\n')
164
+
165
+ def perform_component_write(self, file) -> None: # type: ignore
166
+ if self.description:
167
+ component_entry = inspect.cleandoc(
168
+ """
169
+ {}:
170
+ description: {}
171
+ content:
172
+ application/json:
173
+ schema:
174
+ $ref: '#/components/schemas/{}'
175
+ """.format(
176
+ self.response_reference,
177
+ self.description,
178
+ self.schema_reference,
179
+ ),
180
+ )
181
+ else:
182
+ component_entry = inspect.cleandoc(
183
+ """
184
+ {}:
185
+ description: {}
186
+ content:
187
+ application/json:
188
+ schema:
189
+ $ref: '#/components/schemas/{}'
190
+ """.format(
191
+ self.response_reference,
192
+ "{} response".format(self.function_name),
193
+ self.schema_reference,
194
+ ),
195
+ )
196
+
197
+ component_entry = self.indent(component_entry, 2 * self.TAB)
198
+ file.write(component_entry)
199
+ file.write('\n')
200
+
201
+
202
+ class SwaggerRequestBody(SwaggerModel):
203
+ def __init__(
204
+ self,
205
+ function_name: str,
206
+ schema_reference: str,
207
+ description: Optional[str] = None,
208
+ required: bool = True,
209
+ ) -> None:
210
+ super(SwaggerRequestBody, self).__init__()
211
+ self.function_name = function_name
212
+ self.description = description
213
+ self.required = required
214
+ self.request_body_reference = \
215
+ self.function_name + '_request_body'
216
+ self.schema_reference = schema_reference
217
+
218
+ def perform_write(self, file) -> None: # type: ignore
219
+ request_body_entry = inspect.cleandoc(
220
+ """
221
+ requestBody:
222
+ required: {}
223
+ content:
224
+ application/json:
225
+ schema:
226
+ $ref: '#/components/schemas/{}'
227
+ """.format(
228
+ self.required,
229
+ self.schema_reference,
230
+ ),
231
+ )
232
+ request_body_entry = self.indent(request_body_entry, 3 * self.TAB)
233
+ file.write(request_body_entry)
234
+ file.write('\n')
235
+
236
+ def perform_component_write(self, file) -> None: # type: ignore
237
+ component_entry = inspect.cleandoc(
238
+ """
239
+ {}:
240
+ description: {}
241
+ required: {}
242
+ content:
243
+ application/json:
244
+ schema:
245
+ $ref: '#/components/schemas/{}'
246
+ """.format(
247
+ self.request_body_reference,
248
+ self.description,
249
+ self.required,
250
+ self.schema_reference,
251
+ ),
252
+ )
253
+
254
+ component_entry = self.indent(component_entry, 2 * self.TAB)
255
+ file.write(component_entry)
256
+ file.write('\n')
257
+
258
+
259
+ class SwaggerOperationId(SwaggerModel):
260
+ def __init__(self, function_name: str) -> None:
261
+ super(SwaggerOperationId, self).__init__()
262
+ self.operation_id = function_name
263
+
264
+ def perform_write(self, file) -> None: # type: ignore
265
+ operation_id_entry = "operationId: '{}'".format(self.operation_id)
266
+ operation_id_entry = self.indent(operation_id_entry, 3 * self.TAB)
267
+ file.write(operation_id_entry)
268
+ file.write('\n')
269
+
270
+
271
+ class SwaggerParameters(SwaggerModel):
272
+ def perform_write(self, file) -> None: # type: ignore
273
+ parameters_entry = "parameters:"
274
+ parameters_entry = self.indent(parameters_entry, 3 * self.TAB)
275
+ file.write(parameters_entry)
276
+ file.write('\n')
277
+
278
+
279
+ class SwaggerTag(SwaggerModel):
280
+ def __init__(self, group_name: str) -> None:
281
+ super(SwaggerTag, self).__init__()
282
+ self.group_name = group_name
283
+
284
+ def perform_write(self, file) -> None: # type: ignore
285
+ group_entry = inspect.cleandoc(
286
+ """
287
+ tags:
288
+ - {}
289
+ """.format(self.group_name),
290
+ )
291
+
292
+ group_entry = self.indent(group_entry, 3 * self.TAB)
293
+ file.write(group_entry)
294
+ file.write('\n')
295
+
296
+
297
+ class SwaggerRequestType(SwaggerModel):
298
+
299
+ def __init__(self, function_name: str, request_type: RequestType, summary: Optional[str] = None, description: Optional[str] = None) -> None:
300
+ super(SwaggerRequestType, self).__init__()
301
+ self.request_type = request_type
302
+ self.function_name = function_name
303
+ self.summary = summary
304
+ self.description = description
305
+
306
+ def perform_write(self, file) -> None: # type: ignore
307
+ request_type_entry = "{}:".format(self.request_type.value)
308
+ if self.summary is not None:
309
+ request_type_entry += f"\n summary: {self.summary}"
310
+ if self.description is not None:
311
+ request_type_entry += f"\n description: |\n{self.TAB}{self.TAB}{self.description}"
312
+ request_type_entry = self.indent(request_type_entry, 2 * self.TAB)
313
+
314
+ file.write(request_type_entry)
315
+ file.write('\n')
316
+
317
+
318
+ class SwaggerPathParameter(SwaggerModel):
319
+ def __init__(
320
+ self,
321
+ input_type: Optional[str] = None,
322
+ name: Optional[str] = None,
323
+ description: Optional[str] = None,
324
+ required: bool = True,
325
+ ) -> None:
326
+ super(SwaggerPathParameter, self).__init__()
327
+ self.input_type = InputType.from_string(input_type) # type: ignore
328
+ self.name = name
329
+ self.description = f'description: {description}' if description else ''
330
+ self.required = required
331
+
332
+ def perform_write(self, file): # type: ignore
333
+ if self.input_type == InputType.UUID:
334
+ parameter_entry = inspect.cleandoc(
335
+ """
336
+ - in: path
337
+ name: {name}
338
+ schema:
339
+ type: string
340
+ format: uuid
341
+ {description}
342
+ required: {required}
343
+ """.format(
344
+ name=self.name,
345
+ required=self.required,
346
+ description=self.description,
347
+ ),
348
+ )
349
+ else:
350
+ parameter_entry = inspect.cleandoc(
351
+ """
352
+ - in: path
353
+ name: {name}
354
+ schema:
355
+ type: {input_type}
356
+ {description}
357
+ required: {required}
358
+ """.format(
359
+ name=self.name,
360
+ required=self.required,
361
+ description=self.description,
362
+ input_type=self.input_type.value,
363
+ ),
364
+ )
365
+
366
+ param = self.indent(parameter_entry, 3 * self.TAB)
367
+ file.write(param)
368
+ file.write("\n")
369
+
370
+
371
+ class SwaggerQueryParameter(SwaggerModel):
372
+ def __init__(
373
+ self,
374
+ input_type: Optional[str] = None,
375
+ input_format: Optional[str] = None,
376
+ default_value: Optional[str] = None,
377
+ name: Optional[str] = None,
378
+ description: Optional[str] = None,
379
+ required: bool = True,
380
+ enum: Optional[list] = None, # type: ignore
381
+ ) -> None:
382
+ super(SwaggerQueryParameter, self).__init__()
383
+ self.input_type = InputType.from_string(input_type) # type: ignore
384
+ self.input_format = f'format: {input_format}' if input_format else ''
385
+ self.default_value = f'default: {default_value}' if default_value else ''
386
+ self.name = name
387
+ self.description = f'description: {description}' if description else ''
388
+ self.required = required
389
+ self.enum = enum
390
+
391
+ def perform_write(self, file) -> None: # type: ignore
392
+ if self.input_type.value == InputType.UUID:
393
+ parameter_entry = inspect.cleandoc(
394
+ """
395
+ - in: query
396
+ name: {name}
397
+ schema:
398
+ type: string
399
+ format: uuid
400
+ {default_value}
401
+ {description}
402
+ required: {required}
403
+ """.format(
404
+ name=self.name,
405
+ required=self.required,
406
+ description=self.description,
407
+ default_value=self.default_value,
408
+ ),
409
+ )
410
+ else:
411
+ if self.enum is not None:
412
+ parameter_entry = inspect.cleandoc(
413
+ """
414
+ - in: query
415
+ name: {name}
416
+ schema:
417
+ type: {input_type}
418
+ {input_format}
419
+ enum: {enum}
420
+ {default_value}
421
+ {description}
422
+ required: {required}
423
+ """.format(
424
+ name=self.name,
425
+ required=self.required,
426
+ description=self.description,
427
+ input_type=self.input_type.value,
428
+ input_format=self.input_format,
429
+ default_value=self.default_value,
430
+ enum=self.enum,
431
+ ),
432
+ )
433
+ else:
434
+ parameter_entry = inspect.cleandoc(
435
+ """
436
+ - in: query
437
+ name: {name}
438
+ schema:
439
+ type: {input_type}
440
+ {input_format}
441
+ {default_value}
442
+ {description}
443
+ required: {required}
444
+ """.format(
445
+ name=self.name,
446
+ required=self.required,
447
+ description=self.description,
448
+ input_type=self.input_type.value,
449
+ input_format=self.input_format,
450
+ default_value=self.default_value,
451
+ ),
452
+ )
453
+
454
+ param = self.indent(parameter_entry, 3 * self.TAB)
455
+ file.write(param)
456
+ file.write("\n")
457
+
458
+ def __hash__(self) -> int:
459
+ return hash(f'{self.input_type}{self.input_format}{self.default_value}{self.name}{self.description}{self.required}')
460
+
461
+ def __eq__(self, other: object) -> bool:
462
+ return hash(self) == hash(other)
463
+
464
+
465
+ class SwaggerPath(SwaggerModel):
466
+ def __init__(self, func_name: str, func_object: Callable[..., Any], group: str, path: str, request_types: List[str]) -> None:
467
+ super(SwaggerPath, self).__init__()
468
+ self.func_name = func_name
469
+ self.func_obj = func_object
470
+ self.path = path
471
+ self.group = group
472
+ self.add_request_types(func_name, func_object, request_types)
473
+
474
+ def add_request_types(self, function_name, function_object, request_types: List[str]) -> None: # type: ignore
475
+ for request_type in request_types:
476
+ if request_type not in {'OPTIONS', 'HEAD'}:
477
+ access: PermissionDefinition = function_object.__closure__[0].cell_contents
478
+ swagger_request_type = SwaggerRequestType(
479
+ function_name,
480
+ RequestType.from_string(request_type),
481
+ f'permission={access.category + "/" if access.category else ""}{access.key}({access.id})',
482
+ function_object.__doc__,
483
+ )
484
+ swagger_request_type.add_swagger_model(SwaggerTag(self.group))
485
+ swagger_request_type.add_swagger_model(SwaggerOperationId(function_name))
486
+ self.add_swagger_model(swagger_request_type)
487
+
488
+ def perform_write(self, file) -> None: # type: ignore
489
+ self.index_path_parameters()
490
+ self.index_query_parameters()
491
+ self.format_path()
492
+
493
+ path_entry = "'{path}':".format(path=self.path)
494
+ path = self.indent(path_entry, self.TAB)
495
+ file.write(path)
496
+ file.write("\n")
497
+
498
+ def index_query_parameters(self) -> None:
499
+ parameter_models = set()
500
+ query_model = self.func_obj.__annotations__.get('query')
501
+ if query_model is not None and issubclass(query_model, BaseModel):
502
+ swagger_request_types = self.get_swagger_child_models_of_type(SwaggerRequestType)
503
+ for swagger_request_type in swagger_request_types:
504
+ parameters: List[SwaggerModel] = swagger_request_type.get_swagger_child_models_of_type(SwaggerParameters)
505
+
506
+ if not parameters:
507
+ parameters = SwaggerParameters() # type: ignore
508
+ swagger_request_type.add_swagger_model(parameters)
509
+ query_schema = query_model.model_json_schema()
510
+ query_required_fields = set(query_schema.get('required')) if query_schema.get('required') is not None else set()
511
+ query_definitions = query_schema.get('$defs')
512
+ for parameter_name, parameter_spec in query_schema.get('properties').items():
513
+ if 'anyOf' in parameter_spec.keys():
514
+ if parameter_spec.get('anyOf')[0].get('$ref') is not None:
515
+ definition = query_definitions.get(parameter_spec.get('$ref', '').split('/')[-1], {})
516
+ parameter_models.add(SwaggerQueryParameter(
517
+ input_type=definition.get('type', 'string'),
518
+ input_format=definition.get('format'),
519
+ default_value=definition.get('default'),
520
+ name=parameter_name,
521
+ description=definition.get('description'),
522
+ enum=definition.get('enum'),
523
+ required=parameter_name in query_required_fields,
524
+ ))
525
+ if parameter_spec.get('anyOf')[0].get('type') is not None:
526
+ parameter_models.add(SwaggerQueryParameter(
527
+ input_type=parameter_spec.get('anyOf')[0].get('type'),
528
+ input_format=parameter_spec.get('format'),
529
+ default_value=parameter_spec.get('default'),
530
+ name=parameter_name,
531
+ description=parameter_spec.get('description'),
532
+ required=parameter_name in query_required_fields,
533
+ ))
534
+ elif parameter_spec.get('$ref') is not None:
535
+ definition = query_definitions.get(parameter_spec.get('$ref', '').split('/')[-1], {})
536
+ parameter_models.add(SwaggerQueryParameter(
537
+ input_type=definition.get('type', 'string'),
538
+ input_format=definition.get('format'),
539
+ default_value=definition.get('default'),
540
+ name=parameter_name,
541
+ description=definition.get('description'),
542
+ enum=definition.get('enum'),
543
+ required=parameter_name in query_required_fields,
544
+ ))
545
+ elif parameter_spec.get('type') is not None:
546
+ parameter_models.add(SwaggerQueryParameter(
547
+ input_type=parameter_spec.get('type') or parameter_spec.get('anyOf')[0].get('type'),
548
+ input_format=parameter_spec.get('format'),
549
+ default_value=parameter_spec.get('default'),
550
+ name=parameter_name,
551
+ description=parameter_spec.get('description'),
552
+ required=parameter_name in query_required_fields,
553
+ ))
554
+ if isinstance(parameters, SwaggerModel):
555
+ parameters.add_swagger_models(parameter_models)
556
+ elif isinstance(parameters, list):
557
+ for parameter in parameters:
558
+ parameter.add_swagger_models(parameter_models)
559
+
560
+ def index_path_parameters(self) -> None:
561
+ parameters = re.findall("<(.*?)>", self.path)
562
+ swagger_request_types = self.get_swagger_child_models_of_type(SwaggerRequestType)
563
+ parameter_models = []
564
+
565
+ if parameters:
566
+ for parameter in parameters:
567
+ if len(parameter.split(':')) > 1:
568
+ input_type, name = parameter.split(':')
569
+ else:
570
+ input_type = 'str'
571
+ name = parameter
572
+ fn_signature = inspect.signature(self.func_obj)
573
+ fn_param_default = fn_signature.parameters.get(name).default # type: ignore
574
+ description = None
575
+ if isinstance(fn_param_default, FieldInfo):
576
+ description = fn_param_default.description
577
+ parameter_models.append(SwaggerPathParameter(input_type, name, description, True))
578
+
579
+ for swagger_request_type in swagger_request_types:
580
+ parameters: List[SwaggerModel] = swagger_request_type.get_swagger_child_models_of_type(SwaggerParameters) # type: ignore
581
+ if not parameters:
582
+ parameters = SwaggerParameters() # type: ignore
583
+ swagger_request_type.add_swagger_model(parameters)
584
+ if isinstance(parameters, SwaggerModel):
585
+ parameters.add_swagger_models(parameter_models)
586
+ elif isinstance(parameters, list):
587
+ for parameter in parameters:
588
+ parameter.add_swagger_models(parameter_models)
589
+
590
+ def format_path(self) -> None:
591
+ if len(re.findall("<(.*?)>", self.path)) > 0:
592
+ swagger_request_types = self.get_swagger_child_models_of_type(SwaggerRequestType)
593
+ parameters = swagger_request_types[-1].get_swagger_child_models_of_type(SwaggerParameters)
594
+ path_parameters = parameters[-1].get_swagger_child_models_of_type(SwaggerPathParameter)
595
+ query_parameters = parameters[-1].get_swagger_child_models_of_type(SwaggerQueryParameter)
596
+ for path_parameter in path_parameters:
597
+ self.path = self.path.replace(
598
+ "<{}:{}>".format(path_parameter.input_type.get_flask_input_type_value(), path_parameter.name),
599
+ "{" + path_parameter.name + "}",
600
+ )
601
+ for query_parameter in query_parameters:
602
+ self.path = self.path.replace(
603
+ "<{}:{}>".format(query_parameter.input_type.get_flask_input_type_value(), query_parameter.name),
604
+ "{" + query_parameter.name + "}",
605
+ )
606
+
607
+
608
+ class SwaggerThreeSpecifier(SwaggerModel, SwaggerSpecifier):
609
+ __slots__ = tuple([
610
+ 'request_bodies',
611
+ 'schemas',
612
+ 'responses',
613
+ 'securities',
614
+ 'swagger_models',
615
+ 'application_name',
616
+ 'application_version',
617
+ ])
618
+
619
+ def __init__(self) -> None:
620
+ super().__init__()
621
+ self.request_bodies = [] # type: ignore
622
+ self.schemas = [] # type: ignore
623
+ self.responses = [] # type: ignore
624
+ self.securities = [] # type: ignore
625
+
626
+ def perform_write(self, file) -> None: # type: ignore
627
+ # Add all request bodies to request_types with same function name
628
+ self._add_request_bodies_to_paths()
629
+ self._add_responses_to_paths()
630
+ self._add_securities_to_paths()
631
+
632
+ meta = inspect.cleandoc("""
633
+ openapi: 3.0.1
634
+ info:
635
+ title: {name}
636
+ description: Generated at {time}. This is the swagger
637
+ ui based on the open api 3.0 specification of the {name}
638
+ version: {version}
639
+ externalDocs:
640
+ description: Find out more about Swagger
641
+ url: 'http://swagger.io'
642
+ servers:
643
+ - url: /
644
+ """.format(name=self.application_name, version=self.application_version, time=datetime.now().strftime("%d/%m/%Y %H:%M:%S")),
645
+ )
646
+
647
+ file.write(meta)
648
+ file.write("\n")
649
+ file.write("paths:")
650
+ file.write("\n")
651
+
652
+ def write(self, file: io.TextIOBase) -> None:
653
+ """
654
+ Overwrite the write method to add some additional functionality.
655
+ After the perform write action the swagger specifier
656
+ wil add the models to the bottom of the swagger definition
657
+ """
658
+ super().write(file)
659
+
660
+ file.write('components:')
661
+ file.write('\n')
662
+ if len(self.securities) > 0:
663
+ securities_entry = 'securitySchemes:'
664
+ securities_entry = self.indent(securities_entry, self.TAB)
665
+ file.write(securities_entry)
666
+ file.write('\n')
667
+ for security in self.securities:
668
+ security.perform_component_write(file)
669
+
670
+ if len(self.request_bodies) > 0:
671
+ request_bodies_entries = 'requestBodies:'
672
+ request_bodies_entries = self.indent(request_bodies_entries, self.TAB)
673
+ file.write(request_bodies_entries)
674
+ file.write('\n')
675
+
676
+ for request_body in self.request_bodies:
677
+ request_body.perform_component_write(file)
678
+
679
+ if len(self.responses) > 0:
680
+ response_entries = 'responses:'
681
+ response_entries = self.indent(response_entries, self.TAB)
682
+ file.write(response_entries)
683
+ file.write('\n')
684
+
685
+ for response in self.responses:
686
+ response.perform_component_write(file)
687
+
688
+ if len(self.schemas) > 0:
689
+ schemas_entries = 'schemas:'
690
+ schemas_entries = self.indent(schemas_entries, self.TAB)
691
+ file.write(schemas_entries)
692
+ file.write('\n')
693
+
694
+ for schema in self.schemas:
695
+ schema.perform_write(file)
696
+
697
+ def _add_request_bodies_to_paths(self) -> None:
698
+ swagger_paths = self.get_swagger_child_models_of_type(SwaggerPath)
699
+ for swagger_path in swagger_paths:
700
+ # Get the request types
701
+ swagger_request_types = swagger_path.get_swagger_child_models_of_type(SwaggerRequestType)
702
+ for swagger_request_type in swagger_request_types:
703
+ for request_body in self.request_bodies:
704
+ if swagger_request_type.function_name == request_body.function_name:
705
+ swagger_request_type.add_swagger_model(request_body)
706
+
707
+ def _add_responses_to_paths(self) -> None:
708
+ swagger_paths = self.get_swagger_child_models_of_type(SwaggerPath)
709
+
710
+ for swagger_path in swagger_paths:
711
+ # Get the request types
712
+ swagger_request_types = swagger_path.get_swagger_child_models_of_type(SwaggerRequestType)
713
+ for swagger_request_type in swagger_request_types:
714
+ for response in self.responses:
715
+ if swagger_request_type.function_name == response.function_name:
716
+ responses_model = swagger_request_type.get_swagger_child_models_of_type(SwaggerResponses)
717
+ if not responses_model:
718
+ responses_model = SwaggerResponses()
719
+ swagger_request_type.add_swagger_model(responses_model)
720
+ responses_model.add_swagger_model(response)
721
+
722
+ def _add_securities_to_paths(self) -> None:
723
+ swagger_paths = self.get_swagger_child_models_of_type(SwaggerPath)
724
+ for swagger_path in swagger_paths:
725
+ swagger_request_types = swagger_path.get_swagger_child_models_of_type(SwaggerRequestType)
726
+ for swagger_request_type in swagger_request_types:
727
+ for security in self.securities:
728
+ if swagger_request_type.function_name in security.function_names:
729
+ swagger_request_type.add_swagger_model(security)
730
+
731
+ def add_endpoint(self, function_name: str, function_object: Callable[..., Any], path: str, request_types: List[str], group: Optional[str] = None) -> None: # type: ignore
732
+ if path == '/static/<path:filename>':
733
+ return
734
+ swagger_paths = self.get_swagger_child_models_of_type(SwaggerPath)
735
+ for swagger_path in swagger_paths:
736
+ if swagger_path.path == path:
737
+ swagger_path.add_request_types(function_name, function_object, request_types)
738
+ return
739
+ new_swagger_path = SwaggerPath(function_name, function_object, group, path, request_types) # type: ignore
740
+ self.add_swagger_model(new_swagger_path)
741
+
742
+ def add_response(
743
+ self,
744
+ function_name: str,
745
+ status_code: int,
746
+ schema: Union[SwaggerSchema, type[BaseModel]],
747
+ description: str = "",
748
+ ) -> None:
749
+ if not isinstance(schema, SwaggerSchema):
750
+ schema = SwaggerSchema(function_name + "_response_schema", schema)
751
+ self.schemas.append(schema)
752
+ swagger_response = SwaggerResponse(function_name, schema.reference_name, status_code, description.strip())
753
+ self.responses.append(swagger_response)
754
+
755
+ def add_query_parameters(self) -> None:
756
+ pass
757
+
758
+ def add_request_body(self, function_name: str, schema) -> None: # type: ignore
759
+ if not isinstance(schema, SwaggerSchema):
760
+ schema = SwaggerSchema(function_name + "_request_body_schema", schema)
761
+ self.schemas.append(schema)
762
+ swagger_request_body = SwaggerRequestBody(function_name, schema.reference_name)
763
+ self.request_bodies.append(swagger_request_body)
764
+
765
+ def add_security(self, function_name: str, security_type: SecurityType) -> None:
766
+ for security in self.securities:
767
+ if security.security_type.equals(security_type):
768
+ security.function_names.append(function_name)
769
+ return
770
+ security_model = SwaggerSecurity([function_name], security_type)
771
+ self.securities.append(security_model)
772
+
773
+ def clean(self) -> None:
774
+ self.schemas = []
775
+ self.securities = []
776
+ self.responses = []
777
+ self.swagger_models = []