nsj-rest-lib2 0.0.13__tar.gz → 0.0.14__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.
Files changed (41) hide show
  1. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/PKG-INFO +1 -1
  2. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/compiler.py +91 -13
  3. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/compiler_structures.py +6 -0
  4. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/dto_compiler.py +4 -3
  5. nsj_rest_lib2-0.0.14/nsj_rest_lib2/compiler/edl_model/entity_model.py +23 -0
  6. nsj_rest_lib2-0.0.13/nsj_rest_lib2/compiler/edl_model/entity_model.py → nsj_rest_lib2-0.0.14/nsj_rest_lib2/compiler/edl_model/entity_model_base.py +10 -15
  7. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/primitives.py +1 -1
  8. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/entity_compiler.py +9 -4
  9. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/property_compiler.py +159 -33
  10. nsj_rest_lib2-0.0.14/nsj_rest_lib2/compiler/util/type_naming_util.py +21 -0
  11. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2.egg-info/PKG-INFO +1 -1
  12. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2.egg-info/SOURCES.txt +2 -0
  13. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/setup.cfg +1 -1
  14. nsj_rest_lib2-0.0.13/nsj_rest_lib2/compiler/util/type_naming_util.py +0 -21
  15. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/README.md +0 -0
  16. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/__init__.py +0 -0
  17. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/__init__.py +0 -0
  18. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/ai_compiler.py +0 -0
  19. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
  20. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
  21. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
  22. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
  23. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
  24. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -0
  25. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
  26. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
  27. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/model.py +0 -0
  28. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
  29. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
  30. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
  31. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/controller/__init__.py +0 -0
  32. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/controller/dynamic_controller.py +0 -0
  33. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/exception.py +0 -0
  34. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/redis_config.py +0 -0
  35. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/service/__init__.py +0 -0
  36. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/service/entity_loader.py +1 -1
  37. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2/settings.py +0 -0
  38. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
  39. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2.egg-info/requires.txt +0 -0
  40. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
  41. {nsj_rest_lib2-0.0.13 → nsj_rest_lib2-0.0.14}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.13
3
+ Version: 0.0.14
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
@@ -3,10 +3,12 @@ import re
3
3
  from typing import Any
4
4
 
5
5
  from nsj_rest_lib2.compiler.compiler_structures import (
6
+ ComponentsCompilerStructure,
6
7
  IndexCompilerStructure,
7
8
  PropertiesCompilerStructure,
8
9
  )
9
10
  from nsj_rest_lib2.compiler.dto_compiler import DTOCompiler
11
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
10
12
  from nsj_rest_lib2.compiler.edl_model.primitives import REGEX_EXTERNAL_REF
11
13
  from nsj_rest_lib2.compiler.entity_compiler import EntityCompiler
12
14
  from nsj_rest_lib2.compiler.model import CompilerResult
@@ -16,16 +18,18 @@ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
16
18
 
17
19
  from nsj_rest_lib2.settings import get_logger
18
20
 
21
+ # TODO Revisar compilação de valores default (sensível a tipos)
22
+ # TODO Implementar suporte a conjuntos
19
23
  # TODO Autenticação nas rotas
20
- # TODO Carregar, dinamicamente, em memória, o código compilado das dependÊncias por relacionamento
21
24
  # TODO Atualizar o status da entidade pelo worker de compilação (e talvez parar uma compilação, quando se delete uma entidade)
22
- # TODO Relacionamentos
25
+ # TODO Mixins
23
26
  # TODO Classes Abstratas
24
27
  # TODO Partial Classes
25
28
  # TODO Migrations
26
- # TODO Adicionar autenticação aos endpoints dinâmicos
27
29
  # TODO Criar imagem docker base para as aplicações, usando venv (para poderem atualizar o pydantic)
28
30
 
31
+ # TODO Suporte ao "min_items" dos relacionamentos no RestLib1
32
+
29
33
 
30
34
  class EDLCompiler:
31
35
  def __init__(self) -> None:
@@ -76,10 +80,18 @@ class EDLCompiler:
76
80
 
77
81
  def _compile_model(
78
82
  self,
79
- entity_model: EntityModel,
83
+ entity_model: EntityModelBase,
80
84
  entity_models: dict[str, EntityModel],
85
+ escopo: str | None = None,
86
+ prefx_class_name: str = "",
81
87
  ) -> CompilerResult:
82
88
 
89
+ if escopo is None and isinstance(entity_model, EntityModel):
90
+ escopo = entity_model.escopo
91
+
92
+ if not escopo:
93
+ raise Exception(f"Escopo não definido para a entidade: {entity_model.id}.")
94
+
83
95
  # Criando um mapa de índices por nome de property
