nsj-rest-lib2 0.0.35__tar.gz → 0.0.37__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.37/PKG-INFO +203 -0
- nsj_rest_lib2-0.0.37/README.md +182 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/compiler.py +111 -3
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/dto_compiler.py +5 -1
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/api_model.py +18 -6
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/model.py +12 -0
- nsj_rest_lib2-0.0.37/nsj_rest_lib2/compiler/response_dto_compiler.py +104 -0
- nsj_rest_lib2-0.0.37/nsj_rest_lib2/compiler/util/type_naming_util.py +162 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/controller/dynamic_controller.py +87 -5
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/service/entity_config_writer.py +19 -44
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/service/entity_loader.py +421 -70
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/settings.py +10 -0
- nsj_rest_lib2-0.0.37/nsj_rest_lib2.egg-info/PKG-INFO +203 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2.egg-info/SOURCES.txt +2 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/setup.cfg +1 -1
- nsj_rest_lib2-0.0.37/tests/test_function_handler_compilation.py +472 -0
- nsj_rest_lib2-0.0.35/PKG-INFO +0 -27
- nsj_rest_lib2-0.0.35/README.md +0 -6
- nsj_rest_lib2-0.0.35/nsj_rest_lib2/compiler/util/type_naming_util.py +0 -80
- nsj_rest_lib2-0.0.35/nsj_rest_lib2.egg-info/PKG-INFO +0 -27
- nsj_rest_lib2-0.0.35/tests/test_function_handler_compilation.py +0 -159
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/compiler_structures.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/column_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/entity_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/entity_model_base.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/entity_model_root.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/index_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/primitives.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/property_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/repository_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/entity_compiler.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/function_get_delete_compiler.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/function_insert_update_compiler.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/function_model.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/migration_compiler.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/migration_compiler_alter_table.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/migration_compiler_create_table.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/migration_compiler_util.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/property_compiler.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/util/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/util/relation_ref.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/util/str_util.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/compiler/util/type_util.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/controller/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/dto/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/dto/escopo_dto.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/entity/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/entity/escopo_entity.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/exception.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/redis_config.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2/service/__init__.py +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2.egg-info/dependency_links.txt +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2.egg-info/requires.txt +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/nsj_rest_lib2.egg-info/top_level.txt +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/pyproject.toml +0 -0
- {nsj_rest_lib2-0.0.35 → nsj_rest_lib2-0.0.37}/tests/test_migration_generation.py +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nsj_rest_lib2
|
|
3
|
+
Version: 0.0.37
|
|
4
|
+
Summary: Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
|
|
5
|
+
Home-page: https://github.com/Nasajon/nsj_rest_lib2
|
|
6
|
+
Author: Nasajon Sistemas
|
|
7
|
+
Author-email: contact.dev@nasajon.com.br
|
|
8
|
+
Project-URL: Source, https://github.com/Nasajon/nsj_rest_lib2
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.4
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: nsj-rest-lib<7.0.0,>=5.1.3
|
|
16
|
+
Requires-Dist: redis<7.0.0,>=6.4.0
|
|
17
|
+
Requires-Dist: nsj-multi-database-lib<3.0.0,>=2.0.1
|
|
18
|
+
Requires-Dist: pydantic<3.0.0,>=2.11.9
|
|
19
|
+
Requires-Dist: black<26.0.0,>=25.1.0
|
|
20
|
+
Requires-Dist: pyyaml<7.0.0,>=6.0.3
|
|
21
|
+
|
|
22
|
+
# RestLib2
|
|
23
|
+
|
|
24
|
+
O RestLib2 é uma plataforma que tem por objetivo o fornecimento de APIs para os clientes, a partir da descrição da entidades envolvidas, e sem a necessidade de programação imperativa tradicional.
|
|
25
|
+
|
|
26
|
+
Em resumo, a partir da declaração em JSON das entidades que compõe um negócio, bem como dos relacionamentos entre estas, as APIs já estarão disponíveis em produção.
|
|
27
|
+
|
|
28
|
+
O objetivo final é permitir que o desenvolvimento de APIs se torne rápido e simples, acessível àqueles que tratam diretamente com o negócio (incluindo implantadores, e, no limite, o próprio cliente).
|
|
29
|
+
|
|
30
|
+
## Links
|
|
31
|
+
|
|
32
|
+
[Guia do EDL](docs/README.md)
|
|
33
|
+
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
34
|
+
|
|
35
|
+
## Rota das APIs geradas
|
|
36
|
+
|
|
37
|
+
A rota para acesso a uma API gerada por variar de acordo com o modo de configuração escolhido pela aplicação que usar o RestLib2. Mas, via de regra, sugere-se o padrão aplicado na API de DadosMestre:
|
|
38
|
+
|
|
39
|
+
```http
|
|
40
|
+
#############################
|
|
41
|
+
# List Prod
|
|
42
|
+
#############################
|
|
43
|
+
GET https://api.nasajon.app/dados-mestre/edl1/clientes?tenant=47&grupo_empresarial=NASAJON HTTP/1.1
|
|
44
|
+
Authorization: Bearer *****
|
|
45
|
+
Accept: application/json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
* Note que, na API de Dados Mestre, todas as APIs do RestLib2 ficarão debaixo da rota: ```https://api.nasajon.app/dados-mestre/edl1/```
|
|
49
|
+
* O endpoint em si, varia a cada JSON, e fica configurado no nó ```api.resource``` do JSON de EDL em questão.
|
|
50
|
+
|
|
51
|
+
## Fluxo básico de uso
|
|
52
|
+
|
|
53
|
+
Há dois modos básicos de publicar novas APIs a partir de um JSON gerado, para uma aplicação previamente configurada para uso da plataforma:
|
|
54
|
+
|
|
55
|
+
### Fluxo por meio de código versionado
|
|
56
|
+
1. Criar o JSON de descrição da entidade desejada, gravando-o no diretório "@schemas/entities" da aplicação em questão (tome [como exemplo o "dados-mestre"](https://github.com/Nasajon/dados-mestre-api/tree/production/%40schemas)).
|
|
57
|
+
2. Fazer push das alterações.
|
|
58
|
+
3. Aguardar o build da aplicação (pode ser útil conferir os logs do job de atualização dos JSON, na aplicação em questão, e também os logs do [worker de compilação](https://ci.nasajon.in/applications/restlib2?view=pods&conditions=false), no Argo).
|
|
59
|
+
4. Testar as APIs compiladas.
|
|
60
|
+
|
|
61
|
+
### Fluxo de deploy direto pela API
|
|
62
|
+
1. Criar o JSON de descrição da entidade desejada.
|
|
63
|
+
2. Registrá-lo diretamente, por meio da API de controle do RestLib2. segue exemplo de chamada abaixo:
|
|
64
|
+
|
|
65
|
+
```http
|
|
66
|
+
###############################
|
|
67
|
+
# Post dinâmico
|
|
68
|
+
###############################
|
|
69
|
+
POST https://api.nasajon.app/restlib2/entities HTTP/1.1
|
|
70
|
+
Authorization: Bearer ******
|
|
71
|
+
Content-Type: application/json
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
"id": "{UUID}",
|
|
75
|
+
"escopo": "{escopo}",
|
|
76
|
+
"tenant": 0,
|
|
77
|
+
"grupo_empresarial": "00000000-0000-0000-0000-000000000000",
|
|
78
|
+
"codigo": "{codigo da entidade}",
|
|
79
|
+
"descricao": "{descrição da entidade}",
|
|
80
|
+
"json_schema": {
|
|
81
|
+
"edl_version": "1.0",
|
|
82
|
+
"escopo": "{escopo}",
|
|
83
|
+
"description": "{descrição da entidade}",
|
|
84
|
+
"id": "cliente",
|
|
85
|
+
"version": "0.0.1",
|
|
86
|
+
...
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Observações:
|
|
92
|
+
* Note que, acima há placeholders a alterar.
|
|
93
|
+
* Note também que o JSON não está completo, e, o correto é deguir a documentação do EDL.
|
|
94
|
+
* Os valores tenant=0 e grupo_empresarial=00000000-0000-0000-0000-000000000000, indicam que a entidade é padrão, e serve para todos os tenants e grupos.
|
|
95
|
+
|
|
96
|
+
3. Essa rota irá retornar algo semelhante a:
|
|
97
|
+
|
|
98
|
+
```http
|
|
99
|
+
HTTP/1.1 202 ACCEPTED
|
|
100
|
+
Location: https://api.nasajon.app/restlib2/entity-compilations/status/{UUID do processo de compilação}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
E, se você fizer uma chamada à rota retornada, poderá acompanhar o status da compilação, incluindo eventuais erros que venham a ocorrer.
|
|
104
|
+
|
|
105
|
+
4. Testar a API compilada.
|
|
106
|
+
|
|
107
|
+
## Como rodar a compilação de EDLs localmente?
|
|
108
|
+
|
|
109
|
+
Você pode testar a compilação de seus EDLs localmente, incluindo a execução de diversas validações sobre os mesmos, desde que todos estejam dispostos num mesmo diertório.
|
|
110
|
+
|
|
111
|
+
Para isso, considere os passos a seguir:
|
|
112
|
+
|
|
113
|
+
1. Crie um diretório, e coloque todos os seus EDLs lá (garantindo que EDLs relacionados estejam no mesmo).
|
|
114
|
+
2. Instale, no seu ambiente pyhton de teste, a biblioteca `nsj-rest-lib2`:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
pip install nsj-rest-lib2
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
3. Rode o comando abaixo (adaptado para seu diretório):
|
|
121
|
+
```sh
|
|
122
|
+
python3 -m nsj_rest_lib2.compiler.compiler -d $(shell pwd)/{diretorio_com_os_edls_json}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
O resultado da compilação será impresso no console (erros, ou código total gerado).
|
|
126
|
+
|
|
127
|
+
## Como configurar uma aplicação para expôr rotas de acordo com o padrão do RestLib2?
|
|
128
|
+
|
|
129
|
+
Para que uma aplicação exponha as rotas no padrão do RestLib2, não são necessários muitos passos. Antes basta:
|
|
130
|
+
|
|
131
|
+
1. Instalar, em sua aplicação, a dependência para o projeto nsj-rest-lib2
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
pip install nsj-rest-lib2
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
138
|
+
|
|
139
|
+
2. Adicione a variável de ambiente abaixo em sua aplciação
|
|
140
|
+
|
|
141
|
+
```env
|
|
142
|
+
ESCOPO_RESTLIB2: "{COLOQUE O IDENTIFICADOR DE ESCOPO QUE DESEJAR PARA SUA APLICAÇÃO (UMA SIMPLES STRING SEM ESPAÇO)}"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
* Esse identificador de escopo deve se único para sua aplicação.
|
|
146
|
+
* É importante nota que sua aplicação só irá expôr JSONs de EDL configurados para o mesmo escopo definido aqui.
|
|
147
|
+
|
|
148
|
+
3. Instale, como dependência a biblioteca, nsj-rest-lib2:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
pip install nsj-rest-lib2
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
155
|
+
|
|
156
|
+
4. Adicione a linha abaixo no arquivo wsgi.py (de inicilização de sua aplicação):
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
160
|
+
from nasajon.injector_factory_multibanco import InjectorFactoryMultibanco
|
|
161
|
+
|
|
162
|
+
setup_dynamic_routes(application, injector_factory=InjectorFactoryMultibanco)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
* No exemplo acima, a rota é multibanco, mas, não é obrigatório.
|
|
166
|
+
* Os parâmetros de setup são:
|
|
167
|
+
* flask_app: Variável obrigatório, que aponte para sua aplicação Flask.
|
|
168
|
+
* multidb: Flag (padrão True)
|
|
169
|
+
* dynamic_root_path: URL padrão base de todos os endpoints do RestLib2 (padrão: "edl1")
|
|
170
|
+
* injector_factory: Classe de injeção de depndência usada (normalmente necessária para aplicações multibanco; o principal uso é justamente manipular a criação da conexão com o BD).
|
|
171
|
+
|
|
172
|
+
## Carregando EDLs direto do disco
|
|
173
|
+
|
|
174
|
+
Também é possível carregar EDLs diretamente do disco, sem depender exclusivamente do Redis. Para isso, use o parâmetro opcional `edls_path` em `setup_dynamic_routes`, apontando para um caminho relativo ao workdir da aplicação (ou absoluto), contendo arquivos `.json`, `.yml` ou `.yaml` com os EDLs.
|
|
175
|
+
|
|
176
|
+
Exemplo:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
180
|
+
|
|
181
|
+
setup_dynamic_routes(
|
|
182
|
+
application,
|
|
183
|
+
injector_factory=InjectorFactoryMultibanco,
|
|
184
|
+
edls_path="@schemas/entities",
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Observações:
|
|
189
|
+
* Os EDLs são carregados e compilados uma vez, ficando em cache em memória. Chamadas seguintes reutilizam o cache.
|
|
190
|
+
* A resolução das entidades ocorre primeiro pelo cache local; se não encontrar, o Redis é consultado (quando habilitado).
|
|
191
|
+
|
|
192
|
+
Para desabilitar o Redis e usar somente EDLs do disco, defina a variável de ambiente abaixo:
|
|
193
|
+
|
|
194
|
+
```env
|
|
195
|
+
EDLS_FROM_REDIS=false
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
O valor padrão de `EDLS_FROM_REDIS` é `true`.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
**Pronto, isso deve bastar para expôr os EDLs configurados para o mesmo escopo da aplicação.**
|
|
202
|
+
|
|
203
|
+
**OBSERVAÇÃO GERAL: Isso só funciona para aplicações Flask.**
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# RestLib2
|
|
2
|
+
|
|
3
|
+
O RestLib2 é uma plataforma que tem por objetivo o fornecimento de APIs para os clientes, a partir da descrição da entidades envolvidas, e sem a necessidade de programação imperativa tradicional.
|
|
4
|
+
|
|
5
|
+
Em resumo, a partir da declaração em JSON das entidades que compõe um negócio, bem como dos relacionamentos entre estas, as APIs já estarão disponíveis em produção.
|
|
6
|
+
|
|
7
|
+
O objetivo final é permitir que o desenvolvimento de APIs se torne rápido e simples, acessível àqueles que tratam diretamente com o negócio (incluindo implantadores, e, no limite, o próprio cliente).
|
|
8
|
+
|
|
9
|
+
## Links
|
|
10
|
+
|
|
11
|
+
[Guia do EDL](docs/README.md)
|
|
12
|
+
[ESPECIFICAÇÃO DO MODELO DE ENTIDADES](docs/especificacao.md)
|
|
13
|
+
|
|
14
|
+
## Rota das APIs geradas
|
|
15
|
+
|
|
16
|
+
A rota para acesso a uma API gerada por variar de acordo com o modo de configuração escolhido pela aplicação que usar o RestLib2. Mas, via de regra, sugere-se o padrão aplicado na API de DadosMestre:
|
|
17
|
+
|
|
18
|
+
```http
|
|
19
|
+
#############################
|
|
20
|
+
# List Prod
|
|
21
|
+
#############################
|
|
22
|
+
GET https://api.nasajon.app/dados-mestre/edl1/clientes?tenant=47&grupo_empresarial=NASAJON HTTP/1.1
|
|
23
|
+
Authorization: Bearer *****
|
|
24
|
+
Accept: application/json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
* Note que, na API de Dados Mestre, todas as APIs do RestLib2 ficarão debaixo da rota: ```https://api.nasajon.app/dados-mestre/edl1/```
|
|
28
|
+
* O endpoint em si, varia a cada JSON, e fica configurado no nó ```api.resource``` do JSON de EDL em questão.
|
|
29
|
+
|
|
30
|
+
## Fluxo básico de uso
|
|
31
|
+
|
|
32
|
+
Há dois modos básicos de publicar novas APIs a partir de um JSON gerado, para uma aplicação previamente configurada para uso da plataforma:
|
|
33
|
+
|
|
34
|
+
### Fluxo por meio de código versionado
|
|
35
|
+
1. Criar o JSON de descrição da entidade desejada, gravando-o no diretório "@schemas/entities" da aplicação em questão (tome [como exemplo o "dados-mestre"](https://github.com/Nasajon/dados-mestre-api/tree/production/%40schemas)).
|
|
36
|
+
2. Fazer push das alterações.
|
|
37
|
+
3. Aguardar o build da aplicação (pode ser útil conferir os logs do job de atualização dos JSON, na aplicação em questão, e também os logs do [worker de compilação](https://ci.nasajon.in/applications/restlib2?view=pods&conditions=false), no Argo).
|
|
38
|
+
4. Testar as APIs compiladas.
|
|
39
|
+
|
|
40
|
+
### Fluxo de deploy direto pela API
|
|
41
|
+
1. Criar o JSON de descrição da entidade desejada.
|
|
42
|
+
2. Registrá-lo diretamente, por meio da API de controle do RestLib2. segue exemplo de chamada abaixo:
|
|
43
|
+
|
|
44
|
+
```http
|
|
45
|
+
###############################
|
|
46
|
+
# Post dinâmico
|
|
47
|
+
###############################
|
|
48
|
+
POST https://api.nasajon.app/restlib2/entities HTTP/1.1
|
|
49
|
+
Authorization: Bearer ******
|
|
50
|
+
Content-Type: application/json
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
"id": "{UUID}",
|
|
54
|
+
"escopo": "{escopo}",
|
|
55
|
+
"tenant": 0,
|
|
56
|
+
"grupo_empresarial": "00000000-0000-0000-0000-000000000000",
|
|
57
|
+
"codigo": "{codigo da entidade}",
|
|
58
|
+
"descricao": "{descrição da entidade}",
|
|
59
|
+
"json_schema": {
|
|
60
|
+
"edl_version": "1.0",
|
|
61
|
+
"escopo": "{escopo}",
|
|
62
|
+
"description": "{descrição da entidade}",
|
|
63
|
+
"id": "cliente",
|
|
64
|
+
"version": "0.0.1",
|
|
65
|
+
...
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Observações:
|
|
71
|
+
* Note que, acima há placeholders a alterar.
|
|
72
|
+
* Note também que o JSON não está completo, e, o correto é deguir a documentação do EDL.
|
|
73
|
+
* Os valores tenant=0 e grupo_empresarial=00000000-0000-0000-0000-000000000000, indicam que a entidade é padrão, e serve para todos os tenants e grupos.
|
|
74
|
+
|
|
75
|
+
3. Essa rota irá retornar algo semelhante a:
|
|
76
|
+
|
|
77
|
+
```http
|
|
78
|
+
HTTP/1.1 202 ACCEPTED
|
|
79
|
+
Location: https://api.nasajon.app/restlib2/entity-compilations/status/{UUID do processo de compilação}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
E, se você fizer uma chamada à rota retornada, poderá acompanhar o status da compilação, incluindo eventuais erros que venham a ocorrer.
|
|
83
|
+
|
|
84
|
+
4. Testar a API compilada.
|
|
85
|
+
|
|
86
|
+
## Como rodar a compilação de EDLs localmente?
|
|
87
|
+
|
|
88
|
+
Você pode testar a compilação de seus EDLs localmente, incluindo a execução de diversas validações sobre os mesmos, desde que todos estejam dispostos num mesmo diertório.
|
|
89
|
+
|
|
90
|
+
Para isso, considere os passos a seguir:
|
|
91
|
+
|
|
92
|
+
1. Crie um diretório, e coloque todos os seus EDLs lá (garantindo que EDLs relacionados estejam no mesmo).
|
|
93
|
+
2. Instale, no seu ambiente pyhton de teste, a biblioteca `nsj-rest-lib2`:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
pip install nsj-rest-lib2
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
3. Rode o comando abaixo (adaptado para seu diretório):
|
|
100
|
+
```sh
|
|
101
|
+
python3 -m nsj_rest_lib2.compiler.compiler -d $(shell pwd)/{diretorio_com_os_edls_json}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
O resultado da compilação será impresso no console (erros, ou código total gerado).
|
|
105
|
+
|
|
106
|
+
## Como configurar uma aplicação para expôr rotas de acordo com o padrão do RestLib2?
|
|
107
|
+
|
|
108
|
+
Para que uma aplicação exponha as rotas no padrão do RestLib2, não são necessários muitos passos. Antes basta:
|
|
109
|
+
|
|
110
|
+
1. Instalar, em sua aplicação, a dependência para o projeto nsj-rest-lib2
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
pip install nsj-rest-lib2
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
117
|
+
|
|
118
|
+
2. Adicione a variável de ambiente abaixo em sua aplciação
|
|
119
|
+
|
|
120
|
+
```env
|
|
121
|
+
ESCOPO_RESTLIB2: "{COLOQUE O IDENTIFICADOR DE ESCOPO QUE DESEJAR PARA SUA APLICAÇÃO (UMA SIMPLES STRING SEM ESPAÇO)}"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
* Esse identificador de escopo deve se único para sua aplicação.
|
|
125
|
+
* É importante nota que sua aplicação só irá expôr JSONs de EDL configurados para o mesmo escopo definido aqui.
|
|
126
|
+
|
|
127
|
+
3. Instale, como dependência a biblioteca, nsj-rest-lib2:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
pip install nsj-rest-lib2
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Não esqueça de fixar a versão usada (como sendo a última), no arquivo requirements.txt.
|
|
134
|
+
|
|
135
|
+
4. Adicione a linha abaixo no arquivo wsgi.py (de inicilização de sua aplicação):
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
139
|
+
from nasajon.injector_factory_multibanco import InjectorFactoryMultibanco
|
|
140
|
+
|
|
141
|
+
setup_dynamic_routes(application, injector_factory=InjectorFactoryMultibanco)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
* No exemplo acima, a rota é multibanco, mas, não é obrigatório.
|
|
145
|
+
* Os parâmetros de setup são:
|
|
146
|
+
* flask_app: Variável obrigatório, que aponte para sua aplicação Flask.
|
|
147
|
+
* multidb: Flag (padrão True)
|
|
148
|
+
* dynamic_root_path: URL padrão base de todos os endpoints do RestLib2 (padrão: "edl1")
|
|
149
|
+
* injector_factory: Classe de injeção de depndência usada (normalmente necessária para aplicações multibanco; o principal uso é justamente manipular a criação da conexão com o BD).
|
|
150
|
+
|
|
151
|
+
## Carregando EDLs direto do disco
|
|
152
|
+
|
|
153
|
+
Também é possível carregar EDLs diretamente do disco, sem depender exclusivamente do Redis. Para isso, use o parâmetro opcional `edls_path` em `setup_dynamic_routes`, apontando para um caminho relativo ao workdir da aplicação (ou absoluto), contendo arquivos `.json`, `.yml` ou `.yaml` com os EDLs.
|
|
154
|
+
|
|
155
|
+
Exemplo:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from nsj_rest_lib2.controller.dynamic_controller import setup_dynamic_routes
|
|
159
|
+
|
|
160
|
+
setup_dynamic_routes(
|
|
161
|
+
application,
|
|
162
|
+
injector_factory=InjectorFactoryMultibanco,
|
|
163
|
+
edls_path="@schemas/entities",
|
|
164
|
+
)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Observações:
|
|
168
|
+
* Os EDLs são carregados e compilados uma vez, ficando em cache em memória. Chamadas seguintes reutilizam o cache.
|
|
169
|
+
* A resolução das entidades ocorre primeiro pelo cache local; se não encontrar, o Redis é consultado (quando habilitado).
|
|
170
|
+
|
|
171
|
+
Para desabilitar o Redis e usar somente EDLs do disco, defina a variável de ambiente abaixo:
|
|
172
|
+
|
|
173
|
+
```env
|
|
174
|
+
EDLS_FROM_REDIS=false
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
O valor padrão de `EDLS_FROM_REDIS` é `true`.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
**Pronto, isso deve bastar para expôr os EDLs configurados para o mesmo escopo da aplicação.**
|
|
181
|
+
|
|
182
|
+
**OBSERVAÇÃO GERAL: Isso só funciona para aplicações Flask.**
|
|
@@ -28,6 +28,7 @@ from nsj_rest_lib2.compiler.util.type_naming_util import (
|
|
|
28
28
|
compile_entity_class_name,
|
|
29
29
|
compile_namespace_keys,
|
|
30
30
|
)
|
|
31
|
+
from nsj_rest_lib2.compiler.response_dto_compiler import ResponseDTOCompiler
|
|
31
32
|
from nsj_rest_lib2.compiler.util.relation_ref import RelationRefParser
|
|
32
33
|
|
|
33
34
|
from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
|
|
@@ -169,9 +170,12 @@ class EDLCompiler:
|
|
|
169
170
|
get_function_name = None
|
|
170
171
|
list_function_name = None
|
|
171
172
|
delete_function_name = None
|
|
173
|
+
handlers: dict[str, Any] = {}
|
|
174
|
+
post_handler = None
|
|
175
|
+
put_handler = None
|
|
176
|
+
patch_handler = None
|
|
172
177
|
|
|
173
178
|
if isinstance(entity_model, EntityModel) and entity_model.api:
|
|
174
|
-
handlers = {}
|
|
175
179
|
if entity_model.api.handlers:
|
|
176
180
|
handlers = {
|
|
177
181
|
(verb or "").lower(): handler
|
|
@@ -179,16 +183,20 @@ class EDLCompiler:
|
|
|
179
183
|
if handler
|
|
180
184
|
}
|
|
181
185
|
|
|
186
|
+
post_handler = handlers.get("post")
|
|
187
|
+
put_handler = handlers.get("put")
|
|
188
|
+
patch_handler = handlers.get("patch")
|
|
189
|
+
|
|
182
190
|
insert_output = self._function_compiler.compile_insert(
|
|
183
191
|
entity_model,
|
|
184
192
|
properties_structure,
|
|
185
|
-
|
|
193
|
+
post_handler,
|
|
186
194
|
prefx_class_name,
|
|
187
195
|
)
|
|
188
196
|
update_output = self._function_compiler.compile_update(
|
|
189
197
|
entity_model,
|
|
190
198
|
properties_structure,
|
|
191
|
-
|
|
199
|
+
put_handler,
|
|
192
200
|
prefx_class_name,
|
|
193
201
|
)
|
|
194
202
|
get_handler = handlers.get("get")
|
|
@@ -354,6 +362,16 @@ class EDLCompiler:
|
|
|
354
362
|
# Adicionando as dependências das relações
|
|
355
363
|
relations_dependencies_complete.extend(relations_dependencies)
|
|
356
364
|
|
|
365
|
+
response_dto_compiler = ResponseDTOCompiler(
|
|
366
|
+
dto_compiler=self._dto_compiler,
|
|
367
|
+
entity_model=entity_model,
|
|
368
|
+
prefx_class_name=prefx_class_name,
|
|
369
|
+
ast_dto_attributes=ast_dto_attributes,
|
|
370
|
+
aux_classes=aux_classes,
|
|
371
|
+
related_imports=related_imports,
|
|
372
|
+
fixed_filters=fixed_filters,
|
|
373
|
+
)
|
|
374
|
+
|
|
357
375
|
# Adicionando as dependências da extensão parcial
|
|
358
376
|
if partial_metadata and partial_base_model:
|
|
359
377
|
relation_dependency = RelationDependency()
|
|
@@ -367,6 +385,68 @@ class EDLCompiler:
|
|
|
367
385
|
relation_dependency.grupo_empresarial = partial_base_model.grupo_empresarial
|
|
368
386
|
relations_dependencies_complete.append(relation_dependency)
|
|
369
387
|
|
|
388
|
+
post_expected = "empty"
|
|
389
|
+
put_expected = "empty"
|
|
390
|
+
patch_expected = "empty"
|
|
391
|
+
get_expected = "empty"
|
|
392
|
+
list_expected = "empty"
|
|
393
|
+
delete_expected = "empty"
|
|
394
|
+
post_properties = None
|
|
395
|
+
put_properties = None
|
|
396
|
+
patch_properties = None
|
|
397
|
+
if isinstance(entity_model, EntityModel) and entity_model.api:
|
|
398
|
+
post_expected, post_properties = (
|
|
399
|
+
response_dto_compiler.handler_result_details(post_handler)
|
|
400
|
+
)
|
|
401
|
+
put_expected, put_properties = (
|
|
402
|
+
response_dto_compiler.handler_result_details(put_handler)
|
|
403
|
+
)
|
|
404
|
+
patch_expected, patch_properties = (
|
|
405
|
+
response_dto_compiler.handler_result_details(patch_handler)
|
|
406
|
+
)
|
|
407
|
+
get_expected, _ = response_dto_compiler.handler_result_details(
|
|
408
|
+
get_handler
|
|
409
|
+
)
|
|
410
|
+
list_expected, _ = response_dto_compiler.handler_result_details(
|
|
411
|
+
list_handler
|
|
412
|
+
)
|
|
413
|
+
delete_expected, _ = response_dto_compiler.handler_result_details(
|
|
414
|
+
delete_handler
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
if post_expected == "partial_row":
|
|
418
|
+
post_class_name, post_code = (
|
|
419
|
+
response_dto_compiler.compile_partial_response_dto(
|
|
420
|
+
"post", post_properties or []
|
|
421
|
+
)
|
|
422
|
+
)
|
|
423
|
+
compiler_result_post_class = post_class_name
|
|
424
|
+
dto_code += "\n\n" + post_code
|
|
425
|
+
else:
|
|
426
|
+
compiler_result_post_class = None
|
|
427
|
+
|
|
428
|
+
if put_expected == "partial_row":
|
|
429
|
+
put_class_name, put_code = (
|
|
430
|
+
response_dto_compiler.compile_partial_response_dto(
|
|
431
|
+
"put", put_properties or []
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
compiler_result_put_class = put_class_name
|
|
435
|
+
dto_code += "\n\n" + put_code
|
|
436
|
+
else:
|
|
437
|
+
compiler_result_put_class = None
|
|
438
|
+
|
|
439
|
+
if patch_expected == "partial_row":
|
|
440
|
+
patch_class_name, patch_code = (
|
|
441
|
+
response_dto_compiler.compile_partial_response_dto(
|
|
442
|
+
"patch", patch_properties or []
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
compiler_result_patch_class = patch_class_name
|
|
446
|
+
dto_code += "\n\n" + patch_code
|
|
447
|
+
else:
|
|
448
|
+
compiler_result_patch_class = None
|
|
449
|
+
|
|
370
450
|
# Construindo o resultado
|
|
371
451
|
compiler_result = CompilerResult()
|
|
372
452
|
compiler_result.entity_class_name = entity_class_name
|
|
@@ -389,6 +469,34 @@ class EDLCompiler:
|
|
|
389
469
|
compiler_result.source_delete_function_type = (
|
|
390
470
|
delete_function_code.strip() or None
|
|
391
471
|
)
|
|
472
|
+
compiler_result.retrieve_after_insert = post_expected == "entity_row"
|
|
473
|
+
compiler_result.retrieve_after_update = put_expected == "entity_row"
|
|
474
|
+
compiler_result.retrieve_after_partial_update = (
|
|
475
|
+
patch_expected == "entity_row"
|
|
476
|
+
)
|
|
477
|
+
compiler_result.post_response_dto_class_name = compiler_result_post_class
|
|
478
|
+
compiler_result.put_response_dto_class_name = compiler_result_put_class
|
|
479
|
+
compiler_result.patch_response_dto_class_name = (
|
|
480
|
+
compiler_result_patch_class
|
|
481
|
+
)
|
|
482
|
+
compiler_result.custom_json_post_response = (
|
|
483
|
+
post_expected == "custom_json"
|
|
484
|
+
)
|
|
485
|
+
compiler_result.custom_json_put_response = (
|
|
486
|
+
put_expected == "custom_json"
|
|
487
|
+
)
|
|
488
|
+
compiler_result.custom_json_patch_response = (
|
|
489
|
+
patch_expected == "custom_json"
|
|
490
|
+
)
|
|
491
|
+
compiler_result.custom_json_get_response = (
|
|
492
|
+
get_expected == "custom_json"
|
|
493
|
+
)
|
|
494
|
+
compiler_result.custom_json_list_response = (
|
|
495
|
+
list_expected == "custom_json"
|
|
496
|
+
)
|
|
497
|
+
compiler_result.custom_json_delete_response = (
|
|
498
|
+
delete_expected == "custom_json"
|
|
499
|
+
)
|
|
392
500
|
|
|
393
501
|
insert_code_compiled = insert_function_code.strip()
|
|
394
502
|
update_code_compiled = update_function_code.strip()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
from typing import Optional
|
|
2
3
|
|
|
3
4
|
import black
|
|
4
5
|
|
|
@@ -21,6 +22,7 @@ class DTOCompiler:
|
|
|
21
22
|
fixed_filters: list[tuple[str, BasicTypes]],
|
|
22
23
|
prefx_class_name: str,
|
|
23
24
|
partial_metadata: dict[str, str] | None,
|
|
25
|
+
class_name_override: Optional[str] = None,
|
|
24
26
|
) -> tuple[str, str]:
|
|
25
27
|
"""
|
|
26
28
|
Compila o código do DTO a partir do AST e retorna o código compilado.
|
|
@@ -158,7 +160,9 @@ class DTOCompiler:
|
|
|
158
160
|
)
|
|
159
161
|
|
|
160
162
|
# Criando o ast da classe
|
|
161
|
-
class_name = compile_dto_class_name(
|
|
163
|
+
class_name = class_name_override or compile_dto_class_name(
|
|
164
|
+
entity_model.id, prefx_class_name
|
|
165
|
+
)
|
|
162
166
|
ast_class = ast.ClassDef(
|
|
163
167
|
name=class_name,
|
|
164
168
|
bases=[ast.Name(id="DTOBase", ctx=ast.Load())],
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Dict, List, Literal, Optional
|
|
4
4
|
|
|
5
|
-
from pydantic import AliasChoices, BaseModel, Field
|
|
5
|
+
from pydantic import AliasChoices, BaseModel, Field, model_validator
|
|
6
6
|
|
|
7
7
|
APIVerbs = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
8
8
|
|
|
@@ -46,10 +46,22 @@ class HandlerCall(BaseModel):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class HandlerResult(BaseModel):
|
|
49
|
-
expected: Literal["empty", "entity_row"] = Field(
|
|
49
|
+
expected: Literal["empty", "entity_row", "custom_json", "partial_row"] = Field(
|
|
50
50
|
default="empty",
|
|
51
51
|
description="Define o formato esperado do retorno da função.",
|
|
52
52
|
)
|
|
53
|
+
properties: Optional[List[str]] = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
description="Lista de propriedades a serem retornadas quando o resultado esperado for 'partial_row'.",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@model_validator(mode="after")
|
|
59
|
+
def validate_partial_row(self):
|
|
60
|
+
if self.expected == "partial_row" and not self.properties:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"result.properties é obrigatório quando result.expected for 'partial_row'."
|
|
63
|
+
)
|
|
64
|
+
return self
|
|
53
65
|
|
|
54
66
|
|
|
55
67
|
class HandlerError(BaseModel):
|
|
@@ -68,12 +80,12 @@ class HandlerError(BaseModel):
|
|
|
68
80
|
|
|
69
81
|
|
|
70
82
|
class HandlerConfig(BaseModel):
|
|
71
|
-
impl: Literal["pg_function"] = Field(
|
|
72
|
-
|
|
83
|
+
impl: Optional[Literal["pg_function"]] = Field(
|
|
84
|
+
default=None,
|
|
73
85
|
description="Somente funções PostgreSQL (pg_function) são suportadas neste estágio.",
|
|
74
86
|
)
|
|
75
|
-
function_ref: str = Field(
|
|
76
|
-
|
|
87
|
+
function_ref: Optional[str] = Field(
|
|
88
|
+
default=None,
|
|
77
89
|
description="Referência qualificada para a função no banco (ex.: schema.fn_nome).",
|
|
78
90
|
)
|
|
79
91
|
call: Optional[HandlerCall] = Field(
|
|
@@ -59,3 +59,15 @@ class CompilerResult:
|
|
|
59
59
|
self.source_get_function_type: str | None = None
|
|
60
60
|
self.source_list_function_type: str | None = None
|
|
61
61
|
self.source_delete_function_type: str | None = None
|
|
62
|
+
self.retrieve_after_insert: bool | None = None
|
|
63
|
+
self.retrieve_after_update: bool | None = None
|
|
64
|
+
self.retrieve_after_partial_update: bool | None = None
|
|
65
|
+
self.post_response_dto_class_name: str | None = None
|
|
66
|
+
self.put_response_dto_class_name: str | None = None
|
|
67
|
+
self.patch_response_dto_class_name: str | None = None
|
|
68
|
+
self.custom_json_post_response: bool | None = None
|
|
69
|
+
self.custom_json_put_response: bool | None = None
|
|
70
|
+
self.custom_json_patch_response: bool | None = None
|
|
71
|
+
self.custom_json_get_response: bool | None = None
|
|
72
|
+
self.custom_json_list_response: bool | None = None
|
|
73
|
+
self.custom_json_delete_response: bool | None = None
|