nsj-rest-lib2 0.0.30__tar.gz → 0.0.32__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 (52) hide show
  1. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/PKG-INFO +1 -1
  2. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/compiler.py +96 -10
  3. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/compiler_structures.py +1 -0
  4. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/primitives.py +12 -3
  5. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/function_get_delete_compiler.py +238 -0
  6. nsj_rest_lib2-0.0.30/nsj_rest_lib2/compiler/function_compiler.py → nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/function_insert_update_compiler.py +2 -4
  7. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/function_model.py +1 -0
  8. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/migration_compiler.py +674 -0
  9. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/migration_compiler_alter_table.py +201 -0
  10. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/migration_compiler_create_table.py +75 -0
  11. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/migration_compiler_util.py +144 -0
  12. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/model.py +11 -0
  13. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/property_compiler.py +38 -32
  14. nsj_rest_lib2-0.0.32/nsj_rest_lib2/compiler/util/relation_ref.py +118 -0
  15. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/util/type_naming_util.py +38 -1
  16. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/controller/dynamic_controller.py +59 -0
  17. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/service/entity_config_writer.py +19 -0
  18. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/service/entity_loader.py +176 -2
  19. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2.egg-info/PKG-INFO +1 -1
  20. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2.egg-info/SOURCES.txt +17 -5
  21. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/setup.cfg +1 -1
  22. nsj_rest_lib2-0.0.32/tests/test_function_handler_compilation.py +152 -0
  23. nsj_rest_lib2-0.0.32/tests/test_migration_generation.py +453 -0
  24. nsj_rest_lib2-0.0.30/nsj_rest_lib2/compiler/ai_compiler.py +0 -285
  25. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/README.md +0 -0
  26. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/__init__.py +0 -0
  27. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/__init__.py +0 -0
  28. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/dto_compiler.py +0 -0
  29. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
  30. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
  31. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
  32. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
  33. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/entity_model.py +0 -0
  34. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/entity_model_base.py +0 -0
  35. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/entity_model_root.py +0 -0
  36. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
  37. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -0
  38. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
  39. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
  40. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/entity_compiler.py +0 -0
  41. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
  42. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
  43. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
  44. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/controller/__init__.py +0 -0
  45. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/exception.py +0 -0
  46. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/redis_config.py +0 -0
  47. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/service/__init__.py +0 -0
  48. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2/settings.py +0 -0
  49. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
  50. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2.egg-info/requires.txt +0 -0
  51. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
  52. {nsj_rest_lib2-0.0.30 → nsj_rest_lib2-0.0.32}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.30
3
+ Version: 0.0.32
4
4
  Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
5
5
  Home-page: https://github.com/Nasajon/nsj_rest_lib2
6
6
  Author: Nasajon Sistemas
@@ -1,5 +1,3 @@
1
- import re
2
-
3
1
  from typing import Any
4
2
 
