nsj-rest-lib2 0.0.8__tar.gz → 0.0.10__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 (40) hide show
  1. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/PKG-INFO +2 -1
  2. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/compiler.py +37 -11
  3. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/dto_compiler.py +27 -1
  4. nsj_rest_lib2-0.0.10/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +16 -0
  5. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/entity_model.py +8 -0
  6. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/primitives.py +17 -5
  7. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +15 -3
  8. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +2 -2
  9. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/entity_compiler.py +2 -1
  10. nsj_rest_lib2-0.0.10/nsj_rest_lib2/compiler/property_compiler.py +522 -0
  11. nsj_rest_lib2-0.0.10/nsj_rest_lib2/compiler/util/type_naming_util.py +21 -0
  12. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/service/entity_loader.py +35 -9
  13. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2.egg-info/PKG-INFO +2 -1
  14. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2.egg-info/SOURCES.txt +2 -0
  15. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2.egg-info/requires.txt +1 -0
  16. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/setup.cfg +2 -1
  17. nsj_rest_lib2-0.0.8/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -6
  18. nsj_rest_lib2-0.0.8/nsj_rest_lib2/compiler/property_compiler.py +0 -369
  19. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/README.md +0 -0
  20. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/__init__.py +0 -0
  21. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/__init__.py +0 -0
  22. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/ai_compiler.py +0 -0
  23. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/compiler_structures.py +0 -0
  24. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
  25. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
  26. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
  27. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
  28. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
  29. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
  30. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
  31. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
  32. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/controller/__init__.py +0 -0
  33. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/controller/dynamic_controller.py +0 -0
  34. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/exception.py +0 -0
  35. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/redis_config.py +0 -0
  36. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/service/__init__.py +0 -0
  37. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2/settings.py +0 -0
  38. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
  39. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
  40. {nsj_rest_lib2-0.0.8 → nsj_rest_lib2-0.0.10}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.8
3
+ Version: 0.0.10
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
 
@@ -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
- ast_dto_attributes, ast_entity_attributes, props_pk, enum_classes = (
106
- self._properties_compiler.compile(
107
- properties_structure,
108
- map_unique_by_property,
109
- entity_model,
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
- edl_json = json.load(f)
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(**edl_json)
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
- class_name = f"{CompilerStrUtil.to_pascal_case(entity_model.id)}DTO"
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())],
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel, Field, model_validator
2
+ from typing import List, Optional, Literal
3
+
4
+
5
+ class ColumnMetaModel(BaseModel):
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
- from typing import List
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 PrimitiveTypes
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: PrimitiveTypes = Field(..., description="Tipo da propriedade.")
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, PrimitiveTypes
3
+ from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes, PropertyType
4
4
 
5
5
 
6
6
  class TraitPropertyMetaModel(BaseModel):
7
- type: PrimitiveTypes = Field(..., description="Tipo da propriedade.")
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 = f"{CompilerStrUtil.to_pascal_case(entity_model.id)}Entity"
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())],