nsj-rest-lib2 0.0.31__tar.gz → 0.0.33__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 (51) hide show
  1. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/PKG-INFO +2 -2
  2. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/compiler.py +80 -3
  3. nsj_rest_lib2-0.0.33/nsj_rest_lib2/compiler/function_get_delete_compiler.py +238 -0
  4. nsj_rest_lib2-0.0.31/nsj_rest_lib2/compiler/function_compiler.py → nsj_rest_lib2-0.0.33/nsj_rest_lib2/compiler/function_insert_update_compiler.py +2 -4
  5. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/function_model.py +1 -0
  6. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/model.py +11 -0
  7. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/util/type_naming_util.py +38 -1
  8. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/controller/dynamic_controller.py +59 -0
  9. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/service/entity_config_writer.py +19 -0
  10. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/service/entity_loader.py +176 -2
  11. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2.egg-info/PKG-INFO +2 -2
  12. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2.egg-info/SOURCES.txt +5 -2
  13. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2.egg-info/requires.txt +1 -1
  14. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/setup.cfg +2 -2
  15. nsj_rest_lib2-0.0.33/tests/test_function_handler_compilation.py +152 -0
  16. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/README.md +0 -0
  17. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/__init__.py +0 -0
  18. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/__init__.py +0 -0
  19. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/compiler_structures.py +0 -0
  20. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/dto_compiler.py +0 -0
  21. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
  22. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
  23. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
  24. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
  25. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/entity_model.py +0 -0
  26. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/entity_model_base.py +0 -0
  27. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/entity_model_root.py +0 -0
  28. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
  29. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/primitives.py +0 -0
  30. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -0
  31. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
  32. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
  33. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/entity_compiler.py +0 -0
  34. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/migration_compiler.py +0 -0
  35. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/migration_compiler_alter_table.py +0 -0
  36. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/migration_compiler_create_table.py +0 -0
  37. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/migration_compiler_util.py +0 -0
  38. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/property_compiler.py +0 -0
  39. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
  40. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/util/relation_ref.py +0 -0
  41. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
  42. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
  43. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/controller/__init__.py +0 -0
  44. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/exception.py +0 -0
  45. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/redis_config.py +0 -0
  46. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/service/__init__.py +0 -0
  47. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2/settings.py +0 -0
  48. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
  49. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
  50. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/pyproject.toml +0 -0
  51. {nsj_rest_lib2-0.0.31 → nsj_rest_lib2-0.0.33}/tests/test_migration_generation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.31
3
+ Version: 0.0.33
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
@@ -12,7 +12,7 @@ Classifier: Topic :: Software Development :: Libraries
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Requires-Python: >=3.4
14
14
  Description-Content-Type: text/markdown
15
- Requires-Dist: nsj-rest-lib<6.0.0,>=5.1.3
15
+ Requires-Dist: nsj-rest-lib<7.0.0,>=5.1.3
16
16
  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
@@ -6,10 +6,13 @@ from nsj_rest_lib2.compiler.compiler_structures import (
6
6
  PropertiesCompilerStructure,
7
7
  )
8
8
  from nsj_rest_lib2.compiler.dto_compiler import DTOCompiler
