nsj-rest-lib2 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ import ast
2
+
3
+ from nsj_rest_lib2.compiler.compiler_structures import (
4
+ IndexCompilerStructure,
5
+ PropertiesCompilerStructure,
6
+ )
7
+ from nsj_rest_lib2.compiler.edl_model.entity_model import EntityModel
8
+ from nsj_rest_lib2.compiler.edl_model.primitives import PrimitiveTypes
9
+ from nsj_rest_lib2.compiler.edl_model.property_meta_model import PropertyMetaModel
10
+ from nsj_rest_lib2.compiler.util.str_util import CompilerStrUtil
11
+ from nsj_rest_lib2.compiler.util.type_util import TypeUtil
12
+
13
+ # TODO pattern
14
+ # TODO lowercase
15
+ # TODO uppercase
16
+ # TODO Adicionar o nome da entidade, no nome das classes de enum (para evitar conflitos no caso das traits)
17
+
18
+
19
+ class EDLPropertyCompiler:
20
+ def compile(
21
+ self,
22
+ properties_structure: PropertiesCompilerStructure,
23
+ map_unique_by_property: dict[str, IndexCompilerStructure],
24
+ entity_model: EntityModel,
25
+ ) -> tuple[list[ast.stmt], list[ast.stmt], list[str], list[ast.stmt]]:
26
+
27
+ # TODO Criar opção de campo calculado?
28
+
29
+ ast_dto_attributes = []
30
+ ast_entity_attributes = []
31
+ props_pk = []
32
+ enum_classes = []
33
+
34
+ if properties_structure.properties is None:
35
+ return (ast_dto_attributes, ast_entity_attributes, props_pk, enum_classes)
36
+
37
+ for pkey in properties_structure.properties:
38
+ prop = properties_structure.properties[pkey]
39
+ enum_class_name = None
40
+
41
+ # DTO
42
+ ## Tratando propriedade simples (não array, não object)
43
+ if prop.type not in [PrimitiveTypes.ARRAY, PrimitiveTypes.OBJECT]:
44
+ keywords = []
45
+
46
+ if prop.pk:
47
+ keywords.append(ast.keyword(arg="pk", value=ast.Constant(True)))
48
+ props_pk.append(pkey)
49
+
50
+ if prop.key_alternative:
51
+ keywords.append(
52
+ ast.keyword(arg="candidate_key", value=ast.Constant(True))
53
+ )
54
+
55
+ if (
56
+ properties_structure.main_properties
57
+ and pkey in properties_structure.main_properties
58
+ ):
59
+ keywords.append(ast.keyword(arg="resume", value=ast.Constant(True)))
60
+
61
+ if (
62
+ properties_structure.required
63
+ and pkey in properties_structure.required
64
+ ):
65
+ keywords.append(
66
+ ast.keyword(arg="not_null", value=ast.Constant(True))
67
+ )
68
+
69
+ if (
70
+ properties_structure.partition_data
71
+ and pkey in properties_structure.partition_data
72
+ ):
73
+ keywords.append(
74
+ ast.keyword(arg="partition_data", value=ast.Constant(True))
75
+ )
76
+
77
+ if pkey in map_unique_by_property:
78
+ unique = map_unique_by_property[pkey].index_model
79
+ keywords.append(
80
+ ast.keyword(
81
+ arg="unique",
82
+ value=ast.Constant(unique.name),
83
+ )
84
+ )
85
+
86
+ if (
87
+ prop.default
88
+ ): # TODO Verificar esse modo de tratar valores default (principalmente expressões)
89
+ keywords.append(
90
+ ast.keyword(
91
+ arg="default_value",
92
+ value=ast.Name(str(prop.default), ctx=ast.Load()),
93
+ )
94
+ )
95
+
96
+ if prop.trim:
97
+ keywords.append(ast.keyword(arg="strip", value=ast.Constant(True)))
98
+
99
+ max = None
100
+ min = None
101
+ if prop.type in [PrimitiveTypes.STRING, PrimitiveTypes.EMAIL]:
102
+ if prop.max_length:
103
+ max = prop.max_length
104
+ elif prop.min_length:
105
+ min = prop.min_length
106
+ elif prop.type in [PrimitiveTypes.INTEGER, PrimitiveTypes.NUMBER]:
107
+ if prop.minimum:
108
+ min = prop.minimum
109
+ elif prop.maximum:
110
+ max = prop.maximum
111
+
112
+ if max:
113
+ keywords.append(
114
+ ast.keyword(arg="max", value=ast.Constant(prop.max_length))
115
+ )
116
+ if min:
117
+ keywords.append(
118
+ ast.keyword(arg="min", value=ast.Constant(prop.min_length))
119
+ )
120
+
121
+ if (
122
+ properties_structure.search_properties
123
+ and pkey in properties_structure.search_properties
124
+ ):
125
+ keywords.append(ast.keyword(arg="search", value=ast.Constant(True)))
126
+ else:
127
+ keywords.append(
128
+ ast.keyword(arg="search", value=ast.Constant(False))
129
+ )
130
+
131
+ if (
132
+ properties_structure.metric_label
133
+ and pkey in properties_structure.metric_label
134
+ ):
135
+ keywords.append(
136
+ ast.keyword(arg="metric_label", value=ast.Constant(True))
137
+ )
138
+
139
+ if prop.type == PrimitiveTypes.CPF and not prop.validator:
140
+ keywords.append(
141
+ ast.keyword(
142
+ arg="validator",
143
+ value=ast.Attribute(
144
+ value=ast.Call(
145
+ func=ast.Name(
146
+ id="DTOFieldValidators", ctx=ast.Load()
147
+ ),
148
+ args=[],
149
+ keywords=[],
150
+ ),
151
+ attr="validate_cpf",
152
+ ctx=ast.Load(),
153
+ ),
154
+ )
155
+ )
156
+ elif prop.type == PrimitiveTypes.CNPJ and not prop.validator:
157
+ keywords.append(
158
+ ast.keyword(
159
+ arg="validator",
160
+ value=ast.Attribute(
161
+ value=ast.Call(
162
+ func=ast.Name(
163
+ id="DTOFieldValidators", ctx=ast.Load()
164
+ ),
165
+ args=[],
166
+ keywords=[],
167
+ ),
168
+ attr="validate_cnpj",
169
+ ctx=ast.Load(),
170
+ ),
171
+ )
172
+ )
173
+ elif prop.type == PrimitiveTypes.CPF_CNPJ and not prop.validator:
174
+ keywords.append(
175
+ ast.keyword(
176
+ arg="validator",
177
+ value=ast.Attribute(
178
+ value=ast.Call(
179
+ func=ast.Name(
180
+ id="DTOFieldValidators", ctx=ast.Load()
181
+ ),
182
+ args=[],
183
+ keywords=[],
184
+ ),
185
+ attr="validate_cpf_or_cnpj",
186
+ ctx=ast.Load(),
187
+ ),
188
+ )
189
+ )
190
+ elif prop.type == PrimitiveTypes.EMAIL and not prop.validator:
191
+ keywords.append(
192
+ ast.keyword(
193
+ arg="validator",
194
+ value=ast.Attribute(
195
+ value=ast.Call(
196
+ func=ast.Name(
197
+ id="DTOFieldValidators", ctx=ast.Load()
198
+ ),
199
+ args=[],
200
+ keywords=[],
201
+ ),
202
+ attr="validate_email",
203
+ ctx=ast.Load(),
204
+ ),
205
+ )
206
+ )
207
+ elif prop.validator:
208
+ keywords.append(
209
+ ast.keyword(
210
+ arg="validator",
211
+ value=ast.Name(prop.validator, ctx=ast.Load()),
212
+ )
213
+ )
214
+
215
+ if prop.immutable:
216
+ keywords.append(
217
+ ast.keyword(arg="read_only", value=ast.Constant(True))
218
+ )
219
+
220
+ if prop.on_save:
221
+ keywords.append(
222
+ ast.keyword(
223
+ arg="convert_to_entity",
224
+ value=ast.Name(prop.on_save, ctx=ast.Load()),
225
+ )
226
+ )
227
+
228
+ if prop.on_retrieve:
229
+ keywords.append(
230
+ ast.keyword(
231
+ arg="convert_from_entity",
232
+ value=ast.Name(id=prop.on_retrieve, ctx=ast.Load()),
233
+ )
234
+ )
235
+
236
+ if prop.domain_config:
237
+ result = self._compile_domain_config(pkey, prop, entity_model)
238
+ if not result:
239
+ raise Exception(
240
+ f"Erro desconhecido ao compilar a propriedade {pkey}"
241
+ )
242
+
243
+ enum_class_name, ast_enum_class = result
244
+ enum_classes.append(ast_enum_class)
245
+
246
+ # Instanciando o atributo AST
247
+ if enum_class_name:
248
+ prop_type = enum_class_name
249
+ else:
250
+ prop_type = TypeUtil.property_type_to_python_type(prop.type)
251
+
252
+ ast_attr = ast.AnnAssign(
253
+ target=ast.Name(
254
+ id=CompilerStrUtil.to_snake_case(pkey), ctx=ast.Store()
255
+ ),
256
+ annotation=ast.Name(
257
+ id=prop_type,
258
+ ctx=ast.Load(),
259
+ ),
260
+ value=ast.Call(
261
+ func=ast.Name(id="DTOField", ctx=ast.Load()),
262
+ args=[],
263
+ keywords=keywords,
264
+ ),
265
+ simple=1,
266
+ )
267
+
268
+ ast_dto_attributes.append(ast_attr)
269
+
270
+ # Entity
271
+ if (
272
+ properties_structure.entity_properties
273
+ and pkey in properties_structure.entity_properties
274
+ ):
275
+ entity_field_name = properties_structure.entity_properties[
276
+ pkey
277
+ ].column
278
+ else:
279
+ entity_field_name = pkey
280
+
281
+ ast_entity_attr = ast.AnnAssign(
282
+ target=ast.Name(
283
+ id=CompilerStrUtil.to_snake_case(entity_field_name),
284
+ ctx=ast.Store(),
285
+ ),
286
+ annotation=ast.Name(
287
+ id=TypeUtil.property_type_to_python_type(prop.type),
288
+ ctx=ast.Load(),
289
+ ),
290
+ value=ast.Constant(value=None),
291
+ simple=1,
292
+ )
293
+
294
+ ast_entity_attributes.append(ast_entity_attr)
295
+
296
+ return ast_dto_attributes, ast_entity_attributes, props_pk, enum_classes
297
+
298
+ def _compile_domain_config(
299
+ self,
300
+ pkey: str,
301
+ prop: PropertyMetaModel,
302
+ entity_model: EntityModel,
303
+ ) -> tuple[str, ast.stmt] | None:
304
+ if not prop.domain_config:
305
+ return None
306
+
307
+ # Verificando se deveria usar o mapped_value
308
+ use_mapped_value = False
309
+ for value in prop.domain_config:
310
+ if value.mapped_value:
311
+ use_mapped_value = True
312
+ break
313
+
314
+ # Compilando as opções do enum
315
+ ast_values = []
316
+ for value in prop.domain_config:
317
+ value_name = CompilerStrUtil.to_snake_case(value.value).upper()
318
+
319
+ if use_mapped_value and value.mapped_value is None:
320
+ raise Exception(
321
+ f"Propriedade '{pkey}' possui domain_config com value '{value.value}' mas sem mapped_value"
322
+ )
323
+
324
+ if value.mapped_value is not None:
325
+ ast_value = ast.Assign(
326
+ targets=[ast.Name(id=value_name, ctx=ast.Store())],
327
+ value=ast.Tuple(
328
+ elts=[
329
+ ast.Constant(value=value.value),
330
+ ast.Constant(value=value.mapped_value),
331
+ ],
332
+ ctx=ast.Load(),
333
+ ),
334
+ )
335
+ else:
336
+ ast_value = ast.Assign(
337
+ targets=[ast.Name(id=value_name, ctx=ast.Store())],
338
+ value=ast.Constant(value=value.value),
339
+ )
340
+
341
+ ast_values.append(ast_value)
342
+
343
+ # Instanciando o atributo AST
344
+ enum_class_name = f"{CompilerStrUtil.to_pascal_case(entity_model.escopo)}{CompilerStrUtil.to_pascal_case(entity_model.id)}{CompilerStrUtil.to_pascal_case(pkey)}Enum"
345
+ ast_enum_class = ast.ClassDef(
346
+ name=enum_class_name,
347
+ bases=[
348
+ ast.Attribute(
349
+ value=ast.Name(id="enum", ctx=ast.Load()),
350
+ attr="Enum",
351
+ ctx=ast.Load(),
352
+ )
353
+ ],
354
+ keywords=[],
355
+ decorator_list=[],
356
+ body=ast_values,
357
+ )
358
+
359
+ return enum_class_name, ast_enum_class
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  import json
2
3
  import sys