84
96
  # TODO Implementar tratamento dos índices de apoio às query (não de unicidade)
85
97
  map_indexes_by_property: dict[str, list[IndexCompilerStructure]] = {}
@@ -109,10 +121,43 @@ class EDLCompiler:
109
121
  ) = self._properties_compiler.compile(
110
122
  properties_structure,
111
123
  map_unique_by_property,
124
+ escopo,
112
125
  entity_model,
113
126
  entity_models,
127
+ prefx_class_name,
114
128
  )
115
129
 
130
+ # Gerando o buffer para os códigos de DTO e Entity
131
+ dto_code = ""
132
+ entity_code = ""
133
+ relations_dependencies_complete = []
134
+
135
+ # Carregando a estrutura de compilação dos components
136
+ components_structure = ComponentsCompilerStructure()
137
+ self._make_components_structures(
138
+ components_structure, entity_model, entity_models
139
+ )
140
+
141
+ # Gerando o código das entidades filhas (components)
142
+ for component_key in components_structure.components:
143
+ component = components_structure.components[component_key]
144
+ component_compiled = self._compile_model(
145
+ component,
146
+ entity_models,
147
+ escopo,
148
+ prefx_class_name=f"{prefx_class_name}_{entity_model.id}",
149
+ )
150
+
151
+ # Guardando o código gerado no buffer
152
+ if component_compiled.dto_code:
153
+ dto_code += component_compiled.dto_code + "\n\n"
154
+ if component_compiled.entity_code:
155
+ entity_code += component_compiled.entity_code + "\n\n"
156
+ if component_compiled.relations_dependencies:
157
+ relations_dependencies_complete.extend(
158
+ component_compiled.relations_dependencies
159
+ )
160
+
116
161
  # Gerando o código do DTO
117
162
  dto_class_name, code_dto = self._dto_compiler.compile(
118
163
  entity_model,
@@ -120,32 +165,65 @@ class EDLCompiler:
120
165
  enum_classes,
121
166
  related_imports,
122
167
  fixed_filters,
168
+ prefx_class_name,
123
169
  )
124
170
 
125
171
  # Gerando o código da Entity
126
172
  entity_class_name, code_entity = self._entity_compiler.compile(
127
- entity_model, ast_entity_attributes, props_pk
173
+ entity_model,
174
+ ast_entity_attributes,
175
+ props_pk,
176
+ prefx_class_name,
128
177
  )
129
178
 
179
+ # Extendendo os buffers com os códigos gerados
180
+ dto_code += code_dto
181
+ entity_code += code_entity
182
+ relations_dependencies_complete.extend(relations_dependencies)
183
+
130
184
  # Construindo o resultado
131
185
  compiler_result = CompilerResult()
132
186
  compiler_result.entity_class_name = entity_class_name
133
- compiler_result.entity_code = code_entity
187
+ compiler_result.entity_code = entity_code
134
188
  compiler_result.dto_class_name = dto_class_name
135
- compiler_result.dto_code = code_dto
189
+ compiler_result.dto_code = dto_code
136
190
 
137
191
  # Compilando questões das APIs
138
- compiler_result.api_expose = entity_model.api.expose
139
- compiler_result.api_resource = entity_model.api.resource
140
- compiler_result.api_verbs = entity_model.api.verbs
141
- compiler_result.relations_dependencies = relations_dependencies
192
+ if isinstance(entity_model, EntityModel):
193
+ compiler_result.api_expose = entity_model.api.expose
194
+ compiler_result.api_resource = entity_model.api.resource
195
+ compiler_result.api_verbs = entity_model.api.verbs
196
+ compiler_result.relations_dependencies = relations_dependencies_complete
142
197
 
143
198
  return compiler_result
144
199
 
200
+ def _make_components_structures(
201
+ self,
202
+ components_structure: ComponentsCompilerStructure,
203
+ entity_model: EntityModelBase,
204
+ entity_models: dict[str, EntityModel],
205
+ ):
206
+ if not entity_model:
207
+ return
208
+
209
+ # Populando com os components do trait
210
+ if entity_model.trait_from:
211
+ trait_model = entity_models[entity_model.trait_from]
212
+
213
+ self._make_components_structures(
214
+ components_structure,
215
+ trait_model,
216
+ entity_models,
217
+ )
218
+
219
+ # Populando com os components da entidade atual
220
+ if entity_model.components:
221
+ components_structure.components.update(entity_model.components)
222
+
145
223
  def _make_properties_structures(
146
224
  self,
147
225
  properties_structure: PropertiesCompilerStructure,
148
- entity_model: EntityModel,
226
+ entity_model: EntityModelBase,
149
227
  entity_models: dict[str, EntityModel],
150
228
  ):
