nsj-rest-lib2 0.0.25__py3-none-any.whl → 0.0.27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,7 @@ from nsj_rest_lib2.compiler.edl_model.primitives import (
18
18
  STR_BASED_TYPES,
19
19
  )
20
20
  from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
21
+ from nsj_rest_lib2.compiler.function_model import FunctionBindingConfig
21
22
  from nsj_rest_lib2.compiler.edl_model.trait_property_meta_model import (
22
23
  TraitPropertyMetaModel,
23
24
  )
@@ -41,6 +42,7 @@ class EDLPropertyCompiler:
41
42
  entity_model: EntityModelBase,
42
43
  entity_models: dict[str, EntityModel],
43
44
  prefx_class_name: str,
45
+ function_bindings: FunctionBindingConfig | None = None,
44
46
  ) -> tuple[
45
47
  list[ast.stmt],
46
48
  list[ast.stmt],
@@ -76,6 +78,9 @@ class EDLPropertyCompiler:
76
78
 
77
79
  # pk_key = pk_keys[0]
78
80
 
81
+ function_bindings = function_bindings or FunctionBindingConfig()
82
+ self._function_bindings = function_bindings
83
+
79
84
  # Instanciando as listas de retorno
80
85
  ast_dto_attributes = []
81
86
  ast_entity_attributes = []
@@ -433,6 +438,50 @@ class EDLPropertyCompiler:
433
438
  ),
434
439
  ]
435
440
 
441
+ insert_binding = self._function_bindings.insert_relations.get(pkey)
442
+ if insert_binding:
443
+ if (
444
+ insert_binding.field_name
445
+ and insert_binding.field_name != CompilerStrUtil.to_snake_case(pkey)
446
+ ):
447
+ keywords.append(
448
+ ast.keyword(
449
+ arg="insert_function_field",
450
+ value=ast.Constant(value=insert_binding.field_name),
451
+ )
452
+ )
453
+ if insert_binding.function_type_class:
454
+ keywords.append(
455
+ ast.keyword(
456
+ arg="insert_function_type",
457
+ value=ast.Name(
458
+ id=insert_binding.function_type_class, ctx=ast.Load()
459
+ ),
460
+ )
461
+ )
462
+
463
+ update_binding = self._function_bindings.update_relations.get(pkey)
464
+ if update_binding:
465
+ if (
466
+ update_binding.field_name
467
+ and update_binding.field_name != CompilerStrUtil.to_snake_case(pkey)
468
+ ):
469
+ keywords.append(
470
+ ast.keyword(
471
+ arg="update_function_field",
472
+ value=ast.Constant(value=update_binding.field_name),
473
+ )
474
+ )
475
+ if update_binding.function_type_class:
476
+ keywords.append(
477
+ ast.keyword(
478
+ arg="update_function_type",
479
+ value=ast.Name(
480
+ id=update_binding.function_type_class, ctx=ast.Load()
481
+ ),
482
+ )
483
+ )
484
+
436
485
  # Tratando das opções básicas do descritor de propriedade
437
486
  if properties_structure.required and pkey in properties_structure.required:
438
487
  keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
@@ -518,6 +567,50 @@ class EDLPropertyCompiler:
518
567
  ),
519
568
  ]
520
569
 
