nsj-rest-lib2 0.0.2__py3-none-any.whl → 0.0.4__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/__init__.py +0 -0
- nsj_rest_lib2/compiler/ai_compiler.py +285 -0
- nsj_rest_lib2/compiler/compiler.py +267 -0
- nsj_rest_lib2/compiler/compiler_structures.py +24 -0
- nsj_rest_lib2/compiler/dto_compiler.py +83 -0
- nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
- nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +262 -0
- nsj_rest_lib2/compiler/edl_model/api_model.py +23 -0
- nsj_rest_lib2/compiler/edl_model/column_meta_model.py +6 -0
- nsj_rest_lib2/compiler/edl_model/entity_model.py +66 -0
- nsj_rest_lib2/compiler/edl_model/index_model.py +13 -0
- nsj_rest_lib2/compiler/edl_model/primitives.py +43 -0
- nsj_rest_lib2/compiler/edl_model/property_meta_model.py +82 -0
- nsj_rest_lib2/compiler/edl_model/repository_model.py +24 -0
- nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +10 -0
- nsj_rest_lib2/compiler/entity_compiler.py +118 -0
- nsj_rest_lib2/compiler/property_compiler.py +359 -0
- nsj_rest_lib2/service/entity_loader.py +51 -34
- nsj_rest_lib2/settings.py +13 -0
- {nsj_rest_lib2-0.0.2.dist-info → nsj_rest_lib2-0.0.4.dist-info}/METADATA +1 -1
- nsj_rest_lib2-0.0.4.dist-info/RECORD +29 -0
- nsj_rest_lib2-0.0.2.dist-info/RECORD +0 -12
- {nsj_rest_lib2-0.0.2.dist-info → nsj_rest_lib2-0.0.4.dist-info}/WHEEL +0 -0
- {nsj_rest_lib2-0.0.2.dist-info → nsj_rest_lib2-0.0.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
4
|
+
from pydantic import BaseModel, Field, RootModel, constr, conint, confloat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
PropertyType = Literal[
|
|
8
|
+
"string", "number", "integer", "boolean", "date", "datetime", "object"
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
# ---------------------------
|
|
12
|
+
# Tipos básicos e utilitários
|
|
13
|
+
# ---------------------------
|
|
14
|
+
|
|
15
|
+
PrimitiveType = Literal[
|
|
16
|
+
"string", "number", "integer", "boolean", "date", "datetime", "object"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Em EDL, type também pode apontar para outra entidade "scope.object" ou "array"
|
|
21
|
+
# e propriedades podem ter metadados comuns.
|
|
22
|
+
class Property(BaseModel):
|
|
23
|
+
# Tipo pode ser primitivo, "array", um caminho de entidade "escopo.entidade"
|
|
24
|
+
# ou um formato especializado (ex.: "uuid", "currency", "cpf", "cnpj"...)
|
|
25
|
+
type: str = Field(
|
|
26
|
+
..., description="Tipo EDL (primitivo, array, ou escopo.entidade)."
|
|
27
|
+
)
|
|
28
|
+
cardinality: Optional[Literal["1_1", "0_1", "1_N", "0_N", "N_N"]] = None
|
|
29
|
+
|
|
30
|
+
# Metadados comuns
|
|
31
|
+
label: Optional[str] = None
|
|
32
|
+
description: Optional[str] = None
|
|
33
|
+
length: Optional[int] = None
|
|
34
|
+
pattern: Optional[str] = None
|
|
35
|
+
default: Optional[Any] = None
|
|
36
|
+
immutable: Optional[bool] = None
|
|
37
|
+
minimum: Optional[confloat()] = None
|
|
38
|
+
maximum: Optional[confloat()] = None
|
|
39
|
+
enum: Optional[List[Union[str, int]]] = None
|
|
40
|
+
|
|
41
|
+
# Chaves
|
|
42
|
+
pk: Optional[bool] = None
|
|
43
|
+
key_alternative: Optional[bool] = None # alias semântico (chave natural)
|
|
44
|
+
pk_substitute: Optional[bool] = None # usado em alguns exemplos
|
|
45
|
+
value: Optional[Any] = None # valor fixo/derivado
|
|
46
|
+
format: Optional[str] = None # ex.: "uuid", "currency", "quantity"
|
|
47
|
+
|
|
48
|
+
# Itens (para arrays embutidos)
|
|
49
|
+
items: Optional[Union["RelationDef", Property, Dict[str, Any]]] = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ComposedMap(RootModel[Dict[str, Any]]):
|
|
53
|
+
"""
|
|
54
|
+
Representa composed_properties: aceita mapeamentos arbitrários
|
|
55
|
+
(strings de caminho e/ou mapas aninhados).
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ---------------------------
|
|
62
|
+
# Relations (objetos inline)
|
|
63
|
+
# ---------------------------
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RelationDef(BaseModel):
|
|
67
|
+
# Em EDL, relations podem ser "object" (estrutura inline) ou "array" de "object"
|
|
68
|
+
type: Optional[str] = Field(None, description='Tipicamente "object" ou "array".')
|
|
69
|
+
# Identificador opcional da relação/objeto
|
|
70
|
+
id_: Optional[str] = Field(None, alias="$id")
|
|
71
|
+
required: Optional[List[str]] = None
|
|
72
|
+
properties: Optional[Dict[str, Property]] = None
|
|
73
|
+
composed_properties: Optional[Dict[str, ComposedMap]] = None
|
|
74
|
+
items: Optional[Union[Property, Dict[str, Any]]] = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------
|
|
78
|
+
# Regras (motor de regras)
|
|
79
|
+
# ---------------------------
|
|
80
|
+
|
|
81
|
+
RuleType = Literal["required", "custom", "format"]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Rule(BaseModel):
|
|
85
|
+
field: str
|
|
86
|
+
type: RuleType
|
|
87
|
+
when: Optional[str] = None
|
|
88
|
+
message: Optional[str] = None
|
|
89
|
+
messageParams: Optional[List[str]] = None
|
|
90
|
+
# Para tipo "format"
|
|
91
|
+
pattern: Optional[str] = None
|
|
92
|
+
# Para tipo "custom"
|
|
93
|
+
fn: Optional[str] = None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ---------------------------
|
|
97
|
+
# Repository (mapeamento físico)
|
|
98
|
+
# ---------------------------
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class LinkToBase(BaseModel):
|
|
102
|
+
base: str
|
|
103
|
+
baseProperty: str
|
|
104
|
+
column: str
|
|
105
|
+
onDelete: Optional[Literal["cascade", "restrict", "set_null"]] = None
|
|
106
|
+
nullable: Optional[bool] = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IndexDef(BaseModel):
|
|
110
|
+
name: str
|
|
111
|
+
fields: List[str]
|
|
112
|
+
unique: Optional[bool] = None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ColumnsMap(RootModel[Dict[str, Any]]):
|
|
116
|
+
"""
|
|
117
|
+
Suporta tanto mapeamentos simples {"prop": {"column": "col"}}
|
|
118
|
+
quanto caminhos aninhados ("totais.valorNf": {"column": "vnf"}).
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class RepositoryBlock(BaseModel):
|
|
125
|
+
map: Optional[str] = None # schema.tabela
|
|
126
|
+
shared_table: Optional[bool] = None
|
|
127
|
+
table_owner: Optional[bool] = None
|
|
128
|
+
linkToBase: Optional[LinkToBase] = None # obrigatório para parciais
|
|
129
|
+
columns: Optional[ColumnsMap] = None
|
|
130
|
+
indexes: Optional[List[IndexDef]] = None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Em muitos exemplos, "repository" é um dicionário de blocos lógicos:
|
|
134
|
+
# { "Documento": {...}, "documento.linhas": {...} }
|
|
135
|
+
Repository = Dict[str, RepositoryBlock]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ---------------------------
|
|
139
|
+
# API e Handlers
|
|
140
|
+
# ---------------------------
|
|
141
|
+
|
|
142
|
+
Verb = Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
|
|
143
|
+
ComposePolicy = Literal["transparent", "readonly", "none"]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class HttpResultSpec(BaseModel):
|
|
147
|
+
statusOnSuccess: Optional[int] = None
|
|
148
|
+
location: Optional[str] = None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class HandlerResult(BaseModel):
|
|
152
|
+
expect: Optional[Literal["entity_row", "rows", "void"]] = None
|
|
153
|
+
entity: Optional[str] = None
|
|
154
|
+
http: Optional[HttpResultSpec] = None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ComposeMappingAttr(BaseModel):
|
|
158
|
+
attr: str
|
|
159
|
+
from_: Optional[str] = Field(None, alias="from")
|
|
160
|
+
# Para arrays: indicar o tipo composto do banco
|
|
161
|
+
as_: Optional[str] = Field(None, alias="as")
|
|
162
|
+
# Mapeamento de itens (quando from aponta para array)
|
|
163
|
+
mapping: Optional[List["ComposeMappingAttr"]] = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class CallCompose(BaseModel):
|
|
167
|
+
typeName: str
|
|
168
|
+
mapping: List[ComposeMappingAttr]
|
|
169
|
+
onMissing: Optional[Literal["error", "ignore", "null"]] = None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ArgBinding(BaseModel):
|
|
173
|
+
to: str
|
|
174
|
+
source: Literal["body", "context"]
|
|
175
|
+
from_: Optional[str] = Field(None, alias="from")
|
|
176
|
+
dbType: Optional[str] = None
|
|
177
|
+
compose: Optional[CallCompose] = None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class CallSpec(BaseModel):
|
|
181
|
+
style: Optional[Literal["named"]] = None
|
|
182
|
+
argBinding: Optional[List[ArgBinding]] = None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class HandlerError(BaseModel):
|
|
186
|
+
sqlstate: Optional[str] = None
|
|
187
|
+
httpStatus: Optional[int] = None
|
|
188
|
+
message: Optional[str] = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class HandlerConfig(BaseModel):
|
|
192
|
+
impl: Optional[str] = None # ex.: "pg_function"
|
|
193
|
+
functionRef: Optional[str] = None
|
|
194
|
+
signature: Optional[List[str]] = None
|
|
195
|
+
call: Optional[CallSpec] = None
|
|
196
|
+
result: Optional[HandlerResult] = None
|
|
197
|
+
errors: Optional[List[HandlerError]] = None
|
|
198
|
+
transaction: Optional[Literal["wrap", "none"]] = None
|
|
199
|
+
validate: Optional[Literal["warn", "strict", "none"]] = None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class APIBlock(BaseModel):
|
|
203
|
+
resource: Optional[str] = None
|
|
204
|
+
expose: Optional[bool] = None
|
|
205
|
+
verbs: Optional[List[Verb]] = None
|
|
206
|
+
filters: Optional[List[str]] = None
|
|
207
|
+
default_sort: Optional[List[str]] = None
|
|
208
|
+
includeExtensionsByDefault: Optional[bool] = None
|
|
209
|
+
subset: Optional[str] = None
|
|
210
|
+
|
|
211
|
+
# Parciais / composição
|
|
212
|
+
composeWithBase: Optional[bool] = None
|
|
213
|
+
composePolicy: Optional[ComposePolicy] = None
|
|
214
|
+
|
|
215
|
+
# Handlers por verbo
|
|
216
|
+
handlers: Optional[Dict[Verb, HandlerConfig]] = None
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# ---------------------------
|
|
220
|
+
# Bloco principal: model
|
|
221
|
+
# ---------------------------
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ModelBlock(BaseModel):
|
|
225
|
+
mixins: Optional[List[str]] = None
|
|
226
|
+
required: Optional[List[str]] = None
|
|
227
|
+
main_properties: Optional[List[str]] = None
|
|
228
|
+
|
|
229
|
+
properties: Dict[str, Property] = Field(default_factory=dict)
|
|
230
|
+
composed_properties: Optional[Dict[str, ComposedMap]] = None
|
|
231
|
+
relations: Optional[Dict[str, RelationDef]] = None
|
|
232
|
+
rules: Optional[List[Rule]] = None
|
|
233
|
+
|
|
234
|
+
# Repository pode conter múltiplos blocos lógicos
|
|
235
|
+
repository: Optional[Repository] = None
|
|
236
|
+
|
|
237
|
+
api: Optional[APIBlock] = None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ---------------------------
|
|
241
|
+
# Arquivo EDL (raiz)
|
|
242
|
+
# ---------------------------
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class EDLFile(BaseModel):
|
|
246
|
+
edl_version: Literal["1.0"]
|
|
247
|
+
escopo: str
|
|
248
|
+
|
|
249
|
+
# Presentes conforme o tipo (entidade normal, trait, subclasse, parcial)
|
|
250
|
+
id: Optional[str] = None
|
|
251
|
+
trait_from: Optional[str] = None
|
|
252
|
+
trait: Optional[str] = None
|
|
253
|
+
extends: Optional[str] = None
|
|
254
|
+
partial_of: Optional[str] = None
|
|
255
|
+
partial: Optional[str] = None
|
|
256
|
+
|
|
257
|
+
# Outros observados nos exemplos
|
|
258
|
+
version: conint(ge=1)
|
|
259
|
+
abstract: Optional[bool] = None
|
|
260
|
+
native: Optional[bool] = None
|
|
261
|
+
|
|
262
|
+
model: ModelBlock
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import List, Optional, Literal
|
|
3
|
+
|
|
4
|
+
APIVerbs = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class APIModel(BaseModel):
|
|
8
|
+
resource: str = Field(
|
|
9
|
+
...,
|
|
10
|
+
description="Nome do recurso REST (rota base dos endpoints; exemplo: 'clientes').",
|
|
11
|
+
)
|
|
12
|
+
expose: Optional[bool] = Field(
|
|
13
|
+
default=True,
|
|
14
|
+
description="Indica se a API deve ser exposta (padrão: True).",
|
|
15
|
+
)
|
|
16
|
+
verbs: Optional[List[APIVerbs]] = Field(
|
|
17
|
+
default=["GET", "POST", "PUT", "DELETE", "PATCH"],
|
|
18
|
+
description="Lista de verbos HTTP suportados pela API (padrão: todos).",
|
|
19
|
+
)
|
|
20
|
+
default_sort: Optional[List[str]] = Field(
|
|
21
|
+
None,
|
|
22
|
+
description="Lista de campos usados na ordenação padrão (padrão: se nada for fornecido, será usada, ao menos, a PK).",
|
|
23
|
+
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
|
|
6
|
+
from nsj_rest_lib2.compiler.edl_model.api_model import APIModel
|
|
7
|
+
from nsj_rest_lib2.compiler.edl_model.trait_property_meta_model import (
|
|
8
|
+
TraitPropertyMetaModel,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EntityModel(BaseModel):
|
|
13
|
+
edl_version: Optional[str] = Field(default="1.0", description="Versão do EDL")
|
|
14
|
+
escopo: str = Field(..., description="Escopo do EDL (define a aplicação).")
|
|
15
|
+
description: str = Field(..., description="Descrição da entidade.")
|
|
16
|
+
id: str = Field(
|
|
17
|
+
...,
|
|
18
|
+
description="Identificador único da entidade, dentro de um determinado escopo (podendo ser sobreescrito para um tenant ou grupo _empresarial).",
|
|
19
|
+
)
|
|
20
|
+
trait_from: Optional[str] = Field(
|
|
21
|
+
None, description="Identificador da entidade que a trait estende."
|
|
22
|
+
)
|
|
23
|
+
version: Optional[str] = Field(
|
|
24
|
+
default="1.0", description="Versão da entidade (padrão: 1.0)."
|
|
25
|
+
)
|
|
26
|
+
abstract: Optional[bool] = Field(
|
|
27
|
+
default=False,
|
|
28
|
+
description="Indica se a entidade é abstrata (padrão: False). Caso positivo, não gera código, mas pode ser usada na geração de código de outras entidades.",
|
|
29
|
+
)
|
|
30
|
+
mixins: Optional[List[str]] = Field(
|
|
31
|
+
None,
|
|
32
|
+
description="Lista de mixins (blocos reutilizáveis) aplicados ao modelo.",
|
|
33
|
+
)
|
|
34
|
+
required: Optional[List[str]] = Field(
|
|
35
|
+
None, description="Lista de campos obrigatórios no modelo."
|
|
36
|
+
)
|
|
37
|
+
partition_data: Optional[List[str]] = Field(
|
|
38
|
+
None,
|
|
39
|
+
description="Lista de propriedades da entidade, que serã usadas para particionamento dos dados no banco (sendo obrigatórias em todas as chamadas, inclusive como um tipo de filtro obrigatório).",
|
|
40
|
+
)
|
|
41
|
+
main_properties: Optional[List[str]] = Field(
|
|
42
|
+
None,
|
|
43
|
+
description="Lista de propriedades resumo do modelo (retornadas em qualquer chamada GET, mesmo que não pedidas por meio do parâmetro 'fields').",
|
|
44
|
+
)
|
|
45
|
+
search_properties: Optional[List[str]] = Field(
|
|
46
|
+
None,
|
|
47
|
+
description="Lista de propriedades do modelo que serão utilizadas nas chamadas de query do usuário (suportando também campos das entidades relacionadas). Sugere-se que a biblioteca crie automaticamente os índices para fulltext search.",
|
|
48
|
+
)
|
|
49
|
+
metric_label: Optional[List[str]] = Field(
|
|
50
|
+
None,
|
|
51
|
+
description="Lista de campos incluídos no agrupamento das métricas enviadas ao OpenTelemetry.",
|
|
52
|
+
)
|
|
53
|
+
trait_properties: Optional[Dict[str, TraitPropertyMetaModel]] = Field(
|
|
54
|
+
None,
|
|
55
|
+
description="Dicionário de propriedades condicionais da trait (isso é, especifica propriedades, e seus valores fixos, que serão usados como um tipo de filtro fixo usado como condição para que a propridade principal assum o papel daquele tipo de trait).",
|
|
56
|
+
)
|
|
57
|
+
properties: Dict[str, PropertyMetaModel] = Field(
|
|
58
|
+
..., description="Dicionário de propriedades do modelo."
|
|
59
|
+
)
|
|
60
|
+
repository: RepositoryModel = Field(
|
|
61
|
+
..., description="Configurações de mapeamento para o banco de dados."
|
|
62
|
+
)
|
|
63
|
+
api: Optional[APIModel] = Field(
|
|
64
|
+
None,
|
|
65
|
+
description="Definição da API REST associada ao modelo, com todos os seus endpoints.",
|
|
66
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class IndexModel(BaseModel):
|
|
6
|
+
name: str = Field(..., description="Nome do índice no banco de dados.")
|
|
7
|
+
columns: List[str] = Field(
|
|
8
|
+
..., description="Lista de colunas que compõem o índice."
|
|
9
|
+
)
|
|
10
|
+
unique: Optional[bool] = Field(
|
|
11
|
+
default=False,
|
|
12
|
+
description="Indica se é um índice de unicidade (padrão: False). Se não for de unicidade, será um índice usado para otimização de queries.",
|
|
13
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PrimitiveTypes(enum.Enum):
|
|
6
|
+
# TODO Validar esses tipos
|
|
7
|
+
STRING = "string"
|
|
8
|
+
NUMBER = "number"
|
|
9
|
+
INTEGER = "integer"
|
|
10
|
+
BOOLEAN = "boolean"
|
|
11
|
+
ARRAY = "array"
|
|
12
|
+
OBJECT = "object"
|
|
13
|
+
UUID = "uuid"
|
|
14
|
+
CURRENCY = "currency"
|
|
15
|
+
QUANTITY = "quantity"
|
|
16
|
+
CPF = "cpf"
|
|
17
|
+
CNPJ = "cnpj"
|
|
18
|
+
CPF_CNPJ = "cpf_cnpj"
|
|
19
|
+
EMAIL = "email"
|
|
20
|
+
DATE = "date"
|
|
21
|
+
DATETIME = "datetime"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
|
|
25
|
+
PrimitiveTypes.STRING: "str",
|
|
26
|
+
PrimitiveTypes.NUMBER: "float",
|
|
27
|
+
PrimitiveTypes.INTEGER: "int",
|
|
28
|
+
PrimitiveTypes.BOOLEAN: "bool",
|
|
29
|
+
PrimitiveTypes.ARRAY: "List",
|
|
30
|
+
PrimitiveTypes.OBJECT: "dict",
|
|
31
|
+
PrimitiveTypes.UUID: "uuid.UUID",
|
|
32
|
+
PrimitiveTypes.CURRENCY: "float",
|
|
33
|
+
PrimitiveTypes.QUANTITY: "float",
|
|
34
|
+
PrimitiveTypes.CPF: "str",
|
|
35
|
+
PrimitiveTypes.CNPJ: "str",
|
|
36
|
+
PrimitiveTypes.CPF_CNPJ: "str",
|
|
37
|
+
PrimitiveTypes.EMAIL: "str",
|
|
38
|
+
PrimitiveTypes.DATE: "datetime.date",
|
|
39
|
+
PrimitiveTypes.DATETIME: "datetime.datetime",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
BasicTypes = int | bool | float | str
|
|
43
|
+
DefaultTypes = BasicTypes | List[BasicTypes]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes, DefaultTypes
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import PrimitiveTypes
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DomainConfigModel(BaseModel):
|
|
9
|
+
value: str = Field(..., description="Valor do enum.")
|
|
10
|
+
mapped_value: Optional[BasicTypes] = Field(
|
|
11
|
+
None, description="Valor mapeado do enum (gravado no BD)."
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PropertyMetaModel(BaseModel):
|
|
16
|
+
type: PrimitiveTypes = Field(..., description="Tipo da propriedade.")
|
|
17
|
+
label: Optional[str] = Field(None, description="Rótulo da propriedade.")
|
|
18
|
+
description: Optional[str] = Field(None, description="Descrição da propriedade.")
|
|
19
|
+
default: Optional[DefaultTypes] = Field(
|
|
20
|
+
None,
|
|
21
|
+
description="Valor padrão da propriedade (caso não fornecido numa chamada de persistência). Pode indicar um valor, ou uma expressão (ex.: 'now()' para data/hora atual).",
|
|
22
|
+
)
|
|
23
|
+
pk: Optional[bool] = Field(
|
|
24
|
+
default=False, description="Indica se a propriedade é parte da chave primária."
|
|
25
|
+
)
|
|
26
|
+
key_alternative: Optional[bool] = Field(
|
|
27
|
+
default=False,
|
|
28
|
+
description="Indica se a propriedade é parte de uma chave alternativa (chave natural).",
|
|
29
|
+
alias="keyAlternative",
|
|
30
|
+
)
|
|
31
|
+
# Validação
|
|
32
|
+
max_length: Optional[int] = Field(
|
|
33
|
+
None, description="Comprimento máximo (para strings)."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
## TODO Sugestões de validação
|
|
37
|
+
min_length: Optional[int] = Field(
|
|
38
|
+
None, description="Comprimento mínimo (para strings).", alias="minLength"
|
|
39
|
+
)
|
|
40
|
+
pattern: Optional[str] = Field(
|
|
41
|
+
None,
|
|
42
|
+
description="Expressão regular que o valor da propriedade deve obedecer (para strings).",
|
|
43
|
+
)
|
|
44
|
+
minimum: Optional[float] = Field(
|
|
45
|
+
None, description="Valor mínimo permitido (para números)."
|
|
46
|
+
)
|
|
47
|
+
maximum: Optional[float] = Field(
|
|
48
|
+
None, description="Valor máximo permitido (para números)."
|
|
49
|
+
)
|
|
50
|
+
validator: Optional[str] = Field(
|
|
51
|
+
None,
|
|
52
|
+
description="Caminho completo para uma função de validação e alteração personalizada (ex.: 'module.validator'). Sendo que as funções dinâmicas sempre estarão no modulo XPTO.",
|
|
53
|
+
) # TODO Definir padrão de carregamento do módulo de funções customizadas
|
|
54
|
+
domain_config: Optional[List[DomainConfigModel]] = Field(
|
|
55
|
+
None,
|
|
56
|
+
description="Lista de valores permitidos.",
|
|
57
|
+
)
|
|
58
|
+
immutable: Optional[bool] = Field(
|
|
59
|
+
default=False,
|
|
60
|
+
description="Indica se a propriedade é imutável (não pode ser alterada após a criação).",
|
|
61
|
+
)
|
|
62
|
+
trim: Optional[bool] = Field(
|
|
63
|
+
default=False,
|
|
64
|
+
description="Indica se espaços em branco devem ser removidos do início e fim (para strings).",
|
|
65
|
+
)
|
|
66
|
+
lowercase: Optional[bool] = Field(
|
|
67
|
+
default=False,
|
|
68
|
+
description="Indica se o valor deve ser convertido para minúsculas (para strings).",
|
|
69
|
+
)
|
|
70
|
+
uppercase: Optional[bool] = Field(
|
|
71
|
+
default=False,
|
|
72
|
+
description="Indica se o valor deve ser convertido para maiúsculas (para strings).",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
on_save: Optional[str] = Field(
|
|
76
|
+
None,
|
|
77
|
+
description="Função de conversão para o tipo da entidade (ex.: converter string para uuid).",
|
|
78
|
+
)
|
|
79
|
+
on_retrieve: Optional[str] = Field(
|
|
80
|
+
None,
|
|
81
|
+
description="Função de conversão do tipo da entidade para o tipo da API (ex.: converter uuid para string).",
|
|
82
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from nsj_rest_lib2.compiler.edl_model.column_meta_model import ColumnMetaModel
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.index_model import IndexModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RepositoryModel(BaseModel):
|
|
9
|
+
map: str = Field(
|
|
10
|
+
..., description="Nome da tabela, no BD, para a qual a entidade é mapeada."
|
|
11
|
+
)
|
|
12
|
+
shared_table: Optional[bool] = Field(
|
|
13
|
+
default=False,
|
|
14
|
+
description="Indica se a tabela é compartilhada entre múltiplas entidades (padrão: False).",
|
|
15
|
+
)
|
|
16
|
+
table_owner: Optional[bool] = Field(
|
|
17
|
+
None, description="Indica que essa entidade é dona da tabela (schema)."
|
|
18
|
+
) # TODO Validar explicação
|
|
19
|
+
properties: Optional[dict[str, ColumnMetaModel]] = Field(
|
|
20
|
+
None, description="Dicionário de colunas da entidade."
|
|
21
|
+
)
|
|
22
|
+
indexes: Optional[List[IndexModel]] = Field(
|
|
23
|
+
None, description="Lista de índices de banco de dados, associados à entidade."
|
|
24
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
from nsj_rest_lib2.compiler.edl_model.primitives import BasicTypes, PrimitiveTypes
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TraitPropertyMetaModel(BaseModel):
|
|
7
|
+
type: PrimitiveTypes = Field(..., description="Tipo da propriedade.")
|
|
8
|
+
value: BasicTypes = Field(
|
|
9
|
+
..., description="Valor fixo da propriedade de condicionamento do trait."
|
|
10
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
import black
|
|
4
|
+
|
|
5
|
+
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
6
|
+
from nsj_rest_lib2.compiler.edl_model.repository_model import RepositoryModel
|
|
7
|
+
from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EntityCompiler:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
def compile(
|
|
15
|
+
self,
|
|
16
|
+
entity_model: EntityModel,
|
|
17
|
+
ast_entity_attributes: list[ast.stmt],
|
|
18
|
+
props_pk: list[str],
|
|
19
|
+
) -> tuple[str, str]:
|
|
20
|
+
# Imports
|
|
21
|
+
imports = [
|
|
22
|
+
# import datetime
|
|
23
|
+
ast.Import(names=[ast.alias(name="datetime", asname=None)]),
|
|
24
|
+
# import uuid
|
|
25
|
+
ast.Import(names=[ast.alias(name="uuid", asname=None)]),
|
|
26
|
+
# from nsj_rest_lib.entity.entity_base import EntityBase
|
|
27
|
+
ast.ImportFrom(
|
|
28
|
+
module="nsj_rest_lib.entity.entity_base",
|
|
29
|
+
names=[ast.alias(name="EntityBase", asname=None)],
|
|
30
|
+
level=0,
|
|
31
|
+
),
|
|
32
|
+
# from nsj_rest_lib.decorator.entity import Entity
|
|
33
|
+
ast.ImportFrom(
|
|
34
|
+
module="nsj_rest_lib.decorator.entity",
|
|
35
|
+
names=[ast.alias(name="Entity", asname=None)],
|
|
36
|
+
level=0,
|
|
37
|
+
),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Entity
|
|
41
|
+
if len(props_pk) > 1:
|
|
42
|
+
raise Exception(
|
|
43
|
+
f"Entidade '{entity_model.id}' possui mais de uma chave primária (ainda não suportado): {props_pk}"
|
|
44
|
+
)
|
|
45
|
+
elif len(props_pk) <= 0:
|
|
46
|
+
raise Exception(
|
|
47
|
+
f"Entidade '{entity_model.id}' não possui nenhuma chave primária (ainda não suportado)."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
default_order_props = []
|
|
51
|
+
if entity_model.api and entity_model.api.default_sort:
|
|
52
|
+
default_order_props = entity_model.api.default_sort
|
|
53
|
+
|
|
54
|
+
default_order_fields = []
|
|
55
|
+
for prop in default_order_props:
|
|
56
|
+
if (
|
|
57
|
+
entity_model.repository.properties
|
|
58
|
+
and prop in entity_model.repository.properties
|
|
59
|
+
):
|
|
60
|
+
field = entity_model.repository.properties[prop].column
|
|
61
|
+
else:
|
|
62
|
+
field = prop
|
|
63
|
+
|
|
64
|
+
default_order_fields.append(CompilerStrUtil.to_snake_case(field))
|
|
65
|
+
|
|
66
|
+
if CompilerStrUtil.to_snake_case(props_pk[0]) not in default_order_fields:
|
|
67
|
+
default_order_fields.append(CompilerStrUtil.to_snake_case(props_pk[0]))
|
|
68
|
+
|
|
69
|
+
class_name = f"{CompilerStrUtil.to_pascal_case(entity_model.id)}Entity"
|
|
70
|
+
ast_class = ast.ClassDef(
|
|
71
|
+
name=class_name,
|
|
72
|
+
bases=[ast.Name(id="EntityBase", ctx=ast.Load())],
|
|
73
|
+
keywords=[],
|
|
74
|
+
decorator_list=[
|
|
75
|
+
ast.Call(
|
|
76
|
+
func=ast.Name(id="Entity", ctx=ast.Load()),
|
|
77
|
+
args=[],
|
|
78
|
+
keywords=[
|
|
79
|
+
ast.keyword(
|
|
80
|
+
arg="table_name",
|
|
81
|
+
value=ast.Constant(value=entity_model.repository.map),
|
|
82
|
+
),
|
|
83
|
+
ast.keyword(
|
|
84
|
+
arg="pk_field",
|
|
85
|
+
value=ast.Constant(
|
|
86
|
+
value=CompilerStrUtil.to_snake_case(props_pk[0])
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
ast.keyword(
|
|
90
|
+
arg="default_order_fields",
|
|
91
|
+
value=ast.List(
|
|
92
|
+
elts=[
|
|
93
|
+
ast.Constant(value=field)
|
|
94
|
+
for field in default_order_fields
|
|
95
|
+
],
|
|
96
|
+
ctx=ast.Load(),
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
],
|
|
100
|
+
)
|
|
101
|
+
],
|
|
102
|
+
body=ast_entity_attributes,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Definindo o módulo
|
|
106
|
+
module = ast.Module(
|
|
107
|
+
body=imports + [ast_class],
|
|
108
|
+
type_ignores=[],
|
|
109
|
+
)
|
|
110
|
+
module = ast.fix_missing_locations(module)
|
|
111
|
+
|
|
112
|
+
# Compilando o AST do DTO para o código Python
|
|
113
|
+
code = ast.unparse(module)
|
|
114
|
+
|
|
115
|
+
# Chamando o black para formatar o código Python do DTO
|
|
116
|
+
code = black.format_str(code, mode=black.FileMode())
|
|
117
|
+
|
|
118
|
+
return (class_name, code)
|