3
4
  import threading
@@ -7,13 +8,16 @@ from nsj_rest_lib.settings import get_logger
7
8
 
8
9
  from nsj_rest_lib2.exception import MissingEntityConfigException
9
10
  from nsj_rest_lib2.redis_config import get_redis
10
- from nsj_rest_lib2.settings import ESCOPO_RESTLIB2
11
+ from nsj_rest_lib2.settings import ESCOPO_RESTLIB2, MIN_TIME_SOURCE_REFRESH
11
12
 
12
13
 
14
+ # TODO Verificar se está atualizando o loaded_at, quando o hash não muda
13
15
  class LoadedEntity:
14
- dto_class_name: str = ""
15
- entity_class_name: str = ""
16
- entity_hash: str = ""
16
+ def __init__(self):
17
+ self.dto_class_name: str = ""
18
+ self.entity_class_name: str = ""
19
+ self.entity_hash: str = ""
20
+ self.loaded_at: datetime.datetime = datetime.datetime.now()
17
21
 
18
22
 
19
23
  class Namespace:
@@ -24,7 +28,7 @@ class Namespace:
24
28
  self.module: types.ModuleType = None
25
29
 
26
30
 
27
- namespaces_dict = {}
31
+ namespaces_dict: dict[str, Namespace] = {}
28
32
 
