nsj-rest-lib2 0.0.8__py3-none-any.whl → 0.0.9__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 +37 -11
- nsj_rest_lib2/compiler/dto_compiler.py +27 -1
- nsj_rest_lib2/compiler/edl_model/column_meta_model.py +12 -2
- nsj_rest_lib2/compiler/edl_model/entity_model.py +8 -0
- nsj_rest_lib2/compiler/edl_model/primitives.py +17 -5
- nsj_rest_lib2/compiler/edl_model/property_meta_model.py +15 -3
- nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +2 -2
- nsj_rest_lib2/compiler/entity_compiler.py +2 -1
- nsj_rest_lib2/compiler/property_compiler.py +385 -232
- nsj_rest_lib2/compiler/util/type_naming_util.py +21 -0
- nsj_rest_lib2/service/entity_loader.py +35 -9
- {nsj_rest_lib2-0.0.8.dist-info → nsj_rest_lib2-0.0.9.dist-info}/METADATA +2 -1
- {nsj_rest_lib2-0.0.8.dist-info → nsj_rest_lib2-0.0.9.dist-info}/RECORD +15 -14
- {nsj_rest_lib2-0.0.8.dist-info → nsj_rest_lib2-0.0.9.dist-info}/WHEEL +0 -0
- {nsj_rest_lib2-0.0.8.dist-info → nsj_rest_lib2-0.0.9.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from typing import Any
|
|
2
3
|
|
|
3
4
|
from nsj_rest_lib2.compiler.compiler_structures import (
|
|
@@ -5,6 +6,7 @@ from nsj_rest_lib2.compiler.compiler_structures import (
|
|
|
5
6
|
PropertiesCompilerStructure,
|
|
6
7
|
)
|
|
7
8
|
from nsj_rest_lib2.compiler.dto_compiler import DTOCompiler
|
|
9
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import REGEX_EXTERNAL_REF
|
|
8
10
|
from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
|
|
9
11
|
from nsj_rest_lib2.compiler.entity_compiler import EntityCompiler
|
|
10
12
|
from nsj_rest_lib2.compiler.property_compiler import EDLPropertyCompiler
|
|
@@ -13,11 +15,15 @@ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
|
13
15
|
|
|
14
16
|
from nsj_rest_lib2.settings import get_logger
|
|
15
17
|
|
|
18
|
+
# TODO Autenticação nas rotas
|
|
19
|
+
# TODO Carregar, dinamicamente, em memória, o código compilado das dependÊncias por relacionamento
|
|
16
20
|
# TODO Atualizar o status da entidade pelo worker de compilação (e talvez parar uma compilação, quando se delete uma entidade)
|
|
17
21
|
# TODO Relacionamentos
|
|
18
22
|
# TODO Classes Abstratas
|
|
19
23
|
# TODO Partial Classes
|
|
20
24
|
# TODO Migrations
|
|
25
|
+
# TODO Adicionar autenticação aos endpoints dinâmicos
|
|
26
|
+
# TODO Criar imagem docker base para as aplicações, usando venv (para poderem atualizar o pydantic)
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
class CompilerResult:
|
|
@@ -102,17 +108,22 @@ class EDLCompiler:
|
|
|
102
108
|
)
|
|
103
109
|
|
|
104
110
|
# Criando a lista de atributos do DTO e da Entity; e recuperando as chaves primarias
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
(
|
|
112
|
+
ast_dto_attributes,
|
|
113
|
+
ast_entity_attributes,
|
|
114
|
+
props_pk,
|
|
115
|
+
enum_classes,
|
|
116
|
+
related_imports,
|
|
117
|
+
) = self._properties_compiler.compile(
|
|
118
|
+
properties_structure,
|
|
119
|
+
map_unique_by_property,
|
|
120
|
+
entity_model,
|
|
121
|
+
entity_models,
|
|
111
122
|
)
|
|
112
123
|
|
|
113
124
|
# Gerando o código do DTO
|
|
114
125
|
dto_class_name, code_dto = self._dto_compiler.compile(
|
|
115
|
-
entity_model, ast_dto_attributes, enum_classes
|
|
126
|
+
entity_model, ast_dto_attributes, enum_classes, related_imports
|
|
116
127
|
)
|
|
117
128
|
|
|
118
129
|
# Gerando o código da Entity
|
|
@@ -232,16 +243,27 @@ class EDLCompiler:
|
|
|
232
243
|
|
|
233
244
|
def _list_dependencies(self, entity_model: EntityModel) -> list[str]:
|
|
234
245
|
entities: list[str] = []
|
|
246
|
+
|
|
235
247
|
if entity_model.trait_from:
|
|
236
248
|
entities.append(entity_model.trait_from)
|
|
237
249
|
|
|
250
|
+
# Populando com as dependências de propriedades de relacionamento 1_N
|
|
251
|
+
for pkey in entity_model.properties:
|
|
252
|
+
prop = entity_model.properties[pkey]
|
|
253
|
+
|
|
254
|
+
if isinstance(prop.type, str):
|
|
255
|
+
external_match = re.match(REGEX_EXTERNAL_REF, prop.type)
|
|
256
|
+
if external_match:
|
|
257
|
+
external_dependency = external_match.group(0)
|
|
258
|
+
entities.append(external_dependency)
|
|
259
|
+
|
|
238
260
|
return entities
|
|
239
261
|
|
|
240
262
|
|
|
241
263
|
def get_files_from_directory(directory):
|
|
242
264
|
files = []
|
|
243
265
|
for file in os.listdir(directory):
|
|
244
|
-
if file.endswith(".json"):
|
|
266
|
+
if file.endswith(".json") or file.endswith(".yml") or file.endswith(".yaml"):
|
|
245
267
|
files.append(os.path.join(directory, file))
|
|
246
268
|
return files
|
|
247
269
|
|
|
@@ -250,6 +272,7 @@ if __name__ == "__main__":
|
|
|
250
272
|
import argparse
|
|
251
273
|
import json
|
|
252
274
|
import os
|
|
275
|
+
import yaml
|
|
253
276
|
|
|
254
277
|
parser = argparse.ArgumentParser(
|
|
255
278
|
description="Compila arquivos EDL para classes Python"
|
|
@@ -267,13 +290,16 @@ if __name__ == "__main__":
|
|
|
267
290
|
|
|
268
291
|
entities = {}
|
|
269
292
|
for file in files:
|
|
270
|
-
with open(file) as f:
|
|
271
|
-
|
|
293
|
+
with open(file, "r") as f:
|
|
294
|
+
if file.endswith(".json"):
|
|
295
|
+
edl = json.load(f)
|
|
296
|
+
else:
|
|
297
|
+
edl = yaml.safe_load(f)
|
|
272
298
|
|
|
273
299
|
# Instanciando o objeto de modelo de entidade a partir do JSON,
|
|
274
300
|
# e já realizando as validações básicas de tipo e estrutura.
|
|
275
301
|
print(f"Validando arquivo: {file}")
|
|
276
|
-
entity_model = EntityModel(**
|
|
302
|
+
entity_model = EntityModel(**edl)
|
|
277
303
|
|
|
278
304
|
complete_entity_id = f"{entity_model.escopo}/{entity_model.id}"
|
|
279
305
|
entities[complete_entity_id] = entity_model
|
|
@@ -4,6 +4,7 @@ import black
|
|
|
4
4
|
|
|
5
5
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
6
6
|
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
7
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import compile_dto_class_name
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class DTOCompiler:
|
|
@@ -15,6 +16,7 @@ class DTOCompiler:
|
|
|
15
16
|
entity_model: EntityModel,
|
|
16
17
|
ast_dto_attributes: list[ast.stmt],
|
|
17
18
|
enum_classes: list[ast.stmt],
|
|
19
|
+
related_imports: list[tuple[str, str, str]],
|
|
18
20
|
) -> tuple[str, str]:
|
|
19
21
|
"""
|
|
20
22
|
Compila o código do DTO a partir do AST e retorna o código compilado.
|
|
@@ -31,6 +33,7 @@ class DTOCompiler:
|
|
|
31
33
|
:return: Código compilado do DTO
|
|
32
34
|
:rtype: str
|
|
33
35
|
"""
|
|
36
|
+
# Criando o ast dos imports
|
|
34
37
|
imports = [
|
|
35
38
|
# import datetime
|
|
36
39
|
ast.Import(names=[ast.alias(name="datetime", asname=None)]),
|
|
@@ -50,6 +53,12 @@ class DTOCompiler:
|
|
|
50
53
|
names=[ast.alias(name="DTOField", asname=None)],
|
|
51
54
|
level=0,
|
|
52
55
|
),
|
|
56
|
+
# from nsj_rest_lib.descriptor.dto_list_field import DTOField
|
|
57
|
+
ast.ImportFrom(
|
|
58
|
+
module="nsj_rest_lib.descriptor.dto_list_field",
|
|
59
|
+
names=[ast.alias(name="DTOListField", asname=None)],
|
|
60
|
+
level=0,
|
|
61
|
+
),
|
|
53
62
|
# from nsj_rest_lib.descriptor.dto_field_validators import DTOFieldValidators
|
|
54
63
|
ast.ImportFrom(
|
|
55
64
|
module="nsj_rest_lib.descriptor.dto_field_validators",
|
|
@@ -64,7 +73,24 @@ class DTOCompiler:
|
|
|
64
73
|
),
|
|
65
74
|
]
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
for import_ in related_imports:
|
|
77
|
+
imports.append(
|
|
78
|
+
ast.ImportFrom(
|
|
79
|
+
module=f"dynamic.{import_[0]}",
|
|
80
|
+
names=[ast.alias(name=import_[1])],
|
|
81
|
+
level=0,
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
imports.append(
|
|
85
|
+
ast.ImportFrom(
|
|
86
|
+
module=f"dynamic.{import_[0]}",
|
|
87
|
+
names=[ast.alias(name=import_[2])],
|
|
88
|
+
level=0,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Criando o ast da classe
|
|
93
|
+
class_name = compile_dto_class_name(entity_model.id)
|
|
68
94
|
ast_class = ast.ClassDef(
|
|
69
95
|
name=class_name,
|
|
70
96
|
bases=[ast.Name(id="DTOBase", ctx=ast.Load())],
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
1
|
+
from pydantic import BaseModel, Field, model_validator
|
|
2
2
|
from typing import List, Optional, Literal
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ColumnMetaModel(BaseModel):
|
|
6
|
-
column: str = Field(
|
|
6
|
+
column: Optional[str] = Field(None, description="Nome da coluna no banco de dados.")
|
|
7
|
+
relation_column: Optional[str] = Field(
|
|
8
|
+
None,
|
|
9
|
+
description="Nome da coluna usada para relacionamento com outra entidade (para um relacionamento 1_N, indica a coluna da entidade relacionada, que aponta para a PK da entidade corrente).",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
@model_validator(mode="after")
|
|
13
|
+
def check_columns(self):
|
|
14
|
+
if self.column is None and self.relation_column is None:
|
|
15
|
+
raise ValueError("column or relation_column must be provided")
|
|
16
|
+
return self
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
1
3
|
from pydantic import BaseModel, Field
|
|
2
4
|
from typing import Dict, List, Optional
|
|
3
5
|
|
|
@@ -64,3 +66,9 @@ class EntityModel(BaseModel):
|
|
|
64
66
|
...,
|
|
65
67
|
description="Definição da API REST associada ao modelo, com todos os seus endpoints.",
|
|
66
68
|
)
|
|
69
|
+
|
|
70
|
+
# Propriedades de controle da compilação (não fazem parte do JSON de representação das entidades)
|
|
71
|
+
tenant: int = Field(default=0, exclude=True)
|
|
72
|
+
grupo_empresarial: uuid.UUID = Field(
|
|
73
|
+
default=uuid.UUID("00000000-0000-0000-0000-000000000000"), exclude=True
|
|
74
|
+
)
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import enum
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, List, Union
|
|
4
|
+
from pydantic import StringConstraints
|
|
5
|
+
|
|
6
|
+
REGEX_EXTERNAL_REF = r"^(\w+)\/(\w+)$"
|
|
7
|
+
REGEX_INTERNAL_REF = r"^#\/(\w+)\/(\w+)$"
|
|
8
|
+
|
|
9
|
+
ExternalRefType = Annotated[str, StringConstraints(pattern=REGEX_EXTERNAL_REF)]
|
|
10
|
+
InternalRefType = Annotated[str, StringConstraints(pattern=REGEX_INTERNAL_REF)]
|
|
3
11
|
|
|
4
12
|
|
|
5
13
|
class PrimitiveTypes(enum.Enum):
|
|
@@ -8,8 +16,6 @@ class PrimitiveTypes(enum.Enum):
|
|
|
8
16
|
NUMBER = "number"
|
|
9
17
|
INTEGER = "integer"
|
|
10
18
|
BOOLEAN = "boolean"
|
|
11
|
-
ARRAY = "array"
|
|
12
|
-
OBJECT = "object"
|
|
13
19
|
UUID = "uuid"
|
|
14
20
|
CURRENCY = "currency"
|
|
15
21
|
QUANTITY = "quantity"
|
|
@@ -21,13 +27,13 @@ class PrimitiveTypes(enum.Enum):
|
|
|
21
27
|
DATETIME = "datetime"
|
|
22
28
|
|
|
23
29
|
|
|
30
|
+
PropertyType = Union[PrimitiveTypes, ExternalRefType, InternalRefType]
|
|
31
|
+
|
|
24
32
|
MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
|
|
25
33
|
PrimitiveTypes.STRING: "str",
|
|
26
34
|
PrimitiveTypes.NUMBER: "float",
|
|
27
35
|
PrimitiveTypes.INTEGER: "int",
|
|
28
36
|
PrimitiveTypes.BOOLEAN: "bool",
|
|
29
|
-
PrimitiveTypes.ARRAY: "List",
|
|
30
|
-
PrimitiveTypes.OBJECT: "dict",
|
|
31
37
|
PrimitiveTypes.UUID: "uuid.UUID",
|
|
32
38
|
PrimitiveTypes.CURRENCY: "float",
|
|
33
39
|
PrimitiveTypes.QUANTITY: "float",
|
|
@@ -41,3 +47,9 @@ MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
|
|
|
41
47
|
|
|
42
48
|
BasicTypes = int | bool | float | str
|
|
43
49
|
DefaultTypes = BasicTypes | List[BasicTypes]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CardinalityTypes(enum.Enum):
|
|
53
|
+
C1_1 = "1_1"
|
|
54
|
+
C1_N = "1_N"
|
|
55
|
+
CN_N = "N_N"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
1
|
+
from pydantic import BaseModel, Field, model_validator
|
|
2
2
|
from typing import List, Optional
|
|
3
3
|
|
|
4
4
|
from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes, DefaultTypes
|
|
5
|
-
from nsj_rest_lib2.compiler.edl_model.primitives import
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import CardinalityTypes, PropertyType
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class DomainConfigModel(BaseModel):
|
|
@@ -13,7 +13,7 @@ class DomainConfigModel(BaseModel):
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class PropertyMetaModel(BaseModel):
|
|
16
|
-
type:
|
|
16
|
+
type: PropertyType = Field(..., description="Tipo da propriedade.")
|
|
17
17
|
label: Optional[str] = Field(None, description="Rótulo da propriedade.")
|
|
18
18
|
description: Optional[str] = Field(None, description="Descrição da propriedade.")
|
|
19
19
|
default: Optional[DefaultTypes] = Field(
|
|
@@ -28,6 +28,10 @@ class PropertyMetaModel(BaseModel):
|
|
|
28
28
|
description="Indica se a propriedade é parte de uma chave alternativa (chave natural).",
|
|
29
29
|
alias="keyAlternative",
|
|
30
30
|
)
|
|
31
|
+
cardinality: Optional[CardinalityTypes] = Field(
|
|
32
|
+
None,
|
|
33
|
+
description="Cardinalidade do relacionamento (válido para propriedades do tipo 'array' ou 'object').",
|
|
34
|
+
)
|
|
31
35
|
# Validação
|
|
32
36
|
max_length: Optional[int] = Field(
|
|
33
37
|
None, description="Comprimento máximo (para strings)."
|
|
@@ -80,3 +84,11 @@ class PropertyMetaModel(BaseModel):
|
|
|
80
84
|
None,
|
|
81
85
|
description="Função de conversão do tipo da entidade para o tipo da API (ex.: converter uuid para string).",
|
|
82
86
|
)
|
|
87
|
+
|
|
88
|
+
@model_validator(mode="after")
|
|
89
|
+
def check_cardinality_required(self):
|
|
90
|
+
if isinstance(self.type, str) and self.cardinality is None:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"The property 'cardinality' is required when type point to other entity."
|
|
93
|
+
)
|
|
94
|
+
return self
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field
|
|
2
2
|
|
|
3
|
-
from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes,
|
|
3
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes, PropertyType
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class TraitPropertyMetaModel(BaseModel):
|
|
7
|
-
type:
|
|
7
|
+
type: PropertyType = Field(..., description="Tipo da propriedade.")
|
|
8
8
|
value: BasicTypes = Field(
|
|
9
9
|
..., description="Valor fixo da propriedade de condicionamento do trait."
|
|
10
10
|
)
|
|
@@ -5,6 +5,7 @@ import black
|
|
|
5
5
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
6
6
|
from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
|
|
7
7
|
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
8
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import compile_entity_class_name
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class EntityCompiler:
|
|
@@ -66,7 +67,7 @@ class EntityCompiler:
|
|
|
66
67
|
if CompilerStrUtil.to_snake_case(props_pk[0]) not in default_order_fields:
|
|
67
68
|
default_order_fields.append(CompilerStrUtil.to_snake_case(props_pk[0]))
|
|
68
69
|
|
|
69
|
-
class_name =
|
|
70
|
+
class_name = compile_entity_class_name(entity_model.id)
|
|
70
71
|
ast_class = ast.ClassDef(
|
|
71
72
|
name=class_name,
|
|
72
73
|
bases=[ast.Name(id="EntityBase", ctx=ast.Load())],
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
import re
|
|
2
3
|
|
|
3
4
|
from nsj_rest_lib2.compiler.compiler_structures import (
|
|
4
5
|
IndexCompilerStructure,
|
|
5
6
|
PropertiesCompilerStructure,
|
|
6
7
|
)
|
|
7
8
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
8
|
-
from nsj_rest_lib2.compiler.edl_model.primitives import
|
|
9
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import (
|
|
10
|
+
CardinalityTypes,
|
|
11
|
+
PrimitiveTypes,
|
|
12
|
+
REGEX_EXTERNAL_REF,
|
|
13
|
+
REGEX_INTERNAL_REF,
|
|
14
|
+
)
|
|
9
15
|
from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
|
|
10
16
|
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
17
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
18
|
+
compile_dto_class_name,
|
|
19
|
+
compile_entity_class_name,
|
|
20
|
+
compile_namespace_keys,
|
|
21
|
+
)
|
|
11
22
|
from nsj_rest_lib2.compiler.util.type_util import TypeUtil
|
|
12
23
|
|
|
13
24
|
# TODO pattern
|
|
@@ -22,288 +33,430 @@ class EDLPropertyCompiler:
|
|
|
22
33
|
properties_structure: PropertiesCompilerStructure,
|
|
23
34
|
map_unique_by_property: dict[str, IndexCompilerStructure],
|
|
24
35
|
entity_model: EntityModel,
|
|
25
|
-
|
|
36
|
+
entity_models: dict[str, EntityModel],
|
|
37
|
+
) -> tuple[
|
|
38
|
+
list[ast.stmt],
|
|
39
|
+
list[ast.stmt],
|
|
40
|
+
list[str],
|
|
41
|
+
list[ast.stmt],
|
|
42
|
+
list[tuple[str, str, str]],
|
|
43
|
+
]:
|
|
26
44
|
|
|
27
45
|
# TODO Criar opção de campo calculado?
|
|
28
46
|
|
|
47
|
+
# Descobrindo os atributos marcados como PK (e recuperando a chave primária)
|
|
48
|
+
# pk_keys = []
|
|
49
|
+
# for pkey in properties_structure.properties:
|
|
50
|
+
# prop = properties_structure.properties[pkey]
|
|
51
|
+
|
|
52
|
+
# if isinstance(prop.type, PrimitiveTypes):
|
|
53
|
+
# if prop.pk:
|
|
54
|
+
# pk_keys.append(pkey)
|
|
55
|
+
|
|
56
|
+
# if len(pk_keys) > 1:
|
|
57
|
+
# raise Exception(
|
|
58
|
+
# f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {pk_keys}"
|
|
59
|
+
# )
|
|
60
|
+
# elif len(pk_keys) == 0:
|
|
61
|
+
# raise Exception(
|
|
62
|
+
# f"Entidade '{entity_model.id}' não tem nenhuma chave primária (ainda não suportado)"
|
|
63
|
+
# )
|
|
64
|
+
|
|
65
|
+
# pk_key = pk_keys[0]
|
|
66
|
+
|
|
67
|
+
# Instanciando as listas de retorno
|
|
29
68
|
ast_dto_attributes = []
|
|
30
69
|
ast_entity_attributes = []
|
|
31
70
|
props_pk = []
|
|
32
71
|
enum_classes = []
|
|
72
|
+
related_imports = []
|
|
33
73
|
|
|
34
74
|
if properties_structure.properties is None:
|
|
35
75
|
return (ast_dto_attributes, ast_entity_attributes, props_pk, enum_classes)
|
|
36
76
|
|
|
37
77
|
for pkey in properties_structure.properties:
|
|
38
78
|
prop = properties_structure.properties[pkey]
|
|
39
|
-
enum_class_name = None
|
|
40
79
|
|
|
41
80
|
# DTO
|
|
42
81
|
## Tratando propriedade simples (não array, não object)
|
|
43
|
-
if prop.type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
if isinstance(prop.type, PrimitiveTypes):
|
|
83
|
+
self.compile_simple_property(
|
|
84
|
+
properties_structure,
|
|
85
|
+
map_unique_by_property,
|
|
86
|
+
entity_model,
|
|
87
|
+
ast_dto_attributes,
|
|
88
|
+
ast_entity_attributes,
|
|
89
|
+
props_pk,
|
|
90
|
+
enum_classes,
|
|
91
|
+
pkey,
|
|
92
|
+
prop,
|
|
93
|
+
)
|
|
49
94
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ast.keyword(arg="candidate_key", value=ast.Constant(True))
|
|
53
|
-
)
|
|
95
|
+
elif isinstance(prop.type, str):
|
|
96
|
+
external_match = re.match(REGEX_EXTERNAL_REF, prop.type)
|
|
54
97
|
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
):
|
|
59
|
-
keywords.append(ast.keyword(arg="resume", value=ast.Constant(True)))
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
properties_structure.required
|
|
63
|
-
and pkey in properties_structure.required
|
|
64
|
-
):
|
|
65
|
-
keywords.append(
|
|
66
|
-
ast.keyword(arg="not_null", value=ast.Constant(True))
|
|
67
|
-
)
|
|
98
|
+
if external_match:
|
|
99
|
+
# Resolvendo o id da entidade
|
|
100
|
+
related_entity_id = external_match.group(2)
|
|
68
101
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
keywords.append(
|
|
74
|
-
ast.keyword(arg="partition_data", value=ast.Constant(True))
|
|
102
|
+
# Resolvendo o nome das classes de DTO e Entity
|
|
103
|
+
related_dto_class_name = compile_dto_class_name(related_entity_id)
|
|
104
|
+
related_entity_class_name = compile_entity_class_name(
|
|
105
|
+
related_entity_id
|
|
75
106
|
)
|
|
76
107
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
keywords.append(
|
|
80
|
-
ast.keyword(
|
|
81
|
-
arg="unique",
|
|
82
|
-
value=ast.Constant(unique.name),
|
|
83
|
-
)
|
|
84
|
-
)
|
|
108
|
+
# Resolvendo o caminho do import
|
|
109
|
+
related_entity_key = external_match.group(0)
|
|
85
110
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
ast.keyword(
|
|
91
|
-
arg="default_value",
|
|
92
|
-
value=ast.Name(str(prop.default), ctx=ast.Load()),
|
|
111
|
+
related_entity = entity_models.get(related_entity_key)
|
|
112
|
+
if not related_entity:
|
|
113
|
+
raise Exception(
|
|
114
|
+
f"Entidade '{entity_model.id}' possui uma referência externa para uma entidade inexistente: '{related_entity_key}', por meio da propriedade: '{pkey}'."
|
|
93
115
|
)
|
|
94
|
-
)
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
min = None
|
|
101
|
-
if prop.type in [PrimitiveTypes.STRING, PrimitiveTypes.EMAIL]:
|
|
102
|
-
if prop.max_length:
|
|
103
|
-
max = prop.max_length
|
|
104
|
-
elif prop.min_length:
|
|
105
|
-
min = prop.min_length
|
|
106
|
-
elif prop.type in [PrimitiveTypes.INTEGER, PrimitiveTypes.NUMBER]:
|
|
107
|
-
if prop.minimum:
|
|
108
|
-
min = prop.minimum
|
|
109
|
-
elif prop.maximum:
|
|
110
|
-
max = prop.maximum
|
|
111
|
-
|
|
112
|
-
if max:
|
|
113
|
-
keywords.append(
|
|
114
|
-
ast.keyword(arg="max", value=ast.Constant(prop.max_length))
|
|
115
|
-
)
|
|
116
|
-
if min:
|
|
117
|
-
keywords.append(
|
|
118
|
-
ast.keyword(arg="min", value=ast.Constant(prop.min_length))
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
properties_structure.search_properties
|
|
123
|
-
and pkey in properties_structure.search_properties
|
|
124
|
-
):
|
|
125
|
-
keywords.append(ast.keyword(arg="search", value=ast.Constant(True)))
|
|
126
|
-
else:
|
|
127
|
-
keywords.append(
|
|
128
|
-
ast.keyword(arg="search", value=ast.Constant(False))
|
|
117
|
+
tenant = related_entity.tenant
|
|
118
|
+
grupo_empresarial = related_entity.grupo_empresarial
|
|
119
|
+
grupo_key, tenant_key, default_key = compile_namespace_keys(
|
|
120
|
+
tenant, grupo_empresarial
|
|
129
121
|
)
|
|
130
122
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
123
|
+
if (
|
|
124
|
+
tenant
|
|
125
|
+
and tenant != 0
|
|
126
|
+
and grupo_empresarial
|
|
127
|
+
and grupo_empresarial != "00000000-0000-0000-0000-000000000000"
|
|
128
|
+
):
|
|
129
|
+
related_import = grupo_key
|
|
130
|
+
elif tenant and tenant != 0:
|
|
131
|
+
related_import = tenant_key
|
|
132
|
+
else:
|
|
133
|
+
related_import = default_key
|
|
134
|
+
|
|
135
|
+
related_imports.append(
|
|
136
|
+
(
|
|
137
|
+
related_import,
|
|
138
|
+
related_dto_class_name,
|
|
139
|
+
related_entity_class_name,
|
|
140
|
+
)
|
|
137
141
|
)
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
),
|
|
148
|
-
args=[],
|
|
149
|
-
keywords=[],
|
|
143
|
+
# Instanciando o ast
|
|
144
|
+
if prop.cardinality == CardinalityTypes.C1_N:
|
|
145
|
+
# Para relacionamentos 1_N
|
|
146
|
+
keywords = [
|
|
147
|
+
ast.keyword(
|
|
148
|
+
arg="dto_type",
|
|
149
|
+
value=ast.Name(
|
|
150
|
+
id=related_dto_class_name, ctx=ast.Load()
|
|
150
151
|
),
|
|
151
|
-
attr="validate_cpf",
|
|
152
|
-
ctx=ast.Load(),
|
|
153
152
|
),
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
ast.keyword(
|
|
159
|
-
arg="validator",
|
|
160
|
-
value=ast.Attribute(
|
|
161
|
-
value=ast.Call(
|
|
162
|
-
func=ast.Name(
|
|
163
|
-
id="DTOFieldValidators", ctx=ast.Load()
|
|
164
|
-
),
|
|
165
|
-
args=[],
|
|
166
|
-
keywords=[],
|
|
153
|
+
ast.keyword(
|
|
154
|
+
arg="entity_type",
|
|
155
|
+
value=ast.Name(
|
|
156
|
+
id=related_entity_class_name, ctx=ast.Load()
|
|
167
157
|
),
|
|
168
|
-
attr="validate_cnpj",
|
|
169
|
-
ctx=ast.Load(),
|
|
170
158
|
),
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
# Resolvendo a coluna usada no relacionamento
|
|
162
|
+
if (
|
|
163
|
+
not properties_structure.entity_properties
|
|
164
|
+
or pkey not in properties_structure.entity_properties
|
|
165
|
+
or not properties_structure.entity_properties[
|
|
166
|
+
pkey
|
|
167
|
+
].relation_column
|
|
168
|
+
):
|
|
169
|
+
raise Exception(
|
|
170
|
+
f"Propriedade '{pkey}' possui um relacionamento, mas nenhuma coluna de relacioanamento foi apontada na propriedade correspondente no repository."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
relation_column = properties_structure.entity_properties[
|
|
174
|
+
pkey
|
|
175
|
+
].relation_column
|
|
176
|
+
|
|
177
|
+
keywords.append(
|
|
178
|
+
ast.keyword(
|
|
179
|
+
arg="related_entity_field",
|
|
180
|
+
value=ast.Constant(value=relation_column),
|
|
181
|
+
)
|
|
171
182
|
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
arg="validator",
|
|
177
|
-
value=ast.Attribute(
|
|
178
|
-
value=ast.Call(
|
|
179
|
-
func=ast.Name(
|
|
180
|
-
id="DTOFieldValidators", ctx=ast.Load()
|
|
181
|
-
),
|
|
182
|
-
args=[],
|
|
183
|
-
keywords=[],
|
|
184
|
-
),
|
|
185
|
-
attr="validate_cpf_or_cnpj",
|
|
186
|
-
ctx=ast.Load(),
|
|
183
|
+
|
|
184
|
+
ast_attr = ast.AnnAssign(
|
|
185
|
+
target=ast.Name(
|
|
186
|
+
id=CompilerStrUtil.to_snake_case(pkey), ctx=ast.Store()
|
|
187
187
|
),
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
elif prop.type == PrimitiveTypes.EMAIL and not prop.validator:
|
|
191
|
-
keywords.append(
|
|
192
|
-
ast.keyword(
|
|
193
|
-
arg="validator",
|
|
194
|
-
value=ast.Attribute(
|
|
195
|
-
value=ast.Call(
|
|
196
|
-
func=ast.Name(
|
|
197
|
-
id="DTOFieldValidators", ctx=ast.Load()
|
|
198
|
-
),
|
|
199
|
-
args=[],
|
|
200
|
-
keywords=[],
|
|
201
|
-
),
|
|
202
|
-
attr="validate_email",
|
|
188
|
+
annotation=ast.Name(
|
|
189
|
+
id="list",
|
|
203
190
|
ctx=ast.Load(),
|
|
204
191
|
),
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
value=ast.Name(prop.validator, ctx=ast.Load()),
|
|
212
|
-
)
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
if prop.immutable:
|
|
216
|
-
keywords.append(
|
|
217
|
-
ast.keyword(arg="read_only", value=ast.Constant(True))
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
if prop.on_save:
|
|
221
|
-
keywords.append(
|
|
222
|
-
ast.keyword(
|
|
223
|
-
arg="convert_to_entity",
|
|
224
|
-
value=ast.Name(prop.on_save, ctx=ast.Load()),
|
|
225
|
-
)
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
if prop.on_retrieve:
|
|
229
|
-
keywords.append(
|
|
230
|
-
ast.keyword(
|
|
231
|
-
arg="convert_from_entity",
|
|
232
|
-
value=ast.Name(id=prop.on_retrieve, ctx=ast.Load()),
|
|
233
|
-
)
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
if prop.domain_config:
|
|
237
|
-
result = self._compile_domain_config(pkey, prop, entity_model)
|
|
238
|
-
if not result:
|
|
239
|
-
raise Exception(
|
|
240
|
-
f"Erro desconhecido ao compilar a propriedade {pkey}"
|
|
192
|
+
value=ast.Call(
|
|
193
|
+
func=ast.Name(id="DTOListField", ctx=ast.Load()),
|
|
194
|
+
args=[],
|
|
195
|
+
keywords=keywords,
|
|
196
|
+
),
|
|
197
|
+
simple=1,
|
|
241
198
|
)
|
|
242
199
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
)
|
|
200
|
+
ast_dto_attributes.append(ast_attr)
|
|
201
|
+
else:
|
|
202
|
+
# TODO
|
|
203
|
+
pass
|
|
265
204
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
205
|
+
elif re.match(REGEX_INTERNAL_REF, prop.type):
|
|
206
|
+
# TODO
|
|
207
|
+
pass
|
|
269
208
|
else:
|
|
270
|
-
|
|
209
|
+
raise Exception(f"Tipo de propriedade não suportado: {prop.type}")
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
ast_dto_attributes,
|
|
213
|
+
ast_entity_attributes,
|
|
214
|
+
props_pk,
|
|
215
|
+
enum_classes,
|
|
216
|
+
related_imports,
|
|
217
|
+
)
|
|
271
218
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
219
|
+
def compile_simple_property(
|
|
220
|
+
self,
|
|
221
|
+
properties_structure,
|
|
222
|
+
map_unique_by_property,
|
|
223
|
+
entity_model,
|
|
224
|
+
ast_dto_attributes,
|
|
225
|
+
ast_entity_attributes,
|
|
226
|
+
props_pk,
|
|
227
|
+
enum_classes,
|
|
228
|
+
pkey,
|
|
229
|
+
prop,
|
|
230
|
+
):
|
|
231
|
+
enum_class_name = None
|
|
232
|
+
keywords = []
|
|
233
|
+
|
|
234
|
+
if prop.pk:
|
|
235
|
+
keywords.append(ast.keyword(arg="pk", value=ast.Constant(True)))
|
|
236
|
+
props_pk.append(pkey)
|
|
237
|
+
|
|
238
|
+
if prop.key_alternative:
|
|
239
|
+
keywords.append(ast.keyword(arg="candidate_key", value=ast.Constant(True)))
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
properties_structure.main_properties
|
|
243
|
+
and pkey in properties_structure.main_properties
|
|
244
|
+
):
|
|
245
|
+
keywords.append(ast.keyword(arg="resume", value=ast.Constant(True)))
|
|
246
|
+
|
|
247
|
+
if properties_structure.required and pkey in properties_structure.required:
|
|
248
|
+
keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
|
|
249
|
+
|
|
250
|
+
if (
|
|
251
|
+
properties_structure.partition_data
|
|
252
|
+
and pkey in properties_structure.partition_data
|
|
253
|
+
):
|
|
254
|
+
keywords.append(ast.keyword(arg="partition_data", value=ast.Constant(True)))
|
|
255
|
+
|
|
256
|
+
if pkey in map_unique_by_property:
|
|
257
|
+
unique = map_unique_by_property[pkey].index_model
|
|
258
|
+
keywords.append(
|
|
259
|
+
ast.keyword(
|
|
260
|
+
arg="unique",
|
|
261
|
+
value=ast.Constant(unique.name),
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if (
|
|
266
|
+
prop.default
|
|
267
|
+
): # TODO Verificar esse modo de tratar valores default (principalmente expressões)
|
|
268
|
+
keywords.append(
|
|
269
|
+
ast.keyword(
|
|
270
|
+
arg="default_value",
|
|
271
|
+
value=ast.Name(str(prop.default), ctx=ast.Load()),
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if prop.trim:
|
|
276
|
+
keywords.append(ast.keyword(arg="strip", value=ast.Constant(True)))
|
|
277
|
+
|
|
278
|
+
max = None
|
|
279
|
+
min = None
|
|
280
|
+
if prop.type in [PrimitiveTypes.STRING, PrimitiveTypes.EMAIL]:
|
|
281
|
+
if prop.max_length:
|
|
282
|
+
max = prop.max_length
|
|
283
|
+
elif prop.min_length:
|
|
284
|
+
min = prop.min_length
|
|
285
|
+
elif prop.type in [PrimitiveTypes.INTEGER, PrimitiveTypes.NUMBER]:
|
|
286
|
+
if prop.minimum:
|
|
287
|
+
min = prop.minimum
|
|
288
|
+
elif prop.maximum:
|
|
289
|
+
max = prop.maximum
|
|
290
|
+
|
|
291
|
+
if max:
|
|
292
|
+
keywords.append(ast.keyword(arg="max", value=ast.Constant(prop.max_length)))
|
|
293
|
+
if min:
|
|
294
|
+
keywords.append(ast.keyword(arg="min", value=ast.Constant(prop.min_length)))
|
|
295
|
+
|
|
296
|
+
if (
|
|
297
|
+
properties_structure.search_properties
|
|
298
|
+
and pkey in properties_structure.search_properties
|
|
299
|
+
):
|
|
300
|
+
keywords.append(ast.keyword(arg="search", value=ast.Constant(True)))
|
|
301
|
+
else:
|
|
302
|
+
keywords.append(ast.keyword(arg="search", value=ast.Constant(False)))
|
|
303
|
+
|
|
304
|
+
if (
|
|
305
|
+
properties_structure.metric_label
|
|
306
|
+
and pkey in properties_structure.metric_label
|
|
307
|
+
):
|
|
308
|
+
keywords.append(ast.keyword(arg="metric_label", value=ast.Constant(True)))
|
|
309
|
+
|
|
310
|
+
if prop.type == PrimitiveTypes.CPF and not prop.validator:
|
|
311
|
+
keywords.append(
|
|
312
|
+
ast.keyword(
|
|
313
|
+
arg="validator",
|
|
314
|
+
value=ast.Attribute(
|
|
315
|
+
value=ast.Call(
|
|
316
|
+
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
317
|
+
args=[],
|
|
318
|
+
keywords=[],
|
|
319
|
+
),
|
|
320
|
+
attr="validate_cpf",
|
|
278
321
|
ctx=ast.Load(),
|
|
279
322
|
),
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
323
|
+
)
|
|
324
|
+
)
|
|
325
|
+
elif prop.type == PrimitiveTypes.CNPJ and not prop.validator:
|
|
326
|
+
keywords.append(
|
|
327
|
+
ast.keyword(
|
|
328
|
+
arg="validator",
|
|
329
|
+
value=ast.Attribute(
|
|
330
|
+
value=ast.Call(
|
|
331
|
+
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
332
|
+
args=[],
|
|
333
|
+
keywords=[],
|
|
334
|
+
),
|
|
335
|
+
attr="validate_cnpj",
|
|
336
|
+
ctx=ast.Load(),
|
|
284
337
|
),
|
|
285
|
-
simple=1,
|
|
286
338
|
)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
339
|
+
)
|
|
340
|
+
elif prop.type == PrimitiveTypes.CPF_CNPJ and not prop.validator:
|
|
341
|
+
keywords.append(
|
|
342
|
+
ast.keyword(
|
|
343
|
+
arg="validator",
|
|
344
|
+
value=ast.Attribute(
|
|
345
|
+
value=ast.Call(
|
|
346
|
+
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
347
|
+
args=[],
|
|
348
|
+
keywords=[],
|
|
349
|
+
),
|
|
350
|
+
attr="validate_cpf_or_cnpj",
|
|
351
|
+
ctx=ast.Load(),
|
|
295
352
|
),
|
|
296
|
-
|
|
297
|
-
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
elif prop.type == PrimitiveTypes.EMAIL and not prop.validator:
|
|
356
|
+
keywords.append(
|
|
357
|
+
ast.keyword(
|
|
358
|
+
arg="validator",
|
|
359
|
+
value=ast.Attribute(
|
|
360
|
+
value=ast.Call(
|
|
361
|
+
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
362
|
+
args=[],
|
|
363
|
+
keywords=[],
|
|
364
|
+
),
|
|
365
|
+
attr="validate_email",
|
|
298
366
|
ctx=ast.Load(),
|
|
299
367
|
),
|
|
300
|
-
value=ast.Constant(value=None),
|
|
301
|
-
simple=1,
|
|
302
368
|
)
|
|
369
|
+
)
|
|
370
|
+
elif prop.validator:
|
|
371
|
+
keywords.append(
|
|
372
|
+
ast.keyword(
|
|
373
|
+
arg="validator",
|
|
374
|
+
value=ast.Name(prop.validator, ctx=ast.Load()),
|
|
375
|
+
)
|
|
376
|
+
)
|
|
303
377
|
|
|
304
|
-
|
|
378
|
+
if prop.immutable:
|
|
379
|
+
keywords.append(ast.keyword(arg="read_only", value=ast.Constant(True)))
|
|
380
|
+
|
|
381
|
+
if prop.on_save:
|
|
382
|
+
keywords.append(
|
|
383
|
+
ast.keyword(
|
|
384
|
+
arg="convert_to_entity",
|
|
385
|
+
value=ast.Name(prop.on_save, ctx=ast.Load()),
|
|
386
|
+
)
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
if prop.on_retrieve:
|
|
390
|
+
keywords.append(
|
|
391
|
+
ast.keyword(
|
|
392
|
+
arg="convert_from_entity",
|
|
393
|
+
value=ast.Name(id=prop.on_retrieve, ctx=ast.Load()),
|
|
394
|
+
)
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if prop.domain_config:
|
|
398
|
+
result = self._compile_domain_config(pkey, prop, entity_model)
|
|
399
|
+
if not result:
|
|
400
|
+
raise Exception(f"Erro desconhecido ao compilar a propriedade {pkey}")
|
|
401
|
+
|
|
402
|
+
enum_class_name, ast_enum_class = result
|
|
403
|
+
enum_classes.append(ast_enum_class)
|
|
404
|
+
|
|
405
|
+
# Resolvendo o nome da propriedade no Entity
|
|
406
|
+
if (
|
|
407
|
+
properties_structure.entity_properties
|
|
408
|
+
and pkey in properties_structure.entity_properties
|
|
409
|
+
):
|
|
410
|
+
entity_field_name = properties_structure.entity_properties[pkey].column
|
|
411
|
+
else:
|
|
412
|
+
entity_field_name = pkey
|
|
413
|
+
|
|
414
|
+
# Escrevendo, se necessário, o alias para o nome da entity
|
|
415
|
+
if entity_field_name != pkey:
|
|
416
|
+
keywords.append(
|
|
417
|
+
ast.keyword(
|
|
418
|
+
arg="entity_field",
|
|
419
|
+
value=ast.Constant(value=entity_field_name),
|
|
420
|
+
)
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Instanciando o atributo AST
|
|
424
|
+
if enum_class_name:
|
|
425
|
+
prop_type = enum_class_name
|
|
426
|
+
else:
|
|
427
|
+
prop_type = TypeUtil.property_type_to_python_type(prop.type)
|
|
428
|
+
|
|
429
|
+
ast_attr = ast.AnnAssign(
|
|
430
|
+
target=ast.Name(id=CompilerStrUtil.to_snake_case(pkey), ctx=ast.Store()),
|
|
431
|
+
annotation=ast.Name(
|
|
432
|
+
id=prop_type,
|
|
433
|
+
ctx=ast.Load(),
|
|
434
|
+
),
|
|
435
|
+
value=ast.Call(
|
|
436
|
+
func=ast.Name(id="DTOField", ctx=ast.Load()),
|
|
437
|
+
args=[],
|
|
438
|
+
keywords=keywords,
|
|
439
|
+
),
|
|
440
|
+
simple=1,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
ast_dto_attributes.append(ast_attr)
|
|
444
|
+
|
|
445
|
+
# Entity
|
|
446
|
+
ast_entity_attr = ast.AnnAssign(
|
|
447
|
+
target=ast.Name(
|
|
448
|
+
id=CompilerStrUtil.to_snake_case(entity_field_name),
|
|
449
|
+
ctx=ast.Store(),
|
|
450
|
+
),
|
|
451
|
+
annotation=ast.Name(
|
|
452
|
+
id=TypeUtil.property_type_to_python_type(prop.type),
|
|
453
|
+
ctx=ast.Load(),
|
|
454
|
+
),
|
|
455
|
+
value=ast.Constant(value=None),
|
|
456
|
+
simple=1,
|
|
457
|
+
)
|
|
305
458
|
|
|
306
|
-
|
|
459
|
+
ast_entity_attributes.append(ast_entity_attr)
|
|
307
460
|
|
|
308
461
|
def _compile_domain_config(
|
|
309
462
|
self,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def compile_namespace_keys(
|
|
7
|
+
tenant: str | int | None, grupo_empresarial: str | uuid.UUID | None
|
|
8
|
+
) -> tuple[str, str, str]:
|
|
9
|
+
grupo_key = f"tenant_{tenant}.ge_{grupo_empresarial}"
|
|
10
|
+
tenant_key = f"tenant_{tenant}"
|
|
11
|
+
default_key = "default"
|
|
12
|
+
|
|
13
|
+
return (grupo_key, tenant_key, default_key)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def compile_dto_class_name(entity_id: str) -> str:
|
|
17
|
+
return f"{CompilerStrUtil.to_pascal_case(entity_id)}DTO"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def compile_entity_class_name(entity_id: str) -> str:
|
|
21
|
+
return f"{CompilerStrUtil.to_pascal_case(entity_id)}Entity"
|
|
@@ -6,6 +6,7 @@ import types
|
|
|
6
6
|
|
|
7
7
|
from nsj_rest_lib.settings import get_logger
|
|
8
8
|
|
|
9
|
+
from nsj_rest_lib2.compiler.util.type_naming_util import compile_namespace_keys
|
|
9
10
|
from nsj_rest_lib2.exception import MissingEntityConfigException
|
|
10
11
|
from nsj_rest_lib2.redis_config import get_redis
|
|
11
12
|
from nsj_rest_lib2.settings import ESCOPO_RESTLIB2, MIN_TIME_SOURCE_REFRESH
|
|
@@ -44,9 +45,9 @@ class EntityLoader:
|
|
|
44
45
|
grupo_empresarial: str | None,
|
|
45
46
|
) -> tuple[str, str, dict, bool, list[str]]:
|
|
46
47
|
# Montando as chaves dos namespaces
|
|
47
|
-
grupo_key =
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
grupo_key, tenant_key, default_key = compile_namespace_keys(
|
|
49
|
+
tenant, grupo_empresarial
|
|
50
|
+
)
|
|
50
51
|
|
|
51
52
|
result = self._load_entity_source_from_memory(
|
|
52
53
|
entity_resource, grupo_key, tenant_key, default_key
|
|
@@ -182,6 +183,18 @@ class EntityLoader:
|
|
|
182
183
|
with self._lock:
|
|
183
184
|
namespaces_dict.clear()
|
|
184
185
|
|
|
186
|
+
def _ensure_dynamic_package(self):
|
|
187
|
+
"""
|
|
188
|
+
Garante que exista um pacote 'dynamic' em sys.modules.
|
|
189
|
+
"""
|
|
190
|
+
pkg = sys.modules.get("dynamic")
|
|
191
|
+
if pkg is None:
|
|
192
|
+
pkg = types.ModuleType("dynamic")
|
|
193
|
+
pkg.__path__ = [] # marca como pacote
|
|
194
|
+
pkg.__package__ = "dynamic"
|
|
195
|
+
sys.modules["dynamic"] = pkg
|
|
196
|
+
return pkg
|
|
197
|
+
|
|
185
198
|
def _execute_entity_source(
|
|
186
199
|
self,
|
|
187
200
|
entity_config_str: str,
|
|
@@ -233,19 +246,32 @@ class EntityLoader:
|
|
|
233
246
|
|
|
234
247
|
# Carregando a entidade no namespace
|
|
235
248
|
with self._lock:
|
|
249
|
+
self._ensure_dynamic_package()
|
|
250
|
+
|
|
236
251
|
namespace = namespaces_dict.get(entity_config_key)
|
|
237
252
|
if namespace is None:
|
|
238
253
|
namespace = Namespace()
|
|
239
254
|
namespace.key = entity_config_key
|
|
240
255
|
namespaces_dict[entity_config_key] = namespace
|
|
241
256
|
|
|
242
|
-
#
|
|
243
|
-
|
|
244
|
-
sys.modules
|
|
245
|
-
|
|
257
|
+
# Hot reload: removendo o módulo do sys.modules, se existir
|
|
258
|
+
full_name = f"dynamic.{entity_config_key}"
|
|
259
|
+
# if full_name in sys.modules:
|
|
260
|
+
# sys.modules.pop(full_name)
|
|
246
261
|
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
# Executando o código da entidade
|
|
263
|
+
module = sys.modules.get(full_name)
|
|
264
|
+
if not module:
|
|
265
|
+
module = types.ModuleType(full_name)
|
|
266
|
+
module.__package__ = "dynamic"
|
|
267
|
+
module.__dict__["__builtins__"] = __builtins__
|
|
268
|
+
sys.modules[full_name] = module
|
|
269
|
+
|
|
270
|
+
parent = sys.modules["dynamic"]
|
|
271
|
+
setattr(parent, entity_config_key, module)
|
|
272
|
+
|
|
273
|
+
namespace.module = module
|
|
274
|
+
namespace.entities_dict = module.__dict__
|
|
249
275
|
|
|
250
276
|
self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
|
|
251
277
|
self._safe_exec(source_entity, namespace.entities_dict, "Entity source")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
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
|
|
@@ -17,6 +17,7 @@ Requires-Dist: redis<7.0.0,>=6.4.0
|
|
|
17
17
|
Requires-Dist: nsj-multi-database-lib<3.0.0,>=2.0.1
|
|
18
18
|
Requires-Dist: pydantic<3.0.0,>=2.11.9
|
|
19
19
|
Requires-Dist: black<26.0.0,>=25.1.0
|
|
20
|
+
Requires-Dist: pyyaml>7.0.0,>=6.0.3
|
|
20
21
|
|
|
21
22
|
# nsj_rest_lib2
|
|
22
23
|
|
|
@@ -4,29 +4,30 @@ 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=wkREdCrQ6mq6-R-5nC5ZXDGNpSEIPjuSjt-NCxOS6to,12228
|
|
8
8
|
nsj_rest_lib2/compiler/compiler_structures.py,sha256=2bM4_7lG1fytDGxJl6SU9pLsbspiNV36gVn9-O-23Q8,1009
|
|
9
|
-
nsj_rest_lib2/compiler/dto_compiler.py,sha256=
|
|
10
|
-
nsj_rest_lib2/compiler/entity_compiler.py,sha256=
|
|
11
|
-
nsj_rest_lib2/compiler/property_compiler.py,sha256=
|
|
9
|
+
nsj_rest_lib2/compiler/dto_compiler.py,sha256=GAKWFvks0Duqs2EsEr1-BFx2-Kny72jJMbl9ZyIEDXw,4125
|
|
10
|
+
nsj_rest_lib2/compiler/entity_compiler.py,sha256=zLXO6USY4Rr0Hnk4wGep5K8DiHxJv-W-BBz_-g4OhCA,4307
|
|
11
|
+
nsj_rest_lib2/compiler/property_compiler.py,sha256=RR5F-7D2nLG6ThjC0rdqwU3G5SMN1P5tBusZaAPwIMk,18865
|
|
12
12
|
nsj_rest_lib2/compiler/edl_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py,sha256=664QBDcOgVnyfwtUOXO1W7AKaZhueBG335x5DuogruY,7644
|
|
14
14
|
nsj_rest_lib2/compiler/edl_model/api_model.py,sha256=pH0Uiq_64AGvkHqwY44TrulWWZXbi6M0EKJWUhSqKj0,837
|
|
15
|
-
nsj_rest_lib2/compiler/edl_model/column_meta_model.py,sha256=
|
|
16
|
-
nsj_rest_lib2/compiler/edl_model/entity_model.py,sha256=
|
|
15
|
+
nsj_rest_lib2/compiler/edl_model/column_meta_model.py,sha256=3OLsRwEz66Cktzew6Hie3HZXK9u20GXlWUU0HtFtwfk,709
|
|
16
|
+
nsj_rest_lib2/compiler/edl_model/entity_model.py,sha256=sXVfC30P3-q2-fQ3XB_-3vpNjqFPIK5oBIVbwkXNFLU,3662
|
|
17
17
|
nsj_rest_lib2/compiler/edl_model/index_model.py,sha256=cXWlu0hxtro5vvYoirkDW4R3PCnBW5oCCWjRJ6AX5zE,508
|
|
18
|
-
nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=
|
|
19
|
-
nsj_rest_lib2/compiler/edl_model/property_meta_model.py,sha256=
|
|
18
|
+
nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=cWEys87xDpgotyRUXkzvFvY4LHeawzBEEyz0Xed18AQ,1454
|
|
19
|
+
nsj_rest_lib2/compiler/edl_model/property_meta_model.py,sha256=x2SApvI-5MZTErAHeOXnR6qfjflhSs8r6Y9KMtCykV0,3960
|
|
20
20
|
nsj_rest_lib2/compiler/edl_model/repository_model.py,sha256=Vt1HxlaoifP4w5ij1laKDkD1-COBihE8EX1HkdEDUSo,977
|
|
21
|
-
nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=
|
|
21
|
+
nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=ZC2oTvc9Y2Oiy4NLNHQLiMOLRWDAOdF8GJxOw4nqHdk,354
|
|
22
22
|
nsj_rest_lib2/compiler/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
nsj_rest_lib2/compiler/util/str_util.py,sha256=lVP1yHhj1pOd6ULtTnkcfX6Gqrpe4yCBratHUhBNGcI,843
|
|
24
|
+
nsj_rest_lib2/compiler/util/type_naming_util.py,sha256=sahlxtYUPcksqlX1hygaoTF7vuD8tY8eosgOR04YLAs,612
|
|
24
25
|
nsj_rest_lib2/compiler/util/type_util.py,sha256=HTKOH4uRTOY0YgoM8oUv_6cEcReE_bgKYXFBsQCb-3A,357
|
|
25
26
|
nsj_rest_lib2/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
27
|
nsj_rest_lib2/controller/dynamic_controller.py,sha256=XMqxe1NW-NE5XwomXb4pSNdALQHpP74Hc26R4fnmXqg,15194
|
|
27
28
|
nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
nsj_rest_lib2/service/entity_loader.py,sha256=
|
|
29
|
-
nsj_rest_lib2-0.0.
|
|
30
|
-
nsj_rest_lib2-0.0.
|
|
31
|
-
nsj_rest_lib2-0.0.
|
|
32
|
-
nsj_rest_lib2-0.0.
|
|
29
|
+
nsj_rest_lib2/service/entity_loader.py,sha256=PJX-cPPCllhZuqSZzj8ZX_0HZQBnt20dqLc9E0e0GtA,13472
|
|
30
|
+
nsj_rest_lib2-0.0.9.dist-info/METADATA,sha256=SGdzF8Wef-XXnNnH3Q1bKfM-fVAetqyXTGHHIiKDLAk,1360
|
|
31
|
+
nsj_rest_lib2-0.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
+
nsj_rest_lib2-0.0.9.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
|
|
33
|
+
nsj_rest_lib2-0.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|