9
- from nsj_rest_lib2.compiler.function_compiler import (
10
- FunctionCompiler,
9
+ from nsj_rest_lib2.compiler.function_insert_update_compiler import (
10
+ FunctionInsertUpdateCompiler,
11
11
  inject_function_bindings,
12
12
  )
13
+ from nsj_rest_lib2.compiler.function_get_delete_compiler import (
14
+ FunctionGetDeleteCompiler,
15
+ )
13
16
  from nsj_rest_lib2.compiler.function_model import (
14
17
  FunctionBindingConfig,
15
18
  FunctionCompilationOutput,
@@ -37,7 +40,8 @@ class EDLCompiler:
37
40
  self._properties_compiler = EDLPropertyCompiler()
38
41
  self._dto_compiler = DTOCompiler()
39
42
  self._entity_compiler = EntityCompiler()
40
- self._function_compiler = FunctionCompiler()
43
+ self._function_compiler = FunctionInsertUpdateCompiler()
44
+ self._function_get_delete_compiler = FunctionGetDeleteCompiler()
41
45
 
42
46
  def compile_models(
43
47
  self, entity_models: dict[str, EntityModel]
@@ -142,6 +146,15 @@ class EDLCompiler:
142
146
  update_output = FunctionCompilationOutput()
143
147
  insert_function_code = ""
144
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
145
158
 
146
159
  if isinstance(entity_model, EntityModel) and entity_model.api:
147
160
  handlers = {}
@@ -164,6 +177,15 @@ class EDLCompiler:
164
177
  handlers.get("put"),
165
178
  prefx_class_name,
166
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
+ )
167
189
 
168
190
  inject_function_bindings(
169
191
  function_bindings,
@@ -176,6 +198,46 @@ class EDLCompiler:
176
198
  if update_output.code:
177
199
  update_function_code += update_output.code + "\n\n"
178
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
+
179
241
  # Criando a lista de atributos do DTO e da Entity; e recuperando as chaves primarias
180
242
  (
181
243
  ast_dto_attributes,
@@ -299,7 +361,22 @@ class EDLCompiler:
299
361
  compiler_result.dto_code = dto_code
300
362
  compiler_result.relations_dependencies = relations_dependencies_complete
301
363
  compiler_result.insert_function_class_name = insert_output.class_name
364
+ compiler_result.insert_function_name = insert_output.function_name
302
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
+ )
303
380
 
304
381
  insert_code_compiled = insert_function_code.strip()
305
382
  update_code_compiled = update_function_code.strip()
@@ -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
@@ -44,6 +44,17 @@ class CompilerResult:
44
44
  self.api_verbs: list[str] | None = None
45
45
  self.relations_dependencies: list[RelationDependency] | None = None
46
46
  self.insert_function_class_name: str | None = None
47
+ self.insert_function_name: str | None = None
47
48
  self.source_insert_function: str | None = None
48
49
  self.update_function_class_name: str | None = None
50
+ self.update_function_name: str | None = None
49
51
  self.source_update_function: str | None = None
52
+ self.get_function_name: str | None = None
53
+ self.list_function_name: str | None = None
54
+ self.delete_function_name: str | None = None
55
+ self.get_function_type_class_name: str | None = None
56
+ self.list_function_type_class_name: str | None = None
57
+ self.delete_function_type_class_name: str | None = None
58
+ self.source_get_function_type: str | None = None
59
+ self.source_list_function_type: str | None = None
60
+ self.source_delete_function_type: str | None = None
@@ -33,7 +33,17 @@ def compile_function_class_name(
33
33
  incorporando o caminho dos relacionamentos (quando houver).
34
34
  """
35
35
 
36
- suffix = "InsertType" if operation == "insert" else "UpdateType"
36
+ operation = (operation or "").lower()
37
+ suffix_map = {
38
+ "insert": "InsertType",
39
+ "update": "UpdateType",
40
+ "get": "GetType",
41
+ "list": "ListType",
42
+ "delete": "DeleteType",
43
+ }
44
+ suffix = suffix_map.get(
45
+ operation, f"{CompilerStrUtil.to_pascal_case(operation)}Type"
46
+ )
37
47
  path_suffix = "".join(CompilerStrUtil.to_pascal_case(part) for part in path_parts)
38
48
  return (
39
49
  f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}"
@@ -41,3 +51,30 @@ def compile_function_class_name(
41
51
  f"{path_suffix}"
42
52
  f"{suffix}"
43
53
  )
54
+
55
+
56
+ def compile_function_params_dto_class_name(
57
+ entity_id: str,
58
+ prefx_class_name: str,
59
+ verb: str,
60
+ ) -> str:
61
+ """
62
+ Gera o nome da classe DTO usada como envelope de parâmetros para funções
63
+ de GET, LIST e DELETE.
64
+ """
65
+
66
+ verb = (verb or "").lower()
67
+ if verb == "get":
68
+ suffix = "GetParamsDTO"
69
+ elif verb == "list":
70
+ suffix = "ListParamsDTO"
71
+ elif verb == "delete":
72
+ suffix = "DeleteParamsDTO"
73
+ else:
74
+ suffix = f"{CompilerStrUtil.to_pascal_case(verb)}ParamsDTO"
75
+
76
+ return (
77
+ f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}"
78
+ f"{CompilerStrUtil.to_pascal_case(entity_id)}"
79
+ f"{suffix}"
80
+ )
@@ -96,6 +96,14 @@ def setup_dynamic_routes(
96
96
  api_verbs,
97
97
  _insert_function_class_name,
98
98
  _update_function_class_name,
99
+ _insert_function_name,
100
+ _update_function_name,
101
+ _get_function_name,
102
+ list_function_name,
103
+ _delete_function_name,
104
+ _get_function_type_class_name,
105
+ list_function_type_class_name,
106
+ _delete_function_type_class_name,
99
107
  ) = entity_loader.load_entity_source(
100
108
  entity_resource,
101
109
  tenant,
@@ -115,6 +123,10 @@ def setup_dynamic_routes(
115
123
  dto_class=etities_dict[dto_class_name],
116
124
  entity_class=etities_dict[entity_class_name],
117
125
  injector_factory=injector_factory,
126
+ list_function_name=list_function_name,
127
+ list_function_type_class=etities_dict.get(
128
+ list_function_type_class_name
129
+ ),
118
130
  )
119
131
 
120
132
  return route.handle_request(*args, **kwargs)
@@ -152,6 +164,14 @@ def setup_dynamic_routes(
152
164
  api_verbs,
153
165
  _insert_function_class_name,
154
166
  _update_function_class_name,
167
+ _insert_function_name,
168
+ _update_function_name,
169
+ get_function_name,
170
+ _list_function_name,
171
+ _delete_function_name,
172
+ get_function_type_class_name,
173
+ _list_function_type_class_name,
174
+ _delete_function_type_class_name,
155
175
  ) = entity_loader.load_entity_source(
156
176
  entity_resource,
157
177
  tenant,
@@ -171,6 +191,10 @@ def setup_dynamic_routes(
171
191
  dto_class=etities_dict[dto_class_name],
172
192
  entity_class=etities_dict[entity_class_name],
173
193
  injector_factory=injector_factory,
194
+ get_function_name=get_function_name,
195
+ get_function_type_class=etities_dict.get(
196
+ get_function_type_class_name
197
+ ),
174
198
  )
175
199
 
176
200
  return route.handle_request(*args, **kwargs)
@@ -207,6 +231,14 @@ def setup_dynamic_routes(
207
231
  api_verbs,
208
232
  insert_function_class_name,
209
233
  _update_function_class_name,
234
+ insert_function_name,
235
+ _update_function_name,
236
+ _get_function_name,
237
+ _list_function_name,
238
+ _delete_function_name,
239
+ _get_function_type_class_name,
240
+ _list_function_type_class_name,
241
+ _delete_function_type_class_name,
210
242
  ) = entity_loader.load_entity_source(
211
243
  entity_resource,
212
244
  tenant,
@@ -233,6 +265,7 @@ def setup_dynamic_routes(
233
265
  entity_class=etities_dict[entity_class_name],
234
266
  injector_factory=injector_factory,
235
267
  insert_function_type_class=insert_function_type_class,
268
+ insert_function_name=insert_function_name,
236
269
  )
237
270
 
238
271
  return route.handle_request(*args, **kwargs)
@@ -269,6 +302,14 @@ def setup_dynamic_routes(
269
302
  api_verbs,
270
303
  _insert_function_class_name,
271
304
  update_function_class_name,
305
+ _insert_function_name,
306
+ update_function_name,
307
+ _get_function_name,
308
+ _list_function_name,
309
+ _delete_function_name,
310
+ _get_function_type_class_name,
311
+ _list_function_type_class_name,
312
+ _delete_function_type_class_name,
272
313
  ) = entity_loader.load_entity_source(
273
314
  entity_resource,
274
315
  tenant,
@@ -295,6 +336,7 @@ def setup_dynamic_routes(
295
336
  entity_class=etities_dict[entity_class_name],
296
337
  injector_factory=injector_factory,
297
338
  update_function_type_class=update_function_type_class,
339
+ update_function_name=update_function_name,
298
340
  )
299
341
 
300
342
  return route.handle_request(*args, **kwargs)
@@ -331,6 +373,11 @@ def setup_dynamic_routes(
331
373
  api_verbs,
332
374
  _insert_function_class_name,
333
375
  _update_function_class_name,
376
+ _insert_function_name,
377
+ _update_function_name,
378
+ _get_function_name,
379
+ _list_function_name,
380
+ _delete_function_name,
334
381
  ) = entity_loader.load_entity_source(
335
382
  entity_resource,
336
383
  tenant,
@@ -386,6 +433,14 @@ def setup_dynamic_routes(
386
433
  api_verbs,
387
434
  _insert_function_class_name,
388
435
  _update_function_class_name,
436
+ _insert_function_name,
437
+ _update_function_name,
438
+ _get_function_name,
439
+ _list_function_name,
440
+ delete_function_name,
441
+ _get_function_type_class_name,
442
+ _list_function_type_class_name,
443
+ delete_function_type_class_name,
389
444
  ) = entity_loader.load_entity_source(
390
445
  entity_resource,
391
446
  tenant,
@@ -405,6 +460,10 @@ def setup_dynamic_routes(
405
460
  dto_class=etities_dict[dto_class_name],
406
461
  entity_class=etities_dict[entity_class_name],
407
462
  injector_factory=injector_factory,
463
+ delete_function_name=delete_function_name,
464
+ delete_function_type_class=etities_dict.get(
465
+ delete_function_type_class_name
466
+ ),
408
467
  )
409
468
 
410
469
  return route.handle_request(*args, **kwargs)
@@ -71,9 +71,20 @@ class EntityConfigWriter:
71
71
  "dto_class_name": compiler_result.dto_class_name,
72
72
  "entity_class_name": compiler_result.entity_class_name,
73
73
  "insert_function_class_name": compiler_result.insert_function_class_name,
74
+ "insert_function_name": compiler_result.insert_function_name,
74
75
  "source_insert_function": compiler_result.source_insert_function,
75
76
  "update_function_class_name": compiler_result.update_function_class_name,
77
+ "update_function_name": compiler_result.update_function_name,
76
78
  "source_update_function": compiler_result.source_update_function,
79
+ "get_function_name": compiler_result.get_function_name,
80
+ "list_function_name": compiler_result.list_function_name,
81
+ "delete_function_name": compiler_result.delete_function_name,
82
+ "get_function_type_class_name": compiler_result.get_function_type_class_name,
83
+ "list_function_type_class_name": compiler_result.list_function_type_class_name,
84
+ "delete_function_type_class_name": compiler_result.delete_function_type_class_name,
85
+ "source_get_function_type": compiler_result.source_get_function_type,
86
+ "source_list_function_type": compiler_result.source_list_function_type,
87
+ "source_delete_function_type": compiler_result.source_delete_function_type,
77
88
  "source_dto": compiler_result.dto_code,
78
89
  "source_entity": compiler_result.entity_code,
79
90
  "entity_hash": entity_hash
@@ -120,8 +131,16 @@ class EntityConfigWriter:
120
131
  for content in self._iter_hash_chunks(
121
132
  compiler_result.dto_code,
122
133
  compiler_result.entity_code,
134
+ compiler_result.insert_function_name,
135
+ compiler_result.update_function_name,
136
+ compiler_result.get_function_name,
137
+ compiler_result.list_function_name,
138
+ compiler_result.delete_function_name,
123
139
  compiler_result.source_insert_function,
124
140
  compiler_result.source_update_function,
141
+ compiler_result.source_get_function_type,
142
+ compiler_result.source_list_function_type,
143
+ compiler_result.source_delete_function_type,
125
144
  ):
126
145
  hasher.update(content)
127
146
 
@@ -26,6 +26,14 @@ class LoadedEntity:
26
26
  self.relations_dependencies: list[RelationDependency] = []
27
27
  self.insert_function_class_name: str | None = None
28
28
  self.update_function_class_name: str | None = None
29
+ self.insert_function_name: str | None = None
30
+ self.update_function_name: str | None = None
31
+ self.get_function_name: str | None = None
32
+ self.list_function_name: str | None = None
33
+ self.delete_function_name: str | None = None
34
+ self.get_function_type_class_name: str | None = None
35
+ self.list_function_type_class_name: str | None = None
36
+ self.delete_function_type_class_name: str | None = None
29
37
 
30
38
 
31
39
  class Namespace:
@@ -50,7 +58,23 @@ class EntityLoader:
50
58
  grupo_empresarial: str | None,
51
59
  escopo: str = ESCOPO_RESTLIB2,
52
60
  force_reload: bool = False,
53
- ) -> tuple[str, str, dict, bool, list[str], str | None, str | None]:
61
+ ) -> tuple[
62
+ str,
63
+ str,
64
+ dict,
65
+ bool,
66
+ list[str],
67
+ str | None,
68
+ str | None,
69
+ str | None,
70
+ str | None,
71
+ str | None,
72
+ str | None,
73
+ str | None,
74
+ str | None,
75
+ str | None,
76
+ str | None,
77
+ ]:
54
78
  # Assumind o escopo default se necessário
55
79
  if not escopo:
56
80
  escopo = ESCOPO_RESTLIB2
@@ -81,6 +105,20 @@ class EntityLoader:
81
105
  relations_dependencies = loaded_entity.relations_dependencies
82
106
  insert_function_class_name = loaded_entity.insert_function_class_name
83
107
  update_function_class_name = loaded_entity.update_function_class_name
108
+ insert_function_name = loaded_entity.insert_function_name
109
+ update_function_name = loaded_entity.update_function_name
110
+ get_function_name = loaded_entity.get_function_name
111
+ list_function_name = loaded_entity.list_function_name
112
+ delete_function_name = loaded_entity.delete_function_name
113
+ get_function_type_class_name = (
114
+ loaded_entity.get_function_type_class_name
115
+ )
116
+ list_function_type_class_name = (
117
+ loaded_entity.list_function_type_class_name
118
+ )
119
+ delete_function_type_class_name = (
120
+ loaded_entity.delete_function_type_class_name
121
+ )
84
122
 
85
123
  # Verificando se alguma de suas dependências precisariam ser recarregadas
86
124
  for rd in relations_dependencies:
@@ -128,6 +166,14 @@ class EntityLoader:
128
166
  api_verbs,
129
167
  insert_function_class_name,
130
168
  update_function_class_name,
169
+ insert_function_name,
170
+ update_function_name,
171
+ get_function_name,
172
+ list_function_name,
173
+ delete_function_name,
174
+ get_function_type_class_name,
175
+ list_function_type_class_name,
176
+ delete_function_type_class_name,
131
177
  )
132
178
 
133
179
  # Desempacotando resultado
@@ -151,6 +197,14 @@ class EntityLoader:
151
197
  api_verbs,
152
198
  insert_function_class_name,
153
199
  update_function_class_name,
200
+ insert_function_name,
201
+ update_function_name,
202
+ get_function_name,
203
+ list_function_name,
204
+ delete_function_name,
205
+ get_function_type_class_name,
206
+ list_function_type_class_name,
207
+ delete_function_type_class_name,
154
208
  )
155
209
  else:
156
210
  (
@@ -159,6 +213,16 @@ class EntityLoader:
159
213
  namespace,
160
214
  api_expose,
161
215
  api_verbs,
216
+ insert_function_class_name,
217
+ update_function_class_name,
218
+ insert_function_name,
219
+ update_function_name,
220
+ get_function_name,
221
+ list_function_name,
222
+ delete_function_name,
223
+ get_function_type_class_name,
224
+ list_function_type_class_name,
225
+ delete_function_type_class_name,
162
226
  ) = result_execute
163
227
  return (
164
228
  dto_class_name,
@@ -166,6 +230,16 @@ class EntityLoader:
166
230
  namespace.entities_dict,
167
231
  api_expose,
168
232
  api_verbs,
233
+ insert_function_class_name,
234
+ update_function_class_name,
235
+ insert_function_name,
236
+ update_function_name,
237
+ get_function_name,
238
+ list_function_name,
239
+ delete_function_name,
240
+ get_function_type_class_name,
241
+ list_function_type_class_name,
242
+ delete_function_type_class_name,
169
243
  )
170
244
  else:
171
245
  # Se não deu o intervalo de verificação do refresh, retorna o que está em memória
@@ -177,6 +251,14 @@ class EntityLoader:
177
251
  api_verbs,
178
252
  insert_function_class_name,
179
253
  update_function_class_name,
254
+ insert_function_name,
255
+ update_function_name,
256
+ get_function_name,
257
+ list_function_name,
258
+ delete_function_name,
259
+ get_function_type_class_name,
260
+ list_function_type_class_name,
261
+ delete_function_type_class_name,
180
262
  )
181
263
 
182
264
  # Se não conseguir recuperar a entidade, procura no redis:
@@ -213,6 +295,14 @@ class EntityLoader:
213
295
  api_verbs,
214
296
  insert_function_class_name,
215
297
  update_function_class_name,
298
+ insert_function_name,
299
+ update_function_name,
300
+ get_function_name,
301
+ list_function_name,
302
+ delete_function_name,
303
+ get_function_type_class_name,
304
+ list_function_type_class_name,
305
+ delete_function_type_class_name,
216
306
  ) = result_execute
217
307
 
218
308
  return (
@@ -223,6 +313,14 @@ class EntityLoader:
223
313
  api_verbs,
224
314
  insert_function_class_name,
225
315
  update_function_class_name,
316
+ insert_function_name,
317
+ update_function_name,
318
+ get_function_name,
319
+ list_function_name,
320
+ delete_function_name,
321
+ get_function_type_class_name,
322
+ list_function_type_class_name,
323
+ delete_function_type_class_name,
226
324
  )
227
325
 
228
326
  def clear_namespaces(self):
@@ -253,7 +351,23 @@ class EntityLoader:
253
351
  entity_config_key: str,
254
352
  entity_resource: str,
255
353
  check_refresh: bool = False,
256
- ) -> tuple[str, str, Namespace, bool, list[str], str | None, str | None] | None:
354
+ ) -> tuple[
355
+ str,
356
+ str,
357
+ Namespace,
358
+ bool,
359
+ list[str],
360
+ str | None,
361
+ str | None,
362
+ str | None,
363
+ str | None,
364
+ str | None,
365
+ str | None,
366
+ str | None,
367
+ str | None,
368
+ str | None,
369
+ str | None,
370
+ ] | None:
257
371
  # Interpretando o json de configuração da entidade
258
372
  try:
259
373
  entity_config = json.loads(entity_config_str)
@@ -270,11 +384,28 @@ class EntityLoader:
270
384
  insert_function_class_name = entity_config.get(
271
385
  "insert_function_class_name"
272
386
  )
387
+ insert_function_name = entity_config.get("insert_function_name")
273
388
  insert_function_code = entity_config.get("source_insert_function")
274
389
  update_function_class_name = entity_config.get(
275
390
  "update_function_class_name"
276
391
  )
392
+ update_function_name = entity_config.get("update_function_name")
277
393
  update_function_code = entity_config.get("source_update_function")
394
+ get_function_name = entity_config.get("get_function_name")
395
+ list_function_name = entity_config.get("list_function_name")
396
+ delete_function_name = entity_config.get("delete_function_name")
397
+ get_function_type_class_name = entity_config.get(
398
+ "get_function_type_class_name"
399
+ )
400
+ list_function_type_class_name = entity_config.get(
401
+ "list_function_type_class_name"
402
+ )
403
+ delete_function_type_class_name = entity_config.get(
404
+ "delete_function_type_class_name"
405
+ )
406
+ get_function_code = entity_config.get("source_get_function_type")
407
+ list_function_code = entity_config.get("source_list_function_type")
408
+ delete_function_code = entity_config.get("source_delete_function_type")
278
409
  relations_dependencies = [
279
410
  RelationDependency().from_dict(rd)
280
411
  for rd in entity_config.get("relations_dependencies", [])
@@ -371,6 +502,27 @@ class EntityLoader:
371
502
  "Update Function source",
372
503
  )
373
504
 
505
+ if get_function_code:
506
+ self._safe_exec(
507
+ get_function_code,
508
+ namespace.entities_dict,
509
+ "Get FunctionType source",
510
+ )
511
+
512
+ if list_function_code:
513
+ self._safe_exec(
514
+ list_function_code,
515
+ namespace.entities_dict,
516
+ "List FunctionType source",
517
+ )
518
+
519
+ if delete_function_code:
520
+ self._safe_exec(
521
+ delete_function_code,
522
+ namespace.entities_dict,
523
+ "Delete FunctionType source",
524
+ )
525
+
374
526
  self._safe_exec(source_entity, namespace.entities_dict, "Entity source")
375
527
  self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
376
528
 
@@ -384,6 +536,20 @@ class EntityLoader:
384
536
  loaded_entity.relations_dependencies = relations_dependencies
385
537
  loaded_entity.insert_function_class_name = insert_function_class_name
386
538
  loaded_entity.update_function_class_name = update_function_class_name
539
+ loaded_entity.insert_function_name = insert_function_name
540
+ loaded_entity.update_function_name = update_function_name
541
+ loaded_entity.get_function_name = get_function_name
542
+ loaded_entity.list_function_name = list_function_name
543
+ loaded_entity.delete_function_name = delete_function_name
544
+ loaded_entity.get_function_type_class_name = (
545
+ get_function_type_class_name
546
+ )
547
+ loaded_entity.list_function_type_class_name = (
548
+ list_function_type_class_name
549
+ )
550
+ loaded_entity.delete_function_type_class_name = (
551
+ delete_function_type_class_name
552
+ )
387
553
 
388
554
  namespace.loaded_entities[entity_resource] = loaded_entity
389
555
 
@@ -395,6 +561,14 @@ class EntityLoader:
395
561
  api_verbs,
396
562
  insert_function_class_name,
397
563
  update_function_class_name,
564
+ insert_function_name,
565
+ update_function_name,
566
+ get_function_name,
567
+ list_function_name,
568
+ delete_function_name,
569
+ get_function_type_class_name,
570
+ list_function_type_class_name,
571
+ delete_function_type_class_name,
398
572
  )
399
573
 
400
574
  def _safe_exec(self, source_code, context, description):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.31
3
+ Version: 0.0.33
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
@@ -12,7 +12,7 @@ Classifier: Topic :: Software Development :: Libraries
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Requires-Python: >=3.4
14
14
  Description-Content-Type: text/markdown
15
- Requires-Dist: nsj-rest-lib<6.0.0,>=5.1.3
15
+ Requires-Dist: nsj-rest-lib<7.0.0,>=5.1.3
16
16
  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
@@ -15,7 +15,8 @@ setup.cfg
15
15
  ./nsj_rest_lib2/compiler/compiler_structures.py
16
16
  ./nsj_rest_lib2/compiler/dto_compiler.py
17
17
  ./nsj_rest_lib2/compiler/entity_compiler.py
18
- ./nsj_rest_lib2/compiler/function_compiler.py
18
+ ./nsj_rest_lib2/compiler/function_get_delete_compiler.py
19
+ ./nsj_rest_lib2/compiler/function_insert_update_compiler.py
19
20
  ./nsj_rest_lib2/compiler/function_model.py
20
21
  ./nsj_rest_lib2/compiler/migration_compiler.py
21
22
  ./nsj_rest_lib2/compiler/migration_compiler_alter_table.py
@@ -59,7 +60,8 @@ nsj_rest_lib2/compiler/compiler.py
59
60
  nsj_rest_lib2/compiler/compiler_structures.py
60
61
  nsj_rest_lib2/compiler/dto_compiler.py
61
62
  nsj_rest_lib2/compiler/entity_compiler.py
62
- nsj_rest_lib2/compiler/function_compiler.py
63
+ nsj_rest_lib2/compiler/function_get_delete_compiler.py
64
+ nsj_rest_lib2/compiler/function_insert_update_compiler.py
63
65
  nsj_rest_lib2/compiler/function_model.py
64
66
  nsj_rest_lib2/compiler/migration_compiler.py
65
67
  nsj_rest_lib2/compiler/migration_compiler_alter_table.py
@@ -89,4 +91,5 @@ nsj_rest_lib2/controller/dynamic_controller.py
89
91
  nsj_rest_lib2/service/__init__.py
90
92
  nsj_rest_lib2/service/entity_config_writer.py
91
93
  nsj_rest_lib2/service/entity_loader.py
94
+ tests/test_function_handler_compilation.py
92
95
  tests/test_migration_generation.py
@@ -1,4 +1,4 @@
1
- nsj-rest-lib<6.0.0,>=5.1.3
1
+ nsj-rest-lib<7.0.0,>=5.1.3
2
2
  redis<7.0.0,>=6.4.0
3
3
  nsj-multi-database-lib<3.0.0,>=2.0.1
4
4
  pydantic<3.0.0,>=2.11.9
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = nsj_rest_lib2
3
- version = 0.0.31
3
+ version = 0.0.33
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).
@@ -21,7 +21,7 @@ package_dir =
21
21
  packages = find:
22
22
  python_requires = >=3.4
23
23
  install_requires =
24
- nsj-rest-lib>=5.1.3,<6.0.0
24
+ nsj-rest-lib>=5.1.3,<7.0.0
25
25
  redis>=6.4.0,<7.0.0
26
26
  nsj-multi-database-lib>=2.0.1,<3.0.0
27
27
  pydantic>=2.11.9,<3.0.0
@@ -0,0 +1,152 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ ROOT_DIR = Path(__file__).resolve().parents[1]
7
+ if str(ROOT_DIR) not in sys.path:
8
+ sys.path.insert(0, str(ROOT_DIR))
9
+
10
+ os.environ.setdefault("ESCOPO_RESTLIB2", "test-scope")
11
+
12
+ from nsj_rest_lib2.compiler.compiler import EDLCompiler
13
+ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
14
+ from nsj_rest_lib2.compiler.edl_model.entity_model_root import EntityModelRoot
15
+ from nsj_rest_lib2.compiler.util.type_naming_util import (
16
+ compile_function_class_name,
17
+ )
18
+
19
+
20
+ def _load_entity(path: Path):
21
+ with path.open("r") as fp:
22
+ edl_data = json.load(fp)
23
+
24
+ model = (
25
+ EntityModelRoot(**edl_data)
26
+ if edl_data.get("mixin", False)
27
+ else EntityModel(**edl_data)
28
+ )
29
+ complete_id = f"{model.escopo}/{model.id}"
30
+ return complete_id, model
31
+
32
+
33
+ def _load_entity_models(base_dir: Path) -> dict[str, EntityModel]:
34
+ models: dict[str, EntityModel] = {}
35
+ for path in base_dir.iterdir():
36
+ if path.suffix.lower() != ".json":
37
+ continue
38
+ key, model = _load_entity(path)
39
+ models[key] = model
40
+ return models
41
+
42
+
43
+ def test_compile_handlers_for_classificacao_financeira_generates_delete_function_type():
44
+ base_dir = ROOT_DIR / "@schemas_test"
45
+ entity_models = _load_entity_models(base_dir)
46
+
47
+ model_key = "financas/ClassificacaoFinanceira"
48
+ entity_model = entity_models[model_key]
49
+
50
+ compiler = EDLCompiler()
51
+ result = compiler.compile_model(entity_model, list(entity_models.items()))
52
+
53
+ assert result is not None
54
+
55
+ # Nomes das funções de banco para post/put/delete
56
+ assert result.insert_function_name == "financas.api_classificacaofinanceiranovo"
57
+ assert result.update_function_name == "financas.api_classificacaofinanceiraalterar"
58
+ assert result.delete_function_name == "financas.api_classificacaofinanceiraexcluir"
59
+
60
+ # FunctionType para delete
61
+ delete_code = result.source_delete_function_type
62
+ assert delete_code is not None
63
+
64
+ expected_class = compile_function_class_name(entity_model.id, "", [], "delete")
65
+ assert result.delete_function_type_class_name == expected_class
66
+ assert f"class {expected_class}" in delete_code
67
+ # Campos esperados (derivados do mapping do handler de delete)
68
+ assert "classificacao: uuid.UUID = FunctionField(" in delete_code
69
+ assert "grupoempresarial: uuid.UUID = FunctionField(" in delete_code
70
+
71
+
72
+ def test_compile_handlers_generates_get_and_list_function_types_from_edl():
73
+ edl_json = {
74
+ "edl_version": "1.0",
75
+ "escopo": "test",
76
+ "description": "Entidade de teste para funções GET/LIST.",
77
+ "id": "Foo",
78
+ "version": "1.0",
79
+ "properties": {
80
+ "id": {"type": "uuid", "pk": True},
81
+ "codigo": {"type": "string"},
82
+ },
83
+ "repository": {
84
+ "map": "test.foo",
85
+ "shared_table": False,
86
+ "properties": {
87
+ "id": {"column": "id"},
88
+ "codigo": {"column": "codigo"},
89
+ },
90
+ "indexes": [],
91
+ },
92
+ "api": {
93
+ "resource": "foos",
94
+ "expose": True,
95
+ "verbs": ["GET"],
96
+ "handlers": {
97
+ "get": {
98
+ "impl": "pg_function",
99
+ "function_ref": "test.fn_foo_get",
100
+ "call": {
101
+ "arg_binding": {
102
+ "type_name": "test.tfoo_get",
103
+ "mapping": [
104
+ {"attr": "id_func", "from": "path.id"},
105
+ {"attr": "tenant", "from": "args.tenant"},
106
+ ],
107
+ }
108
+ },
109
+ "result": {"expected": "entity_row"},
110
+ },
111
+ "list": {
112
+ "impl": "pg_function",
113
+ "function_ref": "test.fn_foo_list",
114
+ "call": {
115
+ "arg_binding": {
116
+ "type_name": "test.tfoo_list",
117
+ "mapping": [
118
+ {"attr": "tenant", "from": "args.tenant"},
119
+ {"attr": "search", "from": "args.search"},
120
+ ],
121
+ }
122
+ },
123
+ "result": {"expected": "entity_row"},
124
+ },
125
+ },
126
+ },
127
+ }
128
+
129
+ compiler = EDLCompiler()
130
+ result = compiler.compile_model_from_edl(edl_json, [])
131
+
132
+ assert result is not None
133
+ assert result.get_function_name == "test.fn_foo_get"
134
+ assert result.list_function_name == "test.fn_foo_list"
135
+
136
+ get_code = result.source_get_function_type
137
+ list_code = result.source_list_function_type
138
+ assert get_code is not None
139
+ assert list_code is not None
140
+
141
+ expected_get_class = compile_function_class_name("Foo", "", [], "get")
142
+ expected_list_class = compile_function_class_name("Foo", "", [], "list")
143
+
144
+ assert result.get_function_type_class_name == expected_get_class
145
+ assert result.list_function_type_class_name == expected_list_class
146
+ assert f"class {expected_get_class}" in get_code
147
+ assert "id_func: uuid.UUID = FunctionField(" in get_code
148
+ assert "tenant: Any = FunctionField(" in get_code
149
+
150
+ assert f"class {expected_list_class}" in list_code
151
+ assert "tenant: Any = FunctionField(" in list_code
152
+ assert "search: Any = FunctionField(" in list_code
File without changes