570
+ insert_binding = self._function_bindings.insert_relations.get(pkey)
571
+ if insert_binding:
572
+ if (
573
+ insert_binding.field_name
574
+ and insert_binding.field_name != CompilerStrUtil.to_snake_case(pkey)
575
+ ):
576
+ keywords.append(
577
+ ast.keyword(
578
+ arg="insert_function_field",
579
+ value=ast.Constant(value=insert_binding.field_name),
580
+ )
581
+ )
582
+ if insert_binding.function_type_class:
583
+ keywords.append(
584
+ ast.keyword(
585
+ arg="insert_function_type",
586
+ value=ast.Name(
587
+ id=insert_binding.function_type_class, ctx=ast.Load()
588
+ ),
589
+ )
590
+ )
591
+
592
+ update_binding = self._function_bindings.update_relations.get(pkey)
593
+ if update_binding:
594
+ if (
595
+ update_binding.field_name
596
+ and update_binding.field_name != CompilerStrUtil.to_snake_case(pkey)
597
+ ):
598
+ keywords.append(
599
+ ast.keyword(
600
+ arg="update_function_field",
601
+ value=ast.Constant(value=update_binding.field_name),
602
+ )
603
+ )
604
+ if update_binding.function_type_class:
605
+ keywords.append(
606
+ ast.keyword(
607
+ arg="update_function_type",
608
+ value=ast.Name(
609
+ id=update_binding.function_type_class, ctx=ast.Load()
610
+ ),
611
+ )
612
+ )
613
+
521
614
  # Tratando das opções básicas do descritor de propriedade
522
615
  if properties_structure.required and pkey in properties_structure.required:
523
616
  keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
@@ -861,6 +954,24 @@ class EDLPropertyCompiler:
861
954
  )
862
955
  )
863
956
 
957
+ insert_binding = self._function_bindings.insert_fields.get(pkey)
958
+ if insert_binding:
959
+ keywords.append(
960
+ ast.keyword(
961
+ arg="insert_function_field",
962
+ value=ast.Constant(value=insert_binding),
963
+ )
964
+ )
965
+
966
+ update_binding = self._function_bindings.update_fields.get(pkey)
967
+ if update_binding:
968
+ keywords.append(
969
+ ast.keyword(
970
+ arg="update_function_field",
971
+ value=ast.Constant(value=update_binding),
972
+ )
973
+ )
974
+
864
975
  # Resolvendo o nome da propriedade no Entity
865
976
  if (
866
977
  properties_structure.entity_properties
@@ -1,4 +1,5 @@
1
1
  import uuid
2
+ from typing import Iterable
2
3
 
3
4
  from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
4
5
 
@@ -19,3 +20,24 @@ def compile_dto_class_name(entity_id: str, prefx_class_name: str = "") -> str:
19
20
 
20
21
  def compile_entity_class_name(entity_id: str, prefx_class_name: str = "") -> str:
21
22
  return f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}{CompilerStrUtil.to_pascal_case(entity_id)}Entity"
23
+
24
+
25
+ def compile_function_class_name(
26
+ entity_id: str,
27
+ prefx_class_name: str,
28
+ path_parts: Iterable[str],
29
+ operation: str,
30
+ ) -> str:
31
+ """
32
+ Gera o nome da classe para FunctionTypes (insert/update),
33
+ incorporando o caminho dos relacionamentos (quando houver).
34
+ """
35
+
36
+ suffix = "InsertType" if operation == "insert" else "UpdateType"
37
+ path_suffix = "".join(CompilerStrUtil.to_pascal_case(part) for part in path_parts)
38
+ return (
39
+ f"{CompilerStrUtil.to_pascal_case(prefx_class_name)}"
40
+ f"{CompilerStrUtil.to_pascal_case(entity_id)}"
41
+ f"{path_suffix}"
42
+ f"{suffix}"
43
+ )
@@ -22,11 +22,12 @@ from nsj_rest_lib2.exception import MissingEntityConfigException
22
22
  from nsj_rest_lib2.service.entity_loader import EntityLoader
23
23
 
24
24
 
25
- def _get_tenant_grupo() -> tuple[str, str]:
25
+ def _get_query_args() -> tuple[str, str, bool]:
26
26
  # Tentando ler do query args
27
27
  query_args = request.args
28
28
  tenant = query_args.get("tenant")
29
29
  grupo_empresarial = query_args.get("grupo_empresarial")
30
+ force_reload = query_args.get("force_reload", "false").lower() == "true"
30
31
 
31
32
  # Tentando ler do corpo da requisição
32
33
  try:
