nsj-rest-lib2 0.0.3__py3-none-any.whl → 0.0.5__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.
@@ -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,6 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import List, Optional, Literal
3
+
4
+
5
+ class ColumnMetaModel(BaseModel):
6
+ column: str = Field(..., description="Nome da coluna no banco de dados.")
@@ -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)