29
33
 
30
34
  class EntityLoader:
@@ -48,34 +52,51 @@ class EntityLoader:
48
52
 
49
53
  # Se conseguiu carregar da memória, verifica se houve alteração no hash, em relação ao redis
50
54
  if result is not None:
51
- # Desempacotando o result
52
- dto_class_name, entity_class_name, entities_dict, entity_config_key = result
55
+ # Desempacotando o result e recuperando informações do namespace
56
+ (
57
+ entity_config_key,
58
+ namespace,
59
+ ) = result
53
60
 
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
- )
61
+ loaded_entity = namespace.loaded_entities[entity_id]
62
+ dto_class_name = loaded_entity.dto_class_name
63
+ entity_class_name = loaded_entity.entity_class_name
64
+ entities_dict = namespace.entities_dict
58
65
 
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)
66
+ # Se o tempo entre o carregamento e agora for maior do que MIN_TIME_SOURCE_REFRESH minutos,
67
+ # verifica se precisa de refresh
68
+ time_diff = datetime.datetime.now() - loaded_entity.loaded_at
62
69
 
63
- # Desempacotando resultado
64
- entity_config_key, entity_config_str = loaded_config
70
+ if time_diff.total_seconds() >= MIN_TIME_SOURCE_REFRESH * 60:
71
+ # Recuperando do Redis direto pela key (faz uma só chamada ao redis)
72
+ loaded_config = self._load_entity_config_from_redis(
73
+ entity_id, grupo_key, tenant_key, default_key, entity_config_key
74
+ )
65
75
 
