nsj-rest-lib2 0.0.21__tar.gz → 0.0.23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/PKG-INFO +1 -14
- nsj_rest_lib2-0.0.23/README.md +6 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/compiler.py +3 -13
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/primitives.py +8 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -1
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/property_compiler.py +313 -247
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/util/str_util.py +10 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/service/entity_loader.py +0 -1
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2.egg-info/PKG-INFO +1 -14
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/setup.cfg +1 -1
- nsj_rest_lib2-0.0.21/README.md +0 -19
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/ai_compiler.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/compiler_structures.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/dto_compiler.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/api_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model_base.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model_root.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/entity_compiler.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/model.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/util/type_naming_util.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/controller/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/controller/dynamic_controller.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/exception.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/redis_config.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/service/__init__.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/settings.py +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2.egg-info/SOURCES.txt +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2.egg-info/requires.txt +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
- {nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.23
|
|
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
|
|
@@ -25,16 +25,3 @@ Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configur
|
|
|
25
25
|
|
|
26
26
|
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
27
27
|
|
|
28
|
-
## TODO
|
|
29
|
-
* Valores default
|
|
30
|
-
* Usar trait_properties como valor default
|
|
31
|
-
* Não está carregando a lista de dependências de execução, para os components
|
|
32
|
-
* Implementar gravação de entidades com extensão parcial
|
|
33
|
-
* Unificar o arquivo redis_config.py
|
|
34
|
-
* Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
|
|
35
|
-
* Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
|
|
36
|
-
* Implementar e documentar campos join
|
|
37
|
-
* Implementar e documentar conjuntos
|
|
38
|
-
* Dar erro ao haver conflito de nomes das propriedades (por qualquer tipo de herança)
|
|
39
|
-
* Rotas para o inline
|
|
40
|
-
* inline será para composição (arquivo externo é agregação)
|
|
@@ -25,18 +25,6 @@ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
|
25
25
|
|
|
26
26
|
from nsj_rest_lib2.settings import get_logger
|
|
27
27
|
|
|
28
|
-
# TODO GET e POST de relacionamentos 1X1
|
|
29
|
-
# TODO Revisar compilação de valores default (sensível a tipos)
|
|
30
|
-
# TODO Implementar suporte a conjuntos
|
|
31
|
-
# TODO Autenticação nas rotas
|
|
32
|
-
# TODO Atualizar o status da entidade pelo worker de compilação (e talvez parar uma compilação, quando se delete uma entidade)
|
|
33
|
-
# TODO Classes Abstratas
|
|
34
|
-
# TODO Partial Classes
|
|
35
|
-
# TODO Migrations
|
|
36
|
-
# TODO Criar imagem docker base para as aplicações, usando venv (para poderem atualizar o pydantic)
|
|
37
|
-
|
|
38
|
-
# TODO Suporte ao "min_items" dos relacionamentos no RestLib1
|
|
39
|
-
|
|
40
28
|
|
|
41
29
|
class EDLCompiler:
|
|
42
30
|
def __init__(self) -> None:
|
|
@@ -117,7 +105,9 @@ class EDLCompiler:
|
|
|
117
105
|
f"Entidade base '{entity_model.partial_of}' da extensão parcial '{entity_model.id}' é inválida."
|
|
118
106
|
)
|
|
119
107
|
|
|
120
|
-
self._validate_partial_model(
|
|
108
|
+
self._validate_partial_model(
|
|
109
|
+
entity_model, base_model_candidate, entity_models
|
|
110
|
+
)
|
|
121
111
|
partial_metadata = self._build_partial_metadata(
|
|
122
112
|
entity_model, base_model_candidate
|
|
123
113
|
)
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/primitives.py
RENAMED
|
@@ -48,6 +48,14 @@ MAPPING_PRIMITIVE_TYPES_TO_PYTHON = {
|
|
|
48
48
|
BasicTypes = int | bool | float | str
|
|
49
49
|
DefaultTypes = BasicTypes | List[BasicTypes]
|
|
50
50
|
|
|
51
|
+
STR_BASED_TYPES = {
|
|
52
|
+
PrimitiveTypes.STRING,
|
|
53
|
+
PrimitiveTypes.EMAIL,
|
|
54
|
+
PrimitiveTypes.CPF,
|
|
55
|
+
PrimitiveTypes.CNPJ,
|
|
56
|
+
PrimitiveTypes.CPF_CNPJ,
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
|
|
52
60
|
class CardinalityTypes(enum.Enum):
|
|
53
61
|
C1_1 = "1_1"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
import datetime
|
|
2
3
|
import re
|
|
4
|
+
import uuid
|
|
3
5
|
|
|
4
6
|
from nsj_rest_lib2.compiler.compiler_structures import (
|
|
5
7
|
IndexCompilerStructure,
|
|
@@ -13,6 +15,7 @@ from nsj_rest_lib2.compiler.edl_model.primitives import (
|
|
|
13
15
|
PrimitiveTypes,
|
|
14
16
|
REGEX_EXTERNAL_REF,
|
|
15
17
|
REGEX_INTERNAL_REF,
|
|
18
|
+
STR_BASED_TYPES,
|
|
16
19
|
)
|
|
17
20
|
from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
|
|
18
21
|
from nsj_rest_lib2.compiler.edl_model.trait_property_meta_model import (
|
|
@@ -27,10 +30,6 @@ from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
|
27
30
|
)
|
|
28
31
|
from nsj_rest_lib2.compiler.util.type_util import TypeUtil
|
|
29
32
|
|
|
30
|
-
# TODO pattern
|
|
31
|
-
# TODO lowercase
|
|
32
|
-
# TODO uppercase
|
|
33
|
-
|
|
34
33
|
|
|
35
34
|
class EDLPropertyCompiler:
|
|
36
35
|
|
|
@@ -51,9 +50,6 @@ class EDLPropertyCompiler:
|
|
|
51
50
|
list[RelationDependency],
|
|
52
51
|
list[tuple[str, BasicTypes]],
|
|
53
52
|
]:
|
|
54
|
-
|
|
55
|
-
# TODO Criar opção de campo calculado?
|
|
56
|
-
|
|
57
53
|
# Descobrindo os atributos marcados como PK (e recuperando a chave primária)
|
|
58
54
|
# TODO Verificar se devemos manter essa verificação
|
|
59
55
|
pk_keys = []
|
|
@@ -64,8 +60,8 @@ class EDLPropertyCompiler:
|
|
|
64
60
|
if prop.pk:
|
|
65
61
|
pk_keys.append(pkey)
|
|
66
62
|
|
|
67
|
-
is_partial_extension = (
|
|
68
|
-
|
|
63
|
+
is_partial_extension = isinstance(entity_model, EntityModel) and bool(
|
|
64
|
+
entity_model.partial_of
|
|
69
65
|
)
|
|
70
66
|
|
|
71
67
|
if not entity_model.mixin:
|
|
@@ -92,6 +88,10 @@ class EDLPropertyCompiler:
|
|
|
92
88
|
if properties_structure.properties is None:
|
|
93
89
|
return (ast_dto_attributes, ast_entity_attributes, props_pk, aux_classes)
|
|
94
90
|
|
|
91
|
+
trait_fixed_filters = self._merge_trait_extends_properties(
|
|
92
|
+
properties_structure
|
|
93
|
+
)
|
|
94
|
+
|
|
95
95
|
composed_properties = properties_structure.composed_properties or {}
|
|
96
96
|
|
|
97
97
|
aggregator_class_names: dict[str, str] = {}
|
|
@@ -155,6 +155,9 @@ class EDLPropertyCompiler:
|
|
|
155
155
|
prefx_class_name,
|
|
156
156
|
)
|
|
157
157
|
|
|
158
|
+
if pkey in trait_fixed_filters:
|
|
159
|
+
fixed_filters.append((pkey, trait_fixed_filters[pkey]))
|
|
160
|
+
|
|
158
161
|
elif isinstance(prop.type, str):
|
|
159
162
|
# Tratando propriedade de relacionamento
|
|
160
163
|
external_match = re.match(REGEX_EXTERNAL_REF, prop.type)
|
|
@@ -197,40 +200,6 @@ class EDLPropertyCompiler:
|
|
|
197
200
|
f"Tipo da propriedade '{pkey}' não suportado: {prop.type}"
|
|
198
201
|
)
|
|
199
202
|
|
|
200
|
-
for pkey in properties_structure.trait_properties:
|
|
201
|
-
prop = properties_structure.trait_properties[pkey]
|
|
202
|
-
|
|
203
|
-
self._compile_trait_extends_property(
|
|
204
|
-
properties_structure,
|
|
205
|
-
map_unique_by_property,
|
|
206
|
-
pkey,
|
|
207
|
-
prop,
|
|
208
|
-
ast_dto_attributes,
|
|
209
|
-
ast_entity_attributes,
|
|
210
|
-
fixed_filters,
|
|
211
|
-
escopo,
|
|
212
|
-
entity_model,
|
|
213
|
-
aux_classes,
|
|
214
|
-
prefx_class_name,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
for pkey in properties_structure.extends_properties:
|
|
218
|
-
prop = properties_structure.extends_properties[pkey]
|
|
219
|
-
|
|
220
|
-
self._compile_trait_extends_property(
|
|
221
|
-
properties_structure,
|
|
222
|
-
map_unique_by_property,
|
|
223
|
-
pkey,
|
|
224
|
-
prop,
|
|
225
|
-
ast_dto_attributes,
|
|
226
|
-
ast_entity_attributes,
|
|
227
|
-
fixed_filters,
|
|
228
|
-
escopo,
|
|
229
|
-
entity_model,
|
|
230
|
-
aux_classes,
|
|
231
|
-
prefx_class_name,
|
|
232
|
-
)
|
|
233
|
-
|
|
234
203
|
for composed_key, class_name in aggregator_class_names.items():
|
|
235
204
|
dto_attributes = aggregator_dto_attributes.get(composed_key, [])
|
|
236
205
|
|
|
@@ -258,199 +227,6 @@ class EDLPropertyCompiler:
|
|
|
258
227
|
fixed_filters,
|
|
259
228
|
)
|
|
260
229
|
|
|
261
|
-
def _compile_trait_extends_property(
|
|
262
|
-
self,
|
|
263
|
-
properties_structure: PropertiesCompilerStructure,
|
|
264
|
-
map_unique_by_property: dict[str, IndexCompilerStructure],
|
|
265
|
-
pkey: str,
|
|
266
|
-
prop: TraitPropertyMetaModel,
|
|
267
|
-
ast_dto_attributes: list[ast.stmt],
|
|
268
|
-
ast_entity_attributes: list[ast.stmt],
|
|
269
|
-
fixed_filters: list[tuple[str, BasicTypes]],
|
|
270
|
-
escopo: str,
|
|
271
|
-
entity_model: EntityModelBase,
|
|
272
|
-
aux_classes: list[ast.stmt],
|
|
273
|
-
prefx_class_name: str,
|
|
274
|
-
):
|
|
275
|
-
enum_class_name = None
|
|
276
|
-
keywords = []
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
properties_structure.main_properties
|
|
280
|
-
and pkey in properties_structure.main_properties
|
|
281
|
-
):
|
|
282
|
-
keywords.append(ast.keyword(arg="resume", value=ast.Constant(True)))
|
|
283
|
-
|
|
284
|
-
if properties_structure.required and pkey in properties_structure.required:
|
|
285
|
-
keywords.append(ast.keyword(arg="not_null", value=ast.Constant(True)))
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
properties_structure.partition_data
|
|
289
|
-
and pkey in properties_structure.partition_data
|
|
290
|
-
):
|
|
291
|
-
keywords.append(ast.keyword(arg="partition_data", value=ast.Constant(True)))
|
|
292
|
-
|
|
293
|
-
if pkey in map_unique_by_property:
|
|
294
|
-
unique = map_unique_by_property[pkey].index_model
|
|
295
|
-
keywords.append(
|
|
296
|
-
ast.keyword(
|
|
297
|
-
arg="unique",
|
|
298
|
-
value=ast.Constant(unique.name),
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
if (
|
|
303
|
-
properties_structure.search_properties
|
|
304
|
-
and pkey in properties_structure.search_properties
|
|
305
|
-
):
|
|
306
|
-
keywords.append(ast.keyword(arg="search", value=ast.Constant(True)))
|
|
307
|
-
else:
|
|
308
|
-
keywords.append(ast.keyword(arg="search", value=ast.Constant(False)))
|
|
309
|
-
|
|
310
|
-
if (
|
|
311
|
-
properties_structure.metric_label
|
|
312
|
-
and pkey in properties_structure.metric_label
|
|
313
|
-
):
|
|
314
|
-
keywords.append(ast.keyword(arg="metric_label", value=ast.Constant(True)))
|
|
315
|
-
|
|
316
|
-
if prop.type == PrimitiveTypes.CPF:
|
|
317
|
-
keywords.append(
|
|
318
|
-
ast.keyword(
|
|
319
|
-
arg="validator",
|
|
320
|
-
value=ast.Attribute(
|
|
321
|
-
value=ast.Call(
|
|
322
|
-
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
323
|
-
args=[],
|
|
324
|
-
keywords=[],
|
|
325
|
-
),
|
|
326
|
-
attr="validate_cpf",
|
|
327
|
-
ctx=ast.Load(),
|
|
328
|
-
),
|
|
329
|
-
)
|
|
330
|
-
)
|
|
331
|
-
elif prop.type == PrimitiveTypes.CNPJ:
|
|
332
|
-
keywords.append(
|
|
333
|
-
ast.keyword(
|
|
334
|
-
arg="validator",
|
|
335
|
-
value=ast.Attribute(
|
|
336
|
-
value=ast.Call(
|
|
337
|
-
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
338
|
-
args=[],
|
|
339
|
-
keywords=[],
|
|
340
|
-
),
|
|
341
|
-
attr="validate_cnpj",
|
|
342
|
-
ctx=ast.Load(),
|
|
343
|
-
),
|
|
344
|
-
)
|
|
345
|
-
)
|
|
346
|
-
elif prop.type == PrimitiveTypes.CPF_CNPJ:
|
|
347
|
-
keywords.append(
|
|
348
|
-
ast.keyword(
|
|
349
|
-
arg="validator",
|
|
350
|
-
value=ast.Attribute(
|
|
351
|
-
value=ast.Call(
|
|
352
|
-
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
353
|
-
args=[],
|
|
354
|
-
keywords=[],
|
|
355
|
-
),
|
|
356
|
-
attr="validate_cpf_or_cnpj",
|
|
357
|
-
ctx=ast.Load(),
|
|
358
|
-
),
|
|
359
|
-
)
|
|
360
|
-
)
|
|
361
|
-
elif prop.type == PrimitiveTypes.EMAIL:
|
|
362
|
-
keywords.append(
|
|
363
|
-
ast.keyword(
|
|
364
|
-
arg="validator",
|
|
365
|
-
value=ast.Attribute(
|
|
366
|
-
value=ast.Call(
|
|
367
|
-
func=ast.Name(id="DTOFieldValidators", ctx=ast.Load()),
|
|
368
|
-
args=[],
|
|
369
|
-
keywords=[],
|
|
370
|
-
),
|
|
371
|
-
attr="validate_email",
|
|
372
|
-
ctx=ast.Load(),
|
|
373
|
-
),
|
|
374
|
-
)
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
# Trtando de uma definição de enum
|
|
378
|
-
if prop.domain_config:
|
|
379
|
-
result = self._compile_domain_config(
|
|
380
|
-
pkey, prop, escopo, entity_model, prefx_class_name
|
|
381
|
-
)
|
|
382
|
-
if not result:
|
|
383
|
-
raise Exception(f"Erro desconhecido ao compilar a propriedade {pkey}")
|
|
384
|
-
|
|
385
|
-
enum_class_name, ast_enum_class = result
|
|
386
|
-
aux_classes.append(ast_enum_class)
|
|
387
|
-
|
|
388
|
-
# Resolvendo o nome da propriedade no Entity
|
|
389
|
-
if (
|
|
390
|
-
properties_structure.entity_properties
|
|
391
|
-
and pkey in properties_structure.entity_properties
|
|
392
|
-
):
|
|
393
|
-
entity_field_name = properties_structure.entity_properties[pkey].column
|
|
394
|
-
else:
|
|
395
|
-
entity_field_name = pkey
|
|
396
|
-
|
|
397
|
-
# Escrevendo, se necessário, o alias para o nome da entity
|
|
398
|
-
if entity_field_name != pkey:
|
|
399
|
-
keywords.append(
|
|
400
|
-
ast.keyword(
|
|
401
|
-
arg="entity_field",
|
|
402
|
-
value=ast.Constant(value=entity_field_name),
|
|
403
|
-
)
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
# Instanciando o atributo AST
|
|
407
|
-
if not isinstance(prop.type, PrimitiveTypes):
|
|
408
|
-
raise Exception(
|
|
409
|
-
f"Tipo da trait_property '{pkey}' não suportado: {prop.type} (deveria ser um Tipo Primitivo)"
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
# Instanciando o atributo AST
|
|
413
|
-
if enum_class_name:
|
|
414
|
-
prop_type = enum_class_name
|
|
415
|
-
else:
|
|
416
|
-
prop_type = TypeUtil.property_type_to_python_type(prop.type)
|
|
417
|
-
|
|
418
|
-
ast_attr = ast.AnnAssign(
|
|
419
|
-
target=ast.Name(id=CompilerStrUtil.to_snake_case(pkey), ctx=ast.Store()),
|
|
420
|
-
annotation=ast.Name(
|
|
421
|
-
id=prop_type,
|
|
422
|
-
ctx=ast.Load(),
|
|
423
|
-
),
|
|
424
|
-
value=ast.Call(
|
|
425
|
-
func=ast.Name(id="DTOField", ctx=ast.Load()),
|
|
426
|
-
args=[],
|
|
427
|
-
keywords=keywords,
|
|
428
|
-
),
|
|
429
|
-
simple=1,
|
|
430
|
-
)
|
|
431
|
-
|
|
432
|
-
ast_dto_attributes.append(ast_attr)
|
|
433
|
-
|
|
434
|
-
# Entity
|
|
435
|
-
ast_entity_attr = ast.AnnAssign(
|
|
436
|
-
target=ast.Name(
|
|
437
|
-
id=CompilerStrUtil.to_snake_case(entity_field_name),
|
|
438
|
-
ctx=ast.Store(),
|
|
439
|
-
),
|
|
440
|
-
annotation=ast.Name(
|
|
441
|
-
id=TypeUtil.property_type_to_python_type(prop.type),
|
|
442
|
-
ctx=ast.Load(),
|
|
443
|
-
),
|
|
444
|
-
value=ast.Constant(value=None),
|
|
445
|
-
simple=1,
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
ast_entity_attributes.append(ast_entity_attr)
|
|
449
|
-
|
|
450
|
-
# Guardando como um fixed_filter
|
|
451
|
-
# TODO Pensar em validações para esse value (se está de acordo com o tipo ou enum)
|
|
452
|
-
fixed_filters.append((pkey, prop.value))
|
|
453
|
-
|
|
454
230
|
def _build_aggregator_class_ast(
|
|
455
231
|
self,
|
|
456
232
|
class_name: str,
|
|
@@ -944,16 +720,6 @@ class EDLPropertyCompiler:
|
|
|
944
720
|
)
|
|
945
721
|
)
|
|
946
722
|
|
|
947
|
-
if (
|
|
948
|
-
prop.default
|
|
949
|
-
): # TODO Verificar esse modo de tratar valores default (principalmente expressões)
|
|
950
|
-
keywords.append(
|
|
951
|
-
ast.keyword(
|
|
952
|
-
arg="default_value",
|
|
953
|
-
value=ast.Name(str(prop.default), ctx=ast.Load()),
|
|
954
|
-
)
|
|
955
|
-
)
|
|
956
|
-
|
|
957
723
|
if prop.trim:
|
|
958
724
|
keywords.append(ast.keyword(arg="strip", value=ast.Constant(True)))
|
|
959
725
|
|
|
@@ -1086,6 +852,15 @@ class EDLPropertyCompiler:
|
|
|
1086
852
|
enum_class_name, ast_enum_class = result
|
|
1087
853
|
aux_classes.append(ast_enum_class)
|
|
1088
854
|
|
|
855
|
+
default_value_ast = self._build_default_value_ast(pkey, prop, enum_class_name)
|
|
856
|
+
if default_value_ast is not None:
|
|
857
|
+
keywords.append(
|
|
858
|
+
ast.keyword(
|
|
859
|
+
arg="default_value",
|
|
860
|
+
value=default_value_ast,
|
|
861
|
+
)
|
|
862
|
+
)
|
|
863
|
+
|
|
1089
864
|
# Resolvendo o nome da propriedade no Entity
|
|
1090
865
|
if (
|
|
1091
866
|
properties_structure.entity_properties
|
|
@@ -1118,6 +893,297 @@ class EDLPropertyCompiler:
|
|
|
1118
893
|
|
|
1119
894
|
ast_entity_attributes.append(ast_entity_attr)
|
|
1120
895
|
|
|
896
|
+
def _build_default_value_ast(
|
|
897
|
+
self,
|
|
898
|
+
pkey: str,
|
|
899
|
+
prop: PropertyMetaModel,
|
|
900
|
+
enum_class_name: str | None,
|
|
901
|
+
) -> ast.expr | None:
|
|
902
|
+
default_value = prop.default
|
|
903
|
+
if default_value is None:
|
|
904
|
+
return None
|
|
905
|
+
|
|
906
|
+
if prop.domain_config and enum_class_name:
|
|
907
|
+
return self._build_enum_default_value_ast(
|
|
908
|
+
pkey, prop, enum_class_name, default_value
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
if not isinstance(prop.type, PrimitiveTypes):
|
|
912
|
+
raise ValueError(
|
|
913
|
+
f"Propriedade '{pkey}' não suporta valor default para relacionamentos."
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
return self._build_primitive_default_value_ast(pkey, prop, default_value)
|
|
917
|
+
|
|
918
|
+
def _build_enum_default_value_ast(
|
|
919
|
+
self,
|
|
920
|
+
pkey: str,
|
|
921
|
+
prop: PropertyMetaModel,
|
|
922
|
+
enum_class_name: str,
|
|
923
|
+
default_value: object,
|
|
924
|
+
) -> ast.expr:
|
|
925
|
+
target_option = None
|
|
926
|
+
for option in prop.domain_config or []:
|
|
927
|
+
if option.value == default_value:
|
|
928
|
+
target_option = option
|
|
929
|
+
break
|
|
930
|
+
|
|
931
|
+
if option.mapped_value is not None and option.mapped_value == default_value:
|
|
932
|
+
target_option = option
|
|
933
|
+
break
|
|
934
|
+
|
|
935
|
+
if not target_option:
|
|
936
|
+
raise ValueError(
|
|
937
|
+
f"Propriedade '{pkey}' possui valor default '{default_value}' que não corresponde a nenhuma opção do enum."
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
enum_member_name = CompilerStrUtil.to_enum_member_name(target_option.value)
|
|
941
|
+
|
|
942
|
+
return ast.Attribute(
|
|
943
|
+
value=ast.Name(id=enum_class_name, ctx=ast.Load()),
|
|
944
|
+
attr=enum_member_name,
|
|
945
|
+
ctx=ast.Load(),
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
def _merge_trait_extends_properties(
|
|
949
|
+
self, properties_structure: PropertiesCompilerStructure
|
|
950
|
+
) -> dict[str, BasicTypes]:
|
|
951
|
+
fixed_filters: dict[str, BasicTypes] = {}
|
|
952
|
+
|
|
953
|
+
trait_sources = (
|
|
954
|
+
properties_structure.trait_properties or {},
|
|
955
|
+
properties_structure.extends_properties or {},
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
for prop_dict in trait_sources:
|
|
959
|
+
for pkey, tprop in prop_dict.items():
|
|
960
|
+
if not isinstance(tprop.type, PrimitiveTypes):
|
|
961
|
+
raise ValueError(
|
|
962
|
+
f"Propriedade '{pkey}' definida em trait/extends precisa ser um tipo primitivo."
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
base_prop = properties_structure.properties.get(pkey)
|
|
966
|
+
|
|
967
|
+
if base_prop:
|
|
968
|
+
if base_prop.type != tprop.type:
|
|
969
|
+
raise ValueError(
|
|
970
|
+
f"Tipo da propriedade '{pkey}' em trait/extends não coincide com a definição existente."
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
base_prop.default = tprop.value
|
|
974
|
+
|
|
975
|
+
if tprop.domain_config and not base_prop.domain_config:
|
|
976
|
+
base_prop.domain_config = tprop.domain_config
|
|
977
|
+
else:
|
|
978
|
+
base_prop = PropertyMetaModel(
|
|
979
|
+
type=tprop.type,
|
|
980
|
+
default=tprop.value,
|
|
981
|
+
domain_config=tprop.domain_config,
|
|
982
|
+
)
|
|
983
|
+
properties_structure.properties[pkey] = base_prop
|
|
984
|
+
|
|
985
|
+
fixed_filters[pkey] = tprop.value
|
|
986
|
+
|
|
987
|
+
return fixed_filters
|
|
988
|
+
|
|
989
|
+
def _build_primitive_default_value_ast(
|
|
990
|
+
self,
|
|
991
|
+
pkey: str,
|
|
992
|
+
prop: PropertyMetaModel,
|
|
993
|
+
default_value: object,
|
|
994
|
+
) -> ast.expr:
|
|
995
|
+
primitive_type = prop.type
|
|
996
|
+
|
|
997
|
+
if isinstance(default_value, str):
|
|
998
|
+
expression_ast = self._build_python_expression_from_string(default_value)
|
|
999
|
+
if expression_ast is not None:
|
|
1000
|
+
return expression_ast
|
|
1001
|
+
|
|
1002
|
+
if primitive_type in STR_BASED_TYPES:
|
|
1003
|
+
if not isinstance(default_value, str):
|
|
1004
|
+
raise ValueError(
|
|
1005
|
+
f"Propriedade '{pkey}' exige valor default textual, recebido '{default_value}'."
|
|
1006
|
+
)
|
|
1007
|
+
return ast.Constant(value=default_value)
|
|
1008
|
+
|
|
1009
|
+
if primitive_type == PrimitiveTypes.BOOLEAN:
|
|
1010
|
+
if isinstance(default_value, bool):
|
|
1011
|
+
return ast.Constant(value=default_value)
|
|
1012
|
+
raise ValueError(
|
|
1013
|
+
f"Propriedade '{pkey}' exige valor default booleano, recebido '{default_value}'."
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
if primitive_type == PrimitiveTypes.INTEGER:
|
|
1017
|
+
return self._build_numeric_constant_default_ast(
|
|
1018
|
+
pkey,
|
|
1019
|
+
default_value,
|
|
1020
|
+
int,
|
|
1021
|
+
"inteiro",
|
|
1022
|
+
forbid_fraction=True,
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
if primitive_type in (
|
|
1026
|
+
PrimitiveTypes.NUMBER,
|
|
1027
|
+
PrimitiveTypes.CURRENCY,
|
|
1028
|
+
PrimitiveTypes.QUANTITY,
|
|
1029
|
+
):
|
|
1030
|
+
return self._build_numeric_constant_default_ast(
|
|
1031
|
+
pkey,
|
|
1032
|
+
default_value,
|
|
1033
|
+
float,
|
|
1034
|
+
"numérico",
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
if primitive_type == PrimitiveTypes.UUID:
|
|
1038
|
+
return self._build_uuid_default_ast(pkey, default_value)
|
|
1039
|
+
|
|
1040
|
+
if primitive_type == PrimitiveTypes.DATE:
|
|
1041
|
+
return self._build_iso_datetime_default_ast(
|
|
1042
|
+
pkey,
|
|
1043
|
+
default_value,
|
|
1044
|
+
target="date",
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
if primitive_type == PrimitiveTypes.DATETIME:
|
|
1048
|
+
return self._build_iso_datetime_default_ast(
|
|
1049
|
+
pkey,
|
|
1050
|
+
default_value,
|
|
1051
|
+
target="datetime",
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
raise ValueError(
|
|
1055
|
+
f"Propriedade '{pkey}' não suporta valor default para o tipo '{primitive_type.value}'."
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1058
|
+
def _build_numeric_constant_default_ast(
|
|
1059
|
+
self,
|
|
1060
|
+
pkey: str,
|
|
1061
|
+
default_value: object,
|
|
1062
|
+
cast_type: type,
|
|
1063
|
+
type_label: str,
|
|
1064
|
+
*,
|
|
1065
|
+
forbid_fraction: bool = False,
|
|
1066
|
+
) -> ast.expr:
|
|
1067
|
+
if isinstance(default_value, bool):
|
|
1068
|
+
raise ValueError(
|
|
1069
|
+
f"Propriedade '{pkey}' exige valor default {type_label}, recebido '{default_value}'."
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
value_to_convert = (
|
|
1073
|
+
default_value.strip() if isinstance(default_value, str) else default_value
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
if forbid_fraction and isinstance(value_to_convert, float):
|
|
1077
|
+
if not value_to_convert.is_integer():
|
|
1078
|
+
raise ValueError(
|
|
1079
|
+
f"Propriedade '{pkey}' exige valor default inteiro, recebido '{default_value}'."
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
try:
|
|
1083
|
+
converted_value = cast_type(value_to_convert)
|
|
1084
|
+
except (TypeError, ValueError) as exc:
|
|
1085
|
+
raise ValueError(
|
|
1086
|
+
f"Propriedade '{pkey}' exige valor default {type_label}, recebido '{default_value}'."
|
|
1087
|
+
) from exc
|
|
1088
|
+
|
|
1089
|
+
if forbid_fraction and isinstance(value_to_convert, float):
|
|
1090
|
+
converted_value = int(converted_value)
|
|
1091
|
+
|
|
1092
|
+
return ast.Constant(value=converted_value)
|
|
1093
|
+
|
|
1094
|
+
def _build_uuid_default_ast(
|
|
1095
|
+
self,
|
|
1096
|
+
pkey: str,
|
|
1097
|
+
default_value: object,
|
|
1098
|
+
) -> ast.expr:
|
|
1099
|
+
if isinstance(default_value, uuid.UUID):
|
|
1100
|
+
uuid_value = str(default_value)
|
|
1101
|
+
elif isinstance(default_value, str):
|
|
1102
|
+
try:
|
|
1103
|
+
uuid_value = str(uuid.UUID(default_value))
|
|
1104
|
+
except ValueError as exc:
|
|
1105
|
+
raise ValueError(
|
|
1106
|
+
f"Propriedade '{pkey}' exige UUID válido ou expressão Python, recebido '{default_value}'."
|
|
1107
|
+
) from exc
|
|
1108
|
+
else:
|
|
1109
|
+
raise ValueError(
|
|
1110
|
+
f"Propriedade '{pkey}' exige UUID válido ou expressão Python, recebido '{default_value}'."
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
return ast.Call(
|
|
1114
|
+
func=ast.Attribute(
|
|
1115
|
+
value=ast.Name(id="uuid", ctx=ast.Load()),
|
|
1116
|
+
attr="UUID",
|
|
1117
|
+
ctx=ast.Load(),
|
|
1118
|
+
),
|
|
1119
|
+
args=[ast.Constant(value=uuid_value)],
|
|
1120
|
+
keywords=[],
|
|
1121
|
+
)
|
|
1122
|
+
|
|
1123
|
+
def _build_iso_datetime_default_ast(
|
|
1124
|
+
self,
|
|
1125
|
+
pkey: str,
|
|
1126
|
+
default_value: object,
|
|
1127
|
+
*,
|
|
1128
|
+
target: str,
|
|
1129
|
+
) -> ast.expr:
|
|
1130
|
+
if target == "date":
|
|
1131
|
+
parser = datetime.date
|
|
1132
|
+
else:
|
|
1133
|
+
parser = datetime.datetime
|
|
1134
|
+
|
|
1135
|
+
if isinstance(default_value, parser):
|
|
1136
|
+
iso_value = default_value.isoformat()
|
|
1137
|
+
elif isinstance(default_value, str):
|
|
1138
|
+
iso_value = default_value.strip()
|
|
1139
|
+
else:
|
|
1140
|
+
raise ValueError(
|
|
1141
|
+
f"Propriedade '{pkey}' exige {target} em formato ISO ou expressão Python, recebido '{default_value}'."
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
try:
|
|
1145
|
+
parser.fromisoformat(iso_value)
|
|
1146
|
+
except ValueError as exc:
|
|
1147
|
+
raise ValueError(
|
|
1148
|
+
f"Propriedade '{pkey}' exige {target} em formato ISO ou expressão Python, recebido '{default_value}'."
|
|
1149
|
+
) from exc
|
|
1150
|
+
|
|
1151
|
+
return ast.Call(
|
|
1152
|
+
func=ast.Attribute(
|
|
1153
|
+
value=ast.Attribute(
|
|
1154
|
+
value=ast.Name(id="datetime", ctx=ast.Load()),
|
|
1155
|
+
attr=target,
|
|
1156
|
+
ctx=ast.Load(),
|
|
1157
|
+
),
|
|
1158
|
+
attr="fromisoformat",
|
|
1159
|
+
ctx=ast.Load(),
|
|
1160
|
+
),
|
|
1161
|
+
args=[ast.Constant(value=iso_value)],
|
|
1162
|
+
keywords=[],
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
def _build_python_expression_from_string(self, expression: str) -> ast.expr | None:
|
|
1166
|
+
try:
|
|
1167
|
+
parsed_expr = ast.parse(expression, mode="eval").body
|
|
1168
|
+
except SyntaxError:
|
|
1169
|
+
return None
|
|
1170
|
+
|
|
1171
|
+
if self._is_safe_python_expression(parsed_expr):
|
|
1172
|
+
return parsed_expr
|
|
1173
|
+
|
|
1174
|
+
return None
|
|
1175
|
+
|
|
1176
|
+
def _is_safe_python_expression(self, node: ast.AST) -> bool:
|
|
1177
|
+
if isinstance(node, ast.Name):
|
|
1178
|
+
return True
|
|
1179
|
+
if isinstance(node, ast.Attribute):
|
|
1180
|
+
return self._is_safe_python_expression(node.value)
|
|
1181
|
+
if isinstance(node, ast.Call):
|
|
1182
|
+
if node.keywords or node.args:
|
|
1183
|
+
return False
|
|
1184
|
+
return self._is_safe_python_expression(node.func)
|
|
1185
|
+
return False
|
|
1186
|
+
|
|
1121
1187
|
def _compile_domain_config(
|
|
1122
1188
|
self,
|
|
1123
1189
|
pkey: str,
|
|
@@ -1139,7 +1205,7 @@ class EDLPropertyCompiler:
|
|
|
1139
1205
|
# Compilando as opções do enum
|
|
1140
1206
|
ast_values = []
|
|
1141
1207
|
for value in prop.domain_config:
|
|
1142
|
-
value_name = CompilerStrUtil.
|
|
1208
|
+
value_name = CompilerStrUtil.to_enum_member_name(value.value)
|
|
1143
1209
|
|
|
1144
1210
|
if use_mapped_value and value.mapped_value is None:
|
|
1145
1211
|
raise Exception(
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class CompilerStrUtil:
|
|
2
5
|
@staticmethod
|
|
3
6
|
def to_camel_case(snake_str: str) -> str:
|
|
@@ -20,3 +23,10 @@ class CompilerStrUtil:
|
|
|
20
23
|
snake_str += "_"
|
|
21
24
|
snake_str += char.lower()
|
|
22
25
|
return snake_str
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def to_enum_member_name(raw_value: str) -> str:
|
|
29
|
+
"""Converte um valor livre para o nome de membro enum padronizado."""
|
|
30
|
+
sanitized = re.sub(r"[^0-9A-Za-z]+", "_", raw_value.strip())
|
|
31
|
+
sanitized = sanitized.strip("_")
|
|
32
|
+
return CompilerStrUtil.to_snake_case(sanitized).upper()
|
|
@@ -15,7 +15,6 @@ from nsj_rest_lib2.redis_config import get_redis
|
|
|
15
15
|
from nsj_rest_lib2.settings import ESCOPO_RESTLIB2, MIN_TIME_SOURCE_REFRESH
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
# TODO Verificar se está atualizando o loaded_at, quando o hash não muda
|
|
19
18
|
class LoadedEntity:
|
|
20
19
|
def __init__(self):
|
|
21
20
|
self.dto_class_name: str = ""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nsj_rest_lib2
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.23
|
|
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
|
|
@@ -25,16 +25,3 @@ Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configur
|
|
|
25
25
|
|
|
26
26
|
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
27
27
|
|
|
28
|
-
## TODO
|
|
29
|
-
* Valores default
|
|
30
|
-
* Usar trait_properties como valor default
|
|
31
|
-
* Não está carregando a lista de dependências de execução, para os components
|
|
32
|
-
* Implementar gravação de entidades com extensão parcial
|
|
33
|
-
* Unificar o arquivo redis_config.py
|
|
34
|
-
* Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
|
|
35
|
-
* Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
|
|
36
|
-
* Implementar e documentar campos join
|
|
37
|
-
* Implementar e documentar conjuntos
|
|
38
|
-
* Dar erro ao haver conflito de nomes das propriedades (por qualquer tipo de herança)
|
|
39
|
-
* Rotas para o inline
|
|
40
|
-
* inline será para composição (arquivo externo é agregação)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = nsj_rest_lib2
|
|
3
|
-
version = 0.0.
|
|
3
|
+
version = 0.0.23
|
|
4
4
|
author = Nasajon Sistemas
|
|
5
5
|
author_email = contact.dev@nasajon.com.br
|
|
6
6
|
description = Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
nsj_rest_lib2-0.0.21/README.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# nsj_rest_lib2
|
|
2
|
-
|
|
3
|
-
Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
4
|
-
|
|
5
|
-
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
6
|
-
|
|
7
|
-
## TODO
|
|
8
|
-
* Valores default
|
|
9
|
-
* Usar trait_properties como valor default
|
|
10
|
-
* Não está carregando a lista de dependências de execução, para os components
|
|
11
|
-
* Implementar gravação de entidades com extensão parcial
|
|
12
|
-
* Unificar o arquivo redis_config.py
|
|
13
|
-
* Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
|
|
14
|
-
* Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
|
|
15
|
-
* Implementar e documentar campos join
|
|
16
|
-
* Implementar e documentar conjuntos
|
|
17
|
-
* Dar erro ao haver conflito de nomes das propriedades (por qualquer tipo de herança)
|
|
18
|
-
* Rotas para o inline
|
|
19
|
-
* inline será para composição (arquivo externo é agregação)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py
RENAMED
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model_base.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/entity_model_root.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/index_model.py
RENAMED
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/edl_model/repository_model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/compiler/util/type_naming_util.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nsj_rest_lib2-0.0.21 → nsj_rest_lib2-0.0.23}/nsj_rest_lib2/controller/dynamic_controller.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|