nsj-rest-lib2 0.0.19__py3-none-any.whl → 0.0.21__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/compiler.py +213 -19
- nsj_rest_lib2/compiler/dto_compiler.py +34 -6
- nsj_rest_lib2/compiler/edl_model/entity_model_base.py +4 -0
- nsj_rest_lib2/compiler/edl_model/repository_model.py +23 -0
- nsj_rest_lib2/compiler/entity_compiler.py +87 -30
- nsj_rest_lib2/compiler/property_compiler.py +5 -1
- {nsj_rest_lib2-0.0.19.dist-info → nsj_rest_lib2-0.0.21.dist-info}/METADATA +5 -1
- {nsj_rest_lib2-0.0.19.dist-info → nsj_rest_lib2-0.0.21.dist-info}/RECORD +10 -10
- {nsj_rest_lib2-0.0.19.dist-info → nsj_rest_lib2-0.0.21.dist-info}/WHEEL +0 -0
- {nsj_rest_lib2-0.0.19.dist-info → nsj_rest_lib2-0.0.21.dist-info}/top_level.txt +0 -0
|
@@ -12,9 +12,15 @@ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
|
|
|
12
12
|
from nsj_rest_lib2.compiler.edl_model.entity_model_root import EntityModelRoot
|
|
13
13
|
from nsj_rest_lib2.compiler.edl_model.primitives import REGEX_EXTERNAL_REF
|
|
14
14
|
from nsj_rest_lib2.compiler.entity_compiler import EntityCompiler
|
|
15
|
-
from nsj_rest_lib2.compiler.model import CompilerResult
|
|
15
|
+
from nsj_rest_lib2.compiler.model import CompilerResult, RelationDependency
|
|
16
16
|
from nsj_rest_lib2.compiler.property_compiler import EDLPropertyCompiler
|
|
17
17
|
|
|
18
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
19
|
+
compile_dto_class_name,
|
|
20
|
+
compile_entity_class_name,
|
|
21
|
+
compile_namespace_keys,
|
|
22
|
+
)
|
|
23
|
+
|
|
18
24
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
19
25
|
|
|
20
26
|
from nsj_rest_lib2.settings import get_logger
|
|
@@ -96,6 +102,27 @@ class EDLCompiler:
|
|
|
96
102
|
if not escopo:
|
|
97
103
|
raise Exception(f"Escopo não definido para a entidade: {entity_model.id}.")
|
|
98
104
|
|
|
105
|
+
# Tratando dos dados base para a extensão parcial
|
|
106
|
+
partial_metadata: dict[str, str] | None = None
|
|
107
|
+
partial_base_model: EntityModel | None = None
|
|
108
|
+
if isinstance(entity_model, EntityModel) and entity_model.partial_of:
|
|
109
|
+
if entity_model.partial_of not in entity_models:
|
|
110
|
+
raise Exception(
|
|
111
|
+
f"Entidade base '{entity_model.partial_of}' não encontrada para a extensão parcial '{entity_model.id}'."
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
base_model_candidate = entity_models[entity_model.partial_of]
|
|
115
|
+
if not isinstance(base_model_candidate, EntityModel):
|
|
116
|
+
raise Exception(
|
|
117
|
+
f"Entidade base '{entity_model.partial_of}' da extensão parcial '{entity_model.id}' é inválida."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
self._validate_partial_model(entity_model, base_model_candidate, entity_models)
|
|
121
|
+
partial_metadata = self._build_partial_metadata(
|
|
122
|
+
entity_model, base_model_candidate
|
|
123
|
+
)
|
|
124
|
+
partial_base_model = base_model_candidate
|
|
125
|
+
|
|
99
126
|
# Criando um mapa de índices por nome de property
|
|
100
127
|
# TODO Implementar tratamento dos índices de apoio às query (não de unicidade)
|
|
101
128
|
map_indexes_by_property: dict[str, list[IndexCompilerStructure]] = {}
|
|
@@ -131,6 +158,18 @@ class EDLCompiler:
|
|
|
131
158
|
prefx_class_name,
|
|
132
159
|
)
|
|
133
160
|
|
|
161
|
+
# Adicionando os imports da extensão parcial
|
|
162
|
+
if partial_metadata:
|
|
163
|
+
related_imports.append(
|
|
164
|
+
(
|
|
165
|
+
partial_metadata["module"],
|
|
166
|
+
partial_metadata["dto_class"],
|
|
167
|
+
partial_metadata["entity_class"],
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
related_imports = self._deduplicate_related_imports(related_imports)
|
|
172
|
+
|
|
134
173
|
# Gerando o buffer para os códigos de DTO e Entity
|
|
135
174
|
dto_code = ""
|
|
136
175
|
entity_code = ""
|
|
@@ -175,6 +214,7 @@ class EDLCompiler:
|
|
|
175
214
|
related_imports,
|
|
176
215
|
fixed_filters,
|
|
177
216
|
prefx_class_name,
|
|
217
|
+
partial_metadata,
|
|
178
218
|
)
|
|
179
219
|
|
|
180
220
|
# Gerando o código da Entity
|
|
@@ -183,26 +223,42 @@ class EDLCompiler:
|
|
|
183
223
|
ast_entity_attributes,
|
|
184
224
|
props_pk,
|
|
185
225
|
prefx_class_name,
|
|
226
|
+
partial_metadata,
|
|
186
227
|
)
|
|
187
228
|
|
|
188
229
|
# Extendendo os buffers com os códigos gerados
|
|
189
230
|
dto_code += code_dto
|
|
190
231
|
entity_code += code_entity
|
|
232
|
+
|
|
233
|
+
# Adicionando as dependências das relações
|
|
191
234
|
relations_dependencies_complete.extend(relations_dependencies)
|
|
192
235
|
|
|
236
|
+
# Adicionando as dependências da extensão parcial
|
|
237
|
+
if partial_metadata and partial_base_model:
|
|
238
|
+
relation_dependency = RelationDependency()
|
|
239
|
+
if not partial_base_model.api or not partial_base_model.api.resource:
|
|
240
|
+
raise Exception(
|
|
241
|
+
f"Entidade base '{partial_base_model.id}' não possui configuração de API necessária para extensão parcial."
|
|
242
|
+
)
|
|
243
|
+
relation_dependency.entity_resource = partial_base_model.api.resource
|
|
244
|
+
relation_dependency.entity_scope = partial_base_model.escopo
|
|
245
|
+
relation_dependency.tenant = partial_base_model.tenant
|
|
246
|
+
relation_dependency.grupo_empresarial = partial_base_model.grupo_empresarial
|
|
247
|
+
relations_dependencies_complete.append(relation_dependency)
|
|
248
|
+
|
|
193
249
|
# Construindo o resultado
|
|
194
250
|
compiler_result = CompilerResult()
|
|
195
251
|
compiler_result.entity_class_name = entity_class_name
|
|
196
252
|
compiler_result.entity_code = entity_code
|
|
197
253
|
compiler_result.dto_class_name = dto_class_name
|
|
198
254
|
compiler_result.dto_code = dto_code
|
|
255
|
+
compiler_result.relations_dependencies = relations_dependencies_complete
|
|
199
256
|
|
|
200
257
|
# Compilando questões das APIs
|
|
201
258
|
if isinstance(entity_model, EntityModel):
|
|
202
259
|
compiler_result.api_expose = entity_model.api.expose
|
|
203
260
|
compiler_result.api_resource = entity_model.api.resource
|
|
204
261
|
compiler_result.api_verbs = entity_model.api.verbs
|
|
205
|
-
compiler_result.relations_dependencies = relations_dependencies_complete
|
|
206
262
|
|
|
207
263
|
get_logger().debug(f"código gerado para a entidade: {entity_model.id}")
|
|
208
264
|
get_logger().debug("DTO Code:")
|
|
@@ -212,6 +268,138 @@ class EDLCompiler:
|
|
|
212
268
|
|
|
213
269
|
return compiler_result
|
|
214
270
|
|
|
271
|
+
def _validate_partial_model(
|
|
272
|
+
self,
|
|
273
|
+
partial_model: EntityModel,
|
|
274
|
+
base_model: EntityModel,
|
|
275
|
+
entity_models: dict[str, EntityModel],
|
|
276
|
+
) -> None:
|
|
277
|
+
base_properties_structure = PropertiesCompilerStructure()
|
|
278
|
+
self._make_properties_structures(
|
|
279
|
+
base_properties_structure,
|
|
280
|
+
base_model,
|
|
281
|
+
entity_models,
|
|
282
|
+
)
|
|
283
|
+
aggregated_base_properties = set(base_properties_structure.properties.keys())
|
|
284
|
+
|
|
285
|
+
duplicated_properties = aggregated_base_properties.intersection(
|
|
286
|
+
set(partial_model.properties.keys())
|
|
287
|
+
)
|
|
288
|
+
if duplicated_properties:
|
|
289
|
+
raise Exception(
|
|
290
|
+
f"Extensão parcial '{partial_model.id}' redefine propriedades da entidade base '{base_model.id}': {sorted(duplicated_properties)}."
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
lists_to_check = {
|
|
294
|
+
"required": partial_model.required,
|
|
295
|
+
"main_properties": partial_model.main_properties,
|
|
296
|
+
"partition_data": partial_model.partition_data,
|
|
297
|
+
"search_properties": partial_model.search_properties,
|
|
298
|
+
"metric_label": partial_model.metric_label,
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for list_name, values in lists_to_check.items():
|
|
302
|
+
if not values:
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
conflicts = [
|
|
306
|
+
value
|
|
307
|
+
for value in values
|
|
308
|
+
if isinstance(value, str) and value in aggregated_base_properties
|
|
309
|
+
]
|
|
310
|
+
if conflicts:
|
|
311
|
+
raise Exception(
|
|
312
|
+
f"Extensão parcial '{partial_model.id}' utiliza propriedades da entidade base '{base_model.id}' em '{list_name}': {conflicts}."
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
link = partial_model.repository.link_to_base
|
|
316
|
+
if not link:
|
|
317
|
+
raise Exception(
|
|
318
|
+
f"Extensão parcial '{partial_model.id}' requer configuração 'link_to_base' no bloco 'repository'."
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if not link.base_property:
|
|
322
|
+
raise Exception(
|
|
323
|
+
f"Extensão parcial '{partial_model.id}' requer 'base_property' definido em 'link_to_base'."
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if not link.column:
|
|
327
|
+
raise Exception(
|
|
328
|
+
f"Extensão parcial '{partial_model.id}' requer 'column' definido em 'link_to_base'."
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if link.base_property not in aggregated_base_properties:
|
|
332
|
+
raise Exception(
|
|
333
|
+
f"'base_property' '{link.base_property}' não corresponde a uma propriedade da entidade base '{base_model.id}'."
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# if not base_model.api or not base_model.api.resource:
|
|
337
|
+
# raise Exception(
|
|
338
|
+
# f"Entidade base '{base_model.id}' não possui configuração de API compatível com extensões parciais."
|
|
339
|
+
# )
|
|
340
|
+
|
|
341
|
+
def _build_partial_metadata(
|
|
342
|
+
self, partial_model: EntityModel, base_model: EntityModel
|
|
343
|
+
) -> dict[str, str]:
|
|
344
|
+
link = partial_model.repository.link_to_base
|
|
345
|
+
if link is None:
|
|
346
|
+
raise Exception(
|
|
347
|
+
f"Extensão parcial '{partial_model.id}' está sem configuração 'link_to_base'."
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
grupo_key, tenant_key, default_key = compile_namespace_keys(
|
|
351
|
+
base_model.tenant, base_model.grupo_empresarial
|
|
352
|
+
)
|
|
353
|
+
namespace = self._resolve_namespace_key(
|
|
354
|
+
base_model.tenant,
|
|
355
|
+
base_model.grupo_empresarial,
|
|
356
|
+
grupo_key,
|
|
357
|
+
tenant_key,
|
|
358
|
+
default_key,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
"module": namespace,
|
|
363
|
+
"dto_class": compile_dto_class_name(base_model.id),
|
|
364
|
+
"entity_class": compile_entity_class_name(base_model.id),
|
|
365
|
+
"relation_field": link.column,
|
|
366
|
+
"related_entity_field": link.base_property,
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
def _resolve_namespace_key(
|
|
370
|
+
self,
|
|
371
|
+
tenant: str | int | None,
|
|
372
|
+
grupo_empresarial,
|
|
373
|
+
grupo_key: str,
|
|
374
|
+
tenant_key: str,
|
|
375
|
+
default_key: str,
|
|
376
|
+
) -> str:
|
|
377
|
+
has_tenant = tenant not in (None, 0, "0")
|
|
378
|
+
has_grupo = (
|
|
379
|
+
grupo_empresarial
|
|
380
|
+
and str(grupo_empresarial) != "00000000-0000-0000-0000-000000000000"
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
if has_tenant and has_grupo:
|
|
384
|
+
return grupo_key
|
|
385
|
+
if has_tenant:
|
|
386
|
+
return tenant_key
|
|
387
|
+
return default_key
|
|
388
|
+
|
|
389
|
+
def _deduplicate_related_imports(
|
|
390
|
+
self, related_imports: list[tuple[str, str, str]]
|
|
391
|
+
) -> list[tuple[str, str, str]]:
|
|
392
|
+
deduped: list[tuple[str, str, str]] = []
|
|
393
|
+
seen: set[tuple[str, str, str]] = set()
|
|
394
|
+
|
|
395
|
+
for import_tuple in related_imports:
|
|
396
|
+
if import_tuple in seen:
|
|
397
|
+
continue
|
|
398
|
+
seen.add(import_tuple)
|
|
399
|
+
deduped.append(import_tuple)
|
|
400
|
+
|
|
401
|
+
return deduped
|
|
402
|
+
|
|
215
403
|
def _make_components_structures(
|
|
216
404
|
self,
|
|
217
405
|
components_structure: ComponentsCompilerStructure,
|
|
@@ -295,7 +483,9 @@ class EDLCompiler:
|
|
|
295
483
|
continue
|
|
296
484
|
|
|
297
485
|
if "/" in main_property:
|
|
298
|
-
path_parts = [
|
|
486
|
+
path_parts = [
|
|
487
|
+
part.strip() for part in main_property.split("/") if part
|
|
488
|
+
]
|
|
299
489
|
if len(path_parts) < 2 or not path_parts[0]:
|
|
300
490
|
raise Exception(
|
|
301
491
|
f"Propriedade resumo inválida '{main_property}' na entidade '{entity_model.id}'."
|
|
@@ -430,6 +620,10 @@ class EDLCompiler:
|
|
|
430
620
|
if entity_model.trait_from:
|
|
431
621
|
entities.append(entity_model.trait_from)
|
|
432
622
|
|
|
623
|
+
# Adicionando dependências por classes parciais
|
|
624
|
+
if isinstance(entity_model, EntityModel) and entity_model.partial_of:
|
|
625
|
+
entities.append(entity_model.partial_of)
|
|
626
|
+
|
|
433
627
|
# Populando com as dependências de propriedades de relacionamento
|
|
434
628
|
relations = self._list_dependencies_relations(entity_model)
|
|
435
629
|
|
|
@@ -512,19 +706,19 @@ if __name__ == "__main__":
|
|
|
512
706
|
compiler = EDLCompiler()
|
|
513
707
|
compiler_results = compiler.compile_models(entities)
|
|
514
708
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
709
|
+
with open("output_compilacao_local.py", "w") as f:
|
|
710
|
+
for compiler_result in compiler_results:
|
|
711
|
+
f.write("==========================================================\n")
|
|
712
|
+
f.write(f"Entity: {compiler_result.entity_class_name}\n")
|
|
713
|
+
f.write(f"{compiler_result.entity_code}\n")
|
|
714
|
+
f.write("\n")
|
|
715
|
+
f.write("==========================================================\n")
|
|
716
|
+
f.write(f"DTO: {compiler_result.dto_class_name}\n")
|
|
717
|
+
f.write(f"{compiler_result.dto_code}\n")
|
|
718
|
+
f.write("\n")
|
|
719
|
+
f.write("==========================================================\n")
|
|
720
|
+
f.write(f"API Expose: {compiler_result.api_expose}\n")
|
|
721
|
+
f.write(f"API Route Path: {compiler_result.api_resource}\n")
|
|
722
|
+
f.write(f"API Verbs: {compiler_result.api_verbs}\n")
|
|
723
|
+
f.write("==========================================================\n")
|
|
724
|
+
f.write("\n")
|
|
@@ -20,6 +20,7 @@ class DTOCompiler:
|
|
|
20
20
|
related_imports: list[tuple[str, str, str]],
|
|
21
21
|
fixed_filters: list[tuple[str, BasicTypes]],
|
|
22
22
|
prefx_class_name: str,
|
|
23
|
+
partial_metadata: dict[str, str] | None,
|
|
23
24
|
) -> tuple[str, str]:
|
|
24
25
|
"""
|
|
25
26
|
Compila o código do DTO a partir do AST e retorna o código compilado.
|
|
@@ -110,15 +111,42 @@ class DTOCompiler:
|
|
|
110
111
|
)
|
|
111
112
|
)
|
|
112
113
|
|
|
114
|
+
# Keywords para a extensão parcial
|
|
115
|
+
decorator_keywords: list[ast.keyword] = []
|
|
116
|
+
|
|
117
|
+
if partial_metadata:
|
|
118
|
+
partial_dict_keys = [
|
|
119
|
+
ast.Constant(value="dto"),
|
|
120
|
+
ast.Constant(value="relation_field"),
|
|
121
|
+
]
|
|
122
|
+
partial_dict_values = [
|
|
123
|
+
ast.Name(id=partial_metadata["dto_class"], ctx=ast.Load()),
|
|
124
|
+
ast.Constant(value=partial_metadata["relation_field"]),
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
related_field = partial_metadata.get("related_entity_field")
|
|
128
|
+
if related_field:
|
|
129
|
+
partial_dict_keys.append(ast.Constant(value="related_entity_field"))
|
|
130
|
+
partial_dict_values.append(ast.Constant(value=related_field))
|
|
131
|
+
|
|
132
|
+
decorator_keywords.append(
|
|
133
|
+
ast.keyword(
|
|
134
|
+
arg="partial_of",
|
|
135
|
+
value=ast.Dict(
|
|
136
|
+
keys=partial_dict_keys,
|
|
137
|
+
values=partial_dict_values,
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
113
142
|
# Keywords para tipos usados em fixed_filters
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
keywords_decorator_dto.append(
|
|
143
|
+
if fixed_filters:
|
|
144
|
+
decorator_keywords.append(
|
|
117
145
|
ast.keyword(
|
|
118
146
|
arg="fixed_filters",
|
|
119
147
|
value=ast.Dict(
|
|
120
|
-
keys=[ast.Constant(value=
|
|
121
|
-
values=[ast.Constant(value=
|
|
148
|
+
keys=[ast.Constant(value=item[0]) for item in fixed_filters],
|
|
149
|
+
values=[ast.Constant(value=item[1]) for item in fixed_filters],
|
|
122
150
|
),
|
|
123
151
|
)
|
|
124
152
|
)
|
|
@@ -133,7 +161,7 @@ class DTOCompiler:
|
|
|
133
161
|
ast.Call(
|
|
134
162
|
func=ast.Name(id="DTO", ctx=ast.Load()),
|
|
135
163
|
args=[],
|
|
136
|
-
keywords=
|
|
164
|
+
keywords=decorator_keywords,
|
|
137
165
|
)
|
|
138
166
|
],
|
|
139
167
|
body=ast_dto_attributes,
|
|
@@ -29,6 +29,10 @@ class EntityModelBase(BaseModel):
|
|
|
29
29
|
trait_from: Optional[str] = Field(
|
|
30
30
|
None, description="Identificador da entidade que a trait estende."
|
|
31
31
|
)
|
|
32
|
+
partial_of: Optional[str] = Field(
|
|
33
|
+
None,
|
|
34
|
+
description="Identificador da entidade base estendida por esta extensão parcial (relacionamento 1x1 transparente).",
|
|
35
|
+
)
|
|
32
36
|
mixins: Optional[List[str]] = Field(
|
|
33
37
|
None,
|
|
34
38
|
description="Lista de mixins (blocos reutilizáveis) aplicados ao modelo.",
|
|
@@ -5,6 +5,25 @@ from nsj_rest_lib2.compiler.edl_model.column_meta_model import ColumnMetaModel
|
|
|
5
5
|
from nsj_rest_lib2.compiler.edl_model.index_model import IndexModel
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class RepositoryLinkToBaseModel(BaseModel):
|
|
9
|
+
base_property: str = Field(
|
|
10
|
+
...,
|
|
11
|
+
description="Nome da propriedade na entidade base utilizada para o relacionamento 1x1.",
|
|
12
|
+
)
|
|
13
|
+
column: str = Field(
|
|
14
|
+
...,
|
|
15
|
+
description="Nome da coluna (ou propriedade) na entidade parcial responsável por referenciar a entidade base.",
|
|
16
|
+
)
|
|
17
|
+
on_delete: Optional[str] = Field(
|
|
18
|
+
None,
|
|
19
|
+
description="Política de exclusão configurada no relacionamento com a entidade base.",
|
|
20
|
+
)
|
|
21
|
+
nullable: Optional[bool] = Field(
|
|
22
|
+
False,
|
|
23
|
+
description="Indica se o relacionamento com a entidade base pode ser nulo.",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
8
27
|
class RepositoryModel(BaseModel):
|
|
9
28
|
map: str = Field(
|
|
10
29
|
..., description="Nome da tabela, no BD, para a qual a entidade é mapeada."
|
|
@@ -22,3 +41,7 @@ class RepositoryModel(BaseModel):
|
|
|
22
41
|
indexes: Optional[List[IndexModel]] = Field(
|
|
23
42
|
None, description="Lista de índices de banco de dados, associados à entidade."
|
|
24
43
|
)
|
|
44
|
+
link_to_base: Optional[RepositoryLinkToBaseModel] = Field(
|
|
45
|
+
None,
|
|
46
|
+
description="Configuração de relacionamento 1x1 com a entidade base (extensões parciais).",
|
|
47
|
+
)
|
|
@@ -18,6 +18,7 @@ class EntityCompiler:
|
|
|
18
18
|
ast_entity_attributes: list[ast.stmt],
|
|
19
19
|
props_pk: list[str],
|
|
20
20
|
prefix_class_name: str,
|
|
21
|
+
partial_metadata: dict[str, str] | None,
|
|
21
22
|
) -> tuple[str, str]:
|
|
22
23
|
# Imports
|
|
23
24
|
imports = [
|
|
@@ -39,25 +40,53 @@ class EntityCompiler:
|
|
|
39
40
|
),
|
|
40
41
|
]
|
|
41
42
|
|
|
43
|
+
if partial_metadata:
|
|
44
|
+
imports.append(
|
|
45
|
+
ast.ImportFrom(
|
|
46
|
+
module=f"dynamic.{partial_metadata['module']}",
|
|
47
|
+
names=[
|
|
48
|
+
ast.alias(name=partial_metadata["entity_class"], asname=None)
|
|
49
|
+
],
|
|
50
|
+
level=0,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
42
54
|
# Entity
|
|
43
55
|
if len(props_pk) > 1:
|
|
44
56
|
raise Exception(
|
|
45
57
|
f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {props_pk}"
|
|
46
58
|
)
|
|
47
|
-
|
|
59
|
+
|
|
60
|
+
if len(props_pk) == 0 and not partial_metadata:
|
|
48
61
|
raise Exception(
|
|
49
62
|
f"Entidade '{entity_model.id}' não possui nenhuma chave primária (ainda não suportado)."
|
|
50
63
|
)
|
|
51
64
|
|
|
52
65
|
default_order_props = []
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
# Resolvendo o nome da coluna da chave primária
|
|
68
|
+
if len(props_pk) == 1:
|
|
69
|
+
key_field_property = props_pk[0]
|
|
70
|
+
else:
|
|
71
|
+
key_field_property = (
|
|
72
|
+
partial_metadata["relation_field"] if partial_metadata else None
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
key_field = key_field_property
|
|
76
|
+
if (
|
|
77
|
+
len(props_pk) == 1
|
|
78
|
+
and entity_model.repository.properties
|
|
79
|
+
and key_field_property in entity_model.repository.properties
|
|
80
|
+
and entity_model.repository.properties[key_field_property].column
|
|
81
|
+
):
|
|
82
|
+
key_field = entity_model.repository.properties[key_field_property].column
|
|
83
|
+
elif partial_metadata and partial_metadata.get("relation_field"):
|
|
84
|
+
key_field = partial_metadata["relation_field"]
|
|
85
|
+
|
|
86
|
+
if key_field is None:
|
|
87
|
+
raise Exception(
|
|
88
|
+
f"Não foi possível determinar a chave primária para a entidade '{entity_model.id}'."
|
|
89
|
+
)
|
|
61
90
|
|
|
62
91
|
if (
|
|
63
92
|
isinstance(entity_model, EntityModel)
|
|
@@ -90,28 +119,9 @@ class EntityCompiler:
|
|
|
90
119
|
ast.Call(
|
|
91
120
|
func=ast.Name(id="Entity", ctx=ast.Load()),
|
|
92
121
|
args=[],
|
|
93
|
-
keywords=
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
value=ast.Constant(value=entity_model.repository.map),
|
|
97
|
-
),
|
|
98
|
-
ast.keyword(
|
|
99
|
-
arg="pk_field",
|
|
100
|
-
value=ast.Constant(
|
|
101
|
-
value=CompilerStrUtil.to_snake_case(key_field)
|
|
102
|
-
),
|
|
103
|
-
),
|
|
104
|
-
ast.keyword(
|
|
105
|
-
arg="default_order_fields",
|
|
106
|
-
value=ast.List(
|
|
107
|
-
elts=[
|
|
108
|
-
ast.Constant(value=field)
|
|
109
|
-
for field in default_order_fields
|
|
110
|
-
],
|
|
111
|
-
ctx=ast.Load(),
|
|
112
|
-
),
|
|
113
|
-
),
|
|
114
|
-
],
|
|
122
|
+
keywords=self._build_entity_decorator_keywords(
|
|
123
|
+
entity_model, key_field, default_order_fields, partial_metadata
|
|
124
|
+
),
|
|
115
125
|
)
|
|
116
126
|
],
|
|
117
127
|
body=ast_entity_attributes,
|
|
@@ -131,3 +141,50 @@ class EntityCompiler:
|
|
|
131
141
|
code = black.format_str(code, mode=black.FileMode())
|
|
132
142
|
|
|
133
143
|
return (class_name, code)
|
|
144
|
+
|
|
145
|
+
def _build_entity_decorator_keywords(
|
|
146
|
+
self,
|
|
147
|
+
entity_model: EntityModelBase,
|
|
148
|
+
key_field: str,
|
|
149
|
+
default_order_fields: list[str],
|
|
150
|
+
partial_metadata: dict[str, str] | None,
|
|
151
|
+
) -> list[ast.keyword]:
|
|
152
|
+
keywords = [
|
|
153
|
+
ast.keyword(
|
|
154
|
+
arg="table_name",
|
|
155
|
+
value=ast.Constant(value=entity_model.repository.map),
|
|
156
|
+
),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
if not partial_metadata:
|
|
160
|
+
keywords.extend(
|
|
161
|
+
[
|
|
162
|
+
ast.keyword(
|
|
163
|
+
arg="pk_field",
|
|
164
|
+
value=ast.Constant(
|
|
165
|
+
value=CompilerStrUtil.to_snake_case(key_field)
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
ast.keyword(
|
|
169
|
+
arg="default_order_fields",
|
|
170
|
+
value=ast.List(
|
|
171
|
+
elts=[
|
|
172
|
+
ast.Constant(value=field)
|
|
173
|
+
for field in default_order_fields
|
|
174
|
+
],
|
|
175
|
+
ctx=ast.Load(),
|
|
176
|
+
),
|
|
177
|
+
),
|
|
178
|
+
]
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if partial_metadata:
|
|
182
|
+
keywords.insert(
|
|
183
|
+
0,
|
|
184
|
+
ast.keyword(
|
|
185
|
+
arg="partial_of",
|
|
186
|
+
value=ast.Name(id=partial_metadata["entity_class"], ctx=ast.Load()),
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return keywords
|
|
@@ -64,12 +64,16 @@ class EDLPropertyCompiler:
|
|
|
64
64
|
if prop.pk:
|
|
65
65
|
pk_keys.append(pkey)
|
|
66
66
|
|
|
67
|
+
is_partial_extension = (
|
|
68
|
+
isinstance(entity_model, EntityModel) and bool(entity_model.partial_of)
|
|
69
|
+
)
|
|
70
|
+
|
|
67
71
|
if not entity_model.mixin:
|
|
68
72
|
if len(pk_keys) > 1:
|
|
69
73
|
raise Exception(
|
|
70
74
|
f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {pk_keys}"
|
|
71
75
|
)
|
|
72
|
-
elif len(pk_keys) == 0:
|
|
76
|
+
elif len(pk_keys) == 0 and not is_partial_extension:
|
|
73
77
|
raise Exception(
|
|
74
78
|
f"Entidade '{entity_model.id}' não tem nenhuma chave primária (ainda não suportado)"
|
|
75
79
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.21
|
|
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
|
|
@@ -26,6 +26,10 @@ Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configur
|
|
|
26
26
|
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
27
27
|
|
|
28
28
|
## TODO
|
|
29
|
+
* Valores default
|
|
30
|
+
* Usar trait_properties como valor default
|
|
31
|
+
* Não está carregando a lista de dependências de execução, para os components
|
|
32
|
+
* Implementar gravação de entidades com extensão parcial
|
|
29
33
|
* Unificar o arquivo redis_config.py
|
|
30
34
|
* Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
|
|
31
35
|
* Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
|
|
@@ -4,23 +4,23 @@ nsj_rest_lib2/redis_config.py,sha256=4KLcvYS3nJO7PMQgF6F9_j6r-TyqcS7TBbd3LEQuKDU
|
|
|
4
4
|
nsj_rest_lib2/settings.py,sha256=Hn_o1HZmievnYb8D1kNT2Nq-OEjxbyNjOiOpbnFsMwE,367
|
|
5
5
|
nsj_rest_lib2/compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
nsj_rest_lib2/compiler/ai_compiler.py,sha256=shAtN4T0ai_52qh15L3mK1QbeC6glHOR6C3J3gj14II,9029
|
|
7
|
-
nsj_rest_lib2/compiler/compiler.py,sha256=
|
|
7
|
+
nsj_rest_lib2/compiler/compiler.py,sha256=LoJr8oFKF5Tpa_-ffqa0kBBqypbNVlvJo00RRpqGPUk,27568
|
|
8
8
|
nsj_rest_lib2/compiler/compiler_structures.py,sha256=a_DF_nU4VgCt0sWJI03Ky6fPHvoIM0cjMkwQb_dYv6Q,1437
|
|
9
|
-
nsj_rest_lib2/compiler/dto_compiler.py,sha256=
|
|
10
|
-
nsj_rest_lib2/compiler/entity_compiler.py,sha256
|
|
9
|
+
nsj_rest_lib2/compiler/dto_compiler.py,sha256=mvZo19owtpFTjanCE20T0jmBKjhKXIVUsnR5raiGoRM,6728
|
|
10
|
+
nsj_rest_lib2/compiler/entity_compiler.py,sha256=-mDA1-dHKYp0umQi1A4GbA0F1hlaC-gQRqj69vkADC4,6536
|
|
11
11
|
nsj_rest_lib2/compiler/model.py,sha256=QDBoM26qoZdiNcykl1nvXCrFDhg-6Q__QzVq6uY1QzE,1460
|
|
12
|
-
nsj_rest_lib2/compiler/property_compiler.py,sha256=
|
|
12
|
+
nsj_rest_lib2/compiler/property_compiler.py,sha256=Kqo9c1AvAjBN-7A4PBvrrXRPkngogH7FUZ6-pMuf8TE,41460
|
|
13
13
|
nsj_rest_lib2/compiler/edl_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py,sha256=664QBDcOgVnyfwtUOXO1W7AKaZhueBG335x5DuogruY,7644
|
|
15
15
|
nsj_rest_lib2/compiler/edl_model/api_model.py,sha256=pH0Uiq_64AGvkHqwY44TrulWWZXbi6M0EKJWUhSqKj0,837
|
|
16
16
|
nsj_rest_lib2/compiler/edl_model/column_meta_model.py,sha256=s0sEVkoW1hV2_hto1mws4XhzKGH_b4NzhaOiwFH25Ks,694
|
|
17
17
|
nsj_rest_lib2/compiler/edl_model/entity_model.py,sha256=Yc6wvjsiwacmz796mZIU-i9hxzNV9yuLPdULGKYHNbM,854
|
|
18
|
-
nsj_rest_lib2/compiler/edl_model/entity_model_base.py,sha256=
|
|
18
|
+
nsj_rest_lib2/compiler/edl_model/entity_model_base.py,sha256=eRn0pirIPHvniqGpT0xE-mmgqz5RIVtqghxcnfxKNdQ,4345
|
|
19
19
|
nsj_rest_lib2/compiler/edl_model/entity_model_root.py,sha256=VinsxFlNyCaKKk37ZzcbmWaWgoUP2-dZBG61Ke7NvVs,231
|
|
20
20
|
nsj_rest_lib2/compiler/edl_model/index_model.py,sha256=cXWlu0hxtro5vvYoirkDW4R3PCnBW5oCCWjRJ6AX5zE,508
|
|
21
21
|
nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=GTo8e1mf9munvl_J1-fmaXgSJ8yQvGX56f2rwS85M1Y,1459
|
|
22
22
|
nsj_rest_lib2/compiler/edl_model/property_meta_model.py,sha256=ie3roNZAqYTwz7q0TTdceMjY5khnBiED2yp0XBqqk7E,3909
|
|
23
|
-
nsj_rest_lib2/compiler/edl_model/repository_model.py,sha256=
|
|
23
|
+
nsj_rest_lib2/compiler/edl_model/repository_model.py,sha256=OmtUzSq2LKmM76VXefGrKwg8xtVFCutt3JNH2nv42qU,1821
|
|
24
24
|
nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=NtMVZeOPu3LJwXI-8tCOLVuQiGua_0t7kL1h9gL7HuM,657
|
|
25
25
|
nsj_rest_lib2/compiler/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
nsj_rest_lib2/compiler/util/str_util.py,sha256=lVP1yHhj1pOd6ULtTnkcfX6Gqrpe4yCBratHUhBNGcI,843
|
|
@@ -30,7 +30,7 @@ nsj_rest_lib2/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
30
30
|
nsj_rest_lib2/controller/dynamic_controller.py,sha256=XMqxe1NW-NE5XwomXb4pSNdALQHpP74Hc26R4fnmXqg,15194
|
|
31
31
|
nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
nsj_rest_lib2/service/entity_loader.py,sha256=uB0xXih9Px2jSTMdZNIu6_3dngE37K7adKX03cYSRug,15660
|
|
33
|
-
nsj_rest_lib2-0.0.
|
|
34
|
-
nsj_rest_lib2-0.0.
|
|
35
|
-
nsj_rest_lib2-0.0.
|
|
36
|
-
nsj_rest_lib2-0.0.
|
|
33
|
+
nsj_rest_lib2-0.0.21.dist-info/METADATA,sha256=dp0eblopq0bUAmCc6x53ICZmJcWy9h7p0ckM7cg2xLE,1789
|
|
34
|
+
nsj_rest_lib2-0.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
35
|
+
nsj_rest_lib2-0.0.21.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
|
|
36
|
+
nsj_rest_lib2-0.0.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|