nsj-rest-lib2 0.0.7__tar.gz → 0.0.8__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.7 → nsj_rest_lib2-0.0.8}/PKG-INFO +1 -1
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/compiler.py +16 -5
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/entity_model.py +2 -2
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/property_compiler.py +20 -10
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/controller/dynamic_controller.py +92 -44
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/service/entity_loader.py +91 -34
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2.egg-info/PKG-INFO +1 -1
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/setup.cfg +1 -1
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/README.md +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/ai_compiler.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/compiler_structures.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/dto_compiler.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/primitives.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/entity_compiler.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/controller/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/exception.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/redis_config.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/service/__init__.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/settings.py +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2.egg-info/SOURCES.txt +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2.egg-info/requires.txt +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
- {nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/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.8
|
|
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
|
|
@@ -13,14 +13,11 @@ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
|
13
13
|
|
|
14
14
|
from nsj_rest_lib2.settings import get_logger
|
|
15
15
|
|
|
16
|
-
# TODO Rever a questão do tempo de expiração ser renovado, na leitura do redis
|
|
17
16
|
# TODO Atualizar o status da entidade pelo worker de compilação (e talvez parar uma compilação, quando se delete uma entidade)
|
|
18
17
|
# TODO Relacionamentos
|
|
19
18
|
# TODO Classes Abstratas
|
|
20
19
|
# TODO Partial Classes
|
|
21
20
|
# TODO Migrations
|
|
22
|
-
# TODO Migrar para a nsj_rest_lib2
|
|
23
|
-
# TODO Alterar o padrão de nomenclatura para snake_case
|
|
24
21
|
|
|
25
22
|
|
|
26
23
|
class CompilerResult:
|
|
@@ -29,7 +26,9 @@ class CompilerResult:
|
|
|
29
26
|
self.dto_code: str | None = None
|
|
30
27
|
self.entity_class_name: str | None = None
|
|
31
28
|
self.entity_code: str | None = None
|
|
32
|
-
|
|
29
|
+
self.api_expose: bool | None = None
|
|
30
|
+
self.api_resource: str | None = None
|
|
31
|
+
self.api_verbs: list[str] | None = None
|
|
33
32
|
|
|
34
33
|
|
|
35
34
|
class EDLCompiler:
|
|
@@ -121,13 +120,18 @@ class EDLCompiler:
|
|
|
121
120
|
entity_model, ast_entity_attributes, props_pk
|
|
122
121
|
)
|
|
123
122
|
|
|
124
|
-
#
|
|
123
|
+
# Construindo o resultado
|
|
125
124
|
compiler_result = CompilerResult()
|
|
126
125
|
compiler_result.entity_class_name = entity_class_name
|
|
127
126
|
compiler_result.entity_code = code_entity
|
|
128
127
|
compiler_result.dto_class_name = dto_class_name
|
|
129
128
|
compiler_result.dto_code = code_dto
|
|
130
129
|
|
|
130
|
+
# Compilando questões das APIs
|
|
131
|
+
compiler_result.api_expose = entity_model.api.expose
|
|
132
|
+
compiler_result.api_resource = entity_model.api.resource
|
|
133
|
+
compiler_result.api_verbs = entity_model.api.verbs
|
|
134
|
+
|
|
131
135
|
return compiler_result
|
|
132
136
|
|
|
133
137
|
def _make_properties_structures(
|
|
@@ -286,3 +290,10 @@ if __name__ == "__main__":
|
|
|
286
290
|
print(f"DTO: {compiler_result.dto_class_name}")
|
|
287
291
|
print(f"{compiler_result.dto_code}")
|
|
288
292
|
print("\n")
|
|
293
|
+
|
|
294
|
+
print("==========================================================")
|
|
295
|
+
print("API Expose: ", compiler_result.api_expose)
|
|
296
|
+
print("API Route Path: ", compiler_result.api_resource)
|
|
297
|
+
print("API Verbs: ", compiler_result.api_verbs)
|
|
298
|
+
print("==========================================================")
|
|
299
|
+
print("\n")
|
{nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/entity_model.py
RENAMED
|
@@ -60,7 +60,7 @@ class EntityModel(BaseModel):
|
|
|
60
60
|
repository: RepositoryModel = Field(
|
|
61
61
|
..., description="Configurações de mapeamento para o banco de dados."
|
|
62
62
|
)
|
|
63
|
-
api:
|
|
64
|
-
|
|
63
|
+
api: APIModel = Field(
|
|
64
|
+
...,
|
|
65
65
|
description="Definição da API REST associada ao modelo, com todos os seus endpoints.",
|
|
66
66
|
)
|
|
@@ -243,6 +243,26 @@ class EDLPropertyCompiler:
|
|
|
243
243
|
enum_class_name, ast_enum_class = result
|
|
244
244
|
enum_classes.append(ast_enum_class)
|
|
245
245
|
|
|
246
|
+
# Resolvendo o nome da propriedade no Entity
|
|
247
|
+
if (
|
|
248
|
+
properties_structure.entity_properties
|
|
249
|
+
and pkey in properties_structure.entity_properties
|
|
250
|
+
):
|
|
251
|
+
entity_field_name = properties_structure.entity_properties[
|
|
252
|
+
pkey
|
|
253
|
+
].column
|
|
254
|
+
else:
|
|
255
|
+
entity_field_name = pkey
|
|
256
|
+
|
|
257
|
+
# Escrevendo, se necessário, o alias para o nome da entity
|
|
258
|
+
if entity_field_name != pkey:
|
|
259
|
+
keywords.append(
|
|
260
|
+
ast.keyword(
|
|
261
|
+
arg="entity_field",
|
|
262
|
+
value=ast.Constant(value=entity_field_name),
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
246
266
|
# Instanciando o atributo AST
|
|
247
267
|
if enum_class_name:
|
|
248
268
|
prop_type = enum_class_name
|
|
@@ -268,16 +288,6 @@ class EDLPropertyCompiler:
|
|
|
268
288
|
ast_dto_attributes.append(ast_attr)
|
|
269
289
|
|
|
270
290
|
# Entity
|
|
271
|
-
if (
|
|
272
|
-
properties_structure.entity_properties
|
|
273
|
-
and pkey in properties_structure.entity_properties
|
|
274
|
-
):
|
|
275
|
-
entity_field_name = properties_structure.entity_properties[
|
|
276
|
-
pkey
|
|
277
|
-
].column
|
|
278
|
-
else:
|
|
279
|
-
entity_field_name = pkey
|
|
280
|
-
|
|
281
291
|
ast_entity_attr = ast.AnnAssign(
|
|
282
292
|
target=ast.Name(
|
|
283
293
|
id=CompilerStrUtil.to_snake_case(entity_field_name),
|
|
@@ -55,17 +55,17 @@ def setup_dynamic_routes(
|
|
|
55
55
|
injector_factory: Any = None,
|
|
56
56
|
) -> None:
|
|
57
57
|
|
|
58
|
-
COLLECTION_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<
|
|
59
|
-
ONE_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<
|
|
58
|
+
COLLECTION_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_resource>"
|
|
59
|
+
ONE_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_resource>/<id>"
|
|
60
60
|
|
|
61
61
|
def list_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
62
62
|
|
|
63
63
|
def list_dynamic(*args: Any, **kwargs: Any):
|
|
64
64
|
# Recuperando o identificador da entidade
|
|
65
|
-
if "
|
|
65
|
+
if "entity_resource" not in kwargs:
|
|
66
66
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
67
67
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
68
|
-
|
|
68
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
69
69
|
|
|
70
70
|
# Lendo tenant e grupo_empresarial
|
|
71
71
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -73,12 +73,20 @@ def setup_dynamic_routes(
|
|
|
73
73
|
try:
|
|
74
74
|
# Recuperando o código do DTO e Entity correspondente
|
|
75
75
|
entity_loader = EntityLoader()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
(
|
|
77
|
+
dto_class_name,
|
|
78
|
+
entity_class_name,
|
|
79
|
+
etities_dict,
|
|
80
|
+
api_expose,
|
|
81
|
+
api_verbs,
|
|
82
|
+
) = entity_loader.load_entity_source(
|
|
83
|
+
entity_resource, tenant, grupo_empresarial
|
|
80
84
|
)
|
|
81
85
|
|
|
86
|
+
# Verificando se essa API deve ser exposta
|
|
87
|
+
if not api_expose or "GET" not in api_verbs:
|
|
88
|
+
return ("", 405, {})
|
|
89
|
+
|
|
82
90
|
# Executando o list pelo RestLib
|
|
83
91
|
route = ListRoute(
|
|
84
92
|
url=COLLECTION_DYNAMIC_ROUTE,
|
|
@@ -90,7 +98,7 @@ def setup_dynamic_routes(
|
|
|
90
98
|
|
|
91
99
|
return route.handle_request(*args, **kwargs)
|
|
92
100
|
except MissingEntityConfigException:
|
|
93
|
-
msg = f"Entity configuration for {
|
|
101
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
94
102
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
95
103
|
|
|
96
104
|
return list_dynamic
|
|
@@ -99,10 +107,10 @@ def setup_dynamic_routes(
|
|
|
99
107
|
|
|
100
108
|
def get_dynamic(*args: Any, **kwargs: Any):
|
|
101
109
|
# Recuperando o identificador da entidade
|
|
102
|
-
if "
|
|
110
|
+
if "entity_resource" not in kwargs:
|
|
103
111
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
104
112
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
105
|
-
|
|
113
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
106
114
|
|
|
107
115
|
# Lendo tenant e grupo_empresarial
|
|
108
116
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -110,12 +118,20 @@ def setup_dynamic_routes(
|
|
|
110
118
|
try:
|
|
111
119
|
# Recuperando o código do DTO e Entity correspondente
|
|
112
120
|
entity_loader = EntityLoader()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
(
|
|
122
|
+
dto_class_name,
|
|
123
|
+
entity_class_name,
|
|
124
|
+
etities_dict,
|
|
125
|
+
api_expose,
|
|
126
|
+
api_verbs,
|
|
127
|
+
) = entity_loader.load_entity_source(
|
|
128
|
+
entity_resource, tenant, grupo_empresarial
|
|
117
129
|
)
|
|
118
130
|
|
|
131
|
+
# Verificando se essa API deve ser exposta
|
|
132
|
+
if not api_expose or "GET" not in api_verbs:
|
|
133
|
+
return ("", 405, {})
|
|
134
|
+
|
|
119
135
|
# Executando o list pelo RestLib
|
|
120
136
|
route = GetRoute(
|
|
121
137
|
url=ONE_DYNAMIC_ROUTE,
|
|
@@ -127,7 +143,7 @@ def setup_dynamic_routes(
|
|
|
127
143
|
|
|
128
144
|
return route.handle_request(*args, **kwargs)
|
|
129
145
|
except MissingEntityConfigException:
|
|
130
|
-
msg = f"Entity configuration for {
|
|
146
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
131
147
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
132
148
|
|
|
133
149
|
return get_dynamic
|
|
@@ -135,10 +151,10 @@ def setup_dynamic_routes(
|
|
|
135
151
|
def post_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
136
152
|
def post_dynamic(*args: Any, **kwargs: Any):
|
|
137
153
|
# Recuperando o identificador da entidade
|
|
138
|
-
if "
|
|
154
|
+
if "entity_resource" not in kwargs:
|
|
139
155
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
140
156
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
141
|
-
|
|
157
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
142
158
|
|
|
143
159
|
# Lendo tenant e grupo_empresarial
|
|
144
160
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -146,12 +162,20 @@ def setup_dynamic_routes(
|
|
|
146
162
|
try:
|
|
147
163
|
# Recuperando o código do DTO e Entity correspondente
|
|
148
164
|
entity_loader = EntityLoader()
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
(
|
|
166
|
+
dto_class_name,
|
|
167
|
+
entity_class_name,
|
|
168
|
+
etities_dict,
|
|
169
|
+
api_expose,
|
|
170
|
+
api_verbs,
|
|
171
|
+
) = entity_loader.load_entity_source(
|
|
172
|
+
entity_resource, tenant, grupo_empresarial
|
|
153
173
|
)
|
|
154
174
|
|
|
175
|
+
# Verificando se essa API deve ser exposta
|
|
176
|
+
if not api_expose or "POST" not in api_verbs:
|
|
177
|
+
return ("", 405, {})
|
|
178
|
+
|
|
155
179
|
# Executando o list pelo RestLib
|
|
156
180
|
route = PostRoute(
|
|
157
181
|
url=COLLECTION_DYNAMIC_ROUTE,
|
|
@@ -163,7 +187,7 @@ def setup_dynamic_routes(
|
|
|
163
187
|
|
|
164
188
|
return route.handle_request(*args, **kwargs)
|
|
165
189
|
except MissingEntityConfigException:
|
|
166
|
-
msg = f"Entity configuration for {
|
|
190
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
167
191
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
168
192
|
|
|
169
193
|
return post_dynamic
|
|
@@ -171,10 +195,10 @@ def setup_dynamic_routes(
|
|
|
171
195
|
def put_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
172
196
|
def put_dynamic(*args: Any, **kwargs: Any):
|
|
173
197
|
# Recuperando o identificador da entidade
|
|
174
|
-
if "
|
|
198
|
+
if "entity_resource" not in kwargs:
|
|
175
199
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
176
200
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
177
|
-
|
|
201
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
178
202
|
|
|
179
203
|
# Lendo tenant e grupo_empresarial
|
|
180
204
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -182,12 +206,20 @@ def setup_dynamic_routes(
|
|
|
182
206
|
try:
|
|
183
207
|
# Recuperando o código do DTO e Entity correspondente
|
|
184
208
|
entity_loader = EntityLoader()
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
209
|
+
(
|
|
210
|
+
dto_class_name,
|
|
211
|
+
entity_class_name,
|
|
212
|
+
etities_dict,
|
|
213
|
+
api_expose,
|
|
214
|
+
api_verbs,
|
|
215
|
+
) = entity_loader.load_entity_source(
|
|
216
|
+
entity_resource, tenant, grupo_empresarial
|
|
189
217
|
)
|
|
190
218
|
|
|
219
|
+
# Verificando se essa API deve ser exposta
|
|
220
|
+
if not api_expose or "PUT" not in api_verbs:
|
|
221
|
+
return ("", 405, {})
|
|
222
|
+
|
|
191
223
|
# Executando o list pelo RestLib
|
|
192
224
|
route = PutRoute(
|
|
193
225
|
url=ONE_DYNAMIC_ROUTE,
|
|
@@ -199,7 +231,7 @@ def setup_dynamic_routes(
|
|
|
199
231
|
|
|
200
232
|
return route.handle_request(*args, **kwargs)
|
|
201
233
|
except MissingEntityConfigException:
|
|
202
|
-
msg = f"Entity configuration for {
|
|
234
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
203
235
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
204
236
|
|
|
205
237
|
return put_dynamic
|
|
@@ -207,10 +239,10 @@ def setup_dynamic_routes(
|
|
|
207
239
|
def patch_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
208
240
|
def patch_dynamic(*args: Any, **kwargs: Any):
|
|
209
241
|
# Recuperando o identificador da entidade
|
|
210
|
-
if "
|
|
242
|
+
if "entity_resource" not in kwargs:
|
|
211
243
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
212
244
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
213
|
-
|
|
245
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
214
246
|
|
|
215
247
|
# Lendo tenant e grupo_empresarial
|
|
216
248
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -218,12 +250,20 @@ def setup_dynamic_routes(
|
|
|
218
250
|
try:
|
|
219
251
|
# Recuperando o código do DTO e Entity correspondente
|
|
220
252
|
entity_loader = EntityLoader()
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
253
|
+
(
|
|
254
|
+
dto_class_name,
|
|
255
|
+
entity_class_name,
|
|
256
|
+
etities_dict,
|
|
257
|
+
api_expose,
|
|
258
|
+
api_verbs,
|
|
259
|
+
) = entity_loader.load_entity_source(
|
|
260
|
+
entity_resource, tenant, grupo_empresarial
|
|
225
261
|
)
|
|
226
262
|
|
|
263
|
+
# Verificando se essa API deve ser exposta
|
|
264
|
+
if not api_expose or "PATCH" not in api_verbs:
|
|
265
|
+
return ("", 405, {})
|
|
266
|
+
|
|
227
267
|
# Executando o list pelo RestLib
|
|
228
268
|
route = PatchRoute(
|
|
229
269
|
url=ONE_DYNAMIC_ROUTE,
|
|
@@ -235,7 +275,7 @@ def setup_dynamic_routes(
|
|
|
235
275
|
|
|
236
276
|
return route.handle_request(*args, **kwargs)
|
|
237
277
|
except MissingEntityConfigException:
|
|
238
|
-
msg = f"Entity configuration for {
|
|
278
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
239
279
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
240
280
|
|
|
241
281
|
return patch_dynamic
|
|
@@ -243,10 +283,10 @@ def setup_dynamic_routes(
|
|
|
243
283
|
def delete_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
|
|
244
284
|
def delete_dynamic(*args: Any, **kwargs: Any):
|
|
245
285
|
# Recuperando o identificador da entidade
|
|
246
|
-
if "
|
|
286
|
+
if "entity_resource" not in kwargs:
|
|
247
287
|
msg = "Faltando parâmetro identificador da entidade na URL."
|
|
248
288
|
return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
|
|
249
|
-
|
|
289
|
+
entity_resource = kwargs.pop("entity_resource")
|
|
250
290
|
|
|
251
291
|
# Lendo tenant e grupo_empresarial
|
|
252
292
|
tenant, grupo_empresarial = _get_tenant_grupo()
|
|
@@ -254,12 +294,20 @@ def setup_dynamic_routes(
|
|
|
254
294
|
try:
|
|
255
295
|
# Recuperando o código do DTO e Entity correspondente
|
|
256
296
|
entity_loader = EntityLoader()
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
297
|
+
(
|
|
298
|
+
dto_class_name,
|
|
299
|
+
entity_class_name,
|
|
300
|
+
etities_dict,
|
|
301
|
+
api_expose,
|
|
302
|
+
api_verbs,
|
|
303
|
+
) = entity_loader.load_entity_source(
|
|
304
|
+
entity_resource, tenant, grupo_empresarial
|
|
261
305
|
)
|
|
262
306
|
|
|
307
|
+
# Verificando se essa API deve ser exposta
|
|
308
|
+
if not api_expose or "DELETE" not in api_verbs:
|
|
309
|
+
return ("", 405, {})
|
|
310
|
+
|
|
263
311
|
# Executando o list pelo RestLib
|
|
264
312
|
route = DeleteRoute(
|
|
265
313
|
url=ONE_DYNAMIC_ROUTE,
|
|
@@ -271,7 +319,7 @@ def setup_dynamic_routes(
|
|
|
271
319
|
|
|
272
320
|
return route.handle_request(*args, **kwargs)
|
|
273
321
|
except MissingEntityConfigException:
|
|
274
|
-
msg = f"Entity configuration for {
|
|
322
|
+
msg = f"Entity configuration for {entity_resource} not found."
|
|
275
323
|
return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
|
|
276
324
|
|
|
277
325
|
return delete_dynamic
|
|
@@ -18,6 +18,8 @@ class LoadedEntity:
|
|
|
18
18
|
self.entity_class_name: str = ""
|
|
19
19
|
self.entity_hash: str = ""
|
|
20
20
|
self.loaded_at: datetime.datetime = datetime.datetime.now()
|
|
21
|
+
self.api_expose: bool = False
|
|
22
|
+
self.api_verbs: list[str] = []
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
class Namespace:
|
|
@@ -37,17 +39,17 @@ class EntityLoader:
|
|
|
37
39
|
|
|
38
40
|
def load_entity_source(
|
|
39
41
|
self,
|
|
40
|
-
|
|
42
|
+
entity_resource: str,
|
|
41
43
|
tenant: str | None,
|
|
42
44
|
grupo_empresarial: str | None,
|
|
43
|
-
) -> tuple[str, str, dict]:
|
|
45
|
+
) -> tuple[str, str, dict, bool, list[str]]:
|
|
44
46
|
# Montando as chaves dos namespaces
|
|
45
47
|
grupo_key = f"tenant_{tenant}.ge_{grupo_empresarial}"
|
|
46
48
|
tenant_key = f"tenant_{tenant}"
|
|
47
49
|
default_key = "default"
|
|
48
50
|
|
|
49
51
|
result = self._load_entity_source_from_memory(
|
|
50
|
-
|
|
52
|
+
entity_resource, grupo_key, tenant_key, default_key
|
|
51
53
|
)
|
|
52
54
|
|
|
53
55
|
# Se conseguiu carregar da memória, verifica se houve alteração no hash, em relação ao redis
|
|
@@ -58,24 +60,39 @@ class EntityLoader:
|
|
|
58
60
|
namespace,
|
|
59
61
|
) = result
|
|
60
62
|
|
|
61
|
-
loaded_entity = namespace.loaded_entities[
|
|
63
|
+
loaded_entity = namespace.loaded_entities[entity_resource]
|
|
62
64
|
dto_class_name = loaded_entity.dto_class_name
|
|
63
65
|
entity_class_name = loaded_entity.entity_class_name
|
|
64
66
|
entities_dict = namespace.entities_dict
|
|
67
|
+
api_expose = loaded_entity.api_expose
|
|
68
|
+
api_verbs = loaded_entity.api_verbs
|
|
65
69
|
|
|
66
70
|
# Se o tempo entre o carregamento e agora for maior do que MIN_TIME_SOURCE_REFRESH minutos,
|
|
67
71
|
# verifica se precisa de refresh
|
|
68
72
|
time_diff = datetime.datetime.now() - loaded_entity.loaded_at
|
|
69
73
|
|
|
70
74
|
if time_diff.total_seconds() >= MIN_TIME_SOURCE_REFRESH * 60:
|
|
75
|
+
# Renovando o tempo de refresh
|
|
76
|
+
loaded_entity.loaded_at = datetime.datetime.now()
|
|
77
|
+
|
|
71
78
|
# Recuperando do Redis direto pela key (faz uma só chamada ao redis)
|
|
72
79
|
loaded_config = self._load_entity_config_from_redis(
|
|
73
|
-
|
|
80
|
+
entity_resource,
|
|
81
|
+
grupo_key,
|
|
82
|
+
tenant_key,
|
|
83
|
+
default_key,
|
|
84
|
+
entity_config_key,
|
|
74
85
|
)
|
|
75
86
|
|
|
76
87
|
# Se não achar no redis, usa o que estava em memória
|
|
77
88
|
if not loaded_config:
|
|
78
|
-
return (
|
|
89
|
+
return (
|
|
90
|
+
dto_class_name,
|
|
91
|
+
entity_class_name,
|
|
92
|
+
entities_dict,
|
|
93
|
+
api_expose,
|
|
94
|
+
api_verbs,
|
|
95
|
+
)
|
|
79
96
|
|
|
80
97
|
# Desempacotando resultado
|
|
81
98
|
entity_config_key, entity_config_str = loaded_config
|
|
@@ -84,23 +101,47 @@ class EntityLoader:
|
|
|
84
101
|
result_execute = self._execute_entity_source(
|
|
85
102
|
entity_config_str,
|
|
86
103
|
entity_config_key,
|
|
87
|
-
|
|
104
|
+
entity_resource,
|
|
88
105
|
check_refresh=True,
|
|
89
106
|
)
|
|
90
107
|
|
|
91
108
|
# Se não carregou novo código, usa o que estava em memória
|
|
92
109
|
if result_execute is None:
|
|
93
|
-
return (
|
|
110
|
+
return (
|
|
111
|
+
dto_class_name,
|
|
112
|
+
entity_class_name,
|
|
113
|
+
entities_dict,
|
|
114
|
+
api_expose,
|
|
115
|
+
api_verbs,
|
|
116
|
+
)
|
|
94
117
|
else:
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
(
|
|
119
|
+
dto_class_name,
|
|
120
|
+
entity_class_name,
|
|
121
|
+
namespace,
|
|
122
|
+
api_expose,
|
|
123
|
+
api_verbs,
|
|
124
|
+
) = result_execute
|
|
125
|
+
return (
|
|
126
|
+
dto_class_name,
|
|
127
|
+
entity_class_name,
|
|
128
|
+
namespace.entities_dict,
|
|
129
|
+
api_expose,
|
|
130
|
+
api_verbs,
|
|
131
|
+
)
|
|
97
132
|
else:
|
|
98
133
|
# Se não deu o intervalo de verificação do refresh, retorna o que está em memória
|
|
99
|
-
return (
|
|
134
|
+
return (
|
|
135
|
+
dto_class_name,
|
|
136
|
+
entity_class_name,
|
|
137
|
+
entities_dict,
|
|
138
|
+
api_expose,
|
|
139
|
+
api_verbs,
|
|
140
|
+
)
|
|
100
141
|
|
|
101
142
|
# Se não conseguir recuperar a entidade, procura no redis:
|
|
102
143
|
loaded_config = self._load_entity_config_from_redis(
|
|
103
|
-
|
|
144
|
+
entity_resource, grupo_key, tenant_key, default_key, None
|
|
104
145
|
)
|
|
105
146
|
|
|
106
147
|
# Se também não achar no redis, lanca exceção
|
|
@@ -112,14 +153,24 @@ class EntityLoader:
|
|
|
112
153
|
|
|
113
154
|
# Executando o código da entidade
|
|
114
155
|
result_execute = self._execute_entity_source(
|
|
115
|
-
entity_config_str, entity_config_key,
|
|
156
|
+
entity_config_str, entity_config_key, entity_resource
|
|
116
157
|
)
|
|
117
158
|
|
|
118
159
|
if result_execute is None:
|
|
119
|
-
raise RuntimeError(
|
|
120
|
-
|
|
160
|
+
raise RuntimeError(
|
|
161
|
+
f"Erro desconhecido carregando entidade: {entity_resource}"
|
|
162
|
+
)
|
|
163
|
+
dto_class_name, entity_class_name, namespace, api_expose, api_verbs = (
|
|
164
|
+
result_execute
|
|
165
|
+
)
|
|
121
166
|
|
|
122
|
-
return (
|
|
167
|
+
return (
|
|
168
|
+
dto_class_name,
|
|
169
|
+
entity_class_name,
|
|
170
|
+
namespace.entities_dict,
|
|
171
|
+
api_expose,
|
|
172
|
+
api_verbs,
|
|
173
|
+
)
|
|
123
174
|
|
|
124
175
|
def clear_namespaces(self):
|
|
125
176
|
"""
|
|
@@ -135,9 +186,9 @@ class EntityLoader:
|
|
|
135
186
|
self,
|
|
136
187
|
entity_config_str: str,
|
|
137
188
|
entity_config_key: str,
|
|
138
|
-
|
|
189
|
+
entity_resource: str,
|
|
139
190
|
check_refresh: bool = False,
|
|
140
|
-
) -> tuple[str, str, Namespace] | None:
|
|
191
|
+
) -> tuple[str, str, Namespace, bool, list[str]] | None:
|
|
141
192
|
# Interpretando o json de configuração da entidade
|
|
142
193
|
try:
|
|
143
194
|
entity_config = json.loads(entity_config_str)
|
|
@@ -147,14 +198,18 @@ class EntityLoader:
|
|
|
147
198
|
source_dto = entity_config["source_dto"]
|
|
148
199
|
source_entity = entity_config["source_entity"]
|
|
149
200
|
entity_hash = entity_config["entity_hash"]
|
|
201
|
+
|
|
202
|
+
api_expose = entity_config["api_expose"]
|
|
203
|
+
# api_resource = entity_config["api_resource"]
|
|
204
|
+
api_verbs = entity_config["api_verbs"]
|
|
150
205
|
except json.JSONDecodeError as e:
|
|
151
206
|
if not check_refresh:
|
|
152
207
|
raise RuntimeError(
|
|
153
|
-
f"Erro ao decodificar JSON da entidade {
|
|
208
|
+
f"Erro ao decodificar JSON da entidade {entity_resource}; na chave {entity_config_key}: {e}"
|
|
154
209
|
)
|
|
155
210
|
else:
|
|
156
211
|
get_logger().error(
|
|
157
|
-
f"Erro ao decodificar JSON da entidade {
|
|
212
|
+
f"Erro ao decodificar JSON da entidade {entity_resource}; na chave {entity_config_key}: {e}"
|
|
158
213
|
)
|
|
159
214
|
return None
|
|
160
215
|
|
|
@@ -164,7 +219,7 @@ class EntityLoader:
|
|
|
164
219
|
if not loaded_namespace:
|
|
165
220
|
return None
|
|
166
221
|
|
|
167
|
-
loaded_entity = loaded_namespace.loaded_entities.get(
|
|
222
|
+
loaded_entity = loaded_namespace.loaded_entities.get(entity_resource)
|
|
168
223
|
if not loaded_entity:
|
|
169
224
|
return None
|
|
170
225
|
|
|
@@ -173,7 +228,7 @@ class EntityLoader:
|
|
|
173
228
|
|
|
174
229
|
# Imprimindo alerta de load no log
|
|
175
230
|
get_logger().debug(
|
|
176
|
-
f"Carregando entidade {
|
|
231
|
+
f"Carregando entidade {entity_resource} no namespace {entity_config_key}."
|
|
177
232
|
)
|
|
178
233
|
|
|
179
234
|
# Carregando a entidade no namespace
|
|
@@ -200,10 +255,12 @@ class EntityLoader:
|
|
|
200
255
|
loaded_entity.dto_class_name = dto_class_name
|
|
201
256
|
loaded_entity.entity_class_name = entity_class_name
|
|
202
257
|
loaded_entity.entity_hash = entity_hash
|
|
258
|
+
loaded_entity.api_expose = api_expose
|
|
259
|
+
loaded_entity.api_verbs = api_verbs
|
|
203
260
|
|
|
204
|
-
namespace.loaded_entities[
|
|
261
|
+
namespace.loaded_entities[entity_resource] = loaded_entity
|
|
205
262
|
|
|
206
|
-
return (dto_class_name, entity_class_name, namespace)
|
|
263
|
+
return (dto_class_name, entity_class_name, namespace, api_expose, api_verbs)
|
|
207
264
|
|
|
208
265
|
def _safe_exec(self, source_code, context, description):
|
|
209
266
|
try:
|
|
@@ -214,35 +271,35 @@ class EntityLoader:
|
|
|
214
271
|
|
|
215
272
|
def _load_entity_config_from_redis(
|
|
216
273
|
self,
|
|
217
|
-
|
|
274
|
+
entity_resource: str,
|
|
218
275
|
grupo_key: str,
|
|
219
276
|
tenant_key: str,
|
|
220
277
|
default_key: str,
|
|
221
278
|
entity_config_key: str | None,
|
|
222
279
|
) -> tuple[str, str] | None:
|
|
223
280
|
get_logger().debug(
|
|
224
|
-
f"Procurando a configuração da entidade {
|
|
281
|
+
f"Procurando a configuração da entidade {entity_resource} no redis"
|
|
225
282
|
)
|
|
226
283
|
|
|
227
284
|
if entity_config_key is not None:
|
|
228
285
|
entity_config_str = get_redis(
|
|
229
|
-
"entity_config", ESCOPO_RESTLIB2, entity_config_key,
|
|
286
|
+
"entity_config", ESCOPO_RESTLIB2, entity_config_key, entity_resource
|
|
230
287
|
)
|
|
231
288
|
|
|
232
289
|
else:
|
|
233
290
|
entity_config_key = grupo_key
|
|
234
291
|
entity_config_str = get_redis(
|
|
235
|
-
"entity_config", ESCOPO_RESTLIB2, grupo_key,
|
|
292
|
+
"entity_config", ESCOPO_RESTLIB2, grupo_key, entity_resource
|
|
236
293
|
)
|
|
237
294
|
if entity_config_str is None:
|
|
238
295
|
entity_config_key = tenant_key
|
|
239
296
|
entity_config_str = get_redis(
|
|
240
|
-
"entity_config", ESCOPO_RESTLIB2, tenant_key,
|
|
297
|
+
"entity_config", ESCOPO_RESTLIB2, tenant_key, entity_resource
|
|
241
298
|
)
|
|
242
299
|
if entity_config_str is None:
|
|
243
300
|
entity_config_key = default_key
|
|
244
301
|
entity_config_str = get_redis(
|
|
245
|
-
"entity_config", ESCOPO_RESTLIB2, default_key,
|
|
302
|
+
"entity_config", ESCOPO_RESTLIB2, default_key, entity_resource
|
|
246
303
|
)
|
|
247
304
|
|
|
248
305
|
# Se não encontrar no redis, retorna None
|
|
@@ -253,7 +310,7 @@ class EntityLoader:
|
|
|
253
310
|
|
|
254
311
|
def _load_entity_source_from_memory(
|
|
255
312
|
self,
|
|
256
|
-
|
|
313
|
+
entity_resource: str,
|
|
257
314
|
grupo_key: str,
|
|
258
315
|
tenant_key: str,
|
|
259
316
|
default_key: str,
|
|
@@ -263,19 +320,19 @@ class EntityLoader:
|
|
|
263
320
|
|
|
264
321
|
# Pesquisando a entidade no namespace mais específico (grupo_empresarial)
|
|
265
322
|
grupo_namespace = namespaces_dict.get(grupo_key)
|
|
266
|
-
if grupo_namespace and
|
|
323
|
+
if grupo_namespace and entity_resource in grupo_namespace.loaded_entities:
|
|
267
324
|
entity_config_key = grupo_key
|
|
268
325
|
namespace = grupo_namespace
|
|
269
326
|
|
|
270
327
|
# Pesquisando a entidade no namespace intermediário (tenant)
|
|
271
328
|
tenant_namespace = namespaces_dict.get(tenant_key)
|
|
272
|
-
if tenant_namespace and
|
|
329
|
+
if tenant_namespace and entity_resource in tenant_namespace.loaded_entities:
|
|
273
330
|
entity_config_key = tenant_key
|
|
274
331
|
namespace = tenant_namespace
|
|
275
332
|
|
|
276
333
|
# Pesquisando a entidade no namespace padrão (default)
|
|
277
334
|
default_namespace = namespaces_dict.get(default_key)
|
|
278
|
-
if default_namespace and
|
|
335
|
+
if default_namespace and entity_resource in default_namespace.loaded_entities:
|
|
279
336
|
entity_config_key = default_key
|
|
280
337
|
namespace = default_namespace
|
|
281
338
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
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.8
|
|
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).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py
RENAMED
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.7 → nsj_rest_lib2-0.0.8}/nsj_rest_lib2/compiler/edl_model/repository_model.py
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|