nsj-rest-lib2 0.0.29__py3-none-any.whl → 0.0.31__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.
- nsj_rest_lib2/compiler/compiler.py +16 -7
- nsj_rest_lib2/compiler/compiler_structures.py +1 -0
- nsj_rest_lib2/compiler/dto_compiler.py +9 -3
- nsj_rest_lib2/compiler/edl_model/primitives.py +13 -2
- nsj_rest_lib2/compiler/entity_compiler.py +4 -0
- nsj_rest_lib2/compiler/migration_compiler.py +674 -0
- nsj_rest_lib2/compiler/migration_compiler_alter_table.py +201 -0
- nsj_rest_lib2/compiler/migration_compiler_create_table.py +75 -0
- nsj_rest_lib2/compiler/migration_compiler_util.py +144 -0
- nsj_rest_lib2/compiler/property_compiler.py +64 -66
- nsj_rest_lib2/compiler/util/relation_ref.py +118 -0
- {nsj_rest_lib2-0.0.29.dist-info → nsj_rest_lib2-0.0.31.dist-info}/METADATA +1 -1
- {nsj_rest_lib2-0.0.29.dist-info → nsj_rest_lib2-0.0.31.dist-info}/RECORD +15 -11
- nsj_rest_lib2/compiler/ai_compiler.py +0 -285
- {nsj_rest_lib2-0.0.29.dist-info → nsj_rest_lib2-0.0.31.dist-info}/WHEEL +0 -0
- {nsj_rest_lib2-0.0.29.dist-info → nsj_rest_lib2-0.0.31.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Literal, Sequence
|
|
4
|
+
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.entity_model_base import EntityModelBase
|
|
6
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import (
|
|
7
|
+
REGEX_EXTERNAL_COMPONENT_REF,
|
|
8
|
+
REGEX_EXTERNAL_REF,
|
|
9
|
+
REGEX_INTERNAL_REF,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
RelationRefType = Literal["internal", "external", "external_component"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class RelationRef:
|
|
18
|
+
ref_type: RelationRefType
|
|
19
|
+
scope: str | None
|
|
20
|
+
entity: str
|
|
21
|
+
components: list[str]
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def entity_key(self) -> str | None:
|
|
25
|
+
if not self.scope:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
return f"{self.scope}/{self.entity}"
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def target_id(self) -> str:
|
|
32
|
+
return self.components[-1] if self.components else self.entity
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def prefx_class_name(self) -> str:
|
|
36
|
+
if not self.components:
|
|
37
|
+
return ""
|
|
38
|
+
|
|
39
|
+
prefix_parts: list[str] = [self.entity, *self.components[:-1]]
|
|
40
|
+
return f"_{'_'.join(prefix_parts)}"
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def is_external(self) -> bool:
|
|
44
|
+
return self.ref_type in {"external", "external_component"}
|
|
45
|
+
|
|
46
|
+
def resolve_model(
|
|
47
|
+
self,
|
|
48
|
+
entity_models: dict[str, EntityModelBase],
|
|
49
|
+
base_model: EntityModelBase | None = None,
|
|
50
|
+
) -> EntityModelBase | None:
|
|
51
|
+
entity_key = self.entity_key
|
|
52
|
+
resolved_base: EntityModelBase | None = None
|
|
53
|
+
|
|
54
|
+
if entity_key:
|
|
55
|
+
resolved_base = entity_models.get(entity_key)
|
|
56
|
+
elif base_model:
|
|
57
|
+
resolved_base = base_model
|
|
58
|
+
|
|
59
|
+
if not resolved_base:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
return RelationRefParser.follow_components(resolved_base, self.components)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class RelationRefParser:
|
|
66
|
+
@staticmethod
|
|
67
|
+
def parse(ref: str) -> RelationRef | None:
|
|
68
|
+
internal_match = re.match(REGEX_INTERNAL_REF, ref)
|
|
69
|
+
if internal_match:
|
|
70
|
+
components = RelationRefParser._split_components(internal_match.group(1))
|
|
71
|
+
if not components:
|
|
72
|
+
return None
|
|
73
|
+
entity = components[0]
|
|
74
|
+
extra_components = components[1:] if len(components) > 1 else []
|
|
75
|
+
return RelationRef("internal", None, entity, extra_components)
|
|
76
|
+
|
|
77
|
+
external_component_match = re.match(REGEX_EXTERNAL_COMPONENT_REF, ref)
|
|
78
|
+
if external_component_match:
|
|
79
|
+
components = RelationRefParser._split_components(
|
|
80
|
+
external_component_match.group(3)
|
|
81
|
+
)
|
|
82
|
+
return RelationRef(
|
|
83
|
+
"external_component",
|
|
84
|
+
external_component_match.group(1),
|
|
85
|
+
external_component_match.group(2),
|
|
86
|
+
components,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
external_match = re.match(REGEX_EXTERNAL_REF, ref)
|
|
90
|
+
if external_match:
|
|
91
|
+
return RelationRef(
|
|
92
|
+
"external",
|
|
93
|
+
external_match.group(1),
|
|
94
|
+
external_match.group(2),
|
|
95
|
+
[],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def follow_components(
|
|
102
|
+
base_model: EntityModelBase, components: Sequence[str]
|
|
103
|
+
) -> EntityModelBase | None:
|
|
104
|
+
target_model: EntityModelBase | None = base_model
|
|
105
|
+
for component in components:
|
|
106
|
+
if not target_model or not target_model.components:
|
|
107
|
+
return None
|
|
108
|
+
target_model = target_model.components.get(component)
|
|
109
|
+
if target_model is None:
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
return target_model
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def _split_components(components_path: str) -> list[str]:
|
|
116
|
+
# Remove marcadores opcionais '#/components/' intermediários
|
|
117
|
+
cleaned = components_path.replace("#/components/", "/").replace("#", "")
|
|
118
|
+
return [part for part in cleaned.split("/") if part]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.31
|
|
4
4
|
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
5
|
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
6
|
Author: Nasajon Sistemas
|
|
@@ -3,15 +3,18 @@ nsj_rest_lib2/exception.py,sha256=E9uMUdoCCQOVQfc8f6gD9b5Pxurf3Q4SytDCcqSlkZ8,56
|
|
|
3
3
|
nsj_rest_lib2/redis_config.py,sha256=4KLcvYS3nJO7PMQgF6F9_j6r-TyqcS7TBbd3LEQuKDU,629
|
|
4
4
|
nsj_rest_lib2/settings.py,sha256=Hn_o1HZmievnYb8D1kNT2Nq-OEjxbyNjOiOpbnFsMwE,367
|
|
5
5
|
nsj_rest_lib2/compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
nsj_rest_lib2/compiler/
|
|
7
|
-
nsj_rest_lib2/compiler/
|
|
8
|
-
nsj_rest_lib2/compiler/
|
|
9
|
-
nsj_rest_lib2/compiler/
|
|
10
|
-
nsj_rest_lib2/compiler/entity_compiler.py,sha256=-mDA1-dHKYp0umQi1A4GbA0F1hlaC-gQRqj69vkADC4,6536
|
|
6
|
+
nsj_rest_lib2/compiler/compiler.py,sha256=dDH3WXEaWg6eg95Bix1ZMY_fuB3prgps8xsevw9wYE4,30915
|
|
7
|
+
nsj_rest_lib2/compiler/compiler_structures.py,sha256=stspjqJGXU7Vz3BqQ-ZF5ZmumFm3R4jpkWgVVsXW5d0,1488
|
|
8
|
+
nsj_rest_lib2/compiler/dto_compiler.py,sha256=Fs6VysuAjBgct7_Wc0sugvx7z9tfnlZcSrBu0l0Pyp0,7019
|
|
9
|
+
nsj_rest_lib2/compiler/entity_compiler.py,sha256=LeGEBxsjAmZZog2gh4vjUX1aFp9JSVgHHOdTkY0aH-s,6733
|
|
11
10
|
nsj_rest_lib2/compiler/function_compiler.py,sha256=t5e_NoDB0IckiIEuYLsvk2lPSwrcrjL91s4LTQkpRqA,18085
|
|
12
11
|
nsj_rest_lib2/compiler/function_model.py,sha256=iSKMlCSZDWlP_aTdlcbYQhsEAIMH-XbSJUU0-KNtLCs,2066
|
|
12
|
+
nsj_rest_lib2/compiler/migration_compiler.py,sha256=vlG54XmYRqzIo0Iey-4HbSRzPg3Ylp5kpWWCGFAVzx8,29023
|
|
13
|
+
nsj_rest_lib2/compiler/migration_compiler_alter_table.py,sha256=awtqVrKox86rmlQV7K17JzZJZqz9cF7McshLBlLx65s,7969
|
|
14
|
+
nsj_rest_lib2/compiler/migration_compiler_create_table.py,sha256=h22cU53EFnuB5t28fMZ_r7MM-14Vqqu3emebBUJN2LY,2606
|
|
15
|
+
nsj_rest_lib2/compiler/migration_compiler_util.py,sha256=WB_GRX78nTRKlZkgL-4yowwlQoAe81llZ-E2AYfIbh4,5168
|
|
13
16
|
nsj_rest_lib2/compiler/model.py,sha256=8UxzTvcNgZQdLwC592o8ZMVPhG3AloBCg5q2V72Up8U,1688
|
|
14
|
-
nsj_rest_lib2/compiler/property_compiler.py,sha256=
|
|
17
|
+
nsj_rest_lib2/compiler/property_compiler.py,sha256=hMQX5vrLqjOEM_wdzuerxuARMq5JhZQ_R8cUVoLHNNM,47631
|
|
15
18
|
nsj_rest_lib2/compiler/edl_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
19
|
nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py,sha256=664QBDcOgVnyfwtUOXO1W7AKaZhueBG335x5DuogruY,7644
|
|
17
20
|
nsj_rest_lib2/compiler/edl_model/api_model.py,sha256=7RpZkUGROZmTBh2psgTG_b3e4xxjc3uBr1EYb-wiAkc,3810
|
|
@@ -20,11 +23,12 @@ nsj_rest_lib2/compiler/edl_model/entity_model.py,sha256=Yc6wvjsiwacmz796mZIU-i9h
|
|
|
20
23
|
nsj_rest_lib2/compiler/edl_model/entity_model_base.py,sha256=eRn0pirIPHvniqGpT0xE-mmgqz5RIVtqghxcnfxKNdQ,4345
|
|
21
24
|
nsj_rest_lib2/compiler/edl_model/entity_model_root.py,sha256=VinsxFlNyCaKKk37ZzcbmWaWgoUP2-dZBG61Ke7NvVs,231
|
|
22
25
|
nsj_rest_lib2/compiler/edl_model/index_model.py,sha256=cXWlu0hxtro5vvYoirkDW4R3PCnBW5oCCWjRJ6AX5zE,508
|
|
23
|
-
nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=
|
|
26
|
+
nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=FK2U3rRJIp2j_R_RuQtoRj_MSyua0-eH_QEoCGnutOE,1963
|
|
24
27
|
nsj_rest_lib2/compiler/edl_model/property_meta_model.py,sha256=ciphkgdTtfPfLpeMQpcf3GXQ_qx7xl1tzgeUoJBjPBU,3871
|
|
25
28
|
nsj_rest_lib2/compiler/edl_model/repository_model.py,sha256=OmtUzSq2LKmM76VXefGrKwg8xtVFCutt3JNH2nv42qU,1821
|
|
26
29
|
nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=NtMVZeOPu3LJwXI-8tCOLVuQiGua_0t7kL1h9gL7HuM,657
|
|
27
30
|
nsj_rest_lib2/compiler/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
nsj_rest_lib2/compiler/util/relation_ref.py,sha256=1M_e-NyeqozlIYLl1rB76KqkmtMvgtqH_nJS-NK0Pck,3695
|
|
28
32
|
nsj_rest_lib2/compiler/util/str_util.py,sha256=0ReIQ2Vy4zAmVMvGv0FcUehRQw15hlz0e7yDsF89ghk,1178
|
|
29
33
|
nsj_rest_lib2/compiler/util/type_naming_util.py,sha256=fPyzsEsbG9sgzaUJghamijcmZ6Odh_rIv3zk-lcP5pQ,1419
|
|
30
34
|
nsj_rest_lib2/compiler/util/type_util.py,sha256=HTKOH4uRTOY0YgoM8oUv_6cEcReE_bgKYXFBsQCb-3A,357
|
|
@@ -33,7 +37,7 @@ nsj_rest_lib2/controller/dynamic_controller.py,sha256=UJtlr0_A6bGJ1gD24oj3481tCb
|
|
|
33
37
|
nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
38
|
nsj_rest_lib2/service/entity_config_writer.py,sha256=DC8M4BYKfaOkNty1YFmAmWiNvENjvmiUqDKgsAV3eNc,4688
|
|
35
39
|
nsj_rest_lib2/service/entity_loader.py,sha256=I8Ua2CPCxxUovHJn8SSRz0bYfmdFqfWj-JQfNTBBypc,17842
|
|
36
|
-
nsj_rest_lib2-0.0.
|
|
37
|
-
nsj_rest_lib2-0.0.
|
|
38
|
-
nsj_rest_lib2-0.0.
|
|
39
|
-
nsj_rest_lib2-0.0.
|
|
40
|
+
nsj_rest_lib2-0.0.31.dist-info/METADATA,sha256=I1o7yO3ACZoRB6Qnw1qRhegqWBv5_MoK_f-VgIppAcM,1094
|
|
41
|
+
nsj_rest_lib2-0.0.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
42
|
+
nsj_rest_lib2-0.0.31.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
|
|
43
|
+
nsj_rest_lib2-0.0.31.dist-info/RECORD,,
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
Gerador de DTO e Entity a partir de um EDL JSON (v1.x) — AGORA usando 'repository'
|
|
6
|
-
no lugar de 'storage', e propagando o de-para de colunas:
|
|
7
|
-
- Entity usa os nomes físicos vindos de model.repository.columns[*].column (quando houver).
|
|
8
|
-
- DTOField ganha entity_field="<nome_do_atributo_na_Entity>" para cada propriedade.
|
|
9
|
-
|
|
10
|
-
Uso:
|
|
11
|
-
python generator.py caminho/para/arquivo.edl.json
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import json
|
|
15
|
-
import sys
|
|
16
|
-
import keyword
|
|
17
|
-
from typing import Any, Dict, List, Tuple
|
|
18
|
-
|
|
19
|
-
# -----------------------
|
|
20
|
-
# Helpers de transformação
|
|
21
|
-
# -----------------------
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def py_identifier(name: str) -> str:
|
|
25
|
-
"""Garante que o nome é um identificador Python válido."""
|
|
26
|
-
n = (name or "").strip().replace("-", "_")
|
|
27
|
-
if not n:
|
|
28
|
-
n = "_"
|
|
29
|
-
if not n.isidentifier() or keyword.iskeyword(n):
|
|
30
|
-
n = f"{n}_"
|
|
31
|
-
return n
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def detect_pk(model_props: Dict[str, Any]) -> str:
|
|
35
|
-
for logical, meta in model_props.items():
|
|
36
|
-
if isinstance(meta, dict) and meta.get("pk") is True:
|
|
37
|
-
return logical
|
|
38
|
-
return ""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def to_python_type(prop: Dict[str, Any]) -> str:
|
|
42
|
-
t = (prop.get("type") or "string").lower()
|
|
43
|
-
fmt = (prop.get("format") or "").lower()
|
|
44
|
-
|
|
45
|
-
if fmt in {"uuid"}:
|
|
46
|
-
return "uuid.UUID"
|
|
47
|
-
if t in {"datetime"}:
|
|
48
|
-
return "datetime.datetime"
|
|
49
|
-
if t in {"date"}:
|
|
50
|
-
return "datetime.date"
|
|
51
|
-
if t in {"integer"}:
|
|
52
|
-
return "int"
|
|
53
|
-
if t in {"number"}:
|
|
54
|
-
return "float"
|
|
55
|
-
if t in {"boolean"}:
|
|
56
|
-
return "bool"
|
|
57
|
-
return "str"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def build_entity_field_name(logical: str, columns_map: Dict[str, Any]) -> str:
|
|
61
|
-
"""
|
|
62
|
-
Retorna o nome do atributo na Entity para um dado campo lógico,
|
|
63
|
-
priorizando o nome físico em repository.columns[logical].column (se existir).
|
|
64
|
-
"""
|
|
65
|
-
col_meta = (columns_map or {}).get(logical) or {}
|
|
66
|
-
entity_attr = col_meta.get("column") or logical
|
|
67
|
-
return py_identifier(entity_attr)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def dto_field_args(
|
|
71
|
-
logical: str,
|
|
72
|
-
prop: Dict[str, Any],
|
|
73
|
-
required: List[str],
|
|
74
|
-
columns_map: Dict[str, Any],
|
|
75
|
-
) -> Dict[str, Any]:
|
|
76
|
-
args: Dict[str, Any] = {}
|
|
77
|
-
|
|
78
|
-
# Política: sempre expor (resume=True) e nunca ocultar campos que existem na origem
|
|
79
|
-
args["resume"] = True
|
|
80
|
-
|
|
81
|
-
# PK / not_null
|
|
82
|
-
if prop.get("pk") is True:
|
|
83
|
-
args["pk"] = True
|
|
84
|
-
args["not_null"] = True
|
|
85
|
-
if logical in set(required):
|
|
86
|
-
args["not_null"] = True
|
|
87
|
-
|
|
88
|
-
# Strings: strip + limites
|
|
89
|
-
t = (prop.get("type") or "string").lower()
|
|
90
|
-
if t == "string":
|
|
91
|
-
args["strip"] = True
|
|
92
|
-
if "length" in prop:
|
|
93
|
-
args["max"] = int(prop["length"])
|
|
94
|
-
if "minimum" in prop:
|
|
95
|
-
args["min"] = int(prop["minimum"])
|
|
96
|
-
|
|
97
|
-
# Números
|
|
98
|
-
if t in {"integer", "number"}:
|
|
99
|
-
if "minimum" in prop:
|
|
100
|
-
args["min"] = (
|
|
101
|
-
int(prop["minimum"]) if t == "integer" else float(prop["minimum"])
|
|
102
|
-
)
|
|
103
|
-
if "maximum" in prop:
|
|
104
|
-
args["max"] = (
|
|
105
|
-
int(prop["maximum"]) if t == "integer" else float(prop["maximum"])
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# Formatos especiais
|
|
109
|
-
fmt = (prop.get("format") or "").lower()
|
|
110
|
-
if fmt == "uuid":
|
|
111
|
-
args["validator"] = "DTOFieldValidators().validate_uuid"
|
|
112
|
-
args["min"] = 36
|
|
113
|
-
args["max"] = 36
|
|
114
|
-
|
|
115
|
-
# Default lógico (quando presente)
|
|
116
|
-
if "default" in prop and prop["default"] is not None:
|
|
117
|
-
default_val = prop["default"]
|
|
118
|
-
if isinstance(default_val, (int, float, bool)):
|
|
119
|
-
args["default_value"] = repr(default_val)
|
|
120
|
-
elif isinstance(default_val, str) and default_val.endswith("()"):
|
|
121
|
-
args["default_value"] = default_val
|
|
122
|
-
else:
|
|
123
|
-
args["default_value"] = repr(default_val)
|
|
124
|
-
|
|
125
|
-
# Sempre informar o nome do atributo correspondente na Entity
|
|
126
|
-
args["entity_field"] = build_entity_field_name(logical, columns_map)
|
|
127
|
-
|
|
128
|
-
return args
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def render_dto(edl: Dict[str, Any]) -> Tuple[str, str]:
|
|
132
|
-
model = edl.get("model", {}) or {}
|
|
133
|
-
props: Dict[str, Any] = model.get("properties", {}) or {}
|
|
134
|
-
required: List[str] = model.get("required", []) or []
|
|
135
|
-
repository = model.get("repository", {}) or {}
|
|
136
|
-
columns_map: Dict[str, Any] = repository.get("columns", {}) or {}
|
|
137
|
-
|
|
138
|
-
entity_name_full = edl.get("id") or "Entity"
|
|
139
|
-
class_base = entity_name_full.split(".")[-1]
|
|
140
|
-
class_name = f"{class_base[0].upper()}{class_base[1:]}"
|
|
141
|
-
dto_class = f"{class_name}DTO"
|
|
142
|
-
|
|
143
|
-
# imports
|
|
144
|
-
need_uuid = any(((p.get("format") or "").lower() == "uuid") for p in props.values())
|
|
145
|
-
need_datetime = any(
|
|
146
|
-
((p.get("type") or "").lower() in {"datetime", "date"}) for p in props.values()
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
header_imports = [
|
|
150
|
-
"from nsj_rest_lib.decorator.dto import DTO",
|
|
151
|
-
"from nsj_rest_lib.descriptor.dto_field import DTOField",
|
|
152
|
-
"from nsj_rest_lib.descriptor.dto_field_validators import DTOFieldValidators",
|
|
153
|
-
"from nsj_rest_lib.dto.dto_base import DTOBase",
|
|
154
|
-
]
|
|
155
|
-
if need_uuid:
|
|
156
|
-
header_imports.insert(0, "import uuid")
|
|
157
|
-
if need_datetime:
|
|
158
|
-
header_imports.insert(0, "import datetime")
|
|
159
|
-
|
|
160
|
-
lines: List[str] = []
|
|
161
|
-
lines.extend(header_imports)
|
|
162
|
-
lines.append("")
|
|
163
|
-
lines.append("")
|
|
164
|
-
lines.append("@DTO()")
|
|
165
|
-
lines.append(f"class {dto_class}(DTOBase):")
|
|
166
|
-
if not props:
|
|
167
|
-
lines.append(" pass")
|
|
168
|
-
return (dto_class, "\n".join(lines))
|
|
169
|
-
|
|
170
|
-
for logical in props:
|
|
171
|
-
meta = props[logical] or {}
|
|
172
|
-
py_type = to_python_type(meta)
|
|
173
|
-
field_args = dto_field_args(logical, meta, required, columns_map)
|
|
174
|
-
|
|
175
|
-
# Monta chamada DTOField(...)
|
|
176
|
-
arg_parts = []
|
|
177
|
-
for k, v in field_args.items():
|
|
178
|
-
if k == "validator":
|
|
179
|
-
arg_parts.append(f"{k}={v}")
|
|
180
|
-
else:
|
|
181
|
-
arg_parts.append(f"{k}={repr(v)}")
|
|
182
|
-
args_str = ", ".join(arg_parts) if arg_parts else ""
|
|
183
|
-
|
|
184
|
-
lines.append("")
|
|
185
|
-
lines.append(f" {py_identifier(logical)}: {py_type} = DTOField({args_str})")
|
|
186
|
-
|
|
187
|
-
return (dto_class, "\n".join(lines))
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def render_entity(edl: Dict[str, Any]) -> Tuple[str, str]:
|
|
191
|
-
model = edl.get("model", {}) or {}
|
|
192
|
-
props: Dict[str, Any] = model.get("properties", {}) or {}
|
|
193
|
-
repository = model.get("repository", {}) or {}
|
|
194
|
-
api = model.get("api", {}) or {}
|
|
195
|
-
|
|
196
|
-
entity_name_full = edl.get("id") or "Entity"
|
|
197
|
-
class_base = entity_name_full.split(".")[-1]
|
|
198
|
-
class_name = f"{class_base[0].upper()}{class_base[1:]}"
|
|
199
|
-
entity_class = f"{class_name}Entity"
|
|
200
|
-
|
|
201
|
-
table_name = repository.get("map") or "schema.tabela"
|
|
202
|
-
pk_field = detect_pk(props) or "id"
|
|
203
|
-
|
|
204
|
-
# default_order_fields = api.default_sort (removendo prefixos '+'|'-')
|
|
205
|
-
default_sort: List[str] = []
|
|
206
|
-
for item in api.get("default_sort", []) or []:
|
|
207
|
-
fld = str(item).lstrip("+-")
|
|
208
|
-
if fld in props:
|
|
209
|
-
default_sort.append(fld)
|
|
210
|
-
if not default_sort:
|
|
211
|
-
default_sort = [pk_field] if pk_field else []
|
|
212
|
-
|
|
213
|
-
# imports
|
|
214
|
-
need_uuid = any(((p.get("format") or "").lower() == "uuid") for p in props.values())
|
|
215
|
-
need_datetime = any(
|
|
216
|
-
((p.get("type") or "").lower() in {"datetime", "date"}) for p in props.values()
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
header_imports = [
|
|
220
|
-
"from nsj_rest_lib.entity.entity_base import EntityBase",
|
|
221
|
-
"from nsj_rest_lib.decorator.entity import Entity",
|
|
222
|
-
]
|
|
223
|
-
if need_uuid:
|
|
224
|
-
header_imports.insert(0, "import uuid")
|
|
225
|
-
if need_datetime:
|
|
226
|
-
header_imports.insert(0, "import datetime")
|
|
227
|
-
|
|
228
|
-
columns_map: Dict[str, Any] = repository.get("columns", {}) or {}
|
|
229
|
-
|
|
230
|
-
lines: List[str] = []
|
|
231
|
-
lines.extend(header_imports)
|
|
232
|
-
lines.append("")
|
|
233
|
-
lines.append("")
|
|
234
|
-
lines.append("@Entity(")
|
|
235
|
-
lines.append(f" table_name={repr(table_name)},")
|
|
236
|
-
lines.append(f" pk_field={repr(py_identifier(pk_field))},")
|
|
237
|
-
if default_sort:
|
|
238
|
-
lines.append(
|
|
239
|
-
f" default_order_fields={[py_identifier(x) for x in default_sort]},"
|
|
240
|
-
)
|
|
241
|
-
lines.append(")")
|
|
242
|
-
lines.append(f"class {entity_class}(EntityBase):")
|
|
243
|
-
|
|
244
|
-
if not props:
|
|
245
|
-
lines.append(" pass")
|
|
246
|
-
return (entity_class, "\n".join(lines))
|
|
247
|
-
|
|
248
|
-
for logical, meta in props.items():
|
|
249
|
-
py_type = to_python_type(meta)
|
|
250
|
-
entity_attr = build_entity_field_name(logical, columns_map)
|
|
251
|
-
lines.append(f" {entity_attr}: {py_type} = None")
|
|
252
|
-
|
|
253
|
-
return (entity_class, "\n".join(lines))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def generate_from_edl(edl: Dict[str, Any]) -> Tuple[str, str, str, str]:
|
|
257
|
-
dto_class_name, dto_code = render_dto(edl)
|
|
258
|
-
entity_class_name, entity_code = render_entity(edl)
|
|
259
|
-
return (dto_class_name, dto_code, entity_class_name, entity_code)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
# -----------------------
|
|
263
|
-
# CLI
|
|
264
|
-
# -----------------------
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def main():
|
|
268
|
-
if len(sys.argv) < 2:
|
|
269
|
-
print("Uso: python generator.py caminho/para/arquivo.edl.json", file=sys.stderr)
|
|
270
|
-
sys.exit(2)
|
|
271
|
-
|
|
272
|
-
with open(sys.argv[1], "r", encoding="utf-8") as f:
|
|
273
|
-
edl = json.load(f)
|
|
274
|
-
|
|
275
|
-
_, dto_code, _, entity_code = generate_from_edl(edl)
|
|
276
|
-
|
|
277
|
-
sep = "\n" + ("#" * 80) + "\n"
|
|
278
|
-
print(sep + "# DTO\n" + sep)
|
|
279
|
-
print(dto_code)
|
|
280
|
-
print(sep + "# ENTITY\n" + sep)
|
|
281
|
-
print(entity_code)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if __name__ == "__main__":
|
|
285
|
-
main()
|
|
File without changes
|
|
File without changes
|