66
- # Executando o código da entidade, 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
- )
76
+ # Se não achar no redis, usa o que estava em memória
77
+ if not loaded_config:
78
+ return (dto_class_name, entity_class_name, entities_dict)
73
79
 
74
- if result_execute is None:
75
- return (dto_class_name, entity_class_name, entities_dict)
80
+ # Desempacotando resultado
81
+ entity_config_key, entity_config_str = loaded_config
82
+
83
+ # Executando o código da entidade, só se houver mudança no hash
84
+ result_execute = self._execute_entity_source(
85
+ entity_config_str,
86
+ entity_config_key,
87
+ entity_id,
88
+ check_refresh=True,
89
+ )
90
+
91
+ # Se não carregou novo código, usa o que estava em memória
92
+ if result_execute is None:
93
+ return (dto_class_name, entity_class_name, entities_dict)
94
+ else:
95
+ dto_class_name, entity_class_name, namespace = result_execute
96
+ return (dto_class_name, entity_class_name, namespace.entities_dict)
76
97
  else:
77
- dto_class_name, entity_class_name, namespace = result_execute
78
- return (dto_class_name, entity_class_name, namespace.entities_dict)
98
+ # Se não deu o intervalo de verificação do refresh, retorna o que está em memória
99
+ return (dto_class_name, entity_class_name, entities_dict)
79
100
 