5
3
  from nsj_rest_lib2.compiler.compiler_structures import (
@@ -8,17 +6,19 @@ from nsj_rest_lib2.compiler.compiler_structures import (
8
6
  PropertiesCompilerStructure,
9
7
  )
10
8
  from nsj_rest_lib2.compiler.dto_compiler import DTOCompiler
11
- from nsj_rest_lib2.compiler.function_compiler import (
12
- FunctionCompiler,
9
+ from nsj_rest_lib2.compiler.function_insert_update_compiler import (
10
+ FunctionInsertUpdateCompiler,
13
11
  inject_function_bindings,
14
12
  )
13
+ from nsj_rest_lib2.compiler.function_get_delete_compiler import (
14
+ FunctionGetDeleteCompiler,
15
+ )
15
16
  from nsj_rest_lib2.compiler.function_model import (
16
17
  FunctionBindingConfig,
17
18
  FunctionCompilationOutput,
18
19
  )
19
20
  from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
20
21
  from nsj_rest_lib2.compiler.edl_model.entity_model_root import EntityModelRoot
21
- from nsj_rest_lib2.compiler.edl_model.primitives import REGEX_EXTERNAL_REF
22
22
  from nsj_rest_lib2.compiler.entity_compiler import EntityCompiler
23
23
  from nsj_rest_lib2.compiler.model import CompilerResult, RelationDependency
24
24
  from nsj_rest_lib2.compiler.property_compiler import EDLPropertyCompiler
@@ -28,6 +28,7 @@ from nsj_rest_lib2.compiler.util.type_naming_util import (
28
28
  compile_entity_class_name,
29
29
  compile_namespace_keys,
30
30
  )
31
+ from nsj_rest_lib2.compiler.util.relation_ref import RelationRefParser
31
32
 
32
33
  from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
33
34
 
@@ -39,7 +40,8 @@ class EDLCompiler:
39
40
  self._properties_compiler = EDLPropertyCompiler()
40
41
  self._dto_compiler = DTOCompiler()
41
42
  self._entity_compiler = EntityCompiler()
42
- self._function_compiler = FunctionCompiler()
43
+ self._function_compiler = FunctionInsertUpdateCompiler()
44
+ self._function_get_delete_compiler = FunctionGetDeleteCompiler()
43
45
 
44
46
  def compile_models(
45
47
  self, entity_models: dict[str, EntityModel]
@@ -144,6 +146,15 @@ class EDLCompiler:
144
146
  update_output = FunctionCompilationOutput()
145
147
  insert_function_code = ""
146
148
  update_function_code = ""
149
+ get_function_code = ""
150
+ list_function_code = ""
151
+ delete_function_code = ""
152
+ get_function_type_class = None
153
+ list_function_type_class = None
154
+ delete_function_type_class = None
155
+ get_function_name = None
156
+ list_function_name = None
157
+ delete_function_name = None
147
158
 
148
159
  if isinstance(entity_model, EntityModel) and entity_model.api:
149
160
  handlers = {}
@@ -166,6 +177,15 @@ class EDLCompiler:
166
177
  handlers.get("put"),
167
178
  prefx_class_name,
168
179
  )
180
+ get_handler = handlers.get("get")
181
+ list_handler = handlers.get("list")
182
+ delete_handler = handlers.get("delete")
183
+
184
+ get_function_name = get_handler.function_ref if get_handler else None
185
+ list_function_name = list_handler.function_ref if list_handler else None
186
+ delete_function_name = (
187
+ delete_handler.function_ref if delete_handler else None
188
+ )
169
189
 
170
190
  inject_function_bindings(
171
191
  function_bindings,
@@ -178,6 +198,46 @@ class EDLCompiler:
178
198
  if update_output.code:
179
199
  update_function_code += update_output.code + "\n\n"
180
200
 
201
+ # Gerando FunctionTypes para GET/LIST/DELETE (quando configuradas)
202
+ get_output = self._function_get_delete_compiler.compile(
203
+ entity_model,
204
+ properties_structure,
205
+ get_handler,
206
+ prefx_class_name,
207
+ verb="get",
208
+ )
209
+ if get_output.class_name and get_output.code:
210
+ get_function_type_class = get_output.class_name
211
+ get_function_code = get_output.code
212
+ if get_output.function_name:
213
+ get_function_name = get_output.function_name
214
+
215
+ list_output = self._function_get_delete_compiler.compile(
216
+ entity_model,
217
+ properties_structure,
218
+ list_handler,
219
+ prefx_class_name,
220
+ verb="list",
221
+ )
222
+ if list_output.class_name and list_output.code:
223
+ list_function_type_class = list_output.class_name
224
+ list_function_code = list_output.code
225
+ if list_output.function_name:
226
+ list_function_name = list_output.function_name
227
+
228
+ delete_output = self._function_get_delete_compiler.compile(
229
+ entity_model,
230
+ properties_structure,
231
+ delete_handler,
232
+ prefx_class_name,
233
+ verb="delete",
234
+ )
235
+ if delete_output.class_name and delete_output.code:
236
+ delete_function_type_class = delete_output.class_name
237
+ delete_function_code = delete_output.code
238
+ if delete_output.function_name:
239
+ delete_function_name = delete_output.function_name
240
+
181
241
  # Criando a lista de atributos do DTO e da Entity; e recuperando as chaves primarias
182
242
  (
183
243
  ast_dto_attributes,
@@ -301,7 +361,22 @@ class EDLCompiler:
301
361
  compiler_result.dto_code = dto_code
302
362
  compiler_result.relations_dependencies = relations_dependencies_complete
303
363
  compiler_result.insert_function_class_name = insert_output.class_name
364
+ compiler_result.insert_function_name = insert_output.function_name
304
365
  compiler_result.update_function_class_name = update_output.class_name
366
+ compiler_result.update_function_name = update_output.function_name
367
+ compiler_result.get_function_name = get_function_name
368
+ compiler_result.list_function_name = list_function_name
369
+ compiler_result.delete_function_name = delete_function_name
370
+ compiler_result.get_function_type_class_name = get_function_type_class
371
+ compiler_result.list_function_type_class_name = list_function_type_class
372
+ compiler_result.delete_function_type_class_name = delete_function_type_class
373
+ compiler_result.source_get_function_type = get_function_code.strip() or None
374
+ compiler_result.source_list_function_type = (
375
+ list_function_code.strip() or None
376
+ )
377
+ compiler_result.source_delete_function_type = (
378
+ delete_function_code.strip() or None
379
+ )
305
380
 
306
381
  insert_code_compiled = insert_function_code.strip()
307
382
  update_code_compiled = update_function_code.strip()
@@ -496,6 +571,7 @@ class EDLCompiler:
496
571
  properties_structure: PropertiesCompilerStructure,
497
572
  entity_model: EntityModelBase,
498
573
  entity_models: dict[str, EntityModel],
574
+ relation_type: str = "self",
499
575
  ):
500
576
  if not entity_model:
501
577
  return
@@ -511,6 +587,7 @@ class EDLCompiler:
511
587
  properties_structure,
512
588
  mixin_model,
513
589
  entity_models,
590
+ relation_type="mixin",
514
591
  )
515
592
 
516
593
  # Populando com as propriedades da superclasse (extends)
@@ -521,6 +598,7 @@ class EDLCompiler:
521
598
  properties_structure,
522
599
  super_model,
523
600
  entity_models,
601
+ relation_type="extends",
524
602
  )
525
603
 
526
604
  # Populando com as propriedades do trait
@@ -531,10 +609,18 @@ class EDLCompiler:
531
609
  properties_structure,
532
610
  trait_model,
533
611
  entity_models,
612
+ relation_type="trait",
534
613
  )