151
229
  if not entity_model:
@@ -188,7 +266,7 @@ class EDLCompiler:
188
266
  self,
189
267
  map_indexes_by_property: dict[str, list[IndexCompilerStructure]],
190
268
  map_unique_by_property: dict[str, IndexCompilerStructure],
191
- entity_model: EntityModel,
269
+ entity_model: EntityModelBase,
192
270
  entity_models: dict[str, EntityModel],
193
271
  deep: int = 1,
194
272
  ):
@@ -1,4 +1,5 @@
1
1
  from nsj_rest_lib2.compiler.edl_model.column_meta_model import ColumnMetaModel
2
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
2
3
  from nsj_rest_lib2.compiler.edl_model.index_model import IndexModel
3
4
  from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
4
5
  from nsj_rest_lib2.compiler.edl_model.trait_property_meta_model import (
@@ -22,3 +23,8 @@ class PropertiesCompilerStructure:
22
23
  self.metric_label: list[str] = []
23
24
  self.entity_properties: dict[str, ColumnMetaModel] = {}
24
25
  self.trait_properties: dict[str, TraitPropertyMetaModel] = {}
26
+
27
+
28
+ class ComponentsCompilerStructure:
29
+ def __init__(self) -> None:
30
+ self.components: dict[str, EntityModelBase] = {}
@@ -3,8 +3,8 @@ import ast
3
3
  import black
4
4
 
5
5
  from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
6
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
6
7
  from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes
7
- from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
8
8
  from nsj_rest_lib2.compiler.util.type_naming_util import compile_dto_class_name
9
9
 
10
10
 
@@ -14,11 +14,12 @@ class DTOCompiler:
14
14
 
15
15
  def compile(
16
16
  self,
17
- entity_model: EntityModel,
17
+ entity_model: EntityModelBase,
18
18
  ast_dto_attributes: list[ast.stmt],
19
19
  enum_classes: list[ast.stmt],
20
20
  related_imports: list[tuple[str, str, str]],
21
21
  fixed_filters: list[tuple[str, BasicTypes]],
22
+ prefx_class_name: str,
22
23
  ) -> tuple[str, str]:
23
24
  """
24
25
  Compila o código do DTO a partir do AST e retorna o código compilado.
@@ -117,7 +118,7 @@ class DTOCompiler:
117
118
  )
118
119
 
119
120
  # Criando o ast da classe
120
- class_name = compile_dto_class_name(entity_model.id)
121
+ class_name = compile_dto_class_name(entity_model.id, prefx_class_name)
121
122
  ast_class = ast.ClassDef(
122
123
  name=class_name,
123
124
  bases=[ast.Name(id="DTOBase", ctx=ast.Load())],
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import Field
4
+ from typing import Optional
5
+
6
+ from nsj_rest_lib2.compiler.edl_model.api_model import APIModel
7
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
8
+
9
+
10
+ class EntityModel(EntityModelBase):
11
+ edl_version: Optional[str] = Field(default="1.0", description="Versão do EDL")
12
+ escopo: str = Field(..., description="Escopo do EDL (define a aplicação).")
13
+ version: Optional[str] = Field(
14
+ default="1.0", description="Versão da entidade (padrão: 1.0)."
15
+ )
16
+ abstract: Optional[bool] = Field(
17
+ default=False,
18
+ description="Indica se a entidade é abstrata (padrão: False). Caso positivo, não gera código, mas pode ser usada na geração de código de outras entidades.",
19
+ )
20
+ api: APIModel = Field(
21
+ ...,
22
+ description="Definição da API REST associada ao modelo, com todos os seus endpoints.",
23
+ )
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import uuid
2
4
 
3
5
  from pydantic import BaseModel, Field
@@ -5,15 +7,12 @@ from typing import Dict, List, Optional
5
7
 
6
8
  from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
7
9
  from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
8
- from nsj_rest_lib2.compiler.edl_model.api_model import APIModel
9
10
  from nsj_rest_lib2.compiler.edl_model.trait_property_meta_model import (
10
11
  TraitPropertyMetaModel,
11
12
  )
12
13
 
13
14
 
14
- class EntityModel(BaseModel):
15
- edl_version: Optional[str] = Field(default="1.0", description="Versão do EDL")
16
- escopo: str = Field(..., description="Escopo do EDL (define a aplicação).")
15
+ class EntityModelBase(BaseModel):
17
16
  description: str = Field(..., description="Descrição da entidade.")
18
17
  id: str = Field(
19
18
  ...,
@@ -22,13 +21,6 @@ class EntityModel(BaseModel):
22
21
  trait_from: Optional[str] = Field(
23
22
  None, description="Identificador da entidade que a trait estende."
24
23
  )
25
- version: Optional[str] = Field(
26
- default="1.0", description="Versão da entidade (padrão: 1.0)."
27
- )
28
- abstract: Optional[bool] = Field(
29
- default=False,
30
- description="Indica se a entidade é abstrata (padrão: False). Caso positivo, não gera código, mas pode ser usada na geração de código de outras entidades.",
31
- )
32
24
  mixins: Optional[List[str]] = Field(
33
25
  None,
34
26
  description="Lista de mixins (blocos reutilizáveis) aplicados ao modelo.",
@@ -59,16 +51,19 @@ class EntityModel(BaseModel):
59
51
  properties: Dict[str, PropertyMetaModel] = Field(
60
52
  ..., description="Dicionário de propriedades do modelo."
61
53
  )
54
+ components: Optional[Dict[str, "EntityModelBase"]] = Field(
55
+ None,
56
+ description="Entidades filhas, relacionadas por composição e definidas inline (evitando a criação de novos arquivos EDL para a definição de entidades componentes da entidade atual).",
57
+ )
62
58
  repository: RepositoryModel = Field(
63
59
  ..., description="Configurações de mapeamento para o banco de dados."
64
60
  )
65
- api: APIModel = Field(
66
- ...,
67
- description="Definição da API REST associada ao modelo, com todos os seus endpoints.",
68
- )
69
61
 
70
62
  # Propriedades de controle da compilação (não fazem parte do JSON de representação das entidades)
71
63
  tenant: int = Field(default=0, exclude=True)
72
64
  grupo_empresarial: uuid.UUID = Field(
73
65
  default=uuid.UUID("00000000-0000-0000-0000-000000000000"), exclude=True
74
66
  )
67
+
68
+
69
+ EntityModelBase.model_rebuild()
@@ -4,7 +4,7 @@ from typing import Annotated, List, Union
4
4
  from pydantic import StringConstraints
5
5
 
6
6
  REGEX_EXTERNAL_REF = r"^(\w+)\/(\w+)$"
7
- REGEX_INTERNAL_REF = r"^#\/(\w+)\/(\w+)$"
7
+ REGEX_INTERNAL_REF = r"^#\/components\/(\w+)$"
8
8
 
9
9
  ExternalRefType = Annotated[str, StringConstraints(pattern=REGEX_EXTERNAL_REF)]
10
10
  InternalRefType = Annotated[str, StringConstraints(pattern=REGEX_INTERNAL_REF)]
@@ -3,7 +3,7 @@ import ast
3
3
  import black
4
4
 
5
5
  from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
6
- from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
6
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
7
7
  from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
8
8
  from nsj_rest_lib2.compiler.util.type_naming_util import compile_entity_class_name
9
9
 
@@ -14,9 +14,10 @@ class EntityCompiler:
14
14
 
15
15
  def compile(
16
16
  self,
17
- entity_model: EntityModel,
17
+ entity_model: EntityModelBase,
18
18
  ast_entity_attributes: list[ast.stmt],
19
19
  props_pk: list[str],
20
+ prefix_class_name: str,
20
21
  ) -> tuple[str, str]:
21
22
  # Imports
22
23
  imports = [
@@ -49,7 +50,11 @@ class EntityCompiler:
49
50
  )
50
51
 
51
52
  default_order_props = []
52
- if entity_model.api and entity_model.api.default_sort:
53
+ if (
54
+ isinstance(entity_model, EntityModel)
55
+ and entity_model.api
56
+ and entity_model.api.default_sort
57
+ ):
53
58
  default_order_props = entity_model.api.default_sort
54
59
 
55
60
  default_order_fields = []
@@ -67,7 +72,7 @@ class EntityCompiler:
67
72
  if CompilerStrUtil.to_snake_case(props_pk[0]) not in default_order_fields:
68
73
  default_order_fields.append(CompilerStrUtil.to_snake_case(props_pk[0]))
69
74
 
70
- class_name = compile_entity_class_name(entity_model.id)
75
+ class_name = compile_entity_class_name(entity_model.id, prefix_class_name)
71
76
  ast_class = ast.ClassDef(
72
77
  name=class_name,
73
78
  bases=[ast.Name(id="EntityBase", ctx=ast.Load())],
@@ -6,6 +6,7 @@ from nsj_rest_lib2.compiler.compiler_structures import (
6
6
  PropertiesCompilerStructure,
7
7
  )
8
8
  from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
9
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
9
10
  from nsj_rest_lib2.compiler.edl_model.primitives import (
10
11
  BasicTypes,
11
12
  CardinalityTypes,
@@ -29,16 +30,18 @@ from nsj_rest_lib2.compiler.util.type_util import TypeUtil
29
30
  # TODO pattern
30
31
  # TODO lowercase
31
32
  # TODO uppercase
32
- # TODO Adicionar o nome da entidade, no nome das classes de enum (para evitar conflitos no caso das traits)
33
33
 
34
34
 
35
35
  class EDLPropertyCompiler:
36
+
36
37
  def compile(
37
38
  self,
38
39
  properties_structure: PropertiesCompilerStructure,
39
40
  map_unique_by_property: dict[str, IndexCompilerStructure],
40
- entity_model: EntityModel,
41
+ escopo: str,
42
+ entity_model: EntityModelBase,
41
43
  entity_models: dict[str, EntityModel],
44
+ prefx_class_name: str,
42
45
  ) -> tuple[
43
46
  list[ast.stmt],
44
47
  list[ast.stmt],
@@ -52,22 +55,23 @@ class EDLPropertyCompiler:
52
55
  # TODO Criar opção de campo calculado?
53
56
 
54
57
  # Descobrindo os atributos marcados como PK (e recuperando a chave primária)
55
- # pk_keys = []
56
- # for pkey in properties_structure.properties:
57
- # prop = properties_structure.properties[pkey]
58
-
59
- # if isinstance(prop.type, PrimitiveTypes):
60
- # if prop.pk:
61
- # pk_keys.append(pkey)
62
-
63
- # if len(pk_keys) > 1:
64
- # raise Exception(
65
- # f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {pk_keys}"
66
- # )
67
- # elif len(pk_keys) == 0:
68
- # raise Exception(
69
- # f"Entidade '{entity_model.id}' não tem nenhuma chave primária (ainda não suportado)"
70
- # )
58
+ # TODO Verificar se devemos manter essa verificação
59
+ pk_keys = []
60
+ for pkey in properties_structure.properties:
61
+ prop = properties_structure.properties[pkey]
62
+
63
+ if isinstance(prop.type, PrimitiveTypes):
64
+ if prop.pk:
65
+ pk_keys.append(pkey)
66
+
67
+ if len(pk_keys) > 1:
68
+ raise Exception(
69
+ f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {pk_keys}"
70
+ )
71
+ elif len(pk_keys) == 0:
72
+ raise Exception(
73
+ f"Entidade '{entity_model.id}' não tem nenhuma chave primária (ainda não suportado)"
74
+ )
71
75
 
72
76
  # pk_key = pk_keys[0]
73
77
 
@@ -87,11 +91,12 @@ class EDLPropertyCompiler:
87
91
  prop = properties_structure.properties[pkey]
88
92
 
89
93
  # DTO
90
- ## Tratando propriedade simples (não array, não object)
91
94
  if isinstance(prop.type, PrimitiveTypes):
95
+ # Tratando propriedade simples (não array, não object)
92
96
  self._compile_simple_property(
93
97
  properties_structure,
94
98
  map_unique_by_property,
99
+ escopo,
95
100
  entity_model,
96
101
  ast_dto_attributes,
97
102
  ast_entity_attributes,
@@ -99,9 +104,11 @@ class EDLPropertyCompiler:
99
104
  enum_classes,
100
105
  pkey,
101
106
  prop,
107
+ prefx_class_name,
102
108
  )
103
109
 
104
110
  elif isinstance(prop.type, str):
111
+ # Tratando propriedade de relacionamento
105
112
  external_match = re.match(REGEX_EXTERNAL_REF, prop.type)
106
113
  internal_match = re.match(REGEX_INTERNAL_REF, prop.type)
107
114
 
@@ -125,8 +132,18 @@ class EDLPropertyCompiler:
125
132
  )
126
133
 
127
134
  elif internal_match:
128
- # TODO
129
- pass
135
+ related_entity_id = internal_match.group(1)
136
+
137
+ self._compile_internal_relation(
138
+ related_entity_id,
139
+ entity_model,
140
+ properties_structure,
141
+ ast_dto_attributes,
142
+ ast_entity_attributes,
143
+ pkey,
144
+ prop,
145
+ prefx_class_name,
146
+ )
130
147
  else:
131
148
  raise Exception(
132
149
  f"Tipo da propriedade '{pkey}' não suportado: {prop.type}"
@@ -143,8 +160,10 @@ class EDLPropertyCompiler:
143
160
  ast_dto_attributes,
144
161
  ast_entity_attributes,
145
162
  fixed_filters,
163
+ escopo,
146
164
  entity_model,
147
165
  enum_classes,
166
+ prefx_class_name,
148
167
  )
149
168
 
150
169
  return (
@@ -166,8 +185,10 @@ class EDLPropertyCompiler:
166
185
  ast_dto_attributes: list[ast.stmt],
167
186
  ast_entity_attributes: list[ast.stmt],
168
187
  fixed_filters: list[tuple[str, BasicTypes]],
169
- entity_model: EntityModel,
188
+ escopo: str,
189
+ entity_model: EntityModelBase,
170
190
  enum_classes: list[ast.stmt],
191
+ prefx_class_name: str,
171
192
  ):
172
193
  enum_class_name = None
173
194
  keywords = []
@@ -273,7 +294,9 @@ class EDLPropertyCompiler:
273
294
 
274
295
  # Trtando de uma definição de enum
275
296
  if prop.domain_config:
276
- result = self._compile_domain_config(pkey, prop, entity_model)
297
+ result = self._compile_domain_config(
298
+ pkey, prop, escopo, entity_model, prefx_class_name
299
+ )
277
300
  if not result:
278
301
  raise Exception(f"Erro desconhecido ao compilar a propriedade {pkey}")
279
302
 
@@ -343,14 +366,14 @@ class EDLPropertyCompiler:
343
366
  ast_entity_attributes.append(ast_entity_attr)
344
367
 
345
368
  # Guardando como um fixed_filter
346
- # TODO Pernsar em validações para esse value (se está de acordo com o tipo ou enum)
369
+ # TODO Pensar em validações para esse value (se está de acordo com o tipo ou enum)
347
370
  fixed_filters.append((pkey, prop.value))
348
371
 
349
372
  def _compile_external_relation(
350
373
  self,
351
374
  related_entity_id: str,
352
375
  related_entity_key: str,
353
- entity_model: EntityModel,
376
+ entity_model: EntityModelBase,
354
377
  entity_models: dict[str, EntityModel],
355
378
  properties_structure: PropertiesCompilerStructure,
356
379
  ast_dto_attributes: list[ast.stmt],
@@ -414,6 +437,57 @@ class EDLPropertyCompiler:
414
437
  pkey,
415
438
  related_dto_class_name,
416
439
  related_entity_class_name,
440
+ prop,
441
+ )
442
+
443
+ elif prop.cardinality == CardinalityTypes.C1_1:
444
+ self._build_ast_1_1(
445
+ properties_structure,
446
+ ast_dto_attributes,
447
+ ast_entity_attributes,
448
+ pkey,
449
+ related_dto_class_name,
450
+ related_entity_class_name,
451
+ prop,
452
+ )
453
+
454
+ elif prop.cardinality == CardinalityTypes.CN_N:
455
+ # TODO
456
+ pass
457
+ else:
458
+ raise Exception(
459
+ f"Propriedade '{pkey}' da entidade '{entity_model.id}' possui cardinalidade inválida ou não suportada: {prop.cardinality}"
460
+ )
461
+
462
+ def _compile_internal_relation(
463
+ self,
464
+ related_entity_id: str,
465
+ entity_model: EntityModelBase,
466
+ properties_structure: PropertiesCompilerStructure,
467
+ ast_dto_attributes: list[ast.stmt],
468
+ ast_entity_attributes: list[ast.stmt],
469
+ pkey: str,
470
+ prop: PropertyMetaModel,
471
+ prefx_class_name: str,
472
+ ):
473
+ # Resolvendo o nome das classes de DTO e Entity
474
+ related_dto_class_name = compile_dto_class_name(
475
+ related_entity_id, f"{prefx_class_name}_{entity_model.id}"
476
+ )
477
+ related_entity_class_name = compile_entity_class_name(
478
+ related_entity_id, f"{prefx_class_name}_{entity_model.id}"
479
+ )
480
+
481
+ # Instanciando o ast
482
+ if prop.cardinality == CardinalityTypes.C1_N:
483
+ # Para relacionamentos 1_N
484
+ self._build_ast_1_N(
485
+ properties_structure,
486
+ ast_dto_attributes,
487
+ pkey,
488
+ related_dto_class_name,
489
+ related_entity_class_name,
490
+ prop,
417
491
  )
418
492
 
419
493
  elif prop.cardinality == CardinalityTypes.C1_1:
@@ -424,6 +498,7 @@ class EDLPropertyCompiler:
424
498
  pkey,
425
499
  related_dto_class_name,
426
500
  related_entity_class_name,
501
+ prop,
427
502
  )
428
503
 
429
504
  elif prop.cardinality == CardinalityTypes.CN_N:
@@ -441,7 +516,10 @@ class EDLPropertyCompiler:
441
516
  pkey: str,
442
517
  related_dto_class_name: str,
443
518
  related_entity_class_name: str,
519
+ prop: PropertyMetaModel,
444
520
  ):
521
+ # TODO Verificar uso da propriedade relation_key_field do Rest_lib_1
522
+
445
523
  # Propriedade do property descriptor
446
524
  keywords = [
447
525
  ast.keyword(
@@ -454,6 +532,23 @@ class EDLPropertyCompiler:
454
532
  ),
455
533
  ]
456
534
 
535
+ # Tratando das opções básicas do descritor de propriedade
536
+ if properties_structure.required and pkey in properties_structure.required:
537
+ keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
538
+
539
+ if prop.max_length:
540
+ keywords.append(ast.keyword(arg="max", value=ast.Constant(prop.max_length)))
541
+ if prop.min_length:
542
+ keywords.append(ast.keyword(arg="min", value=ast.Constant(prop.min_length)))
543
+
544
+ if prop.validator:
545
+ keywords.append(
546
+ ast.keyword(
547
+ arg="validator",
548
+ value=ast.Name(prop.validator, ctx=ast.Load()),
549
+ )
550
+ )
551
+
457
552
  # Resolvendo a coluna usada no relacionamento
458
553
  if (
459
554
  not properties_structure.entity_properties
@@ -464,7 +559,10 @@ class EDLPropertyCompiler:
464
559
  f"Propriedade '{pkey}' possui um relacionamento, mas nenhuma coluna de relacioanamento foi apontada na propriedade correspondente no repository."
465
560
  )
466
561
 
467
- relation_column = properties_structure.entity_properties[pkey].relation_column
562
+ relation_column_ref = properties_structure.entity_properties[
563
+ pkey
564
+ ].relation_column
565
+ relation_column = str(relation_column_ref).split("/")[-1]
468
566
 
469
567
  keywords.append(
470
568
  ast.keyword(
@@ -497,6 +595,7 @@ class EDLPropertyCompiler:
497
595
  pkey: str,
498
596
  related_dto_class_name: str,
499
597
  related_entity_class_name: str,
598
+ prop: PropertyMetaModel,
500
599
  ):
501
600
  # Propriedade do property descriptor
502
601
  keywords = [
@@ -506,6 +605,24 @@ class EDLPropertyCompiler:
506
605
  ),
507
606
  ]
508
607
 
608
+ # Tratando das opções básicas do descritor de propriedade
609
+ if properties_structure.required and pkey in properties_structure.required:
610
+ keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
611
+
612
+ if (
613
+ properties_structure.main_properties
614
+ and pkey in properties_structure.main_properties
615
+ ):
616
+ keywords.append(ast.keyword(arg="resume", value=ast.Constant(True)))
617
+
618
+ if prop.validator:
619
+ keywords.append(
620
+ ast.keyword(
621
+ arg="validator",
622
+ value=ast.Name(prop.validator, ctx=ast.Load()),
623
+ )
624
+ )
625
+
509
626
  # Resolvendo a coluna usada no relacionamento
510
627
  if (
511
628
  not properties_structure.entity_properties
@@ -525,6 +642,9 @@ class EDLPropertyCompiler:
525
642
  owner_relation = True
526
643
  relation_column = relation_column.split("/")[-1]
527
644
 
645
+ # TODO Verificar, porque desconfio que o apontamento para a coluna de relacionamento
646
+ # para o caso do relacionamento OTHER, não é suportado pelo RestLib (acho que, quando
647
+ # o dono é OTHER, o RestLib sempre aponta para a PK da entidade corrente).
528
648
  keywords.append(
529
649
  ast.keyword(
530
650
  arg="relation_field",
@@ -621,6 +741,7 @@ class EDLPropertyCompiler:
621
741
  self,
622
742
  properties_structure,
623
743
  map_unique_by_property,
744
+ escopo,
624
745
  entity_model,
625
746
  ast_dto_attributes,
626
747
  ast_entity_attributes,
@@ -628,6 +749,7 @@ class EDLPropertyCompiler:
628
749
  enum_classes,
629
750
  pkey,
630
751
  prop,
752
+ prefx_class_name: str,
631
753
  ):
632
754
  enum_class_name = None
633
755
  keywords = []
@@ -681,18 +803,18 @@ class EDLPropertyCompiler:
681
803
  if prop.type in [PrimitiveTypes.STRING, PrimitiveTypes.EMAIL]:
682
804
  if prop.max_length:
683
805
  max = prop.max_length
684
- elif prop.min_length:
806
+ if prop.min_length:
685
807
  min = prop.min_length
686
808
  elif prop.type in [PrimitiveTypes.INTEGER, PrimitiveTypes.NUMBER]:
687
809
  if prop.minimum:
688
810
  min = prop.minimum
689
- elif prop.maximum:
811
+ if prop.maximum:
690
812
  max = prop.maximum
691
813
 
692
814
  if max:
693
- keywords.append(ast.keyword(arg="max", value=ast.Constant(prop.max_length)))
815
+ keywords.append(ast.keyword(arg="max", value=ast.Constant(max)))
694
816
  if min:
695
- keywords.append(ast.keyword(arg="min", value=ast.Constant(prop.min_length)))
817
+ keywords.append(ast.keyword(arg="min", value=ast.Constant(min)))
696
818
 
697
819
  if (
698
820
  properties_structure.search_properties
@@ -796,7 +918,9 @@ class EDLPropertyCompiler:
796
918
  )
797
919
 
798
920
  if prop.domain_config:
799
- result = self._compile_domain_config(pkey, prop, entity_model)
921
+ result = self._compile_domain_config(
922
+ pkey, prop, escopo, entity_model, prefx_class_name
923
+ )
800
924
  if not result:
801
925
  raise Exception(f"Erro desconhecido ao compilar a propriedade {pkey}")
802
926
 
@@ -839,7 +963,9 @@ class EDLPropertyCompiler:
839
963
  self,
840
964
  pkey: str,
841
965
  prop: PropertyMetaModel | TraitPropertyMetaModel,
842
- entity_model: EntityModel,
966
+ escopo: str,
967
+ entity_model: EntityModelBase,
968
+ prefx_class_name: str,
843
969
  ) -> tuple[str, ast.stmt] | None:
844
970
  if not prop.domain_config:
845
971
  return None
@@ -881,7 +1007,7 @@ class EDLPropertyCompiler:
881
1007
  ast_values.append(ast_value)
882
1008
 
883
1009
  # Instanciando o atributo AST
884
- enum_class_name = f"{CompilerStrUtil.to_pascal_case(entity_model.escopo)}{CompilerStrUtil.to_pascal_case(entity_model.id)}{CompilerStrUtil.to_pascal_case(pkey)}Enum"
1010
+ enum_class_name = f"{CompilerStrUtil.to_pascal_case(escopo)}{CompilerStrUtil.to_pascal_case(prefx_class_name)}{CompilerStrUtil.to_pascal_case(entity_model.id)}{CompilerStrUtil.to_pascal_case(pkey)}Enum"
885
1011
  ast_enum_class = ast.ClassDef(
886
1012
  name=enum_class_name,
887
1013
  bases=[
@@ -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, prefx_class_name: str = "") -> str:
17
+ return f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}{CompilerStrUtil.to_pascal_case(entity_id)}DTO"
18
+
19
+
20
+ def compile_entity_class_name(entity_id: str, prefx_class_name: str = "") -> str:
21
+ return f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}{CompilerStrUtil.to_pascal_case(entity_id)}Entity"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.13
3
+ Version: 0.0.14
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
@@ -23,6 +23,7 @@ setup.cfg
23
23
  ./nsj_rest_lib2/compiler/edl_model/api_model.py
24
24
  ./nsj_rest_lib2/compiler/edl_model/column_meta_model.py
25
25
  ./nsj_rest_lib2/compiler/edl_model/entity_model.py
26
+ ./nsj_rest_lib2/compiler/edl_model/entity_model_base.py
26
27
  ./nsj_rest_lib2/compiler/edl_model/index_model.py
27
28
  ./nsj_rest_lib2/compiler/edl_model/primitives.py
28
29
  ./nsj_rest_lib2/compiler/edl_model/property_meta_model.py
@@ -58,6 +59,7 @@ nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py
58
59
  nsj_rest_lib2/compiler/edl_model/api_model.py
59
60
  nsj_rest_lib2/compiler/edl_model/column_meta_model.py
60
61
  nsj_rest_lib2/compiler/edl_model/entity_model.py
62
+ nsj_rest_lib2/compiler/edl_model/entity_model_base.py
61
63
  nsj_rest_lib2/compiler/edl_model/index_model.py
62
64
  nsj_rest_lib2/compiler/edl_model/primitives.py
63
65
  nsj_rest_lib2/compiler/edl_model/property_meta_model.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = nsj_rest_lib2
3
- version = 0.0.13
3
+ version = 0.0.14
4
4
  author = Nasajon Sistemas
5
5
  author_email = contact.dev@nasajon.com.br
6
6
  description = Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
@@ -1,21 +0,0 @@
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"
File without changes
@@ -317,8 +317,8 @@ class EntityLoader:
317
317
  namespace.module = module
318
318
  namespace.entities_dict = module.__dict__
319
319
 
320
- self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
321
320
  self._safe_exec(source_entity, namespace.entities_dict, "Entity source")
321
+ self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
322
322
 
323
323
  # Gravando a entidade no dict de entidades carregadas
324
324
  loaded_entity = LoadedEntity()