nsj-rest-lib2 0.0.36__py3-none-any.whl → 0.0.37__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.
- nsj_rest_lib2/compiler/util/type_naming_util.py +64 -1
- nsj_rest_lib2/controller/dynamic_controller.py +3 -1
- nsj_rest_lib2/service/entity_config_writer.py +7 -56
- nsj_rest_lib2/service/entity_loader.py +256 -106
- nsj_rest_lib2/settings.py +10 -0
- nsj_rest_lib2-0.0.37.dist-info/METADATA +203 -0
- {nsj_rest_lib2-0.0.36.dist-info → nsj_rest_lib2-0.0.37.dist-info}/RECORD +9 -9
- nsj_rest_lib2-0.0.36.dist-info/METADATA +0 -27
- {nsj_rest_lib2-0.0.36.dist-info → nsj_rest_lib2-0.0.37.dist-info}/WHEEL +0 -0
- {nsj_rest_lib2-0.0.36.dist-info → nsj_rest_lib2-0.0.37.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import hashlib
|
|
1
2
|
import uuid
|
|
2
|
-
from typing import Iterable
|
|
3
|
+
from typing import Iterable, Iterator, Optional
|
|
3
4
|
|
|
5
|
+
from nsj_rest_lib2.compiler.model import CompilerResult
|
|
4
6
|
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
5
7
|
|
|
6
8
|
|
|
@@ -14,6 +16,67 @@ def compile_namespace_keys(
|
|
|
14
16
|
return (grupo_key, tenant_key, default_key)
|
|
15
17
|
|
|
16
18
|
|
|
19
|
+
def resolve_namespace_key(
|
|
20
|
+
tenant: str | int | None, grupo_empresarial: str | uuid.UUID | None
|
|
21
|
+
) -> str:
|
|
22
|
+
grupo_key, tenant_key, default_key = compile_namespace_keys(
|
|
23
|
+
tenant,
|
|
24
|
+
grupo_empresarial,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
tenant
|
|
29
|
+
and tenant != 0
|
|
30
|
+
and grupo_empresarial
|
|
31
|
+
and str(grupo_empresarial) != "00000000-0000-0000-0000-000000000000"
|
|
32
|
+
):
|
|
33
|
+
return grupo_key
|
|
34
|
+
if tenant and tenant != 0:
|
|
35
|
+
return tenant_key
|
|
36
|
+
return default_key
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_entity_hash(compiler_result: CompilerResult) -> str:
|
|
40
|
+
hasher = hashlib.sha256()
|
|
41
|
+
|
|
42
|
+
for content in _iter_hash_chunks(
|
|
43
|
+
compiler_result.dto_code,
|
|
44
|
+
compiler_result.entity_code,
|
|
45
|
+
compiler_result.insert_function_name,
|
|
46
|
+
compiler_result.update_function_name,
|
|
47
|
+
compiler_result.get_function_name,
|
|
48
|
+
compiler_result.list_function_name,
|
|
49
|
+
compiler_result.delete_function_name,
|
|
50
|
+
compiler_result.source_insert_function,
|
|
51
|
+
compiler_result.source_update_function,
|
|
52
|
+
compiler_result.source_get_function_type,
|
|
53
|
+
compiler_result.source_list_function_type,
|
|
54
|
+
compiler_result.source_delete_function_type,
|
|
55
|
+
compiler_result.post_response_dto_class_name,
|
|
56
|
+
compiler_result.put_response_dto_class_name,
|
|
57
|
+
compiler_result.patch_response_dto_class_name,
|
|
58
|
+
str(compiler_result.retrieve_after_insert),
|
|
59
|
+
str(compiler_result.retrieve_after_update),
|
|
60
|
+
str(compiler_result.retrieve_after_partial_update),
|
|
61
|
+
str(compiler_result.custom_json_post_response),
|
|
62
|
+
str(compiler_result.custom_json_put_response),
|
|
63
|
+
str(compiler_result.custom_json_patch_response),
|
|
64
|
+
str(compiler_result.custom_json_get_response),
|
|
65
|
+
str(compiler_result.custom_json_list_response),
|
|
66
|
+
str(compiler_result.custom_json_delete_response),
|
|
67
|
+
compiler_result.service_account,
|
|
68
|
+
):
|
|
69
|
+
hasher.update(content)
|
|
70
|
+
|
|
71
|
+
return hasher.hexdigest()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _iter_hash_chunks(*chunks: Optional[str]) -> Iterator[bytes]:
|
|
75
|
+
for chunk in chunks:
|
|
76
|
+
if chunk:
|
|
77
|
+
yield chunk.encode("utf-8")
|
|
78
|
+
|
|
79
|
+
|
|
17
80
|
def compile_dto_class_name(entity_id: str, prefx_class_name: str = "") -> str:
|
|
18
81
|
return f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}{CompilerStrUtil.to_pascal_case(entity_id)}DTO"
|
|
19
82
|
|
|
@@ -55,6 +55,7 @@ def setup_dynamic_routes(
|
|
|
55
55
|
dynamic_root_path: str = "edl1",
|
|
56
56
|
injector_factory: Any = None,
|
|
57
57
|
escopo_in_url: bool = False,
|
|
58
|
+
edls_path: str | None = None,
|
|
58
59
|
) -> None:
|
|
59
60
|
|
|
60
61
|
if not escopo_in_url:
|
|
@@ -68,6 +69,8 @@ def setup_dynamic_routes(
|
|
|
68
69
|
f"/{APP_NAME}/{dynamic_root_path}/<entity_escopo>/<entity_resource>/<id>"
|
|
69
70
|
)
|
|
70
71
|
|
|
72
|
+
entity_loader = EntityLoader(edls_path=edls_path)
|
|
73
|
+
|
|
71
74
|
def _dynamic_route_wrapper(
|
|
72
75
|
route_builder: Callable[[tuple[Any, ...]], Callable[..., Any]],
|
|
73
76
|
endpoint_suffix: str | None = None,
|
|
@@ -83,7 +86,6 @@ def setup_dynamic_routes(
|
|
|
83
86
|
tenant, grupo_empresarial, force_reload = _get_query_args()
|
|
84
87
|
|
|
85
88
|
try:
|
|
86
|
-
entity_loader = EntityLoader()
|
|
87
89
|
entity_config = entity_loader.load_entity_source(
|
|
88
90
|
entity_resource,
|
|
89
91
|
tenant,
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import hashlib
|
|
4
3
|
import json
|
|
5
|
-
from typing import Any, Dict
|
|
4
|
+
from typing import Any, Dict
|
|
6
5
|
|
|
7
6
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
8
7
|
from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
|
|
9
8
|
from nsj_rest_lib2.compiler.model import CompilerResult
|
|
10
|
-
from nsj_rest_lib2.compiler.util.type_naming_util import
|
|
9
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
10
|
+
build_entity_hash,
|
|
11
|
+
resolve_namespace_key,
|
|
12
|
+
)
|
|
11
13
|
from nsj_rest_lib2.redis_config import set_redis
|
|
12
14
|
from nsj_rest_lib2.settings import ESCOPO_RESTLIB2
|
|
13
15
|
|
|
@@ -113,21 +115,7 @@ class EntityConfigWriter:
|
|
|
113
115
|
tenant = getattr(entity_model, "tenant", None)
|
|
114
116
|
grupo_empresarial = getattr(entity_model, "grupo_empresarial", None)
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
tenant,
|
|
118
|
-
grupo_empresarial,
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
tenant
|
|
123
|
-
and tenant != 0
|
|
124
|
-
and grupo_empresarial
|
|
125
|
-
and str(grupo_empresarial) != "00000000-0000-0000-0000-000000000000"
|
|
126
|
-
):
|
|
127
|
-
return grupo_key
|
|
128
|
-
if tenant and tenant != 0:
|
|
129
|
-
return tenant_key
|
|
130
|
-
return default_key
|
|
118
|
+
return resolve_namespace_key(tenant, grupo_empresarial)
|
|
131
119
|
|
|
132
120
|
def _resolve_resource(self, entity_model: EntityModelBase) -> str:
|
|
133
121
|
if not isinstance(entity_model, EntityModel) or not entity_model.api:
|
|
@@ -139,41 +127,4 @@ class EntityConfigWriter:
|
|
|
139
127
|
return entity_model.api.resource
|
|
140
128
|
|
|
141
129
|
def _build_entity_hash(self, compiler_result: CompilerResult) -> str:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
for content in self._iter_hash_chunks(
|
|
145
|
-
compiler_result.dto_code,
|
|
146
|
-
compiler_result.entity_code,
|
|
147
|
-
compiler_result.insert_function_name,
|
|
148
|
-
compiler_result.update_function_name,
|
|
149
|
-
compiler_result.get_function_name,
|
|
150
|
-
compiler_result.list_function_name,
|
|
151
|
-
compiler_result.delete_function_name,
|
|
152
|
-
compiler_result.source_insert_function,
|
|
153
|
-
compiler_result.source_update_function,
|
|
154
|
-
compiler_result.source_get_function_type,
|
|
155
|
-
compiler_result.source_list_function_type,
|
|
156
|
-
compiler_result.source_delete_function_type,
|
|
157
|
-
compiler_result.post_response_dto_class_name,
|
|
158
|
-
compiler_result.put_response_dto_class_name,
|
|
159
|
-
compiler_result.patch_response_dto_class_name,
|
|
160
|
-
str(compiler_result.retrieve_after_insert),
|
|
161
|
-
str(compiler_result.retrieve_after_update),
|
|
162
|
-
str(compiler_result.retrieve_after_partial_update),
|
|
163
|
-
str(compiler_result.custom_json_post_response),
|
|
164
|
-
str(compiler_result.custom_json_put_response),
|
|
165
|
-
str(compiler_result.custom_json_patch_response),
|
|
166
|
-
str(compiler_result.custom_json_get_response),
|
|
167
|
-
str(compiler_result.custom_json_list_response),
|
|
168
|
-
str(compiler_result.custom_json_delete_response),
|
|
169
|
-
compiler_result.service_account,
|
|
170
|
-
):
|
|
171
|
-
hasher.update(content)
|
|
172
|
-
|
|
173
|
-
return hasher.hexdigest()
|
|
174
|
-
|
|
175
|
-
@staticmethod
|
|
176
|
-
def _iter_hash_chunks(*chunks: Optional[str]) -> Iterator[bytes]:
|
|
177
|
-
for chunk in chunks:
|
|
178
|
-
if chunk:
|
|
179
|
-
yield chunk.encode("utf-8")
|
|
130
|
+
return build_entity_hash(compiler_result)
|
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import json
|
|
3
|
+
import os
|
|
3
4
|
import re
|
|
4
5
|
import sys
|
|
5
6
|
import threading
|
|
6
7
|
import types
|
|
8
|
+
from typing import Any
|
|
7
9
|
|
|
8
10
|
from nsj_rest_lib.settings import get_logger
|
|
9
11
|
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
from nsj_rest_lib2.compiler.compiler import EDLCompiler
|
|
15
|
+
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
16
|
+
from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
|
|
17
|
+
from nsj_rest_lib2.compiler.edl_model.entity_model_root import EntityModelRoot
|
|
10
18
|
from nsj_rest_lib2.compiler.edl_model.primitives import REGEX_EXTERNAL_REF
|
|
11
|
-
from nsj_rest_lib2.compiler.model import RelationDependency
|
|
12
|
-
from nsj_rest_lib2.compiler.util.type_naming_util import
|
|
19
|
+
from nsj_rest_lib2.compiler.model import CompilerResult, RelationDependency
|
|
20
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
21
|
+
build_entity_hash as build_entity_hash_util,
|
|
22
|
+
compile_namespace_keys,
|
|
23
|
+
resolve_namespace_key as resolve_namespace_key_util,
|
|
24
|
+
)
|
|
25
|
+
from nsj_rest_lib2.dto.escopo_dto import EscopoDTO
|
|
13
26
|
from nsj_rest_lib2.exception import MissingEntityConfigException
|
|
14
|
-
from nsj_rest_lib2.
|
|
15
|
-
|
|
27
|
+
from nsj_rest_lib2.settings import (
|
|
28
|
+
EDLS_FROM_REDIS,
|
|
29
|
+
ESCOPO_RESTLIB2,
|
|
30
|
+
MIN_TIME_SOURCE_REFRESH,
|
|
31
|
+
)
|
|
16
32
|
|
|
17
33
|
|
|
18
34
|
class LoadedEntity:
|
|
@@ -24,6 +40,7 @@ class LoadedEntity:
|
|
|
24
40
|
self.api_expose: bool = False
|
|
25
41
|
self.api_verbs: list[str] = []
|
|
26
42
|
self.relations_dependencies: list[RelationDependency] = []
|
|
43
|
+
self.never_expire: bool = False
|
|
27
44
|
self.service_account: str | None = None
|
|
28
45
|
self.insert_function_class_name: str | None = None
|
|
29
46
|
self.update_function_class_name: str | None = None
|
|
@@ -61,8 +78,10 @@ namespaces_dict: dict[str, Namespace] = {}
|
|
|
61
78
|
|
|
62
79
|
|
|
63
80
|
class EntityLoader:
|
|
64
|
-
def __init__(self) -> types.NoneType:
|
|
81
|
+
def __init__(self, edls_path: str | None = None) -> types.NoneType:
|
|
65
82
|
self._lock = threading.Lock()
|
|
83
|
+
if edls_path:
|
|
84
|
+
self._load_edls_from_disk(edls_path)
|
|
66
85
|
|
|
67
86
|
def load_entity_source(
|
|
68
87
|
self,
|
|
@@ -147,6 +166,37 @@ class EntityLoader:
|
|
|
147
166
|
loaded_entity.delete_function_type_class_name
|
|
148
167
|
)
|
|
149
168
|
|
|
169
|
+
cached_result = (
|
|
170
|
+
dto_class_name,
|
|
171
|
+
entity_class_name,
|
|
172
|
+
entities_dict,
|
|
173
|
+
api_expose,
|
|
174
|
+
api_verbs,
|
|
175
|
+
service_account,
|
|
176
|
+
insert_function_class_name,
|
|
177
|
+
update_function_class_name,
|
|
178
|
+
insert_function_name,
|
|
179
|
+
update_function_name,
|
|
180
|
+
get_function_name,
|
|
181
|
+
list_function_name,
|
|
182
|
+
delete_function_name,
|
|
183
|
+
get_function_type_class_name,
|
|
184
|
+
list_function_type_class_name,
|
|
185
|
+
delete_function_type_class_name,
|
|
186
|
+
loaded_entity.retrieve_after_insert,
|
|
187
|
+
loaded_entity.retrieve_after_update,
|
|
188
|
+
loaded_entity.retrieve_after_partial_update,
|
|
189
|
+
loaded_entity.post_response_dto_class_name,
|
|
190
|
+
loaded_entity.put_response_dto_class_name,
|
|
191
|
+
loaded_entity.patch_response_dto_class_name,
|
|
192
|
+
loaded_entity.custom_json_post_response,
|
|
193
|
+
loaded_entity.custom_json_put_response,
|
|
194
|
+
loaded_entity.custom_json_patch_response,
|
|
195
|
+
loaded_entity.custom_json_get_response,
|
|
196
|
+
loaded_entity.custom_json_list_response,
|
|
197
|
+
loaded_entity.custom_json_delete_response,
|
|
198
|
+
)
|
|
199
|
+
|
|
150
200
|
# Verificando se alguma de suas dependências precisariam ser recarregadas
|
|
151
201
|
for rd in relations_dependencies:
|
|
152
202
|
if rd.entity_resource is None or rd.entity_scope is None:
|
|
@@ -162,6 +212,9 @@ class EntityLoader:
|
|
|
162
212
|
force_reload=force_reload,
|
|
163
213
|
)
|
|
164
214
|
|
|
215
|
+
if loaded_entity.never_expire or not EDLS_FROM_REDIS:
|
|
216
|
+
return cached_result
|
|
217
|
+
|
|
165
218
|
# Se o tempo entre o carregamento e agora for maior do que MIN_TIME_SOURCE_REFRESH minutos,
|
|
166
219
|
# verifica se precisa de refresh
|
|
167
220
|
time_diff = datetime.datetime.now() - loaded_entity.loaded_at
|
|
@@ -185,36 +238,7 @@ class EntityLoader:
|
|
|
185
238
|
|
|
186
239
|
# Se não achar no redis, usa o que estava em memória
|
|
187
240
|
if not loaded_config:
|
|
188
|
-
return
|
|
189
|
-
dto_class_name,
|
|
190
|
-
entity_class_name,
|
|
191
|
-
entities_dict,
|
|
192
|
-
api_expose,
|
|
193
|
-
api_verbs,
|
|
194
|
-
service_account,
|
|
195
|
-
insert_function_class_name,
|
|
196
|
-
update_function_class_name,
|
|
197
|
-
insert_function_name,
|
|
198
|
-
update_function_name,
|
|
199
|
-
get_function_name,
|
|
200
|
-
list_function_name,
|
|
201
|
-
delete_function_name,
|
|
202
|
-
get_function_type_class_name,
|
|
203
|
-
list_function_type_class_name,
|
|
204
|
-
delete_function_type_class_name,
|
|
205
|
-
loaded_entity.retrieve_after_insert,
|
|
206
|
-
loaded_entity.retrieve_after_update,
|
|
207
|
-
loaded_entity.retrieve_after_partial_update,
|
|
208
|
-
loaded_entity.post_response_dto_class_name,
|
|
209
|
-
loaded_entity.put_response_dto_class_name,
|
|
210
|
-
loaded_entity.patch_response_dto_class_name,
|
|
211
|
-
loaded_entity.custom_json_post_response,
|
|
212
|
-
loaded_entity.custom_json_put_response,
|
|
213
|
-
loaded_entity.custom_json_patch_response,
|
|
214
|
-
loaded_entity.custom_json_get_response,
|
|
215
|
-
loaded_entity.custom_json_list_response,
|
|
216
|
-
loaded_entity.custom_json_delete_response,
|
|
217
|
-
)
|
|
241
|
+
return cached_result
|
|
218
242
|
|
|
219
243
|
# Desempacotando resultado
|
|
220
244
|
entity_config_key, entity_config_str = loaded_config
|
|
@@ -229,36 +253,7 @@ class EntityLoader:
|
|
|
229
253
|
|
|
230
254
|
# Se não carregou novo código, usa o que estava em memória
|
|
231
255
|
if result_execute is None:
|
|
232
|
-
return
|
|
233
|
-
dto_class_name,
|
|
234
|
-
entity_class_name,
|
|
235
|
-
entities_dict,
|
|
236
|
-
api_expose,
|
|
237
|
-
api_verbs,
|
|
238
|
-
service_account,
|
|
239
|
-
insert_function_class_name,
|
|
240
|
-
update_function_class_name,
|
|
241
|
-
insert_function_name,
|
|
242
|
-
update_function_name,
|
|
243
|
-
get_function_name,
|
|
244
|
-
list_function_name,
|
|
245
|
-
delete_function_name,
|
|
246
|
-
get_function_type_class_name,
|
|
247
|
-
list_function_type_class_name,
|
|
248
|
-
delete_function_type_class_name,
|
|
249
|
-
loaded_entity.retrieve_after_insert,
|
|
250
|
-
loaded_entity.retrieve_after_update,
|
|
251
|
-
loaded_entity.retrieve_after_partial_update,
|
|
252
|
-
loaded_entity.post_response_dto_class_name,
|
|
253
|
-
loaded_entity.put_response_dto_class_name,
|
|
254
|
-
loaded_entity.patch_response_dto_class_name,
|
|
255
|
-
loaded_entity.custom_json_post_response,
|
|
256
|
-
loaded_entity.custom_json_put_response,
|
|
257
|
-
loaded_entity.custom_json_patch_response,
|
|
258
|
-
loaded_entity.custom_json_get_response,
|
|
259
|
-
loaded_entity.custom_json_list_response,
|
|
260
|
-
loaded_entity.custom_json_delete_response,
|
|
261
|
-
)
|
|
256
|
+
return cached_result
|
|
262
257
|
else:
|
|
263
258
|
(
|
|
264
259
|
dto_class_name,
|
|
@@ -322,36 +317,10 @@ class EntityLoader:
|
|
|
322
317
|
)
|
|
323
318
|
else:
|
|
324
319
|
# Se não deu o intervalo de verificação do refresh, retorna o que está em memória
|
|
325
|
-
return
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
api_expose,
|
|
330
|
-
api_verbs,
|
|
331
|
-
service_account,
|
|
332
|
-
insert_function_class_name,
|
|
333
|
-
update_function_class_name,
|
|
334
|
-
insert_function_name,
|
|
335
|
-
update_function_name,
|
|
336
|
-
get_function_name,
|
|
337
|
-
list_function_name,
|
|
338
|
-
delete_function_name,
|
|
339
|
-
get_function_type_class_name,
|
|
340
|
-
list_function_type_class_name,
|
|
341
|
-
delete_function_type_class_name,
|
|
342
|
-
loaded_entity.retrieve_after_insert,
|
|
343
|
-
loaded_entity.retrieve_after_update,
|
|
344
|
-
loaded_entity.retrieve_after_partial_update,
|
|
345
|
-
loaded_entity.post_response_dto_class_name,
|
|
346
|
-
loaded_entity.put_response_dto_class_name,
|
|
347
|
-
loaded_entity.patch_response_dto_class_name,
|
|
348
|
-
loaded_entity.custom_json_post_response,
|
|
349
|
-
loaded_entity.custom_json_put_response,
|
|
350
|
-
loaded_entity.custom_json_patch_response,
|
|
351
|
-
loaded_entity.custom_json_get_response,
|
|
352
|
-
loaded_entity.custom_json_list_response,
|
|
353
|
-
loaded_entity.custom_json_delete_response,
|
|
354
|
-
)
|
|
320
|
+
return cached_result
|
|
321
|
+
|
|
322
|
+
if not EDLS_FROM_REDIS:
|
|
323
|
+
raise MissingEntityConfigException()
|
|
355
324
|
|
|
356
325
|
# Se não conseguir recuperar a entidade, procura no redis:
|
|
357
326
|
loaded_config = self._load_entity_config_from_redis(
|
|
@@ -451,6 +420,178 @@ class EntityLoader:
|
|
|
451
420
|
with self._lock:
|
|
452
421
|
namespaces_dict.clear()
|
|
453
422
|
|
|
423
|
+
def _load_edls_from_disk(self, edls_path: str) -> None:
|
|
424
|
+
if namespaces_dict:
|
|
425
|
+
return
|
|
426
|
+
|
|
427
|
+
resolved_path = self._resolve_edls_path(edls_path)
|
|
428
|
+
if not resolved_path:
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
if os.path.isdir(resolved_path):
|
|
432
|
+
edl_files = self._list_edl_files(resolved_path)
|
|
433
|
+
elif os.path.isfile(resolved_path) and self._is_edl_file(resolved_path):
|
|
434
|
+
edl_files = [resolved_path]
|
|
435
|
+
else:
|
|
436
|
+
return
|
|
437
|
+
|
|
438
|
+
if not edl_files:
|
|
439
|
+
return
|
|
440
|
+
|
|
441
|
+
entities: dict[str, EntityModelBase] = {}
|
|
442
|
+
for file_path in edl_files:
|
|
443
|
+
edl_json = self._read_edl_file(file_path)
|
|
444
|
+
if not isinstance(edl_json, dict):
|
|
445
|
+
continue
|
|
446
|
+
|
|
447
|
+
if edl_json.get("mixin", False):
|
|
448
|
+
entity_model = EntityModelRoot(**edl_json)
|
|
449
|
+
else:
|
|
450
|
+
entity_model = EntityModel(**edl_json)
|
|
451
|
+
|
|
452
|
+
complete_entity_id = f"{entity_model.escopo}/{entity_model.id}"
|
|
453
|
+
entities[complete_entity_id] = entity_model
|
|
454
|
+
|
|
455
|
+
if not entities:
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
escopos: dict[str, EscopoDTO] = {}
|
|
459
|
+
for entity_model in entities.values():
|
|
460
|
+
if entity_model.escopo not in escopos:
|
|
461
|
+
escopos[entity_model.escopo] = EscopoDTO(
|
|
462
|
+
codigo=entity_model.escopo,
|
|
463
|
+
service_account=None,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
compiler = EDLCompiler()
|
|
467
|
+
dependencies = list(entities.items())
|
|
468
|
+
|
|
469
|
+
for entity_model in entities.values():
|
|
470
|
+
escopo_dto = escopos.get(entity_model.escopo)
|
|
471
|
+
if escopo_dto is None:
|
|
472
|
+
continue
|
|
473
|
+
|
|
474
|
+
compiler_result = compiler.compile_model(
|
|
475
|
+
entity_model,
|
|
476
|
+
dependencies,
|
|
477
|
+
escopo=escopo_dto,
|
|
478
|
+
)
|
|
479
|
+
if not compiler_result:
|
|
480
|
+
continue
|
|
481
|
+
|
|
482
|
+
entity_config = self._build_entity_payload(
|
|
483
|
+
entity_model,
|
|
484
|
+
compiler_result,
|
|
485
|
+
)
|
|
486
|
+
entity_resource = entity_config.get("api_resource")
|
|
487
|
+
if not entity_resource:
|
|
488
|
+
continue
|
|
489
|
+
|
|
490
|
+
entity_config_key = self._resolve_namespace_key(entity_model)
|
|
491
|
+
self._execute_entity_source(
|
|
492
|
+
json.dumps(entity_config, ensure_ascii=False),
|
|
493
|
+
entity_config_key,
|
|
494
|
+
entity_resource,
|
|
495
|
+
from_disk=True,
|
|
496
|
+
skip_dependency_load=not EDLS_FROM_REDIS,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
def _resolve_edls_path(self, edls_path: str) -> str | None:
|
|
500
|
+
edls_path = edls_path.strip()
|
|
501
|
+
if not edls_path:
|
|
502
|
+
return None
|
|
503
|
+
if os.path.isabs(edls_path):
|
|
504
|
+
return edls_path
|
|
505
|
+
return os.path.join(os.getcwd(), edls_path)
|
|
506
|
+
|
|
507
|
+
def _is_edl_file(self, file_path: str) -> bool:
|
|
508
|
+
lower_name = file_path.lower()
|
|
509
|
+
return lower_name.endswith(".json") or lower_name.endswith(
|
|
510
|
+
".yml"
|
|
511
|
+
) or lower_name.endswith(".yaml")
|
|
512
|
+
|
|
513
|
+
def _list_edl_files(self, root_path: str) -> list[str]:
|
|
514
|
+
edl_files: list[str] = []
|
|
515
|
+
for current_root, _, files in os.walk(root_path):
|
|
516
|
+
for file_name in files:
|
|
517
|
+
file_path = os.path.join(current_root, file_name)
|
|
518
|
+
if self._is_edl_file(file_path):
|
|
519
|
+
edl_files.append(file_path)
|
|
520
|
+
edl_files.sort()
|
|
521
|
+
return edl_files
|
|
522
|
+
|
|
523
|
+
def _read_edl_file(self, file_path: str) -> dict[str, Any] | None:
|
|
524
|
+
with open(file_path, "r", encoding="utf-8") as file_handler:
|
|
525
|
+
if file_path.lower().endswith(".json"):
|
|
526
|
+
return json.load(file_handler)
|
|
527
|
+
return yaml.safe_load(file_handler)
|
|
528
|
+
|
|
529
|
+
def _resolve_namespace_key(self, entity_model: EntityModelBase) -> str:
|
|
530
|
+
tenant = getattr(entity_model, "tenant", None)
|
|
531
|
+
grupo_empresarial = getattr(entity_model, "grupo_empresarial", None)
|
|
532
|
+
|
|
533
|
+
return resolve_namespace_key_util(tenant, grupo_empresarial)
|
|
534
|
+
|
|
535
|
+
def _build_entity_payload(
|
|
536
|
+
self,
|
|
537
|
+
entity_model: EntityModelBase,
|
|
538
|
+
compiler_result: CompilerResult,
|
|
539
|
+
) -> dict[str, Any]:
|
|
540
|
+
api_resource = compiler_result.api_resource
|
|
541
|
+
if not api_resource:
|
|
542
|
+
if isinstance(entity_model, EntityModel) and entity_model.api:
|
|
543
|
+
api_resource = entity_model.api.resource
|
|
544
|
+
if not api_resource:
|
|
545
|
+
raise ValueError("api.resource é obrigatório para carregar EDLs.")
|
|
546
|
+
|
|
547
|
+
relations = [
|
|
548
|
+
rd.to_dict()
|
|
549
|
+
for rd in (compiler_result.relations_dependencies or [])
|
|
550
|
+
]
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
"dto_class_name": compiler_result.dto_class_name,
|
|
554
|
+
"entity_class_name": compiler_result.entity_class_name,
|
|
555
|
+
"service_account": compiler_result.service_account,
|
|
556
|
+
"insert_function_class_name": compiler_result.insert_function_class_name,
|
|
557
|
+
"insert_function_name": compiler_result.insert_function_name,
|
|
558
|
+
"source_insert_function": compiler_result.source_insert_function,
|
|
559
|
+
"update_function_class_name": compiler_result.update_function_class_name,
|
|
560
|
+
"update_function_name": compiler_result.update_function_name,
|
|
561
|
+
"source_update_function": compiler_result.source_update_function,
|
|
562
|
+
"get_function_name": compiler_result.get_function_name,
|
|
563
|
+
"list_function_name": compiler_result.list_function_name,
|
|
564
|
+
"delete_function_name": compiler_result.delete_function_name,
|
|
565
|
+
"get_function_type_class_name": compiler_result.get_function_type_class_name,
|
|
566
|
+
"list_function_type_class_name": compiler_result.list_function_type_class_name,
|
|
567
|
+
"delete_function_type_class_name": compiler_result.delete_function_type_class_name,
|
|
568
|
+
"source_get_function_type": compiler_result.source_get_function_type,
|
|
569
|
+
"source_list_function_type": compiler_result.source_list_function_type,
|
|
570
|
+
"source_delete_function_type": compiler_result.source_delete_function_type,
|
|
571
|
+
"retrieve_after_insert": compiler_result.retrieve_after_insert,
|
|
572
|
+
"retrieve_after_update": compiler_result.retrieve_after_update,
|
|
573
|
+
"retrieve_after_partial_update": compiler_result.retrieve_after_partial_update,
|
|
574
|
+
"post_response_dto_class_name": compiler_result.post_response_dto_class_name,
|
|
575
|
+
"put_response_dto_class_name": compiler_result.put_response_dto_class_name,
|
|
576
|
+
"patch_response_dto_class_name": compiler_result.patch_response_dto_class_name,
|
|
577
|
+
"custom_json_post_response": compiler_result.custom_json_post_response,
|
|
578
|
+
"custom_json_put_response": compiler_result.custom_json_put_response,
|
|
579
|
+
"custom_json_patch_response": compiler_result.custom_json_patch_response,
|
|
580
|
+
"custom_json_get_response": compiler_result.custom_json_get_response,
|
|
581
|
+
"custom_json_list_response": compiler_result.custom_json_list_response,
|
|
582
|
+
"custom_json_delete_response": compiler_result.custom_json_delete_response,
|
|
583
|
+
"source_dto": compiler_result.dto_code,
|
|
584
|
+
"source_entity": compiler_result.entity_code,
|
|
585
|
+
"entity_hash": self._build_entity_hash(compiler_result),
|
|
586
|
+
"api_expose": compiler_result.api_expose,
|
|
587
|
+
"api_resource": api_resource,
|
|
588
|
+
"api_verbs": compiler_result.api_verbs or [],
|
|
589
|
+
"relations_dependencies": relations,
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
def _build_entity_hash(self, compiler_result: CompilerResult) -> str:
|
|
593
|
+
return build_entity_hash_util(compiler_result)
|
|
594
|
+
|
|
454
595
|
def _ensure_dynamic_package(self):
|
|
455
596
|
"""
|
|
456
597
|
Garante que exista um pacote 'dynamic' em sys.modules.
|
|
@@ -469,6 +610,8 @@ class EntityLoader:
|
|
|
469
610
|
entity_config_key: str,
|
|
470
611
|
entity_resource: str,
|
|
471
612
|
check_refresh: bool = False,
|
|
613
|
+
from_disk: bool = False,
|
|
614
|
+
skip_dependency_load: bool = False,
|
|
472
615
|
) -> tuple[
|
|
473
616
|
str,
|
|
474
617
|
str,
|
|
@@ -590,18 +733,19 @@ class EntityLoader:
|
|
|
590
733
|
return None
|
|
591
734
|
|
|
592
735
|
# Verificando se alguma de suas dependências precisariam ser carregadas (ou recarregadas)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
736
|
+
if not skip_dependency_load:
|
|
737
|
+
for rd in relations_dependencies:
|
|
738
|
+
if rd.entity_resource is None or rd.entity_scope is None:
|
|
739
|
+
raise RuntimeError(
|
|
740
|
+
f"Erro: Dependência de entidade mal formada na entidade {entity_resource}."
|
|
741
|
+
)
|
|
598
742
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
743
|
+
self.load_entity_source(
|
|
744
|
+
rd.entity_resource,
|
|
745
|
+
str(rd.tenant),
|
|
746
|
+
str(rd.grupo_empresarial),
|
|
747
|
+
rd.entity_scope,
|
|
748
|
+
)
|
|
605
749
|
|
|
606
750
|
# Verificando se a entidade precisa ou não de refresh
|
|
607
751
|
if check_refresh:
|
|
@@ -703,6 +847,7 @@ class EntityLoader:
|
|
|
703
847
|
loaded_entity.api_verbs = api_verbs
|
|
704
848
|
loaded_entity.service_account = service_account
|
|
705
849
|
loaded_entity.relations_dependencies = relations_dependencies
|
|
850
|
+
loaded_entity.never_expire = from_disk
|
|
706
851
|
loaded_entity.insert_function_class_name = insert_function_class_name
|
|
707
852
|
loaded_entity.update_function_class_name = update_function_class_name
|
|
708
853
|
loaded_entity.insert_function_name = insert_function_name
|
|
@@ -801,6 +946,11 @@ class EntityLoader:
|
|
|
801
946
|
entity_config_key: str | None,
|
|
802
947
|
escopo: str,
|
|
803
948
|
) -> tuple[str, str] | None:
|
|
949
|
+
if not EDLS_FROM_REDIS:
|
|
950
|
+
return None
|
|
951
|
+
|
|
952
|
+
from nsj_rest_lib2.redis_config import get_redis
|
|
953
|
+
|
|
804
954
|
get_logger().debug(
|
|
805
955
|
f"Procurando a configuração da entidade {entity_resource} no redis. Tenant key: {tenant_key} e Grupo key: {grupo_key}"
|
|
806
956
|
)
|
nsj_rest_lib2/settings.py
CHANGED
|
@@ -12,5 +12,15 @@ else:
|
|
|
12
12
|
MIN_TIME_SOURCE_REFRESH = int(os.getenv("MIN_TIME_SOURCE_REFRESH", "15"))
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def _get_env_bool(name: str, default: bool) -> bool:
|
|
16
|
+
value = os.getenv(name)
|
|
17
|
+
if value is None:
|
|
18
|
+
return default
|
|
19
|
+
return value.strip().lower() in ("1", "true", "t", "yes", "y", "on")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
EDLS_FROM_REDIS = _get_env_bool("EDLS_FROM_REDIS", True)
|
|
23
|
+
|
|
24
|
+
|
|
15
25
|
def get_logger():
|
|
16
26
|
return logging.getLogger(APP_NAME)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nsj_rest_lib2
|
|
3
|
+
Version: 0.0.37
|
|
4
|
+
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
|
+
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
|
+
Author: Nasajon Sistemas
|
|
7
|
+
Author-email: contact.dev@nasajon.com.br
|
|
8
|
+
Project-URL: Source, https://github.com/Nasajon/nsj_rest_lib2
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.4
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: nsj-rest-lib<7.0.0,>=5.1.3
|
|
16
|
+
Requires-Dist: redis<7.0.0,>=6.4.0
|
|
17
|
+
Requires-Dist: nsj-multi-database-lib<3.0.0,>=2.0.1
|
|
18
|
+
Requires-Dist: pydantic<3.0.0,>=2.11.9
|
|
19
|
+
Requires-Dist: black<26.0.0,>=25.1.0
|
|
20
|
+
Requires-Dist: pyyaml<7.0.0,>=6.0.3
|
|
21
|
+
|
|
22
|
+
# RestLib2
|
|
23
|
+
|
|
24
|
+
O RestLib2 é uma plataforma que tem por objetivo o fornecimento de APIs para os clientes, a partir da descrição da entidades envolvidas, e sem a necessidade de programação imperativa tradicional.
|
|
25
|
+
|
|
26
|
+
Em resumo, a partir da declaração em JSON das entidades que compõe um negócio, bem como dos relacionamentos entre estas, as APIs já estarão disponíveis em produção.
|
|
27
|
+
|
|
28
|
+
O objetivo final é permitir que o desenvolvimento de APIs se torne rápido e simples, acessível àqueles que tratam diretamente com o negócio (incluindo implantadores, e, no limite, o próprio cliente).
|
|
29
|
+
|
|
30
|
+
## Links
|
|
31
|
+
|
|
32
|
+
[Guia do EDL](docs/README.md)
|
|
33
|
+
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
34
|
+
|
|
35
|
+
## Rota das APIs geradas
|
|
36
|
+
|
|
37
|
+
A rota para acesso a uma API gerada por variar de acordo com o modo de configuração escolhido pela aplicação que usar o RestLib2. Mas, via de regra, sugere-se o padrão aplicado na API de DadosMestre:
|
|
38
|
+
|
|
39
|
+
```http
|
|
40
|
+
#############################
|
|
41
|
+
# List Prod
|
|
42
|
+
#############################
|
|
43
|
+
GET https://api.nasajon.app/dados-mestre/edl1/clientes?tenant=47&grupo_empresarial=NASAJON HTTP/1.1
|
|
44
|
+
Authorization: Bearer *****
|
|
45
|
+
Accept: application/json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
* Note que, na API de Dados Mestre, todas as APIs do RestLib2 ficarão debaixo da rota: ```https://api.nasajon.app/dados-mestre/edl1/```
|
|
49
|
+
* O endpoint em si, varia a cada JSON, e fica configurado no nó ```api.resource``` do JSON de EDL em questão.
|
|
50
|
+
|
|
51
|
+
## Fluxo básico de uso
|
|
52
|
+
|
|
53
|
+
Há dois modos básicos de publicar novas APIs a partir de um JSON gerado, para uma aplicação previamente configurada para uso da plataforma:
|
|
54
|
+
|
|
55
|
+
### Fluxo por meio de código versionado
|
|
56
|
+
1. Criar o JSON de descrição da entidade desejada, gravando-o no diretório "@schemas/entities" da aplicação em questão (tome [como exemplo o "dados-mestre"](https://github.com/Nasajon/dados-mestre-api/tree/production/%40schemas)).
|
|
57
|
+
2. Fazer push das alterações.
|
|
58
|
+
3. Aguardar o build da aplicação (pode ser útil conferir os logs do job de atualização dos JSON, na aplicação em questão, e também os logs do [worker de compilação](https://ci.nasajon.in/applications/restlib2?view=pods&conditions=false), no Argo).
|
|
59
|
+
4. Testar as APIs compiladas.
|
|
60
|
+
|
|
61
|
+
### Fluxo de deploy direto pela API
|
|
62
|
+
1. Criar o JSON de descrição da entidade desejada.
|
|
63
|
+
2. Registrá-lo diretamente, por meio da API de controle do RestLib2. segue exemplo de chamada abaixo:
|
|
64
|
+
|
|
65
|
+
```http
|
|
66
|
+
###############################
|
|
67
|
+
# Post dinâmico
|
|
68
|
+
###############################
|
|
69
|
+
POST https://api.nasajon.app/restlib2/entities HTTP/1.1
|
|
70
|
+
Authorization: Bearer ******
|
|
71
|
+
Content-Type: application/json
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
"id": "{UUID}",
|
|
75
|
+
"escopo": "{escopo}",
|
|
76
|
+
"tenant": 0,
|
|
77
|
+
"grupo_empresarial": "00000000-0000-0000-0000-000000000000",
|
|
78
|
+
"codigo": "{codigo da entidade}",
|
|
79
|
+
"descricao": "{descrição da entidade}",
|
|
80
|
+
"json_schema": {
|
|
81
|
+
"edl_version": "1.0",
|
|
82
|
+
"escopo": "{escopo}",
|
|
83
|
+
"description": "{descrição da entidade}",
|
|
84
|
+
"id": "cliente",
|
|
85
|
+
"version": "0.0.1",
|
|
86
|
+
...
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Observações:
|
|
92
|
+
* Note que, acima há placeholders a alterar.
|
|
93
|
+
* Note também que o JSON não está completo, e, o correto é deguir a documentação do EDL.
|
|
94
|
+
* Os valores tenant=0 e grupo_empresarial=00000000-0000-0000-0000-000000000000, indicam que a entidade é padrão, e serve para todos os tenants e grupos.
|
|
95
|
+
|
|
96
|
+
3. Essa rota irá retornar algo semelhante a:
|
|
97
|
+
|
|
98
|
+
```http
|
|
99
|
+
HTTP/1.1 202 ACCEPTED
|
|
100
|
+
Location: https://api.nasajon.app/restlib2/entity-compilations/status/{UUID do processo de compilação}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
E, se você fizer uma chamada à rota retornada, poderá acompanhar o status da compilação, incluindo eventuais erros que venham a ocorrer.
|
|
104
|
+
|
|
105
|
+
4. Testar a API compilada.
|
|
106
|
+
|
|
107
|
+
## Como rodar a compilação de EDLs localmente?
|
|
108
|
+
|
|
109
|
+
Você pode testar a compilação de seus EDLs localmente, incluindo a execução de diversas validações sobre os mesmos, desde que todos estejam dispostos num mesmo diertório.
|
|
110
|
+
|
|
111
|
+
Para isso, considere os passos a seguir:
|
|
112
|
+
|
|
113
|
+
1. Crie um diretório, e coloque todos os seus EDLs lá (garantindo que EDLs relacionados estejam no mesmo).
|
|
114
|
+
2. Instale, no seu ambiente pyhton de teste, a biblioteca `nsj-rest-lib2`:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pip install nsj-rest-lib2
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
3. Rode o comando abaixo (adaptado para seu diretório):
|
|
121
|
+
```sh
|
|
122
|
+
python3 -m nsj_rest_lib2.compiler.compiler -d $(shell pwd)/{diretorio_com_os_edls_json}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
O resultado da compilação será impresso no console (erros, ou código total gerado).
|
|
126
|
+
|
|
127
|
+
## Como configurar uma aplicação para expôr rotas de acordo com o padrão do RestLib2?
|
|
128
|
+
|
|
129
|
+
Para que uma aplicação exponha as rotas no padrão do RestLib2, não são necessários muitos passos. Antes basta:
|
|
130
|
+
|
|
131
|
+
1. Instalar, em sua aplicação, a dependência para o projeto nsj-rest-lib2
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
pip install nsj-rest-lib2
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
138
|
+
|
|
139
|
+
2. Adicione a variável de ambiente abaixo em sua aplciação
|
|
140
|
+
|
|
141
|
+
```env
|
|
142
|
+
ESCOPO_RESTLIB2: "{COLOQUE O IDENTIFICADOR DE ESCOPO QUE DESEJAR PARA SUA APLICAÇÃO (UMA SIMPLES STRING SEM ESPAÇO)}"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
* Esse identificador de escopo deve se único para sua aplicação.
|
|
146
|
+
* É importante nota que sua aplicação só irá expôr JSONs de EDL configurados para o mesmo escopo definido aqui.
|
|
147
|
+
|
|
148
|
+
3. Instale, como dependência a biblioteca, nsj-rest-lib2:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
pip install nsj-rest-lib2
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
155
|
+
|
|
156
|
+
4. Adicione a linha abaixo no arquivo wsgi.py (de inicilização de sua aplicação):
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
160
|
+
from nasajon.injector_factory_multibanco import InjectorFactoryMultibanco
|
|
161
|
+
|
|
162
|
+
setup_dynamic_routes(application, injector_factory=InjectorFactoryMultibanco)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
* No exemplo acima, a rota é multibanco, mas, não é obrigatório.
|
|
166
|
+
* Os parâmetros de setup são:
|
|
167
|
+
* flask_app: Variável obrigatório, que aponte para sua aplicação Flask.
|
|
168
|
+
* multidb: Flag (padrão True)
|
|
169
|
+
* dynamic_root_path: URL padrão base de todos os endpoints do RestLib2 (padrão: "edl1")
|
|
170
|
+
* injector_factory: Classe de injeção de depndência usada (normalmente necessária para aplicações multibanco; o principal uso é justamente manipular a criação da conexão com o BD).
|
|
171
|
+
|
|
172
|
+
## Carregando EDLs direto do disco
|
|
173
|
+
|
|
174
|
+
Também é possível carregar EDLs diretamente do disco, sem depender exclusivamente do Redis. Para isso, use o parâmetro opcional `edls_path` em `setup_dynamic_routes`, apontando para um caminho relativo ao workdir da aplicação (ou absoluto), contendo arquivos `.json`, `.yml` ou `.yaml` com os EDLs.
|
|
175
|
+
|
|
176
|
+
Exemplo:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
180
|
+
|
|
181
|
+
setup_dynamic_routes(
|
|
182
|
+
application,
|
|
183
|
+
injector_factory=InjectorFactoryMultibanco,
|
|
184
|
+
edls_path="@schemas/entities",
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Observações:
|
|
189
|
+
* Os EDLs são carregados e compilados uma vez, ficando em cache em memória. Chamadas seguintes reutilizam o cache.
|
|
190
|
+
* A resolução das entidades ocorre primeiro pelo cache local; se não encontrar, o Redis é consultado (quando habilitado).
|
|
191
|
+
|
|
192
|
+
Para desabilitar o Redis e usar somente EDLs do disco, defina a variável de ambiente abaixo:
|
|
193
|
+
|
|
194
|
+
```env
|
|
195
|
+
EDLS_FROM_REDIS=false
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
O valor padrão de `EDLS_FROM_REDIS` é `true`.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
**Pronto, isso deve bastar para expôr os EDLs configurados para o mesmo escopo da aplicação.**
|
|
202
|
+
|
|
203
|
+
**OBSERVAÇÃO GERAL: Isso só funciona para aplicações Flask.**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
nsj_rest_lib2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
nsj_rest_lib2/exception.py,sha256=E9uMUdoCCQOVQfc8f6gD9b5Pxurf3Q4SytDCcqSlkZ8,56
|
|
3
3
|
nsj_rest_lib2/redis_config.py,sha256=4KLcvYS3nJO7PMQgF6F9_j6r-TyqcS7TBbd3LEQuKDU,629
|
|
4
|
-
nsj_rest_lib2/settings.py,sha256=
|
|
4
|
+
nsj_rest_lib2/settings.py,sha256=eK2aIFVhZK8kxEDHCW5agba5XP2aFPoh_PFnNLwCMH8,627
|
|
5
5
|
nsj_rest_lib2/compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
nsj_rest_lib2/compiler/compiler.py,sha256=z-u9UeOhNKxH6bwaPWTxkN_SJjgZ6tGJD_iXc2D2Hr4,39433
|
|
7
7
|
nsj_rest_lib2/compiler/compiler_structures.py,sha256=stspjqJGXU7Vz3BqQ-ZF5ZmumFm3R4jpkWgVVsXW5d0,1488
|
|
@@ -32,18 +32,18 @@ nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=NtMVZeOPu3L
|
|
|
32
32
|
nsj_rest_lib2/compiler/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
nsj_rest_lib2/compiler/util/relation_ref.py,sha256=1M_e-NyeqozlIYLl1rB76KqkmtMvgtqH_nJS-NK0Pck,3695
|
|
34
34
|
nsj_rest_lib2/compiler/util/str_util.py,sha256=0ReIQ2Vy4zAmVMvGv0FcUehRQw15hlz0e7yDsF89ghk,1178
|
|
35
|
-
nsj_rest_lib2/compiler/util/type_naming_util.py,sha256=
|
|
35
|
+
nsj_rest_lib2/compiler/util/type_naming_util.py,sha256=WKBqGZ0a-JO1IO5Q_rHVpLBESUmbc5D_lgF9DWBwkpk,5065
|
|
36
36
|
nsj_rest_lib2/compiler/util/type_util.py,sha256=HTKOH4uRTOY0YgoM8oUv_6cEcReE_bgKYXFBsQCb-3A,357
|
|
37
37
|
nsj_rest_lib2/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
nsj_rest_lib2/controller/dynamic_controller.py,sha256=
|
|
38
|
+
nsj_rest_lib2/controller/dynamic_controller.py,sha256=g7ThrpLvzdhQYxzqcW5WJgCaDcgKzvqoWBEC4c4Tmpk,17474
|
|
39
39
|
nsj_rest_lib2/dto/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
40
40
|
nsj_rest_lib2/dto/escopo_dto.py,sha256=R9gxRwYxOVYhGjR3q03_iXvHm14N905zbRKLsS3jI-A,1393
|
|
41
41
|
nsj_rest_lib2/entity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
nsj_rest_lib2/entity/escopo_entity.py,sha256=T4bxFDzHJvIj-nZ_6d0Xh2oQg21HgoRjp19nt6clY18,338
|
|
43
43
|
nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
-
nsj_rest_lib2/service/entity_config_writer.py,sha256=
|
|
45
|
-
nsj_rest_lib2/service/entity_loader.py,sha256=
|
|
46
|
-
nsj_rest_lib2-0.0.
|
|
47
|
-
nsj_rest_lib2-0.0.
|
|
48
|
-
nsj_rest_lib2-0.0.
|
|
49
|
-
nsj_rest_lib2-0.0.
|
|
44
|
+
nsj_rest_lib2/service/entity_config_writer.py,sha256=QVgEO3Tv5g3dK8MQgCrWD9BZyCKV-BxBLfYNFi53PX8,5806
|
|
45
|
+
nsj_rest_lib2/service/entity_loader.py,sha256=kCAt0uKgnrw4gr48_xjspLDQzl_5PxyUYnM8W1YzIxs,39775
|
|
46
|
+
nsj_rest_lib2-0.0.37.dist-info/METADATA,sha256=5kHSDiNwLEvHSF8VNFBsR5jtnJHMIInItAAcrenG2Dk,8260
|
|
47
|
+
nsj_rest_lib2-0.0.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
48
|
+
nsj_rest_lib2-0.0.37.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
|
|
49
|
+
nsj_rest_lib2-0.0.37.dist-info/RECORD,,
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.36
|
|
4
|
-
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
|
-
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
|
-
Author: Nasajon Sistemas
|
|
7
|
-
Author-email: contact.dev@nasajon.com.br
|
|
8
|
-
Project-URL: Source, https://github.com/Nasajon/nsj_rest_lib2
|
|
9
|
-
Classifier: Development Status :: 3 - Alpha
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Requires-Python: >=3.4
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
Requires-Dist: nsj-rest-lib<7.0.0,>=5.1.3
|
|
16
|
-
Requires-Dist: redis<7.0.0,>=6.4.0
|
|
17
|
-
Requires-Dist: nsj-multi-database-lib<3.0.0,>=2.0.1
|
|
18
|
-
Requires-Dist: pydantic<3.0.0,>=2.11.9
|
|
19
|
-
Requires-Dist: black<26.0.0,>=25.1.0
|
|
20
|
-
Requires-Dist: pyyaml<7.0.0,>=6.0.3
|
|
21
|
-
|
|
22
|
-
# nsj_rest_lib2
|
|
23
|
-
|
|
24
|
-
Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
25
|
-
|
|
26
|
-
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
27
|
-
|
|
File without changes
|
|
File without changes
|