535
614
 
536
615
  # Populando com as propriedades da entidade atual
537
616
  properties_structure.properties.update(entity_model.properties)
617
+ for prop_name in entity_model.properties or {}:
618
+ # Preferimos não sobrescrever quando já marcado como 'self'
619
+ if (
620
+ prop_name not in properties_structure.property_origins
621
+ or relation_type == "self"
622
+ ):
623
+ properties_structure.property_origins[prop_name] = relation_type
538
624
  if entity_model.main_properties:
539
625
  for main_property in entity_model.main_properties:
540
626
  if not isinstance(main_property, str):
@@ -706,10 +792,10 @@ class EDLCompiler:
706
792
  prop = entity_model.properties[pkey]
707
793
 
708
794
  if isinstance(prop.type, str):
709
- external_match = re.match(REGEX_EXTERNAL_REF, prop.type)
710
- if external_match:
711
- external_dependency = external_match.group(0)
712
- entities.append(external_dependency)
795
+ relation_ref = RelationRefParser.parse(prop.type)
796
+ if relation_ref and relation_ref.is_external:
797
+ if relation_ref.entity_key:
798
+ entities.append(relation_ref.entity_key)
713
799
 
714
800
  return entities
715
801
 
@@ -27,6 +27,7 @@ class PropertiesCompilerStructure:
27
27
  self.trait_properties: dict[str, TraitPropertyMetaModel] = {}
28
28
  self.extends_properties: dict[str, ExtendsPropertyMetaModel] = {}
29
29
  self.composed_properties: dict[str, list[str]] = {}
30
+ self.property_origins: dict[str, str] = {}
30
31
 
31
32
 
32
33
  class ComponentsCompilerStructure:
@@ -4,15 +4,20 @@ 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"^#\/components\/(\w+)$"
7
+ REGEX_EXTERNAL_COMPONENT_REF = r"^(\w+)\/(\w+)\/([\w\/#]+)$"
8
+ REGEX_INTERNAL_REF = r"^#\/components\/([\w\/#]+)$"
8
9
 
9
10
  ExternalRefType = Annotated[str, StringConstraints(pattern=REGEX_EXTERNAL_REF)]
