nsj-rest-lib2 0.0.1__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.
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: nsj_rest_lib2
3
+ Version: 0.0.1
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<5.0.0,>=4.9.0
16
+ Requires-Dist: redis<7.0.0,>=6.4.0
17
+
18
+ # nsj_rest_lib2
19
+
20
+ Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
21
+
22
+ ## TODO
23
+ * Colocar tempo para recarregamento das entidades (para checar, no redis, se mudou o hash.)
24
+ * Unificar o arquivo redis_config.py
25
+ * Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
26
+ * Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
@@ -0,0 +1,9 @@
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
+ ## TODO
6
+ * Colocar tempo para recarregamento das entidades (para checar, no redis, se mudou o hash.)
7
+ * Unificar o arquivo redis_config.py
8
+ * Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
9
+ * Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
File without changes
@@ -0,0 +1,300 @@
1
+ import json
2
+
3
+ from typing import Any
4
+
5
+ from flask import Flask, request
6
+
7
+ from nsj_rest_lib.settings import APP_NAME
8
+
9
+ from nsj_gcf_utils.rest_error_util import format_json_error
10
+
11
+ from nsj_multi_database_lib.decorator.multi_database import multi_database
12
+
13
+ from nsj_rest_lib.controller.controller_util import DEFAULT_RESP_HEADERS
14
+ from nsj_rest_lib.controller.list_route import ListRoute
15
+ from nsj_rest_lib.controller.get_route import GetRoute
16
+ from nsj_rest_lib.controller.post_route import PostRoute
17
+ from nsj_rest_lib.controller.put_route import PutRoute
18
+ from nsj_rest_lib.controller.patch_route import PatchRoute
19
+ from nsj_rest_lib.controller.delete_route import DeleteRoute
20
+
21
+ from nsj_rest_lib2.exception import MissingEntityConfigException
22
+ from nsj_rest_lib2.service.entity_loader import EntityLoader
23
+
24
+
25
+ def _get_tenant_grupo() -> tuple[str, str]:
26
+ # Tentando ler do query args
27
+ query_args = request.args
28
+ tenant = query_args.get("tenant")
29
+ grupo_empresarial = query_args.get("grupo_empresarial")
30
+
31
+ # Tentando ler do corpo da requisição
32
+ try:
33
+ body_str = request.data.decode("utf-8")
34
+ body_json = json.loads(body_str)
35
+
36
+ if not tenant:
37
+ tenant = body_json.get("tenant")
38
+ if not grupo_empresarial:
39
+ grupo_empresarial = body_json.get("grupo_empresarial")
40
+ except:
41
+ pass
42
+
43
+ return (str(tenant), str(grupo_empresarial))
44
+
45
+
46
+ def _endpoint_name(func: Any, multidb: bool, root: str) -> str:
47
+ suffix = "_mb" if multidb else ""
48
+ return f"{root}_{func.__name__}{suffix}"
49
+
50
+
51
+ def setup_dynamic_routes(
52
+ flask_app: Flask,
53
+ multidb: bool = True,
54
+ dynamic_root_path: str = "edl1",
55
+ injector_factory: Any = None,
56
+ ) -> None:
57
+
58
+ COLLECTION_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>"
59
+ ONE_DYNAMIC_ROUTE = f"/{APP_NAME}/{dynamic_root_path}/<entity_id>/<id>"
60
+
61
+ def list_dynamic_wrapper(injector_factory: Any, *args: Any, **kwargs: Any) -> Any:
62
+
63
+ def list_dynamic(*args: Any, **kwargs: Any):
64
+ # Recuperando o identificador da entidade
65
+ if "entity_id" not in kwargs:
66
+ msg = "Faltando parâmetro identificador da entidade na URL."
67
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
68
+ entity_id = kwargs.pop("entity_id")
69
+
70
+ # Lendo tenant e grupo_empresarial
71
+ tenant, grupo_empresarial = _get_tenant_grupo()
72
+
73
+ try:
74
+ # Recuperando o código do DTO e Entity correspondente
75
+ entity_loader = EntityLoader()
76
+ dto_class_name, entity_class_name, etities_dict = (
77
+ entity_loader.load_entity_source(
78
+ entity_id, tenant, grupo_empresarial
79
+ )
80
+ )
81
+
82
+ # Executando o list pelo RestLib
83
+ route = ListRoute(
84
+ url=COLLECTION_DYNAMIC_ROUTE,
85
+ http_method="GET",
86
+ dto_class=etities_dict[dto_class_name],
87
+ entity_class=etities_dict[entity_class_name],
88
+ injector_factory=injector_factory,
89
+ )
90
+
91
+ return route.handle_request(*args, **kwargs)
92
+ except MissingEntityConfigException:
93
+ msg = f"Entity configuration for {entity_id} not found."
94
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
95
+
96
+ return list_dynamic
97
+
98
+ def get_dynamic(*args: Any, **kwargs: Any):
99
+ # Recuperando o identificador da entidade
100
+ if "entity_id" not in kwargs:
101
+ msg = "Faltando parâmetro identificador da entidade na URL."
102
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
103
+ entity_id = kwargs.pop("entity_id")
104
+
105
+ # Lendo tenant e grupo_empresarial
106
+ tenant, grupo_empresarial = _get_tenant_grupo()
107
+
108
+ try:
109
+ # Recuperando o código do DTO e Entity correspondente
110
+ entity_loader = EntityLoader()
111
+ dto_class_name, entity_class_name, etities_dict = (
112
+ entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
113
+ )
114
+
115
+ # Executando o list pelo RestLib
116
+ route = GetRoute(
117
+ url=ONE_DYNAMIC_ROUTE,
118
+ http_method="GET",
119
+ dto_class=etities_dict[dto_class_name],
120
+ entity_class=etities_dict[entity_class_name],
121
+ )
122
+
123
+ return route.handle_request(*args, **kwargs)
124
+ except MissingEntityConfigException:
125
+ msg = f"Entity configuration for {entity_id} not found."
126
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
127
+
128
+ def post_dynamic(*args: Any, **kwargs: Any):
129
+ # Recuperando o identificador da entidade
130
+ if "entity_id" not in kwargs:
131
+ msg = "Faltando parâmetro identificador da entidade na URL."
132
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
133
+ entity_id = kwargs.pop("entity_id")
134
+
135
+ # Lendo tenant e grupo_empresarial
136
+ tenant, grupo_empresarial = _get_tenant_grupo()
137
+
138
+ try:
139
+ # Recuperando o código do DTO e Entity correspondente
140
+ entity_loader = EntityLoader()
141
+ dto_class_name, entity_class_name, etities_dict = (
142
+ entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
143
+ )
144
+
145
+ # Executando o list pelo RestLib
146
+ route = PostRoute(
147
+ url=COLLECTION_DYNAMIC_ROUTE,
148
+ http_method="POST",
149
+ dto_class=etities_dict[dto_class_name],
150
+ entity_class=etities_dict[entity_class_name],
151
+ )
152
+
153
+ return route.handle_request(*args, **kwargs)
154
+ except MissingEntityConfigException:
155
+ msg = f"Entity configuration for {entity_id} not found."
156
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
157
+
158
+ def put_dynamic(*args: Any, **kwargs: Any):
159
+ # Recuperando o identificador da entidade
160
+ if "entity_id" not in kwargs:
161
+ msg = "Faltando parâmetro identificador da entidade na URL."
162
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
163
+ entity_id = kwargs.pop("entity_id")
164
+
165
+ # Lendo tenant e grupo_empresarial
166
+ tenant, grupo_empresarial = _get_tenant_grupo()
167
+
168
+ try:
169
+ # Recuperando o código do DTO e Entity correspondente
170
+ entity_loader = EntityLoader()
171
+ dto_class_name, entity_class_name, etities_dict = (
172
+ entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
173
+ )
174
+
175
+ # Executando o list pelo RestLib
176
+ route = PutRoute(
177
+ url=ONE_DYNAMIC_ROUTE,
178
+ http_method="PUT",
179
+ dto_class=etities_dict[dto_class_name],
180
+ entity_class=etities_dict[entity_class_name],
181
+ )
182
+
183
+ return route.handle_request(*args, **kwargs)
184
+ except MissingEntityConfigException:
185
+ msg = f"Entity configuration for {entity_id} not found."
186
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
187
+
188
+ def patch_dynamic(*args: Any, **kwargs: Any):
189
+ # Recuperando o identificador da entidade
190
+ if "entity_id" not in kwargs:
191
+ msg = "Faltando parâmetro identificador da entidade na URL."
192
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
193
+ entity_id = kwargs.pop("entity_id")
194
+
195
+ # Lendo tenant e grupo_empresarial
196
+ tenant, grupo_empresarial = _get_tenant_grupo()
197
+
198
+ try:
199
+ # Recuperando o código do DTO e Entity correspondente
200
+ entity_loader = EntityLoader()
201
+ dto_class_name, entity_class_name, etities_dict = (
202
+ entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
203
+ )
204
+
205
+ # Executando o list pelo RestLib
206
+ route = PatchRoute(
207
+ url=ONE_DYNAMIC_ROUTE,
208
+ http_method="PATCH",
209
+ dto_class=etities_dict[dto_class_name],
210
+ entity_class=etities_dict[entity_class_name],
211
+ )
212
+
213
+ return route.handle_request(*args, **kwargs)
214
+ except MissingEntityConfigException:
215
+ msg = f"Entity configuration for {entity_id} not found."
216
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
217
+
218
+ def delete_dynamic(*args: Any, **kwargs: Any):
219
+ # Recuperando o identificador da entidade
220
+ if "entity_id" not in kwargs:
221
+ msg = "Faltando parâmetro identificador da entidade na URL."
222
+ return (format_json_error(msg), 400, {**DEFAULT_RESP_HEADERS})
223
+ entity_id = kwargs.pop("entity_id")
224
+
225
+ # Lendo tenant e grupo_empresarial
226
+ tenant, grupo_empresarial = _get_tenant_grupo()
227
+
228
+ try:
229
+ # Recuperando o código do DTO e Entity correspondente
230
+ entity_loader = EntityLoader()
231
+ dto_class_name, entity_class_name, etities_dict = (
232
+ entity_loader.load_entity_source(entity_id, tenant, grupo_empresarial)
233
+ )
234
+
235
+ # Executando o list pelo RestLib
236
+ route = DeleteRoute(
237
+ url=ONE_DYNAMIC_ROUTE,
238
+ http_method="DELETE",
239
+ dto_class=etities_dict[dto_class_name],
240
+ entity_class=etities_dict[entity_class_name],
241
+ )
242
+
243
+ return route.handle_request(*args, **kwargs)
244
+ except MissingEntityConfigException:
245
+ msg = f"Entity configuration for {entity_id} not found."
246
+ return (format_json_error(msg), 412, {**DEFAULT_RESP_HEADERS})
247
+
248
+ # Ajustando para o padrão com multi database (se necessário)
249
+ if multidb:
250
+ list_dynamic = multi_database()(list_dynamic_wrapper(injector_factory))
251
+ get_dynamic = multi_database()(get_dynamic)
252
+ post_dynamic = multi_database()(post_dynamic)
253
+ put_dynamic = multi_database()(put_dynamic)
254
+ patch_dynamic = multi_database()(patch_dynamic)
255
+ delete_dynamic = multi_database()(delete_dynamic)
256
+ else:
257
+ list_dynamic = list_dynamic_wrapper(injector_factory)
258
+ get_dynamic = get_dynamic
259
+ post_dynamic = post_dynamic
260
+ put_dynamic = put_dynamic
261
+ patch_dynamic = patch_dynamic
262
+ delete_dynamic = delete_dynamic
263
+
264
+ # Registrando as rotas no flask
265
+ flask_app.add_url_rule(
266
+ COLLECTION_DYNAMIC_ROUTE,
267
+ endpoint=_endpoint_name(list_dynamic, multidb, dynamic_root_path),
268
+ view_func=list_dynamic,
269
+ methods=["GET"],
270
+ )
271
+ flask_app.add_url_rule(
272
+ ONE_DYNAMIC_ROUTE,
273
+ endpoint=_endpoint_name(get_dynamic, multidb, dynamic_root_path),
274
+ view_func=get_dynamic,
275
+ methods=["GET"],
276
+ )
277
+ flask_app.add_url_rule(
278
+ COLLECTION_DYNAMIC_ROUTE,
279
+ endpoint=_endpoint_name(post_dynamic, multidb, dynamic_root_path),
280
+ view_func=post_dynamic,
281
+ methods=["POST"],
282
+ )
283
+ flask_app.add_url_rule(
284
+ ONE_DYNAMIC_ROUTE,
285
+ endpoint=_endpoint_name(put_dynamic, multidb, dynamic_root_path),
286
+ view_func=put_dynamic,
287
+ methods=["PUT"],
288
+ )
289
+ flask_app.add_url_rule(
290
+ ONE_DYNAMIC_ROUTE,
291
+ endpoint=_endpoint_name(patch_dynamic, multidb, dynamic_root_path),
292
+ view_func=patch_dynamic,
293
+ methods=["PATCH"],
294
+ )
295
+ flask_app.add_url_rule(
296
+ ONE_DYNAMIC_ROUTE,
297
+ endpoint=_endpoint_name(delete_dynamic, multidb, dynamic_root_path),
298
+ view_func=delete_dynamic,
299
+ methods=["DELETE"],
300
+ )
@@ -0,0 +1,2 @@
1
+ class MissingEntityConfigException(Exception):
2
+ pass
@@ -0,0 +1,31 @@
1
+ import os
2
+ import redis
3
+
4
+ from typing import Any
5
+
6
+ REDIS_HOST = os.environ["REDIS_HOST"]
7
+ REDIS_PORT = int(os.environ["REDIS_PORT"])
8
+ REDIS_DB = int(os.getenv("REDIS_DB", 0))
9
+
10
+ redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB)
11
+
12
+
13
+ def k(*parts: str) -> str:
14
+ return ":".join(parts)
15
+
16
+
17
+ def get_redis(*args: str) -> Any:
18
+ value = redis_client.get(k(*args))
19
+ if value:
20
+ return value.decode("utf-8")
21
+ return None
22
+
23
+
24
+ def set_redis(*args) -> None:
25
+ value = args[-1]
26
+ redis_client.set(k(*args[:-1]), value)
27
+
28
+
29
+ if __name__ == "__main__":
30
+ set_redis("ping", "pong")
31
+ print(get_redis(("ping")))
File without changes
@@ -0,0 +1,268 @@
1
+ import json
2
+ import sys
3
+ import threading
4
+ import types
5
+
6
+ from nsj_rest_lib.settings import get_logger
7
+
8
+ from nsj_rest_lib2.exception import MissingEntityConfigException
9
+ from nsj_rest_lib2.redis_config import get_redis
10
+ from nsj_rest_lib2.settings import ESCOPO_RESTLIB2
11
+
12
+
13
+ class LoadedEntity:
14
+ dto_class_name: str = ""
15
+ entity_class_name: str = ""
16
+ entity_hash: str = ""
17
+
18
+
19
+ class Namespace:
20
+ def __init__(self):
21
+ self.key: str = ""
22
+ self.loaded_entities: dict[str, LoadedEntity] = {}
23
+ self.entities_dict: dict = None
24
+ self.module: types.ModuleType = None
25
+
26
+
27
+ namespaces_dict = {}
28
+
29
+
30
+ class EntityLoader:
31
+ def __init__(self) -> types.NoneType:
32
+ self._lock = threading.Lock()
33
+
34
+ def load_entity_source(
35
+ self,
36
+ entity_id: str,
37
+ tenant: str | None,
38
+ grupo_empresarial: str | None,
39
+ ) -> tuple[str, str, dict]:
40
+ # Montando as chaves dos namespaces
41
+ grupo_key = f"tenant_{tenant}.ge_{grupo_empresarial}"
42
+ tenant_key = f"tenant_{tenant}"
43
+ default_key = "default"
44
+
45
+ result = self._load_entity_source_from_memory(
46
+ entity_id, grupo_key, tenant_key, default_key
47
+ )
48
+
49
+ # Se conseguiu carregar da memória, verifica se houve alteração no hash, em relação ao redis
50
+ if result is not None:
51
+ # Desempacotando o result
52
+ dto_class_name, entity_class_name, entities_dict, entity_config_key = result
53
+
54
+ # Recuperando do Redis direto pela key (faz uma só chamada ao redis)
55
+ loaded_config = self._load_entity_config_from_redis(
56
+ entity_id, grupo_key, tenant_key, default_key, entity_config_key
57
+ )
58
+
59
+ # Se não achar no redis, usa o que estava em memória
60
+ if not loaded_config:
61
+ return (dto_class_name, entity_class_name, entities_dict)
62
+
63
+ # Desempacotando resultado
64
+ entity_config_key, entity_config_str = loaded_config
65
+
66
+ # Executando o código da entidade, só se houver mudança no hash
67
+ result_execute = self._execute_entity_source(
68
+ entity_config_str,
69
+ entity_config_key,
70
+ entity_id,
71
+ check_refresh=True,
72
+ )
73
+
74
+ if result_execute is None:
75
+ return (dto_class_name, entity_class_name, entities_dict)
76
+ else:
77
+ dto_class_name, entity_class_name, namespace = result_execute
78
+ return (dto_class_name, entity_class_name, namespace.entities_dict)
79
+
80
+ # Se não conseguir recuperar a entidade, procura no redis:
81
+ loaded_config = self._load_entity_config_from_redis(
82
+ entity_id, grupo_key, tenant_key, default_key, None
83
+ )
84
+
85
+ # Se também não achar no redis, lanca exceção
86
+ if not loaded_config:
87
+ raise MissingEntityConfigException()
88
+
89
+ # Desempacotando resultado
90
+ entity_config_key, entity_config_str = loaded_config
91
+
92
+ # Executando o código da entidade
93
+ result_execute = self._execute_entity_source(
94
+ entity_config_str, entity_config_key, entity_id
95
+ )
96
+
97
+ if result_execute is None:
98
+ raise RuntimeError(f"Erro desconhecido carregando entidade: {entity_id}")
99
+ dto_class_name, entity_class_name, namespace = result_execute
100
+
101
+ return (dto_class_name, entity_class_name, namespace.entities_dict)
102
+
103
+ def clear_namespaces(self):
104
+ """
105
+ Clears all loaded namespaces from memory.
106
+
107
+ This method removes all entries from the namespaces_dict, effectively resetting
108
+ the in-memory cache of loaded entities and their associated namespaces.
109
+ """
110
+ with self._lock:
111
+ namespaces_dict.clear()
112
+
113
+ def _execute_entity_source(
114
+ self,
115
+ entity_config_str: str,
116
+ entity_config_key: str,
117
+ entity_id: str,
118
+ check_refresh: bool = False,
119
+ ) -> tuple[str, str, Namespace] | None:
120
+ # Interpretando o json de configuração da entidade
121
+ try:
122
+ entity_config = json.loads(entity_config_str)
123
+
124
+ dto_class_name = entity_config["dto_class_name"]
125
+ entity_class_name = entity_config["entity_class_name"]
126
+ source_dto = entity_config["source_dto"]
127
+ source_entity = entity_config["source_entity"]
128
+ entity_hash = entity_config["entity_hash"]
129
+ except json.JSONDecodeError as e:
130
+ if not check_refresh:
131
+ raise RuntimeError(
132
+ f"Erro ao decodificar JSON da entidade {entity_id}; na chave {entity_config_key}: {e}"
133
+ )
134
+ else:
135
+ get_logger().error(
136
+ f"Erro ao decodificar JSON da entidade {entity_id}; na chave {entity_config_key}: {e}"
137
+ )
138
+ return None
139
+
140
+ # Verificando se a entidade precisa ou não de refresh
141
+ if check_refresh:
142
+ loaded_namespace = namespaces_dict.get(entity_config_key)
143
+ if not loaded_namespace:
144
+ return None
145
+
146
+ loaded_entity = loaded_namespace.loaded_entities.get(entity_id)
147
+ if not loaded_entity:
148
+ return None
149
+
150
+ if loaded_entity.entity_hash == entity_hash:
151
+ return None
152
+
153
+ # Imprimindo alerta de load no log
154
+ get_logger().debug(
155
+ f"Carregando entidade {entity_id} no namespace {entity_config_key}."
156
+ )
157
+
158
+ # Carregando a entidade no namespace
159
+ with self._lock:
160
+ namespace = namespaces_dict.get(entity_config_key)
161
+ if namespace is None:
162
+ namespace = Namespace()
163
+ namespace.key = entity_config_key
164
+ namespaces_dict[entity_config_key] = namespace
165
+
166
+ # Executando o código da entidade
167
+ module = types.ModuleType(entity_config_key)
168
+ sys.modules[entity_config_key] = module
169
+ module.__dict__["__builtins__"] = __builtins__
170
+
171
+ namespace.module = module
172
+ namespace.entities_dict = module.__dict__
173
+
174
+ self._safe_exec(source_dto, namespace.entities_dict, "DTO source")
175
+ self._safe_exec(source_entity, namespace.entities_dict, "Entity source")
176
+
177
+ # Gravando a entidade no dict de entidades carregadas
178
+ loaded_entity = LoadedEntity()
179
+ loaded_entity.dto_class_name = dto_class_name
180
+ loaded_entity.entity_class_name = entity_class_name
181
+ loaded_entity.entity_hash = entity_hash
182
+
183
+ namespace.loaded_entities[entity_id] = loaded_entity
184
+
185
+ return (dto_class_name, entity_class_name, namespace)
186
+
187
+ def _safe_exec(self, source_code, context, description):
188
+ try:
189
+ exec(source_code, context)
190
+ except Exception as e:
191
+ get_logger().error(f"Error executing {description}: {e}")
192
+ raise
193
+
194
+ def _load_entity_config_from_redis(
195
+ self,
196
+ entity_id: str,
197
+ grupo_key: str,
198
+ tenant_key: str,
199
+ default_key: str,
200
+ entity_config_key: str | None,
201
+ ) -> tuple[str, str] | None:
202
+ get_logger().debug(
203
+ f"Procurando a configuração da entidade {entity_id} no redis"
204
+ )
205
+
206
+ if entity_config_key is not None:
207
+ entity_config_str = get_redis(
208
+ "entity_config", ESCOPO_RESTLIB2, entity_config_key, entity_id
209
+ )
210
+
211
+ else:
212
+ entity_config_key = grupo_key
213
+ entity_config_str = get_redis(
214
+ "entity_config", ESCOPO_RESTLIB2, grupo_key, entity_id
215
+ )
216
+ if entity_config_str is None:
217
+ entity_config_key = tenant_key
218
+ entity_config_str = get_redis(
219
+ "entity_config", ESCOPO_RESTLIB2, tenant_key, entity_id
220
+ )
221
+ if entity_config_str is None:
222
+ entity_config_key = default_key
223
+ entity_config_str = get_redis(
224
+ "entity_config", ESCOPO_RESTLIB2, default_key, entity_id
225
+ )
226
+
227
+ # Se não encontrar no redis, retorna None
228
+ if entity_config_str is None:
229
+ return None
230
+
231
+ return (entity_config_key, entity_config_str)
232
+
233
+ def _load_entity_source_from_memory(
234
+ self,
235
+ entity_id: str,
236
+ grupo_key: str,
237
+ tenant_key: str,
238
+ default_key: str,
239
+ ) -> tuple[str, str, dict, str] | None:
240
+ namespace = None
241
+ entity_config_key = None
242
+
243
+ # Pesquisando a entidade no namespace mais específico (grupo_empresarial)
244
+ grupo_namespace = namespaces_dict.get(grupo_key)
245
+ if grupo_namespace and entity_id in grupo_namespace.loaded_entities:
246
+ entity_config_key = grupo_key
247
+ namespace = grupo_namespace
248
+
249
+ # Pesquisando a entidade no namespace intermediário (tenant)
250
+ tenant_namespace = namespaces_dict.get(tenant_key)
251
+ if tenant_namespace and entity_id in tenant_namespace.loaded_entities:
252
+ entity_config_key = tenant_key
253
+ namespace = tenant_namespace
254
+
255
+ # Pesquisando a entidade no namespace padrão (default)
256
+ default_namespace = namespaces_dict.get(default_key)
257
+ if default_namespace and entity_id in default_namespace.loaded_entities:
258
+ entity_config_key = default_key
259
+ namespace = default_namespace
260
+
261
+ if namespace:
262
+ loaded_entity = namespace.loaded_entities[entity_id]
263
+ dto_class_name = loaded_entity.dto_class_name
264
+ entity_class_name = loaded_entity.entity_class_name
265
+ entities_dict = namespace.entities_dict
266
+ return (dto_class_name, entity_class_name, entities_dict, entity_config_key)
267
+ else:
268
+ return None
@@ -0,0 +1,3 @@
1
+ import os
2
+
3
+ ESCOPO_RESTLIB2 = os.environ["ESCOPO_RESTLIB2"]
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: nsj_rest_lib2
3
+ Version: 0.0.1
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<5.0.0,>=4.9.0
16
+ Requires-Dist: redis<7.0.0,>=6.4.0
17
+
18
+ # nsj_rest_lib2
19
+
20
+ Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
21
+
22
+ ## TODO
23
+ * Colocar tempo para recarregamento das entidades (para checar, no redis, se mudou o hash.)
24
+ * Unificar o arquivo redis_config.py
25
+ * Usar pydantic, ou similar, para transformar a configuração das entidades, no redis, num objeto
26
+ * Rever modo de usar o InjectFactory (talvez dando ciência, ao RestLib, do padrão multibanco)
@@ -0,0 +1,29 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.cfg
4
+ ./nsj_rest_lib2/__init__.py
5
+ ./nsj_rest_lib2/exception.py
6
+ ./nsj_rest_lib2/redis_config.py
7
+ ./nsj_rest_lib2/settings.py
8
+ ./nsj_rest_lib2.egg-info/PKG-INFO
9
+ ./nsj_rest_lib2.egg-info/SOURCES.txt
10
+ ./nsj_rest_lib2.egg-info/dependency_links.txt
11
+ ./nsj_rest_lib2.egg-info/requires.txt
12
+ ./nsj_rest_lib2.egg-info/top_level.txt
13
+ ./nsj_rest_lib2/controller/__init__.py
14
+ ./nsj_rest_lib2/controller/dynamic_controller.py
15
+ ./nsj_rest_lib2/service/__init__.py
16
+ ./nsj_rest_lib2/service/entity_loader.py
17
+ nsj_rest_lib2/__init__.py
18
+ nsj_rest_lib2/exception.py
19
+ nsj_rest_lib2/redis_config.py
20
+ nsj_rest_lib2/settings.py
21
+ nsj_rest_lib2.egg-info/PKG-INFO
22
+ nsj_rest_lib2.egg-info/SOURCES.txt
23
+ nsj_rest_lib2.egg-info/dependency_links.txt
24
+ nsj_rest_lib2.egg-info/requires.txt
25
+ nsj_rest_lib2.egg-info/top_level.txt
26
+ nsj_rest_lib2/controller/__init__.py
27
+ nsj_rest_lib2/controller/dynamic_controller.py
28
+ nsj_rest_lib2/service/__init__.py
29
+ nsj_rest_lib2/service/entity_loader.py
@@ -0,0 +1,2 @@
1
+ nsj-rest-lib<5.0.0,>=4.9.0
2
+ redis<7.0.0,>=6.4.0
@@ -0,0 +1 @@
1
+ nsj_rest_lib2
@@ -0,0 +1,6 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=42",
4
+ "wheel"
5
+ ]
6
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,33 @@
1
+ [metadata]
2
+ name = nsj_rest_lib2
3
+ version = 0.0.1
4
+ author = Nasajon Sistemas
5
+ author_email = contact.dev@nasajon.com.br
6
+ description = Biblioteca para permitir a distribuição de rotas dinâmicas numa API, configuradas por meio de EDLs declarativos (em formato JSON).
7
+ long_description = file: README.md
8
+ long_description_content_type = text/markdown
9
+ url = https://github.com/Nasajon/nsj_rest_lib2
10
+ project_urls =
11
+ Source = https://github.com/Nasajon/nsj_rest_lib2
12
+ classifiers =
13
+ Development Status :: 3 - Alpha
14
+ Intended Audience :: Developers
15
+ Topic :: Software Development :: Libraries
16
+ Programming Language :: Python :: 3
17
+
18
+ [options]
19
+ package_dir =
20
+ = ./
21
+ packages = find:
22
+ python_requires = >=3.4
23
+ install_requires =
24
+ nsj-rest-lib>=4.9.0,<5.0.0
25
+ redis>=6.4.0,<7.0.0
26
+
27
+ [options.packages.find]
28
+ where = ./
29
+
30
+ [egg_info]
31
+ tag_build =
32
+ tag_date = 0
33
+