80
101
  # Se não conseguir recuperar a entidade, procura no redis:
81
102
  loaded_config = self._load_entity_config_from_redis(
@@ -236,7 +257,7 @@ class EntityLoader:
236
257
  grupo_key: str,
237
258
  tenant_key: str,
238
259
  default_key: str,
239
- ) -> tuple[str, str, dict, str] | None:
260
+ ) -> tuple[str, Namespace] | None:
240
261
  namespace = None
241
262
  entity_config_key = None
242
263
 
@@ -258,11 +279,7 @@ class EntityLoader:
258
279
  entity_config_key = default_key
259
280
  namespace = default_namespace
260
281
 
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)
282
+ if namespace and entity_config_key:
283
+ return (entity_config_key, namespace)
267
284
  else:
268
285
  return None
nsj_rest_lib2/settings.py CHANGED
@@ -1,3 +1,16 @@
1
+ import logging
1
2
  import os
2
3
 
3
4
  ESCOPO_RESTLIB2 = os.environ["ESCOPO_RESTLIB2"]
5
+ APP_NAME = os.getenv("APP_NAME", "nsj_rest_lib2")
6
+
7
+ ENV = os.getenv("ENV", "dev").lower()
8
+
9
+ if ENV in ("dev", "local"):
10
+ MIN_TIME_SOURCE_REFRESH = 0
11
+ else:
12
+ MIN_TIME_SOURCE_REFRESH = int(os.getenv("MIN_TIME_SOURCE_REFRESH", "15"))
13
+
14
+
15
+ def get_logger():
16
+ return logging.getLogger(APP_NAME)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nsj_rest_lib2
3
- Version: 0.0.2
3
+ Version: 0.0.4
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
@@ -0,0 +1,29 @@
1
+ nsj_rest_lib2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ nsj_rest_lib2/exception.py,sha256=E9uMUdoCCQOVQfc8f6gD9b5Pxurf3Q4SytDCcqSlkZ8,56
3
+ nsj_rest_lib2/redis_config.py,sha256=4KLcvYS3nJO7PMQgF6F9_j6r-TyqcS7TBbd3LEQuKDU,629
4
+ nsj_rest_lib2/settings.py,sha256=Hn_o1HZmievnYb8D1kNT2Nq-OEjxbyNjOiOpbnFsMwE,367
5
+ nsj_rest_lib2/compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ nsj_rest_lib2/compiler/ai_compiler.py,sha256=shAtN4T0ai_52qh15L3mK1QbeC6glHOR6C3J3gj14II,9029
7
+ nsj_rest_lib2/compiler/compiler.py,sha256=Zo9gMaUaHhiGS-5lm0hHSdTn4wRW89OV_xrQPxlezOI,9945
8
+ nsj_rest_lib2/compiler/compiler_structures.py,sha256=2bM4_7lG1fytDGxJl6SU9pLsbspiNV36gVn9-O-23Q8,1009
9
+ nsj_rest_lib2/compiler/dto_compiler.py,sha256=hVmy9WLBl8217zmNSBBTpmSmdzXU6NXy7TJym6eRd-Q,2615
10
+ nsj_rest_lib2/compiler/entity_compiler.py,sha256=ytcS-RPQTJtf56z4HqT3xQLBYzbd6UUPGsaGB1pj_EA,4240
11
+ nsj_rest_lib2/compiler/property_compiler.py,sha256=NwQd3hVypT7CYgy8rK506XTRaz5oxiWUtkZEpGyolRs,13969
12
+ nsj_rest_lib2/compiler/edl_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ nsj_rest_lib2/compiler/edl_model/ai_entity_edl.py,sha256=664QBDcOgVnyfwtUOXO1W7AKaZhueBG335x5DuogruY,7644
14
+ nsj_rest_lib2/compiler/edl_model/api_model.py,sha256=pH0Uiq_64AGvkHqwY44TrulWWZXbi6M0EKJWUhSqKj0,837
15
+ nsj_rest_lib2/compiler/edl_model/column_meta_model.py,sha256=V1VI0LslSDO6oB2cOQrTcBijBnWIPL7s4xNbI2Wb8Kk,195
16
+ nsj_rest_lib2/compiler/edl_model/entity_model.py,sha256=mn19nIuzDdzo7N2K4ElYwKpk8T9tb3pNU4CudkblblI,3375
17
+ nsj_rest_lib2/compiler/edl_model/index_model.py,sha256=cXWlu0hxtro5vvYoirkDW4R3PCnBW5oCCWjRJ6AX5zE,508
18
+ nsj_rest_lib2/compiler/edl_model/primitives.py,sha256=VAzTbpkQAZWkeTdpoXL4FSaO1KEMTPXOxl6WxIJMWHk,1104
19
+ nsj_rest_lib2/compiler/edl_model/property_meta_model.py,sha256=i7lu-JFvPzUDBdsHB6kspGUyCkqf_GuTRmKfpIZj-K0,3444
20
+ nsj_rest_lib2/compiler/edl_model/repository_model.py,sha256=Vt1HxlaoifP4w5ij1laKDkD1-COBihE8EX1HkdEDUSo,977
21
+ nsj_rest_lib2/compiler/edl_model/trait_property_meta_model.py,sha256=NA8eRh5rA0boFmFd-0sHLrilOoOQSeN-amN_4TXcGiA,358
22
+ nsj_rest_lib2/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ nsj_rest_lib2/controller/dynamic_controller.py,sha256=4HS1__ton8cQ1LSvMoZUv93BQEnGV5f2gE-jW5lD510,13448
24
+ nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ nsj_rest_lib2/service/entity_loader.py,sha256=E--DbswAMIn3Ic6LOMotRTcUN4PsArA9oSFAVzadnAQ,10713
26
+ nsj_rest_lib2-0.0.4.dist-info/METADATA,sha256=WUKrR5K1gPeEDjFP0WP_lhb5gDBlyHdPfW0Hnnk_zy8,1196
27
+ nsj_rest_lib2-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ nsj_rest_lib2-0.0.4.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
29
+ nsj_rest_lib2-0.0.4.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- nsj_rest_lib2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- nsj_rest_lib2/exception.py,sha256=E9uMUdoCCQOVQfc8f6gD9b5Pxurf3Q4SytDCcqSlkZ8,56
3
- nsj_rest_lib2/redis_config.py,sha256=4KLcvYS3nJO7PMQgF6F9_j6r-TyqcS7TBbd3LEQuKDU,629
4
- nsj_rest_lib2/settings.py,sha256=F6y6lOAdLhGzZA6NE01x3t1vSAvrm1pC9ImjLhe61IU,59
5
- nsj_rest_lib2/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- nsj_rest_lib2/controller/dynamic_controller.py,sha256=4HS1__ton8cQ1LSvMoZUv93BQEnGV5f2gE-jW5lD510,13448
7
- nsj_rest_lib2/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- nsj_rest_lib2/service/entity_loader.py,sha256=i7ljsgDV4jlG-Flq5u-gmMqJp7SnpNfMRX1MYunln0E,9771
9
- nsj_rest_lib2-0.0.2.dist-info/METADATA,sha256=Z5cHSoCY_AfI8ax7hxGufv236q5ffK6Nbjrw_4RD5bg,1196
10
- nsj_rest_lib2-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- nsj_rest_lib2-0.0.2.dist-info/top_level.txt,sha256=L6zh0EfH8_rur7OJ8_V-El-XEMf4qg3bkF8ADgqLVIA,14
12
- nsj_rest_lib2-0.0.2.dist-info/RECORD,,