11
+ ExternalComponentRefType = Annotated[
12
+ str, StringConstraints(pattern=REGEX_EXTERNAL_COMPONENT_REF)
13
+ ]
10
14
  InternalRefType = Annotated[str, StringConstraints(pattern=REGEX_INTERNAL_REF)]
11
15
 
12
16
 
13
17
  class PrimitiveTypes(enum.Enum):
14
18
  # TODO Validar esses tipos
15
19
  STRING = "string"
20
+ TEXT = "text"
16
21
  NUMBER = "number"
17
22
  INTEGER = "integer"
18
23
  BOOLEAN = "boolean"
@@ -28,10 +33,13 @@ class PrimitiveTypes(enum.Enum):
28
33
  DURATION = "duration"
29
34
 
30
35
 
31
- PropertyType = Union[PrimitiveTypes, ExternalRefType, InternalRefType]
36
+ PropertyType = Union[
37
+ PrimitiveTypes, ExternalRefType, ExternalComponentRefType, InternalRefType
38
+ ]
32
39
 
33
40
  MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
34
41
  PrimitiveTypes.STRING: "str",
42
+ PrimitiveTypes.TEXT: "str",
35
43
  PrimitiveTypes.NUMBER: "float",
36
44
  PrimitiveTypes.INTEGER: "int",
37
45
  PrimitiveTypes.BOOLEAN: "bool",
@@ -44,7 +52,7 @@ MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
44
52
  PrimitiveTypes.EMAIL: "str",
45
53
  PrimitiveTypes.DATE: "datetime.date",
46
54
  PrimitiveTypes.DATETIME: "datetime.datetime",
47
- PrimitiveTypes.DURATION: "relativedelta"
55
+ PrimitiveTypes.DURATION: "relativedelta",
48
56
  }
49
57
 
50
58
  BasicTypes = int | bool | float | str
@@ -52,6 +60,7 @@ DefaultTypes = BasicTypes | List[BasicTypes]
52
60
 