@@ -40,7 +41,7 @@ def _get_tenant_grupo() -> tuple[str, str]:
40
41
  except:
41
42
  pass
42
43
 
43
- return (str(tenant), str(grupo_empresarial))
44
+ return (str(tenant), str(grupo_empresarial), force_reload)
44
45
 
45
46
 
46
47
  def _endpoint_name(func: Any, multidb: bool, root: str) -> str:
@@ -81,13 +82,8 @@ def setup_dynamic_routes(
81
82
  if "entity_escopo" in kwargs:
82
83
  entity_escopo = kwargs.pop("entity_escopo")
83
84
 
84
- # Verificando se foi solicitado reload forçado do código
85
- force_reload = False
86
- if "force_reload" in kwargs:
87
- force_reload = kwargs.pop("force_reload").lower() == "true"
88
-
89
85
  # Lendo tenant e grupo_empresarial
90
- tenant, grupo_empresarial = _get_tenant_grupo()
86
+ tenant, grupo_empresarial, force_reload = _get_query_args()
91
87
 
92
88
  try:
93
89
  # Recuperando o código do DTO e Entity correspondente
@@ -98,6 +94,8 @@ def setup_dynamic_routes(
98
94
  etities_dict,
99
95
  api_expose,
100
96
  api_verbs,
97
+ _insert_function_class_name,
98
+ _update_function_class_name,
101
99
  ) = entity_loader.load_entity_source(
102
100
  entity_resource,
103
101
  tenant,
@@ -140,13 +138,8 @@ def setup_dynamic_routes(
140
138
  if "entity_escopo" in kwargs:
141
139
  entity_escopo = kwargs.pop("entity_escopo")
142
140
 
143
- # Verificando se foi solicitado reload forçado do código
144
- force_reload = False
145
- if "force_reload" in kwargs:
146
- force_reload = kwargs.pop("force_reload").lower() == "true"
147
-
148
141
  # Lendo tenant e grupo_empresarial
149
- tenant, grupo_empresarial = _get_tenant_grupo()
142
+ tenant, grupo_empresarial, force_reload = _get_query_args()
150
143
 
151
144
  try:
152
145
  # Recuperando o código do DTO e Entity correspondente
@@ -157,6 +150,8 @@ def setup_dynamic_routes(
157
150
  etities_dict,
158
151
  api_expose,
159
152
  api_verbs,
153
+ _insert_function_class_name,
154
+ _update_function_class_name,
160
155
  ) = entity_loader.load_entity_source(
161
156
  entity_resource,
162
157
  tenant,
@@ -198,13 +193,8 @@ def setup_dynamic_routes(
198
193
  if "entity_escopo" in kwargs:
199
194
  entity_escopo = kwargs.pop("entity_escopo")
200
195
 
201
- # Verificando se foi solicitado reload forçado do código
202
- force_reload = False
203
- if "force_reload" in kwargs:
204
- force_reload = kwargs.pop("force_reload").lower() == "true"
205
-
206
196
  # Lendo tenant e grupo_empresarial
207
- tenant, grupo_empresarial = _get_tenant_grupo()
197
+ tenant, grupo_empresarial, force_reload = _get_query_args()
208
198
 
209
199
  try:
210
200
  # Recuperando o código do DTO e Entity correspondente
@@ -215,6 +205,8 @@ def setup_dynamic_routes(
215
205
  etities_dict,
216
206
  api_expose,
217
207
  api_verbs,
208
+ insert_function_class_name,
209
+ _update_function_class_name,
218
210
  ) = entity_loader.load_entity_source(
219
211
  entity_resource,
220
212
  tenant,
@@ -228,12 +220,19 @@ def setup_dynamic_routes(
228
220
  return ("", 405, {})
229
221
 
230
222
  # Executando o list pelo RestLib
223
+ insert_function_type_class = (
224
+ etities_dict.get(insert_function_class_name)
225
+ if insert_function_class_name
226
+ else None
227
+ )
228
+
231
229
  route = PostRoute(
232
230
  url=COLLECTION_DYNAMIC_ROUTE,
233
231
  http_method="POST",
234
232
  dto_class=etities_dict[dto_class_name],
235
233
  entity_class=etities_dict[entity_class_name],
236
234
  injector_factory=injector_factory,
235
+ insert_function_type_class=insert_function_type_class,
237
236
  )
238
237
 
239
238
  return route.handle_request(*args, **kwargs)
@@ -256,13 +255,8 @@ def setup_dynamic_routes(
256
255
  if "entity_escopo" in kwargs:
257
256
  entity_escopo = kwargs.pop("entity_escopo")
258
257
 
259
- # Verificando se foi solicitado reload forçado do código
260
- force_reload = False
261
- if "force_reload" in kwargs:
262
- force_reload = kwargs.pop("force_reload").lower() == "true"
263
-
264
258
  # Lendo tenant e grupo_empresarial
265
- tenant, grupo_empresarial = _get_tenant_grupo()
259
+ tenant, grupo_empresarial, force_reload = _get_query_args()
266
260
 
267
261
  try:
268
262
  # Recuperando o código do DTO e Entity correspondente
@@ -273,6 +267,8 @@ def setup_dynamic_routes(
273
267
  etities_dict,
274
268
  api_expose,
275
269
  api_verbs,
270
+ _insert_function_class_name,
271
+ update_function_class_name,
276
272
  ) = entity_loader.load_entity_source(
277
273
  entity_resource,
278
274
  tenant,
@@ -286,12 +282,19 @@ def setup_dynamic_routes(
286
282
  return ("", 405, {})
287
283
 
288
284
  # Executando o list pelo RestLib
285
+ update_function_type_class = (
286
+ etities_dict.get(update_function_class_name)
287
+ if update_function_class_name
288
+ else None
289
+ )
290
+
289
291
  route = PutRoute(
290
292
  url=ONE_DYNAMIC_ROUTE,
291
293
  http_method="PUT",
292
294
  dto_class=etities_dict[dto_class_name],
293
295
  entity_class=etities_dict[entity_class_name],
294
296
  injector_factory=injector_factory,
297
+ update_function_type_class=update_function_type_class,
295
298
  )
296
299
 
297
300
  return route.handle_request(*args, **kwargs)
@@ -314,13 +317,8 @@ def setup_dynamic_routes(
314
317
  if "entity_escopo" in kwargs:
315
318
  entity_escopo = kwargs.pop("entity_escopo")
316
319
 
317
- # Verificando se foi solicitado reload forçado do código
318
- force_reload = False
319
- if "force_reload" in kwargs:
320
- force_reload = kwargs.pop("force_reload").lower() == "true"
321
-
322
320
  # Lendo tenant e grupo_empresarial
323
- tenant, grupo_empresarial = _get_tenant_grupo()
321
+ tenant, grupo_empresarial, force_reload = _get_query_args()
324
322
 
325
323
  try:
326
324
  # Recuperando o código do DTO e Entity correspondente
@@ -331,6 +329,8 @@ def setup_dynamic_routes(
331
329
  etities_dict,
332
330
  api_expose,
333
331
  api_verbs,
332
+ _insert_function_class_name,
333
+ _update_function_class_name,
334
334
  ) = entity_loader.load_entity_source(
335
335
  entity_resource,
336
336
  tenant,
@@ -372,13 +372,8 @@ def setup_dynamic_routes(
372
372
  if "entity_escopo" in kwargs:
373
373
  entity_escopo = kwargs.pop("entity_escopo")
374
374
 
375
- # Verificando se foi solicitado reload forçado do código
376
- force_reload = False
377
- if "force_reload" in kwargs:
378
- force_reload = kwargs.pop("force_reload").lower() == "true"
379
-
380
375
  # Lendo tenant e grupo_empresarial
381
- tenant, grupo_empresarial = _get_tenant_grupo()
376
+ tenant, grupo_empresarial, force_reload = _get_query_args()
382
377
 
383
378
  try:
384
379
  # Recuperando o código do DTO e Entity correspondente
@@ -389,6 +384,8 @@ def setup_dynamic_routes(
389
384
  etities_dict,
390
385
  api_expose,
391
386
  api_verbs,
387
+ _insert_function_class_name,
388
+ _update_function_class_name,
392
389
  ) = entity_loader.load_entity_source(
393
390
  entity_resource,
394
391
  tenant,
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from typing import Any, Dict, Iterator, Optional
6
+
7
+ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
8
+ from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
9
+ from nsj_rest_lib2.compiler.model import CompilerResult
10
+ from nsj_rest_lib2.compiler.util.type_naming_util import compile_namespace_keys
11
+ from nsj_rest_lib2.redis_config import set_redis
12
+ from nsj_rest_lib2.settings import ESCOPO_RESTLIB2
13
+
14
+
15
+ class EntityConfigWriter:
16
+ """
17
+ Serializa o resultado da compilação e publica no Redis usando o mesmo
18
+ layout consumido pelo EntityLoader.
19
+ """
20
+
21
+ def __init__(self, escopo: str = ESCOPO_RESTLIB2) -> None:
22
+ self._escopo = escopo or ESCOPO_RESTLIB2
23
+
24
+ def publish(
25
+ self,
26
+ entity_model: EntityModelBase,
27
+ compiler_result: CompilerResult,
28
+ *,
29
+ entity_hash: str | None = None,
30
+ ) -> Dict[str, Any]:
31
+ """
32
+ Serializa e grava o resultado da compilação no Redis.
33
+
34
+ :return: Payload serializado (útil para inspeção/testes).
35
+ """
36
+
37
+ payload = self.build_payload(
38
+ entity_model, compiler_result, entity_hash=entity_hash
39
+ )
40
+ namespace_key = self._resolve_namespace_key(entity_model)
41
+ resource = self._resolve_resource(entity_model)
42
+
43
+ set_redis(
44
+ "entity_config",
45
+ self._escopo,
46
+ namespace_key,
47
+ resource,
48
+ json.dumps(payload, ensure_ascii=False),
49
+ )
50
+
51
+ return payload
52
+
53
+ def build_payload(
54
+ self,
55
+ entity_model: EntityModelBase,
56
+ compiler_result: CompilerResult,
57
+ *,
58
+ entity_hash: str | None = None,
59
+ ) -> Dict[str, Any]:
60
+ """
61
+ Constrói o dicionário serializável que representa a entidade compilada.
62
+ """
63
+
64
+ resource = self._resolve_resource(entity_model)
65
+ relations = [
66
+ rd.to_dict()
67
+ for rd in (compiler_result.relations_dependencies or [])
68
+ ]
69
+
70
+ return {
71
+ "dto_class_name": compiler_result.dto_class_name,
72
+ "entity_class_name": compiler_result.entity_class_name,
73
+ "insert_function_class_name": compiler_result.insert_function_class_name,
74
+ "source_insert_function": compiler_result.source_insert_function,
75
+ "update_function_class_name": compiler_result.update_function_class_name,
76
+ "source_update_function": compiler_result.source_update_function,
77
+ "source_dto": compiler_result.dto_code,
78
+ "source_entity": compiler_result.entity_code,
79
+ "entity_hash": entity_hash
80
+ if entity_hash is not None
81
+ else self._build_entity_hash(compiler_result),
82
+ "api_expose": compiler_result.api_expose,
83
+ "api_resource": resource,
84
+ "api_verbs": compiler_result.api_verbs or [],
85
+ "relations_dependencies": relations,
86
+ }
87
+
88
+ def _resolve_namespace_key(self, entity_model: EntityModelBase) -> str:
89
+ tenant = getattr(entity_model, "tenant", None)
90
+ grupo_empresarial = getattr(entity_model, "grupo_empresarial", None)
91
+
92
+ grupo_key, tenant_key, default_key = compile_namespace_keys(
93
+ tenant,
94
+ grupo_empresarial,
95
+ )
96
+
97
+ if (
98
+ tenant
99
+ and tenant != 0
100
+ and grupo_empresarial
101
+ and str(grupo_empresarial) != "00000000-0000-0000-0000-000000000000"
102
+ ):
103
+ return grupo_key
104
+ if tenant and tenant != 0:
105
+ return tenant_key
106
+ return default_key
107
+
108
+ def _resolve_resource(self, entity_model: EntityModelBase) -> str:
109
+ if not isinstance(entity_model, EntityModel) or not entity_model.api:
110
+ raise ValueError(
111
+ "EntityModel precisa possuir bloco de API para publicação no Redis."
112
+ )
113
+ if not entity_model.api.resource:
114
+ raise ValueError("api.resource é obrigatório para publicação no Redis.")
115
+ return entity_model.api.resource
116
+
117
+ def _build_entity_hash(self, compiler_result: CompilerResult) -> str:
118
+ hasher = hashlib.sha256()
119
+
120
+ for content in self._iter_hash_chunks(
121
+ compiler_result.dto_code,
122
+ compiler_result.entity_code,
123
+ compiler_result.source_insert_function,
124
+ compiler_result.source_update_function,
125
+ ):
126
+ hasher.update(content)
127
+
128
+ return hasher.hexdigest()
129
+
130
+ @staticmethod
131
+ def _iter_hash_chunks(*chunks: Optional[str]) -> Iterator[bytes]:
132
+ for chunk in chunks:
133
+ if chunk:
134
+ yield chunk.encode("utf-8")
@@ -24,6 +24,8 @@ class LoadedEntity:
24
24
  self.api_expose: bool = False
25
25
  self.api_verbs: list[str] = []
26
26
  self.relations_dependencies: list[RelationDependency] = []
27
+ self.insert_function_class_name: str | None = None
28
+ self.update_function_class_name: str | None = None
27
29
 
28
30
 
29
31
  class Namespace:
@@ -48,7 +50,7 @@ class EntityLoader:
48
50
  grupo_empresarial: str | None,
49
51
  escopo: str = ESCOPO_RESTLIB2,
50
52
  force_reload: bool = False,
51
- ) -> tuple[str, str, dict, bool, list[str]]:
53
+ ) -> tuple[str, str, dict, bool, list[str], str | None, str | None]:
52
54
  # Assumind o escopo default se necessário
53
55
  if not escopo:
54
56
  escopo = ESCOPO_RESTLIB2
@@ -77,6 +79,8 @@ class EntityLoader:
77
79
  api_expose = loaded_entity.api_expose
78
80
  api_verbs = loaded_entity.api_verbs
79
81
  relations_dependencies = loaded_entity.relations_dependencies
82
+ insert_function_class_name = loaded_entity.insert_function_class_name
83
+ update_function_class_name = loaded_entity.update_function_class_name
80
84
 
81
85
  # Verificando se alguma de suas dependências precisariam ser recarregadas
82
86
  for rd in relations_dependencies:
@@ -122,6 +126,8 @@ class EntityLoader:
122
126
  entities_dict,
123
127
  api_expose,
124
128
  api_verbs,
129
+ insert_function_class_name,
130
+ update_function_class_name,
125
131
  )
126
132
 
127
133
  # Desempacotando resultado
@@ -143,6 +149,8 @@ class EntityLoader:
143
149
  entities_dict,
144
150
  api_expose,
145
151
  api_verbs,
152
+ insert_function_class_name,
153
+ update_function_class_name,
146
154
  )
147
155
  else:
148
156
  (
@@ -167,6 +175,8 @@ class EntityLoader:
167
175
  entities_dict,
168
176
  api_expose,
169
177
  api_verbs,
178
+ insert_function_class_name,
179
+ update_function_class_name,
170
180
  )
171
181
 
172
182
  # Se não conseguir recuperar a entidade, procura no redis:
@@ -195,9 +205,15 @@ class EntityLoader:
195
205
  raise RuntimeError(
196
206
  f"Erro desconhecido carregando entidade: {entity_resource}"
197
207
  )
198
- dto_class_name, entity_class_name, namespace, api_expose, api_verbs = (
199
- result_execute
200
- )
208
+ (
209
+ dto_class_name,
210
+ entity_class_name,
211
+ namespace,
212
+ api_expose,
213
+ api_verbs,
214
+ insert_function_class_name,
215
+ update_function_class_name,
216
+ ) = result_execute
201
217
 
202
218
  return (
203
219
  dto_class_name,
@@ -205,6 +221,8 @@ class EntityLoader:
205
221
  namespace.entities_dict,
206
222
  api_expose,
207
223
  api_verbs,
224
+ insert_function_class_name,
225
+ update_function_class_name,
208
226
  )
209
227
 
210
228
  def clear_namespaces(self):
@@ -235,7 +253,7 @@ class EntityLoader:
235
253
  entity_config_key: str,
236
254
  entity_resource: str,
237
255
  check_refresh: bool = False,
238
- ) -> tuple[str, str, Namespace, bool, list[str]] | None:
256
+ ) -> tuple[str, str, Namespace, bool, list[str], str | None, str | None] | None:
239
257
  # Interpretando o json de configuração da entidade
240
258
  try:
241
259
  entity_config = json.loads(entity_config_str)
@@ -249,6 +267,14 @@ class EntityLoader:
249
267
  api_expose = entity_config["api_expose"]
250
268
  # api_resource = entity_config["api_resource"]
251
269
  api_verbs = entity_config["api_verbs"]
270
+ insert_function_class_name = entity_config.get(
271
+ "insert_function_class_name"
272
+ )
273
+ insert_function_code = entity_config.get("source_insert_function")
274
+ update_function_class_name = entity_config.get(
275
+ "update_function_class_name"
276
+ )
277
+ update_function_code = entity_config.get("source_update_function")
252
278
  relations_dependencies = [
253
279
  RelationDependency().from_dict(rd)
254
280
  for rd in entity_config.get("relations_dependencies", [])
@@ -331,6 +357,20 @@ class EntityLoader:
331
357
  get_logger().debug(f"Entity source:\n{source_entity}")
332
358
  get_logger().debug(f"DTO source:\n{source_dto}")
333
359
 
360
+ if insert_function_code:
361
+ self._safe_exec(
362
+ insert_function_code,
363
+ namespace.entities_dict,
364
+ "Insert Function source",
365
+ )
366
+
367
+ if update_function_code:
368
+ self._safe_exec(
369
+ update_function_code,
370
+ namespace.entities_dict,
371
+ "Update Function source",
372
+ )
373
+
334
374
  self._safe_exec(source_entity, namespace.entities_dict, "Entity source")
335
375
  self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
336
376
 
@@ -342,10 +382,20 @@ class EntityLoader:
342
382
  loaded_entity.api_expose = api_expose
343
383
  loaded_entity.api_verbs = api_verbs
344
384
  loaded_entity.relations_dependencies = relations_dependencies
385
+ loaded_entity.insert_function_class_name = insert_function_class_name
386
+ loaded_entity.update_function_class_name = update_function_class_name
345
387
 
346
388
  namespace.loaded_entities[entity_resource] = loaded_entity
347
389
 
348
- return (dto_class_name, entity_class_name, namespace, api_expose, api_verbs)
390
+ return (
391
+ dto_class_name,
392
+ entity_class_name,
393
+ namespace,
394
+ api_expose,
395
+ api_verbs,
396
+ insert_function_class_name,
397
+ update_function_class_name,
398
+ )
349
399
 
350
400
  def _safe_exec(self, source_code, context, description):
351
401
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.25
3
+ Version: 0.0.27
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