nsj-rest-lib2 0.0.1__tar.gz → 0.0.3__tar.gz
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.
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/PKG-INFO +1 -1
- nsj_rest_lib2-0.0.3/nsj_rest_lib2/controller/dynamic_controller.py +331 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/service/entity_loader.py +50 -34
- nsj_rest_lib2-0.0.3/nsj_rest_lib2/settings.py +10 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2.egg-info/PKG-INFO +1 -1
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/setup.cfg +1 -1
- nsj_rest_lib2-0.0.1/nsj_rest_lib2/controller/dynamic_controller.py +0 -300
- nsj_rest_lib2-0.0.1/nsj_rest_lib2/settings.py +0 -3
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/README.md +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/__init__.py +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/controller/__init__.py +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/exception.py +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/redis_config.py +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2/service/__init__.py +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2.egg-info/SOURCES.txt +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2.egg-info/requires.txt +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
- {nsj_rest_lib2-0.0.1 → nsj_rest_lib2-0.0.3}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
5
|
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
6
|
Author: Nasajon Sistemas
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from flask import Flask, request
|
|
6
|
+
|
|
7
|
+
from nsj_rest_lib.settings import APP_NAME
|
|
8
|
+
|
|
9
|
+
from nsj_gcf_utils.rest_error_util import format_json_error
|
|
10
|
+
|
|
11
|
+
from nsj_multi_database_lib.decorator.multi_database import multi_database
|
|
12
|
+
|
|
13
|
+
from nsj_rest_lib.controller.controller_util import DEFAULT_RESP_HEADERS
|
|
14
|
+
from nsj_rest_lib.controller.list_route import ListRoute
|
|
15
|
+
from nsj_rest_lib.controller.get_route import GetRoute
|
|
16
|
+
from nsj_rest_lib.controller.post_route import PostRoute
|
|
17
|
+
from nsj_rest_lib.controller.put_route import PutRoute
|
|
18
|
+
from nsj_rest_lib.controller.patch_route import PatchRoute
|
|
19
|
+
from nsj_rest_lib.controller.delete_route import DeleteRoute
|
|
20
|
+
|
|
21
|
+
from nsj_rest_lib2.exception import MissingEntityConfigException
|
|
22
|
+
from nsj_rest_lib2.service.entity_loader import EntityLoader
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_tenant_grupo() -> tuple[str, str]:
|
|
26
|
+
# Tentando ler do query args
|
|
27
|
+
query_args = request.args
|
|
28
|
+
tenant = query_args.get("tenant")
|
|
29
|
+
grupo_empresarial = query_args.get("grupo_empresarial")
|
|
30
|
+
|
|
31
|
+
# Tentando ler do corpo da requisição
|
|
32
|
+
try:
|
|
33
|
+
body_str = request.data.decode("utf-8")
|
|
34
|
+
body_json = json.loads(body_str)
|
|
35
|
+
|
|
36
|
+
if not tenant:
|
|
37
|
+
tenant = body_json.get("tenant")
|
|
38
|
+
if not grupo_empresarial:
|
|
39
|
+
grupo_empresarial = body_json.get("grupo_empresarial")
|
|
40
|
+
except:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
return (str(tenant), str(grupo_empresarial))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _endpoint_name(func: Any, multidb: bool, root: str) -> str:
|
|
47
|
+
suffix = "_mb" if multidb else ""
|
|
48
|
+
return f"{root}_{func.__name__}{suffix}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def setup_dynamic_routes(
|
|
52
|
+
flask_app: Flask,
|
|
53
|
+
multidb: bool = True,
|
|
54
|
+
dynamic_root_path: str = "edl1",
|
|
55
|
+
injector_factory: Any = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
|
|
58
|
+
COLLECTION_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>"
|
|
59
|
+
ONE_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>/<id>"
|
|
60
|
+
|
|
61
|
+
def list_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
62
|
+
|
|
63
|
+
def list_dynamic(*args: Any, **kwargs: Any):
|
|
64
|
+
# Recuperando o identificador da entidade
|
|
65
|
+
if "entity_id" not in kwargs:
|
|
66
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
67
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
68
|
+
entity_id = kwargs.pop("entity_id")
|
|
69
|
+
|
|
70
|
+
# Lendo tenant e grupo_empresarial
|
|
71
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
75
|
+
entity_loader = EntityLoader()
|
|
76
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
77
|
+
entity_loader.load_entity_source(
|
|
78
|
+
entity_id, tenant, grupo_empresarial
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Executando o list pelo RestLib
|
|
83
|
+
route = ListRoute(
|
|
84
|
+
url=COLLECTION_DYNAMIC_ROUTE,
|
|
85
|
+
http_method="GET",
|
|
86
|
+
dto_class=etities_dict[dto_class_name],
|
|
87
|
+
entity_class=etities_dict[entity_class_name],
|
|
88
|
+
injector_factory=injector_factory,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return route.handle_request(*args, **kwargs)
|
|
92
|
+
except MissingEntityConfigException:
|
|
93
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
94
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
95
|
+
|
|
96
|
+
return list_dynamic
|
|
97
|
+
|
|
98
|
+
def get_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
99
|
+
|
|
100
|
+
def get_dynamic(*args: Any, **kwargs: Any):
|
|
101
|
+
# Recuperando o identificador da entidade
|
|
102
|
+
if "entity_id" not in kwargs:
|
|
103
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
104
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
105
|
+
entity_id = kwargs.pop("entity_id")
|
|
106
|
+
|
|
107
|
+
# Lendo tenant e grupo_empresarial
|
|
108
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
112
|
+
entity_loader = EntityLoader()
|
|
113
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
114
|
+
entity_loader.load_entity_source(
|
|
115
|
+
entity_id, tenant, grupo_empresarial
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Executando o list pelo RestLib
|
|
120
|
+
route = GetRoute(
|
|
121
|
+
url=ONE_DYNAMIC_ROUTE,
|
|
122
|
+
http_method="GET",
|
|
123
|
+
dto_class=etities_dict[dto_class_name],
|
|
124
|
+
entity_class=etities_dict[entity_class_name],
|
|
125
|
+
injector_factory=injector_factory,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return route.handle_request(*args, **kwargs)
|
|
129
|
+
except MissingEntityConfigException:
|
|
130
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
131
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
132
|
+
|
|
133
|
+
return get_dynamic
|
|
134
|
+
|
|
135
|
+
def post_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
136
|
+
def post_dynamic(*args: Any, **kwargs: Any):
|
|
137
|
+
# Recuperando o identificador da entidade
|
|
138
|
+
if "entity_id" not in kwargs:
|
|
139
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
140
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
141
|
+
entity_id = kwargs.pop("entity_id")
|
|
142
|
+
|
|
143
|
+
# Lendo tenant e grupo_empresarial
|
|
144
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
148
|
+
entity_loader = EntityLoader()
|
|
149
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
150
|
+
entity_loader.load_entity_source(
|
|
151
|
+
entity_id, tenant, grupo_empresarial
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Executando o list pelo RestLib
|
|
156
|
+
route = PostRoute(
|
|
157
|
+
url=COLLECTION_DYNAMIC_ROUTE,
|
|
158
|
+
http_method="POST",
|
|
159
|
+
dto_class=etities_dict[dto_class_name],
|
|
160
|
+
entity_class=etities_dict[entity_class_name],
|
|
161
|
+
injector_factory=injector_factory,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return route.handle_request(*args, **kwargs)
|
|
165
|
+
except MissingEntityConfigException:
|
|
166
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
167
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
168
|
+
|
|
169
|
+
return post_dynamic
|
|
170
|
+
|
|
171
|
+
def put_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
172
|
+
def put_dynamic(*args: Any, **kwargs: Any):
|
|
173
|
+
# Recuperando o identificador da entidade
|
|
174
|
+
if "entity_id" not in kwargs:
|
|
175
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
176
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
177
|
+
entity_id = kwargs.pop("entity_id")
|
|
178
|
+
|
|
179
|
+
# Lendo tenant e grupo_empresarial
|
|
180
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
184
|
+
entity_loader = EntityLoader()
|
|
185
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
186
|
+
entity_loader.load_entity_source(
|
|
187
|
+
entity_id, tenant, grupo_empresarial
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Executando o list pelo RestLib
|
|
192
|
+
route = PutRoute(
|
|
193
|
+
url=ONE_DYNAMIC_ROUTE,
|
|
194
|
+
http_method="PUT",
|
|
195
|
+
dto_class=etities_dict[dto_class_name],
|
|
196
|
+
entity_class=etities_dict[entity_class_name],
|
|
197
|
+
injector_factory=injector_factory,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return route.handle_request(*args, **kwargs)
|
|
201
|
+
except MissingEntityConfigException:
|
|
202
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
203
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
204
|
+
|
|
205
|
+
return put_dynamic
|
|
206
|
+
|
|
207
|
+
def patch_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
208
|
+
def patch_dynamic(*args: Any, **kwargs: Any):
|
|
209
|
+
# Recuperando o identificador da entidade
|
|
210
|
+
if "entity_id" not in kwargs:
|
|
211
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
212
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
213
|
+
entity_id = kwargs.pop("entity_id")
|
|
214
|
+
|
|
215
|
+
# Lendo tenant e grupo_empresarial
|
|
216
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
220
|
+
entity_loader = EntityLoader()
|
|
221
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
222
|
+
entity_loader.load_entity_source(
|
|
223
|
+
entity_id, tenant, grupo_empresarial
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Executando o list pelo RestLib
|
|
228
|
+
route = PatchRoute(
|
|
229
|
+
url=ONE_DYNAMIC_ROUTE,
|
|
230
|
+
http_method="PATCH",
|
|
231
|
+
dto_class=etities_dict[dto_class_name],
|
|
232
|
+
entity_class=etities_dict[entity_class_name],
|
|
233
|
+
injector_factory=injector_factory,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return route.handle_request(*args, **kwargs)
|
|
237
|
+
except MissingEntityConfigException:
|
|
238
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
239
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
240
|
+
|
|
241
|
+
return patch_dynamic
|
|
242
|
+
|
|
243
|
+
def delete_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
244
|
+
def delete_dynamic(*args: Any, **kwargs: Any):
|
|
245
|
+
# Recuperando o identificador da entidade
|
|
246
|
+
if "entity_id" not in kwargs:
|
|
247
|
+
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
248
|
+
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
249
|
+
entity_id = kwargs.pop("entity_id")
|
|
250
|
+
|
|
251
|
+
# Lendo tenant e grupo_empresarial
|
|
252
|
+
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
253
|
+
|
|
254
|
+
try:
|
|
255
|
+
# Recuperando o código do DTO e Entity correspondente
|
|
256
|
+
entity_loader = EntityLoader()
|
|
257
|
+
dto_class_name, entity_class_name, etities_dict = (
|
|
258
|
+
entity_loader.load_entity_source(
|
|
259
|
+
entity_id, tenant, grupo_empresarial
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Executando o list pelo RestLib
|
|
264
|
+
route = DeleteRoute(
|
|
265
|
+
url=ONE_DYNAMIC_ROUTE,
|
|
266
|
+
http_method="DELETE",
|
|
267
|
+
dto_class=etities_dict[dto_class_name],
|
|
268
|
+
entity_class=etities_dict[entity_class_name],
|
|
269
|
+
injector_factory=injector_factory,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return route.handle_request(*args, **kwargs)
|
|
273
|
+
except MissingEntityConfigException:
|
|
274
|
+
msg = f"Entity configuration for {entity_id} not found."
|
|
275
|
+
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
276
|
+
|
|
277
|
+
return delete_dynamic
|
|
278
|
+
|
|
279
|
+
# Ajustando para o padrão com multi database (se necessário)
|
|
280
|
+
if multidb:
|
|
281
|
+
list_dynamic = multi_database()(list_dynamic_wrapper(injector_factory))
|
|
282
|
+
get_dynamic = multi_database()(get_dynamic_wrapper(injector_factory))
|
|
283
|
+
post_dynamic = multi_database()(post_dynamic_wrapper(injector_factory))
|
|
284
|
+
put_dynamic = multi_database()(put_dynamic_wrapper(injector_factory))
|
|
285
|
+
patch_dynamic = multi_database()(patch_dynamic_wrapper(injector_factory))
|
|
286
|
+
delete_dynamic = multi_database()(delete_dynamic_wrapper(injector_factory))
|
|
287
|
+
else:
|
|
288
|
+
list_dynamic = list_dynamic_wrapper(injector_factory)
|
|
289
|
+
get_dynamic = get_dynamic_wrapper(injector_factory)
|
|
290
|
+
post_dynamic = post_dynamic_wrapper(injector_factory)
|
|
291
|
+
put_dynamic = put_dynamic_wrapper(injector_factory)
|
|
292
|
+
patch_dynamic = patch_dynamic_wrapper(injector_factory)
|
|
293
|
+
delete_dynamic = delete_dynamic_wrapper(injector_factory)
|
|
294
|
+
|
|
295
|
+
# Registrando as rotas no flask
|
|
296
|
+
flask_app.add_url_rule(
|
|
297
|
+
COLLECTION_DYNAMIC_ROUTE,
|
|
298
|
+
endpoint=_endpoint_name(list_dynamic, multidb, dynamic_root_path),
|
|
299
|
+
view_func=list_dynamic,
|
|
300
|
+
methods=["GET"],
|
|
301
|
+
)
|
|
302
|
+
flask_app.add_url_rule(
|
|
303
|
+
ONE_DYNAMIC_ROUTE,
|
|
304
|
+
endpoint=_endpoint_name(get_dynamic, multidb, dynamic_root_path),
|
|
305
|
+
view_func=get_dynamic,
|
|
306
|
+
methods=["GET"],
|
|
307
|
+
)
|
|
308
|
+
flask_app.add_url_rule(
|
|
309
|
+
COLLECTION_DYNAMIC_ROUTE,
|
|
310
|
+
endpoint=_endpoint_name(post_dynamic, multidb, dynamic_root_path),
|
|
311
|
+
view_func=post_dynamic,
|
|
312
|
+
methods=["POST"],
|
|
313
|
+
)
|
|
314
|
+
flask_app.add_url_rule(
|
|
315
|
+
ONE_DYNAMIC_ROUTE,
|
|
316
|
+
endpoint=_endpoint_name(put_dynamic, multidb, dynamic_root_path),
|
|
317
|
+
view_func=put_dynamic,
|
|
318
|
+
methods=["PUT"],
|
|
319
|
+
)
|
|
320
|
+
flask_app.add_url_rule(
|
|
321
|
+
ONE_DYNAMIC_ROUTE,
|
|
322
|
+
endpoint=_endpoint_name(patch_dynamic, multidb, dynamic_root_path),
|
|
323
|
+
view_func=patch_dynamic,
|
|
324
|
+
methods=["PATCH"],
|
|
325
|
+
)
|
|
326
|
+
flask_app.add_url_rule(
|
|
327
|
+
ONE_DYNAMIC_ROUTE,
|
|
328
|
+
endpoint=_endpoint_name(delete_dynamic, multidb, dynamic_root_path),
|
|
329
|
+
view_func=delete_dynamic,
|
|
330
|
+
methods=["DELETE"],
|
|
331
|
+
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import datetime
|
|
1
2
|
import json
|
|
2
3
|
import sys
|
|
3
4
|
import threading
|
|
@@ -7,13 +8,15 @@ from nsj_rest_lib.settings import get_logger
|
|
|
7
8
|
|
|
8
9
|
from nsj_rest_lib2.exception import MissingEntityConfigException
|
|
9
10
|
from nsj_rest_lib2.redis_config import get_redis
|
|
10
|
-
from nsj_rest_lib2.settings import ESCOPO_RESTLIB2
|
|
11
|
+
from nsj_rest_lib2.settings import ESCOPO_RESTLIB2, MIN_TIME_SOURCE_REFRESH
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class LoadedEntity:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.dto_class_name: str = ""
|
|
17
|
+
self.entity_class_name: str = ""
|
|
18
|
+
self.entity_hash: str = ""
|
|
19
|
+
self.loaded_at: datetime.datetime = datetime.datetime.now()
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
class Namespace:
|
|
@@ -24,7 +27,7 @@ class Namespace:
|
|
|
24
27
|
self.module: types.ModuleType = None
|
|
25
28
|
|
|
26
29
|
|
|
27
|
-
namespaces_dict = {}
|
|
30
|
+
namespaces_dict: dict[str, Namespace] = {}
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
class EntityLoader:
|
|
@@ -48,34 +51,51 @@ class EntityLoader:
|
|
|
48
51
|
|
|
49
52
|
# Se conseguiu carregar da memória, verifica se houve alteração no hash, em relação ao redis
|
|
50
53
|
if result is not None:
|
|
51
|
-
# Desempacotando o result
|
|
52
|
-
|
|
54
|
+
# Desempacotando o result e recuperando informações do namespace
|
|
55
|
+
(
|
|
56
|
+
entity_config_key,
|
|
57
|
+
namespace,
|
|
58
|
+
) = result
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
loaded_entity = namespace.loaded_entities[entity_id]
|
|
61
|
+
dto_class_name = loaded_entity.dto_class_name
|
|
62
|
+
entity_class_name = loaded_entity.entity_class_name
|
|
63
|
+
entities_dict = namespace.entities_dict
|
|
58
64
|
|
|
59
|
-
# Se
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
# Se o tempo entre o carregamento e agora for maior do que MIN_TIME_SOURCE_REFRESH minutos,
|
|
66
|
+
# verifica se precisa de refresh
|
|
67
|
+
time_diff = datetime.datetime.now() - loaded_entity.loaded_at
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
if time_diff.total_seconds() >= MIN_TIME_SOURCE_REFRESH * 60:
|
|
70
|
+
# Recuperando do Redis direto pela key (faz uma só chamada ao redis)
|
|
71
|
+
loaded_config = self._load_entity_config_from_redis(
|
|
72
|
+
entity_id, grupo_key, tenant_key, default_key, entity_config_key
|
|
73
|
+
)
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
entity_config_key,
|
|
70
|
-
entity_id,
|
|
71
|
-
check_refresh=True,
|
|
72
|
-
)
|
|
75
|
+
# Se não achar no redis, usa o que estava em memória
|
|
76
|
+
if not loaded_config:
|
|
77
|
+
return (dto_class_name, entity_class_name, entities_dict)
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
# Desempacotando resultado
|
|
80
|
+
entity_config_key, entity_config_str = loaded_config
|
|
81
|
+
|
|
82
|
+
# Executando o código da entidade, só se houver mudança no hash
|
|
83
|
+
result_execute = self._execute_entity_source(
|
|
84
|
+
entity_config_str,
|
|
85
|
+
entity_config_key,
|
|
86
|
+
entity_id,
|
|
87
|
+
check_refresh=True,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Se não carregou novo código, usa o que estava em memória
|
|
91
|
+
if result_execute is None:
|
|
92
|
+
return (dto_class_name, entity_class_name, entities_dict)
|
|
93
|
+
else:
|
|
94
|
+
dto_class_name, entity_class_name, namespace = result_execute
|
|
95
|
+
return (dto_class_name, entity_class_name, namespace.entities_dict)
|
|
76
96
|
else:
|
|
77
|
-
|
|
78
|
-
return (dto_class_name, entity_class_name,
|
|
97
|
+
# Se não deu o intervalo de verificação do refresh, retorna o que está em memória
|
|
98
|
+
return (dto_class_name, entity_class_name, entities_dict)
|
|
79
99
|
|
|
80
100
|
# Se não conseguir recuperar a entidade, procura no redis:
|
|
81
101
|
loaded_config = self._load_entity_config_from_redis(
|
|
@@ -236,7 +256,7 @@ class EntityLoader:
|
|
|
236
256
|
grupo_key: str,
|
|
237
257
|
tenant_key: str,
|
|
238
258
|
default_key: str,
|
|
239
|
-
) -> tuple[str,
|
|
259
|
+
) -> tuple[str, Namespace] | None:
|
|
240
260
|
namespace = None
|
|
241
261
|
entity_config_key = None
|
|
242
262
|
|
|
@@ -258,11 +278,7 @@ class EntityLoader:
|
|
|
258
278
|
entity_config_key = default_key
|
|
259
279
|
namespace = default_namespace
|
|
260
280
|
|
|
261
|
-
if namespace:
|
|
262
|
-
|
|
263
|
-
dto_class_name = loaded_entity.dto_class_name
|
|
264
|
-
entity_class_name = loaded_entity.entity_class_name
|
|
265
|
-
entities_dict = namespace.entities_dict
|
|
266
|
-
return (dto_class_name, entity_class_name, entities_dict, entity_config_key)
|
|
281
|
+
if namespace and entity_config_key:
|
|
282
|
+
return (entity_config_key, namespace)
|
|
267
283
|
else:
|
|
268
284
|
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
5
|
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
6
|
Author: Nasajon Sistemas
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = nsj_rest_lib2
|
|
3
|
-
version = 0.0.
|
|
3
|
+
version = 0.0.3
|
|
4
4
|
author = Nasajon Sistemas
|
|
5
5
|
author_email = contact.dev@nasajon.com.br
|
|
6
6
|
description = Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from flask import Flask, request
|
|
6
|
-
|
|
7
|
-
from nsj_rest_lib.settings import APP_NAME
|
|
8
|
-
|
|
9
|
-
from nsj_gcf_utils.rest_error_util import format_json_error
|
|
10
|
-
|
|
11
|
-
from nsj_multi_database_lib.decorator.multi_database import multi_database
|
|
12
|
-
|
|
13
|
-
from nsj_rest_lib.controller.controller_util import DEFAULT_RESP_HEADERS
|
|
14
|
-
from nsj_rest_lib.controller.list_route import ListRoute
|
|
15
|
-
from nsj_rest_lib.controller.get_route import GetRoute
|
|
16
|
-
from nsj_rest_lib.controller.post_route import PostRoute
|
|
17
|
-
from nsj_rest_lib.controller.put_route import PutRoute
|
|
18
|
-
from nsj_rest_lib.controller.patch_route import PatchRoute
|
|
19
|
-
from nsj_rest_lib.controller.delete_route import DeleteRoute
|
|
20
|
-
|
|
21
|
-
from nsj_rest_lib2.exception import MissingEntityConfigException
|
|
22
|
-
from nsj_rest_lib2.service.entity_loader import EntityLoader
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _get_tenant_grupo() -> tuple[str, str]:
|
|
26
|
-
# Tentando ler do query args
|
|
27
|
-
query_args = request.args
|
|
28
|
-
tenant = query_args.get("tenant")
|
|
29
|
-
grupo_empresarial = query_args.get("grupo_empresarial")
|
|
30
|
-
|
|
31
|
-
# Tentando ler do corpo da requisição
|
|
32
|
-
try:
|
|
33
|
-
body_str = request.data.decode("utf-8")
|
|
34
|
-
body_json = json.loads(body_str)
|
|
35
|
-
|
|
36
|
-
if not tenant:
|
|
37
|
-
tenant = body_json.get("tenant")
|
|
38
|
-
if not grupo_empresarial:
|
|
39
|
-
grupo_empresarial = body_json.get("grupo_empresarial")
|
|
40
|
-
except:
|
|
41
|
-
pass
|
|
42
|
-
|
|
43
|
-
return (str(tenant), str(grupo_empresarial))
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _endpoint_name(func: Any, multidb: bool, root: str) -> str:
|
|
47
|
-
suffix = "_mb" if multidb else ""
|
|
48
|
-
return f"{root}_{func.__name__}{suffix}"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def setup_dynamic_routes(
|
|
52
|
-
flask_app: Flask,
|
|
53
|
-
multidb: bool = True,
|
|
54
|
-
dynamic_root_path: str = "edl1",
|
|
55
|
-
injector_factory: Any = None,
|
|
56
|
-
) -> None:
|
|
57
|
-
|
|
58
|
-
COLLECTION_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>"
|
|
59
|
-
ONE_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>/<id>"
|
|
60
|
-
|
|
61
|
-
def list_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
62
|
-
|
|
63
|
-
def list_dynamic(*args: Any, **kwargs: Any):
|
|
64
|
-
# Recuperando o identificador da entidade
|
|
65
|
-
if "entity_id" not in kwargs:
|
|
66
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
67
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
68
|
-
entity_id = kwargs.pop("entity_id")
|
|
69
|
-
|
|
70
|
-
# Lendo tenant e grupo_empresarial
|
|
71
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
75
|
-
entity_loader = EntityLoader()
|
|
76
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
77
|
-
entity_loader.load_entity_source(
|
|
78
|
-
entity_id, tenant, grupo_empresarial
|
|
79
|
-
)
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Executando o list pelo RestLib
|
|
83
|
-
route = ListRoute(
|
|
84
|
-
url=COLLECTION_DYNAMIC_ROUTE,
|
|
85
|
-
http_method="GET",
|
|
86
|
-
dto_class=etities_dict[dto_class_name],
|
|
87
|
-
entity_class=etities_dict[entity_class_name],
|
|
88
|
-
injector_factory=injector_factory,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
return route.handle_request(*args, **kwargs)
|
|
92
|
-
except MissingEntityConfigException:
|
|
93
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
94
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
95
|
-
|
|
96
|
-
return list_dynamic
|
|
97
|
-
|
|
98
|
-
def get_dynamic(*args: Any, **kwargs: Any):
|
|
99
|
-
# Recuperando o identificador da entidade
|
|
100
|
-
if "entity_id" not in kwargs:
|
|
101
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
102
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
103
|
-
entity_id = kwargs.pop("entity_id")
|
|
104
|
-
|
|
105
|
-
# Lendo tenant e grupo_empresarial
|
|
106
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
107
|
-
|
|
108
|
-
try:
|
|
109
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
110
|
-
entity_loader = EntityLoader()
|
|
111
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
112
|
-
entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# Executando o list pelo RestLib
|
|
116
|
-
route = GetRoute(
|
|
117
|
-
url=ONE_DYNAMIC_ROUTE,
|
|
118
|
-
http_method="GET",
|
|
119
|
-
dto_class=etities_dict[dto_class_name],
|
|
120
|
-
entity_class=etities_dict[entity_class_name],
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
return route.handle_request(*args, **kwargs)
|
|
124
|
-
except MissingEntityConfigException:
|
|
125
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
126
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
127
|
-
|
|
128
|
-
def post_dynamic(*args: Any, **kwargs: Any):
|
|
129
|
-
# Recuperando o identificador da entidade
|
|
130
|
-
if "entity_id" not in kwargs:
|
|
131
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
132
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
133
|
-
entity_id = kwargs.pop("entity_id")
|
|
134
|
-
|
|
135
|
-
# Lendo tenant e grupo_empresarial
|
|
136
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
137
|
-
|
|
138
|
-
try:
|
|
139
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
140
|
-
entity_loader = EntityLoader()
|
|
141
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
142
|
-
entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# Executando o list pelo RestLib
|
|
146
|
-
route = PostRoute(
|
|
147
|
-
url=COLLECTION_DYNAMIC_ROUTE,
|
|
148
|
-
http_method="POST",
|
|
149
|
-
dto_class=etities_dict[dto_class_name],
|
|
150
|
-
entity_class=etities_dict[entity_class_name],
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
return route.handle_request(*args, **kwargs)
|
|
154
|
-
except MissingEntityConfigException:
|
|
155
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
156
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
157
|
-
|
|
158
|
-
def put_dynamic(*args: Any, **kwargs: Any):
|
|
159
|
-
# Recuperando o identificador da entidade
|
|
160
|
-
if "entity_id" not in kwargs:
|
|
161
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
162
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
163
|
-
entity_id = kwargs.pop("entity_id")
|
|
164
|
-
|
|
165
|
-
# Lendo tenant e grupo_empresarial
|
|
166
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
170
|
-
entity_loader = EntityLoader()
|
|
171
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
172
|
-
entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
# Executando o list pelo RestLib
|
|
176
|
-
route = PutRoute(
|
|
177
|
-
url=ONE_DYNAMIC_ROUTE,
|
|
178
|
-
http_method="PUT",
|
|
179
|
-
dto_class=etities_dict[dto_class_name],
|
|
180
|
-
entity_class=etities_dict[entity_class_name],
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
return route.handle_request(*args, **kwargs)
|
|
184
|
-
except MissingEntityConfigException:
|
|
185
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
186
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
187
|
-
|
|
188
|
-
def patch_dynamic(*args: Any, **kwargs: Any):
|
|
189
|
-
# Recuperando o identificador da entidade
|
|
190
|
-
if "entity_id" not in kwargs:
|
|
191
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
192
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
193
|
-
entity_id = kwargs.pop("entity_id")
|
|
194
|
-
|
|
195
|
-
# Lendo tenant e grupo_empresarial
|
|
196
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
197
|
-
|
|
198
|
-
try:
|
|
199
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
200
|
-
entity_loader = EntityLoader()
|
|
201
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
202
|
-
entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
# Executando o list pelo RestLib
|
|
206
|
-
route = PatchRoute(
|
|
207
|
-
url=ONE_DYNAMIC_ROUTE,
|
|
208
|
-
http_method="PATCH",
|
|
209
|
-
dto_class=etities_dict[dto_class_name],
|
|
210
|
-
entity_class=etities_dict[entity_class_name],
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
return route.handle_request(*args, **kwargs)
|
|
214
|
-
except MissingEntityConfigException:
|
|
215
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
216
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
217
|
-
|
|
218
|
-
def delete_dynamic(*args: Any, **kwargs: Any):
|
|
219
|
-
# Recuperando o identificador da entidade
|
|
220
|
-
if "entity_id" not in kwargs:
|
|
221
|
-
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
222
|
-
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
223
|
-
entity_id = kwargs.pop("entity_id")
|
|
224
|
-
|
|
225
|
-
# Lendo tenant e grupo_empresarial
|
|
226
|
-
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
227
|
-
|
|
228
|
-
try:
|
|
229
|
-
# Recuperando o código do DTO e Entity correspondente
|
|
230
|
-
entity_loader = EntityLoader()
|
|
231
|
-
dto_class_name, entity_class_name, etities_dict = (
|
|
232
|
-
entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
# Executando o list pelo RestLib
|
|
236
|
-
route = DeleteRoute(
|
|
237
|
-
url=ONE_DYNAMIC_ROUTE,
|
|
238
|
-
http_method="DELETE",
|
|
239
|
-
dto_class=etities_dict[dto_class_name],
|
|
240
|
-
entity_class=etities_dict[entity_class_name],
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
return route.handle_request(*args, **kwargs)
|
|
244
|
-
except MissingEntityConfigException:
|
|
245
|
-
msg = f"Entity configuration for {entity_id} not found."
|
|
246
|
-
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
247
|
-
|
|
248
|
-
# Ajustando para o padrão com multi database (se necessário)
|
|
249
|
-
if multidb:
|
|
250
|
-
list_dynamic = multi_database()(list_dynamic_wrapper(injector_factory))
|
|
251
|
-
get_dynamic = multi_database()(get_dynamic)
|
|
252
|
-
post_dynamic = multi_database()(post_dynamic)
|
|
253
|
-
put_dynamic = multi_database()(put_dynamic)
|
|
254
|
-
patch_dynamic = multi_database()(patch_dynamic)
|
|
255
|
-
delete_dynamic = multi_database()(delete_dynamic)
|
|
256
|
-
else:
|
|
257
|
-
list_dynamic = list_dynamic_wrapper(injector_factory)
|
|
258
|
-
get_dynamic = get_dynamic
|
|
259
|
-
post_dynamic = post_dynamic
|
|
260
|
-
put_dynamic = put_dynamic
|
|
261
|
-
patch_dynamic = patch_dynamic
|
|
262
|
-
delete_dynamic = delete_dynamic
|
|
263
|
-
|
|
264
|
-
# Registrando as rotas no flask
|
|
265
|
-
flask_app.add_url_rule(
|
|
266
|
-
COLLECTION_DYNAMIC_ROUTE,
|
|
267
|
-
endpoint=_endpoint_name(list_dynamic, multidb, dynamic_root_path),
|
|
268
|
-
view_func=list_dynamic,
|
|
269
|
-
methods=["GET"],
|
|
270
|
-
)
|
|
271
|
-
flask_app.add_url_rule(
|
|
272
|
-
ONE_DYNAMIC_ROUTE,
|
|
273
|
-
endpoint=_endpoint_name(get_dynamic, multidb, dynamic_root_path),
|
|
274
|
-
view_func=get_dynamic,
|
|
275
|
-
methods=["GET"],
|
|
276
|
-
)
|
|
277
|
-
flask_app.add_url_rule(
|
|
278
|
-
COLLECTION_DYNAMIC_ROUTE,
|
|
279
|
-
endpoint=_endpoint_name(post_dynamic, multidb, dynamic_root_path),
|
|
280
|
-
view_func=post_dynamic,
|
|
281
|
-
methods=["POST"],
|
|
282
|
-
)
|
|
283
|
-
flask_app.add_url_rule(
|
|
284
|
-
ONE_DYNAMIC_ROUTE,
|
|
285
|
-
endpoint=_endpoint_name(put_dynamic, multidb, dynamic_root_path),
|
|
286
|
-
view_func=put_dynamic,
|
|
287
|
-
methods=["PUT"],
|
|
288
|
-
)
|
|
289
|
-
flask_app.add_url_rule(
|
|
290
|
-
ONE_DYNAMIC_ROUTE,
|
|
291
|
-
endpoint=_endpoint_name(patch_dynamic, multidb, dynamic_root_path),
|
|
292
|
-
view_func=patch_dynamic,
|
|
293
|
-
methods=["PATCH"],
|
|
294
|
-
)
|
|
295
|
-
flask_app.add_url_rule(
|
|
296
|
-
ONE_DYNAMIC_ROUTE,
|
|
297
|
-
endpoint=_endpoint_name(delete_dynamic, multidb, dynamic_root_path),
|
|
298
|
-
view_func=delete_dynamic,
|
|
299
|
-
methods=["DELETE"],
|
|
300
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|