53
61
  STR_BASED_TYPES = {
54
62
  PrimitiveTypes.STRING,
63
+ PrimitiveTypes.TEXT,
55
64
  PrimitiveTypes.EMAIL,
56
65
  PrimitiveTypes.CPF,
57
66
  PrimitiveTypes.CNPJ,
@@ -0,0 +1,238 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from nsj_rest_lib2.compiler.compiler_structures import PropertiesCompilerStructure
6
+ from nsj_rest_lib2.compiler.edl_model.api_model import HandlerConfig, HandlerMapping
7
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
8
+ from nsj_rest_lib2.compiler.edl_model.primitives import PrimitiveTypes
9
+ from nsj_rest_lib2.compiler.function_model import FunctionCompilationOutput
10
+ from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
11
+ from nsj_rest_lib2.compiler.util.type_naming_util import compile_function_class_name
12
+ from nsj_rest_lib2.compiler.util.type_util import TypeUtil
13
+
14
+
15
+ class FunctionGetDeleteCompiler:
16
+ """
17
+ Responsável por gerar FunctionTypes (Get/List/Delete) baseados no
18
+ binding de pg_function declarado no EDL.
19
+ """
20
+
21
+ def compile(
22
+ self,
23
+ entity_model: EntityModelBase,
24
+ properties_structure: PropertiesCompilerStructure,
25
+ handler_config: Optional[HandlerConfig],
26
+ prefx_class_name: str,
27
+ verb: str,
28
+ ) -> FunctionCompilationOutput:
29
+ if not isinstance(handler_config, HandlerConfig):
30
+ return FunctionCompilationOutput()
31
+ if handler_config.impl != "pg_function":
32
+ return FunctionCompilationOutput()
33
+ if not handler_config.call or not handler_config.call.arg_binding:
34
+ return FunctionCompilationOutput()
35
+
36
+ arg_binding = handler_config.call.arg_binding
37
+ if not arg_binding.mapping or not arg_binding.type_name:
38
+ return FunctionCompilationOutput()
39
+
40
+ function_name = handler_config.function_ref
41
+ if not function_name:
42
+ return FunctionCompilationOutput()
43
+
44
+ class_name = compile_function_class_name(
45
+ entity_model.id,
46
+ prefx_class_name,
47
+ [],
48
+ verb,
49
+ )
50
+
51
+ fields = self._build_fields(arg_binding.mapping, properties_structure)
52
+ if not fields:
53
+ return FunctionCompilationOutput()
54
+
55
+ code = self._build_code(class_name, arg_binding.type_name, fields, verb)
56
+
57
+ return FunctionCompilationOutput(
58
+ class_name=class_name,
59
+ code=code,
60
+ function_name=function_name,
61
+ )
62
+
63
+ # ------------------------------------------------------------------ #
64
+ # Helpers
65
+ # ------------------------------------------------------------------ #
66
+
67
+ def _build_fields(
68
+ self,
69
+ mappings: list[HandlerMapping],
70
+ properties_structure: PropertiesCompilerStructure,
71
+ ) -> list[tuple[str, str, Optional[str], bool]]:
72
+ """
73
+ Retorna lista de tuplas:
74
+ (python_name, annotation, type_field_name, is_pk)
75
+ """
76
+ fields: list[tuple[str, str, Optional[str], bool]] = []
77
+ used: set[str] = set()
78
+
79
+ for entry in mappings or []:
80
+ attr = (entry.attr or "").strip()
81
+ if not attr:
82
+ continue
83
+
84
+ python_name = self._sanitize_identifier(attr)
85
+ if python_name in used:
86
+ continue
87
+ used.add(python_name)
88
+
89
+ type_field_name = attr if python_name != attr else None
90
+ source = (entry.from_ or "").strip()
91
+ is_pk = source.startswith("path.")
92
+
93
+ annotation = self._resolve_annotation(entry, properties_structure)
94
+
95
+ fields.append(
96
+ (
97
+ python_name,
98
+ annotation,
99
+ type_field_name,
100
+ is_pk,
101
+ )
102
+ )
103
+
104
+ return fields
105
+
106
+ def _resolve_annotation(
107
+ self,
108
+ entry: HandlerMapping,
109
+ properties_structure: PropertiesCompilerStructure,
110
+ ) -> str:
111
+ """
112
+ Tenta inferir o tipo a partir das propriedades da entidade.
113
+ Prioriza:
114
+ - campos referenciados como body.<campo>;
115
+ - ou, se o último segmento de 'from' (após o '.') existir como
116
+ property do EDL, usa esse nome para lookup.
117
+ """
118
+ dto_field = self._extract_dto_field(entry.from_)
119
+
120
+ # Se não veio de body.*, tenta casar o último segmento de 'from'
121
+ # com o nome de alguma propriedade do EDL.
122
+ if not dto_field:
123
+ candidate = CompilerStrUtil.to_snake_case(
124
+ (entry.from_ or "").split(".")[-1]
125
+ )
126
+ if candidate and properties_structure and candidate in properties_structure.properties:
127
+ dto_field = candidate
128
+
129
+ if not dto_field:
130
+ return "Any"
131
+
132
+ prop = (
133
+ properties_structure.properties.get(dto_field)
134
+ if properties_structure
135
+ else None
136
+ )
137
+ if not prop or not isinstance(prop.type, PrimitiveTypes):
138
+ return "Any"
139
+
140
+ return TypeUtil.property_type_to_python_type(prop.type)
141
+
142
+ def _build_code(
143
+ self,
144
+ class_name: str,
145
+ type_name: str,
146
+ fields: list[tuple[str, str, Optional[str], bool]],
147
+ verb: str,
148
+ ) -> str:
149
+ needs_any = any(annotation == "Any" for _, annotation, _, _ in fields)
150
+ needs_uuid = any(annotation == "uuid.UUID" for _, annotation, _, _ in fields)
151
+ needs_datetime = any(
152
+ annotation == "datetime.datetime" for _, annotation, _, _ in fields
153
+ )
154
+
155
+ lines: list[str] = []
156
+ if needs_uuid:
157
+ lines.append("import uuid")
158
+ if needs_datetime:
159
+ lines.append("import datetime")
160
+ if needs_any:
161
+ lines.append("from typing import Any")
162
+
163
+ decorator_module, decorator_name, base_class = self._resolve_decorator(verb)
164
+ lines.extend(
165
+ [
166
+ f"from {decorator_module} import {decorator_name}",
167
+ f"from nsj_rest_lib.entity.function_type_base import {base_class}",
168
+ "from nsj_rest_lib.descriptor.function_field import FunctionField",
169
+ "",
170
+ f"@{decorator_name}(type_name=\"{type_name}\")",
171
+ f"class {class_name}({base_class}):",
172
+ ]
173
+ )
174
+
175
+ for name, annotation, type_field_name, is_pk in fields:
176
+ kwargs: list[str] = []
177
+ if type_field_name:
178
+ kwargs.append(f'type_field_name="{type_field_name}"')
179
+ if is_pk:
180
+ kwargs.append("pk=True")
181
+ kwargs_str = ", ".join(kwargs)
182
+ if kwargs_str:
183
+ kwargs_str = ", " + kwargs_str
184
+ lines.append(
185
+ f" {name}: {annotation} = FunctionField({kwargs_str.lstrip(', ')})"
186
+ )
187
+
188
+ if not fields:
189
+ lines.append(" pass")
190
+
191
+ lines.append("") # newline final
192
+ return "\n".join(lines)
193
+
194
+ def _resolve_decorator(self, verb: str) -> tuple[str, str, str]:
195
+ verb = (verb or "").lower()
196
+ if verb == "get":
197
+ return (
198
+ "nsj_rest_lib.decorator.get_function_type",
199
+ "GetFunctionType",
200
+ "GetFunctionTypeBase",
201
+ )
202
+ if verb == "list":
203
+ return (
204
+ "nsj_rest_lib.decorator.list_function_type",
205
+ "ListFunctionType",
206
+ "ListFunctionTypeBase",
207
+ )
208
+ if verb == "delete":
209
+ return (
210
+ "nsj_rest_lib.decorator.delete_function_type",
211
+ "DeleteFunctionType",
212
+ "DeleteFunctionTypeBase",
213
+ )
214
+ # fallback genérico
215
+ return (
216
+ "nsj_rest_lib.decorator.function_type",
217
+ "FunctionType",
218
+ "FunctionTypeBase",
219
+ )
220
+
221
+ def _sanitize_identifier(self, name: str) -> str:
222
+ candidate = CompilerStrUtil.to_snake_case(name)
223
+ candidate = "".join(ch if ch.isalnum() or ch == "_" else "_" for ch in candidate)
224
+ if not candidate:
225
+ candidate = "field"
226
+ if candidate[0].isdigit():
227
+ candidate = f"_{candidate}"
228
+ return candidate
229
+
230
+ def _extract_dto_field(self, source: Optional[str]) -> Optional[str]:
231
+ if not source:
232
+ return None
233
+
234
+ if source.startswith("body."):
235
+ segment = source[5:].split(".", 1)[0]
236
+ return CompilerStrUtil.to_snake_case(segment)
237
+
238
+ return None
@@ -50,7 +50,7 @@ class _ClassSpec:
50
50
  children: list["_ClassSpec"] = field(default_factory=list)
51
51
 
52
52
 
53
- class FunctionCompiler:
53
+ class FunctionInsertUpdateCompiler:
54
54
  """
55
55
  Responsável por compilar tipos usados por funções de banco (Insert/Update)
56
56
  com base na configuração declarada no EDL.
@@ -156,6 +156,7 @@ class FunctionCompiler:
156
156
  return FunctionCompilationOutput(
157
157
  class_name=class_name,
158
158
  code=code,
159
+ function_name=function_name,
159
160
  field_bindings=field_bindings,
160
161
  relation_bindings=relation_bindings,
161
162
  )
@@ -333,9 +334,6 @@ class FunctionCompiler:
333
334
  args=[],
334
335
  keywords=[
335
336
  ast.keyword(arg="type_name", value=ast.Constant(value=spec.type_name)),
336
- ast.keyword(
337
- arg="function_name", value=ast.Constant(value=spec.function_name)
338
- ),
339
337
  ],
340
338
  )
341
339
 
@@ -64,6 +64,7 @@ class FunctionCompilationOutput:
64
64
 
65
65
  class_name: Optional[str] = None
66
66
  code: Optional[str] = None
67
+ function_name: Optional[str] = None
67
68
  field_bindings: Dict[str, str] = field(default_factory=dict)
68
69
  relation_bindings: Dict[str, FunctionRelationBinding] = field(
69
70
  default_factory=dict