param-manager 0.1.0__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.
- param_manager-0.1.0/PKG-INFO +80 -0
- param_manager-0.1.0/README.md +70 -0
- param_manager-0.1.0/param_manager/__init__.py +29 -0
- param_manager-0.1.0/param_manager/manager.py +505 -0
- param_manager-0.1.0/param_manager.egg-info/PKG-INFO +80 -0
- param_manager-0.1.0/param_manager.egg-info/SOURCES.txt +12 -0
- param_manager-0.1.0/param_manager.egg-info/dependency_links.txt +1 -0
- param_manager-0.1.0/param_manager.egg-info/requires.txt +2 -0
- param_manager-0.1.0/param_manager.egg-info/top_level.txt +4 -0
- param_manager-0.1.0/pyproject.toml +95 -0
- param_manager-0.1.0/setup.cfg +4 -0
- param_manager-0.1.0/tests/__init__.py +0 -0
- param_manager-0.1.0/tests/conftest.py +45 -0
- param_manager-0.1.0/tests/test_param_manager.py +332 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: param-manager
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB.
|
|
5
|
+
Author-email: MatheusLPolidoro <mattpolidoro4@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: tinydb
|
|
9
|
+
Requires-Dist: requests
|
|
10
|
+
|
|
11
|
+
# Biblioteca ParamManager
|
|
12
|
+
|
|
13
|
+
## Descrição
|
|
14
|
+
Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
|
|
15
|
+
|
|
16
|
+
## Funcionalidades
|
|
17
|
+
|
|
18
|
+
- **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
|
|
19
|
+
- **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
|
|
20
|
+
- **Armazenamento Local**: Usa TinyDB para persistir dados localmente
|
|
21
|
+
- **Fallback Automático**: Utiliza dados locais quando a API está indisponível
|
|
22
|
+
- **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
|
|
23
|
+
|
|
24
|
+
## Instalação
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install param-manager
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Uso Básico
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from param_manager import ParamManager
|
|
34
|
+
|
|
35
|
+
# Obter a instância do gerenciador
|
|
36
|
+
param_manager = ParamManager.get_instance()
|
|
37
|
+
|
|
38
|
+
# Recuperar todos os parâmetros de um app
|
|
39
|
+
params = param_manager.get_all_params('nome_do_app')
|
|
40
|
+
|
|
41
|
+
# Recuperar um parâmetro específico
|
|
42
|
+
param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
|
|
43
|
+
|
|
44
|
+
# Limpar o cache para um app específico
|
|
45
|
+
param_manager.clear_cache('nome_do_app')
|
|
46
|
+
|
|
47
|
+
# Obter informações sobre o cache atual
|
|
48
|
+
cache_info = param_manager.get_cache_info()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuração Avançada
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Configurar com URL de API personalizada, duração de cache e timeout
|
|
55
|
+
param_manager = ParamManager.get_instance(
|
|
56
|
+
api_url="http://minha-api.exemplo.com",
|
|
57
|
+
cache_duration=1800, # 30 minutos
|
|
58
|
+
timeout=10 # 10 segundos
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Comportamento de Fallback
|
|
63
|
+
|
|
64
|
+
Quando a API está indisponível, a biblioteca automaticamente:
|
|
65
|
+
1. Tenta acessar a API
|
|
66
|
+
2. Em caso de falha, busca dados do armazenamento local
|
|
67
|
+
3. Retorna os dados mais recentes disponíveis localmente
|
|
68
|
+
|
|
69
|
+
## Estrutura de Arquivos
|
|
70
|
+
|
|
71
|
+
- `param_manager.py`: Implementação principal da biblioteca
|
|
72
|
+
- `test_param_manager.py`: Testes unitários para validar o funcionamento
|
|
73
|
+
- `README.md`: Documentação da biblioteca
|
|
74
|
+
- `requirements.txt`: Dependências necessárias
|
|
75
|
+
|
|
76
|
+
## Dependências
|
|
77
|
+
|
|
78
|
+
- Python 3.6+
|
|
79
|
+
- requests
|
|
80
|
+
- tinydb
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Biblioteca ParamManager
|
|
2
|
+
|
|
3
|
+
## Descrição
|
|
4
|
+
Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
|
|
5
|
+
|
|
6
|
+
## Funcionalidades
|
|
7
|
+
|
|
8
|
+
- **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
|
|
9
|
+
- **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
|
|
10
|
+
- **Armazenamento Local**: Usa TinyDB para persistir dados localmente
|
|
11
|
+
- **Fallback Automático**: Utiliza dados locais quando a API está indisponível
|
|
12
|
+
- **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
|
|
13
|
+
|
|
14
|
+
## Instalação
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install param-manager
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Uso Básico
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from param_manager import ParamManager
|
|
24
|
+
|
|
25
|
+
# Obter a instância do gerenciador
|
|
26
|
+
param_manager = ParamManager.get_instance()
|
|
27
|
+
|
|
28
|
+
# Recuperar todos os parâmetros de um app
|
|
29
|
+
params = param_manager.get_all_params('nome_do_app')
|
|
30
|
+
|
|
31
|
+
# Recuperar um parâmetro específico
|
|
32
|
+
param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
|
|
33
|
+
|
|
34
|
+
# Limpar o cache para um app específico
|
|
35
|
+
param_manager.clear_cache('nome_do_app')
|
|
36
|
+
|
|
37
|
+
# Obter informações sobre o cache atual
|
|
38
|
+
cache_info = param_manager.get_cache_info()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuração Avançada
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
# Configurar com URL de API personalizada, duração de cache e timeout
|
|
45
|
+
param_manager = ParamManager.get_instance(
|
|
46
|
+
api_url="http://minha-api.exemplo.com",
|
|
47
|
+
cache_duration=1800, # 30 minutos
|
|
48
|
+
timeout=10 # 10 segundos
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Comportamento de Fallback
|
|
53
|
+
|
|
54
|
+
Quando a API está indisponível, a biblioteca automaticamente:
|
|
55
|
+
1. Tenta acessar a API
|
|
56
|
+
2. Em caso de falha, busca dados do armazenamento local
|
|
57
|
+
3. Retorna os dados mais recentes disponíveis localmente
|
|
58
|
+
|
|
59
|
+
## Estrutura de Arquivos
|
|
60
|
+
|
|
61
|
+
- `param_manager.py`: Implementação principal da biblioteca
|
|
62
|
+
- `test_param_manager.py`: Testes unitários para validar o funcionamento
|
|
63
|
+
- `README.md`: Documentação da biblioteca
|
|
64
|
+
- `requirements.txt`: Dependências necessárias
|
|
65
|
+
|
|
66
|
+
## Dependências
|
|
67
|
+
|
|
68
|
+
- Python 3.6+
|
|
69
|
+
- requests
|
|
70
|
+
- tinydb
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# ██████╗ █████╗ ██████╗ █████╗ ███╗ ███╗
|
|
2
|
+
# ██╔══██╗██╔══██╗██╔══██╗██╔══██╗████╗ ████║
|
|
3
|
+
# ██████╔╝███████║██████╔╝███████║██╔████╔██║
|
|
4
|
+
# ██╔═══╝ ██╔══██║██╔══██╗██╔══██║██║╚██╔╝██║
|
|
5
|
+
# ██║ ██║ ██║██║ ██║██║ ██║██║ ╚═╝ ██║
|
|
6
|
+
# ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
7
|
+
# ███╗ ███╗ █████╗ ███╗ ██╗ █████╗ ██████╗ ███████╗██████╗
|
|
8
|
+
# ████╗ ████║██╔══██╗████╗ ██║██╔══██╗██╔════╝ ██╔════╝██╔══██╗
|
|
9
|
+
# ██╔████╔██║███████║██╔██╗ ██║███████║██║ ███╗█████╗ ██████╔╝
|
|
10
|
+
# ██║╚██╔╝██║██╔══██║██║╚██╗██║██╔══██║██║ ██║██╔══╝ ██╔══██╗
|
|
11
|
+
# ██║ ╚═╝ ██║██║ ██║██║ ╚████║██║ ██║╚██████╔╝███████╗██║ ██║
|
|
12
|
+
# ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝
|
|
13
|
+
"""
|
|
14
|
+
ParamManager
|
|
15
|
+
~~~~~~~~~~~~~~~
|
|
16
|
+
Biblioteca para gerenciamento de parâmetros.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import pathlib
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
sys.path.append(str(pathlib.Path(__file__).parent))
|
|
24
|
+
|
|
25
|
+
from .manager import ParamManager
|
|
26
|
+
|
|
27
|
+
__author__ = 'MatheusLPolidoro'
|
|
28
|
+
__version__ = '0.1.0'
|
|
29
|
+
__all__ = ['ParamManager', 'TinyDB']
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from tinydb import TinyDB
|
|
9
|
+
|
|
10
|
+
# Configuração de logging
|
|
11
|
+
logging.basicConfig(
|
|
12
|
+
level=logging.INFO,
|
|
13
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
14
|
+
)
|
|
15
|
+
logger = logging.getLogger('ParamManager')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ParamManager:
|
|
19
|
+
"""
|
|
20
|
+
Gerenciador de parâmetros que implementa o padrão Singleton.
|
|
21
|
+
|
|
22
|
+
Esta classe permite recuperar parâmetros de uma API, com sistema de cache
|
|
23
|
+
e fallback para armazenamento local usando TinyDB quando a API está indisponível.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Atributo de classe para armazenar a instância única (padrão Singleton)
|
|
27
|
+
__instance = None
|
|
28
|
+
|
|
29
|
+
def __new__(cls, *args, **kwargs):
|
|
30
|
+
"""
|
|
31
|
+
Implementa o padrão Singleton, garantindo uma única instância da classe.
|
|
32
|
+
"""
|
|
33
|
+
if cls.__instance is None:
|
|
34
|
+
cls.__instance = super(ParamManager, cls).__new__(cls)
|
|
35
|
+
logger.info('Nova instância do ParamManager criada')
|
|
36
|
+
return cls.__instance
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self, api_url: str = None, cache_duration: int = 3600, timeout: int = 5
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Inicializa a instância com configurações.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
api_url: URL base da API de parâmetros. Se None, usa o valor padrão.
|
|
46
|
+
cache_duration: Duração do cache em segundos (padrão: 1 hora).
|
|
47
|
+
timeout: Tempo limite para requisições à API em segundos.
|
|
48
|
+
"""
|
|
49
|
+
# Evita reinicialização se já foi inicializado
|
|
50
|
+
if hasattr(self, '_initialized') and self._initialized:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
self._api_base_url = api_url or 'http://10.111.188.61:8084'
|
|
54
|
+
self._cache_duration = (
|
|
55
|
+
cache_duration # em segundos (1 hora por padrão)
|
|
56
|
+
)
|
|
57
|
+
self._timeout = timeout # em segundos
|
|
58
|
+
|
|
59
|
+
# Dicionário para armazenar resultados em cache
|
|
60
|
+
self._cache = {} # formato: {app_name: {params}}
|
|
61
|
+
|
|
62
|
+
# Dicionário para armazenar timestamps de cada cache
|
|
63
|
+
self._cache_timestamp = {} # formato: {app_name: timestamp}
|
|
64
|
+
|
|
65
|
+
# Cache específico para parâmetros individuais
|
|
66
|
+
self._param_cache = {} # formato: {app_name:param_name: param_value}
|
|
67
|
+
self._param_cache_timestamp = {} # formato: {app_name:param_name: timestamp}
|
|
68
|
+
|
|
69
|
+
# Diretório para o banco de dados local
|
|
70
|
+
db_dir = os.path.join(os.path.expanduser('~'), '.param_manager')
|
|
71
|
+
os.makedirs(db_dir, exist_ok=True)
|
|
72
|
+
|
|
73
|
+
# Instância do TinyDB para armazenamento local
|
|
74
|
+
self._db_path = os.path.join(db_dir, 'params_db.json')
|
|
75
|
+
self._db = TinyDB(self._db_path)
|
|
76
|
+
|
|
77
|
+
self._initialized = True
|
|
78
|
+
logger.info(f'ParamManager inicializado com API: {self._api_base_url}')
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def get_instance(
|
|
82
|
+
api_url: str = None, cache_duration: int = 3600, timeout: int = 5
|
|
83
|
+
) -> 'ParamManager':
|
|
84
|
+
"""
|
|
85
|
+
Método estático para obter a instância única.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
api_url: URL base da API de parâmetros.
|
|
89
|
+
cache_duration: Duração do cache em segundos.
|
|
90
|
+
timeout: Tempo limite para requisições à API em segundos.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
A instância única de ParamManager.
|
|
94
|
+
"""
|
|
95
|
+
if ParamManager.__instance is None:
|
|
96
|
+
ParamManager(api_url, cache_duration, timeout)
|
|
97
|
+
return ParamManager.__instance
|
|
98
|
+
|
|
99
|
+
def get_all_params(self, app_name: str) -> Dict[str, Any]:
|
|
100
|
+
"""
|
|
101
|
+
Recupera todos os parâmetros de um app específico.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
app_name: Nome do aplicativo.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Dicionário com todos os parâmetros do app.
|
|
108
|
+
"""
|
|
109
|
+
logger.info(f'Solicitando todos os parâmetros para o app: {app_name}')
|
|
110
|
+
|
|
111
|
+
# Verifica se há cache válido
|
|
112
|
+
if self._is_cache_valid(app_name):
|
|
113
|
+
logger.info(f'Usando cache para o app: {app_name}')
|
|
114
|
+
return self._cache[app_name]
|
|
115
|
+
|
|
116
|
+
# Se não houver cache válido, tenta buscar da API
|
|
117
|
+
try:
|
|
118
|
+
params = self._fetch_from_api(app_name)
|
|
119
|
+
return params
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f'Erro ao buscar parâmetros da API: {str(e)}')
|
|
122
|
+
return self._handle_api_error(app_name, None, e)
|
|
123
|
+
|
|
124
|
+
def get_param(self, app_name: str, param_name: str) -> Any:
|
|
125
|
+
"""
|
|
126
|
+
Recupera um parâmetro específico de um app.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
app_name: Nome do aplicativo.
|
|
130
|
+
param_name: Nome do parâmetro.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Valor do parâmetro ou None se não encontrado.
|
|
134
|
+
"""
|
|
135
|
+
logger.info(
|
|
136
|
+
f'Solicitando parâmetro {param_name} para o app: {app_name}'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Chave para o cache específico do parâmetro
|
|
140
|
+
param_cache_key = f'{app_name}:{param_name}'
|
|
141
|
+
|
|
142
|
+
# Verifica se há cache específico válido para o parâmetro
|
|
143
|
+
if self._is_param_cache_valid(app_name, param_name):
|
|
144
|
+
logger.info(
|
|
145
|
+
f'Usando cache específico para o parâmetro: {param_name} do app: {app_name}'
|
|
146
|
+
)
|
|
147
|
+
return self._param_cache[param_cache_key]['value']
|
|
148
|
+
|
|
149
|
+
# Se não houver cache específico válido, verifica o cache global do app
|
|
150
|
+
if self._is_cache_valid(app_name):
|
|
151
|
+
logger.info(
|
|
152
|
+
f'Usando cache global do app para o parâmetro: {param_name}'
|
|
153
|
+
)
|
|
154
|
+
params = self._cache[app_name]
|
|
155
|
+
param_value = params.get(param_name)
|
|
156
|
+
|
|
157
|
+
# Atualiza o cache específico do parâmetro
|
|
158
|
+
if param_value is not None:
|
|
159
|
+
self._param_cache[param_cache_key] = param_value
|
|
160
|
+
self._param_cache_timestamp[param_cache_key] = time.time()
|
|
161
|
+
|
|
162
|
+
return param_value.get('value')
|
|
163
|
+
|
|
164
|
+
# Se não houver cache válido, tenta buscar da API
|
|
165
|
+
try:
|
|
166
|
+
# Busca o parâmetro específico da API
|
|
167
|
+
param_value = self._fetch_param_from_api(app_name, param_name)
|
|
168
|
+
if not isinstance(param_value, dict):
|
|
169
|
+
param_value = dict()
|
|
170
|
+
return param_value.get('value')
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.error(f'Erro ao buscar parâmetro da API: {str(e)}')
|
|
173
|
+
|
|
174
|
+
# Tenta recuperar do banco local
|
|
175
|
+
params = self._handle_api_error(app_name, param_name, e)
|
|
176
|
+
return (
|
|
177
|
+
params.get(param_name, dict()).get('value') if params else None
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def _fetch_from_api(
|
|
181
|
+
self, app_name: str, param_name: Optional[str] = None
|
|
182
|
+
) -> Dict[str, Any]:
|
|
183
|
+
"""
|
|
184
|
+
Faz requisição à API para buscar todos os parâmetros de um app.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
app_name: Nome do aplicativo.
|
|
188
|
+
param_name: Nome do parâmetro específico (opcional, mantido para compatibilidade).
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Dicionário com os parâmetros.
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
Exception: Se ocorrer erro na requisição.
|
|
195
|
+
"""
|
|
196
|
+
# Constrói URL apropriada para todos os parâmetros
|
|
197
|
+
url = f'{self._api_base_url}/parameters/apps/{app_name}/params'
|
|
198
|
+
|
|
199
|
+
logger.info(f'Buscando todos os parâmetros da API: {url}')
|
|
200
|
+
|
|
201
|
+
# Faz requisição HTTP
|
|
202
|
+
response = requests.get(url, timeout=self._timeout, verify=False)
|
|
203
|
+
|
|
204
|
+
# Verifica se a requisição foi bem-sucedida
|
|
205
|
+
if response.status_code != 200:
|
|
206
|
+
raise Exception(f'API retornou status code {response.status_code}')
|
|
207
|
+
|
|
208
|
+
# Processa resposta
|
|
209
|
+
data = response.json()
|
|
210
|
+
|
|
211
|
+
# Extrai parâmetros da resposta
|
|
212
|
+
params = data.get('params', {})
|
|
213
|
+
|
|
214
|
+
# Atualiza cache e timestamp
|
|
215
|
+
self._cache[app_name] = params
|
|
216
|
+
self._cache_timestamp[app_name] = time.time()
|
|
217
|
+
|
|
218
|
+
# Salva dados localmente
|
|
219
|
+
self._save_to_local_db(app_name, params)
|
|
220
|
+
|
|
221
|
+
return params
|
|
222
|
+
|
|
223
|
+
def _fetch_param_from_api(self, app_name: str, param_name: str) -> Any:
|
|
224
|
+
"""
|
|
225
|
+
Faz requisição à API para buscar um parâmetro específico.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
app_name: Nome do aplicativo.
|
|
229
|
+
param_name: Nome do parâmetro específico.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Valor do parâmetro ou None se não encontrado.
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
Exception: Se ocorrer erro na requisição.
|
|
236
|
+
"""
|
|
237
|
+
# Constrói URL apropriada para o parâmetro específico
|
|
238
|
+
url = f'{self._api_base_url}/parameters/apps/{app_name}/params/{param_name}'
|
|
239
|
+
|
|
240
|
+
logger.info(f'Buscando parâmetro específico da API: {url}')
|
|
241
|
+
|
|
242
|
+
# Faz requisição HTTP
|
|
243
|
+
response = requests.get(url, timeout=self._timeout, verify=False)
|
|
244
|
+
|
|
245
|
+
# Verifica se a requisição foi bem-sucedida
|
|
246
|
+
if response.status_code != 200:
|
|
247
|
+
raise Exception(f'API retornou status code {response.status_code}')
|
|
248
|
+
|
|
249
|
+
# Processa resposta
|
|
250
|
+
data = response.json()
|
|
251
|
+
|
|
252
|
+
# Extrai parâmetro da resposta
|
|
253
|
+
param_value = data.get('param')
|
|
254
|
+
|
|
255
|
+
# Chave para o cache específico do parâmetro
|
|
256
|
+
param_cache_key = f'{app_name}:{param_name}'
|
|
257
|
+
|
|
258
|
+
# Atualiza o cache específico do parâmetro
|
|
259
|
+
self._param_cache[param_cache_key] = param_value
|
|
260
|
+
self._param_cache_timestamp[param_cache_key] = time.time()
|
|
261
|
+
|
|
262
|
+
# Também atualiza o cache global se existir
|
|
263
|
+
if app_name in self._cache:
|
|
264
|
+
self._cache[app_name][param_name] = param_value
|
|
265
|
+
self._cache_timestamp[app_name] = time.time()
|
|
266
|
+
|
|
267
|
+
# Salva dados localmente
|
|
268
|
+
self._save_to_local_db(app_name, self._cache[app_name])
|
|
269
|
+
|
|
270
|
+
return param_value
|
|
271
|
+
|
|
272
|
+
def _is_cache_valid(self, app_name: str) -> bool:
|
|
273
|
+
"""
|
|
274
|
+
Verifica se o cache global para um app é válido.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
app_name: Nome do aplicativo.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
True se o cache for válido, False caso contrário.
|
|
281
|
+
"""
|
|
282
|
+
# Verifica se existe cache para o app
|
|
283
|
+
if (
|
|
284
|
+
app_name not in self._cache
|
|
285
|
+
or app_name not in self._cache_timestamp
|
|
286
|
+
):
|
|
287
|
+
return False
|
|
288
|
+
|
|
289
|
+
# Verifica se o timestamp é recente (menos de cache_duration segundos)
|
|
290
|
+
current_time = time.time()
|
|
291
|
+
cache_time = self._cache_timestamp[app_name]
|
|
292
|
+
|
|
293
|
+
return (current_time - cache_time) < self._cache_duration
|
|
294
|
+
|
|
295
|
+
def _is_param_cache_valid(self, app_name: str, param_name: str) -> bool:
|
|
296
|
+
"""
|
|
297
|
+
Verifica se o cache específico para um parâmetro é válido.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
app_name: Nome do aplicativo.
|
|
301
|
+
param_name: Nome do parâmetro.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
True se o cache for válido, False caso contrário.
|
|
305
|
+
"""
|
|
306
|
+
# Chave para o cache específico do parâmetro
|
|
307
|
+
param_cache_key = f'{app_name}:{param_name}'
|
|
308
|
+
|
|
309
|
+
# Verifica se existe cache específico para o parâmetro
|
|
310
|
+
if (
|
|
311
|
+
param_cache_key not in self._param_cache
|
|
312
|
+
or param_cache_key not in self._param_cache_timestamp
|
|
313
|
+
):
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
# Verifica se o timestamp é recente (menos de cache_duration segundos)
|
|
317
|
+
current_time = time.time()
|
|
318
|
+
cache_time = self._param_cache_timestamp[param_cache_key]
|
|
319
|
+
|
|
320
|
+
return (current_time - cache_time) < self._cache_duration
|
|
321
|
+
|
|
322
|
+
def _save_to_local_db(self, app_name: str, params: Dict[str, Any]) -> None:
|
|
323
|
+
"""
|
|
324
|
+
Salva parâmetros no banco local.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
app_name: Nome do aplicativo.
|
|
328
|
+
params: Dicionário com os parâmetros.
|
|
329
|
+
"""
|
|
330
|
+
logger.info(f'Salvando parâmetros localmente para o app: {app_name}')
|
|
331
|
+
|
|
332
|
+
# Define a tabela para o app
|
|
333
|
+
table = self._db.table(app_name)
|
|
334
|
+
|
|
335
|
+
# Limpa a tabela antes de inserir novos dados
|
|
336
|
+
table.truncate()
|
|
337
|
+
|
|
338
|
+
# Insere os parâmetros
|
|
339
|
+
table.insert({'timestamp': time.time(), 'params': params})
|
|
340
|
+
|
|
341
|
+
def _get_from_local_db(
|
|
342
|
+
self, app_name: str, param_name: Optional[str] = None
|
|
343
|
+
) -> Dict[str, Any]:
|
|
344
|
+
"""
|
|
345
|
+
Recupera dados do banco local.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
app_name: Nome do aplicativo.
|
|
349
|
+
param_name: Nome do parâmetro específico (opcional).
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Dicionário com os parâmetros ou vazio se não encontrado.
|
|
353
|
+
"""
|
|
354
|
+
logger.info(f'Buscando parâmetros localmente para o app: {app_name}')
|
|
355
|
+
|
|
356
|
+
# Define a tabela para o app
|
|
357
|
+
table = self._db.table(app_name)
|
|
358
|
+
|
|
359
|
+
# Busca o registro mais recente
|
|
360
|
+
records = table.all()
|
|
361
|
+
|
|
362
|
+
if not records:
|
|
363
|
+
logger.warning(
|
|
364
|
+
f'Nenhum registro local encontrado para o app: {app_name}'
|
|
365
|
+
)
|
|
366
|
+
return {}
|
|
367
|
+
|
|
368
|
+
# Ordena por timestamp (mais recente primeiro)
|
|
369
|
+
records.sort(key=lambda x: x.get('timestamp', 0), reverse=True)
|
|
370
|
+
|
|
371
|
+
# Obtém os parâmetros do registro mais recente
|
|
372
|
+
params = records[0].get('params', {})
|
|
373
|
+
|
|
374
|
+
# Filtra por param_name se especificado
|
|
375
|
+
if param_name:
|
|
376
|
+
if param_name in params:
|
|
377
|
+
return {param_name: params[param_name]}
|
|
378
|
+
return {}
|
|
379
|
+
|
|
380
|
+
return params
|
|
381
|
+
|
|
382
|
+
def _handle_api_error(
|
|
383
|
+
self, app_name: str, param_name: Optional[str], error: Exception
|
|
384
|
+
) -> Dict[str, Any]:
|
|
385
|
+
"""
|
|
386
|
+
Trata erros de API.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
app_name: Nome do aplicativo.
|
|
390
|
+
param_name: Nome do parâmetro específico (opcional).
|
|
391
|
+
error: Exceção ocorrida.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
Dados locais se disponíveis ou dicionário vazio.
|
|
395
|
+
"""
|
|
396
|
+
logger.error(f'Erro ao acessar API para {app_name}: {str(error)}')
|
|
397
|
+
logger.info(f'Tentando usar dados locais para {app_name}')
|
|
398
|
+
|
|
399
|
+
# Busca dados locais
|
|
400
|
+
return self._get_from_local_db(app_name, param_name)
|
|
401
|
+
|
|
402
|
+
def clear_cache(
|
|
403
|
+
self, app_name: Optional[str] = None, param_name: Optional[str] = None
|
|
404
|
+
) -> None:
|
|
405
|
+
"""
|
|
406
|
+
Limpa o cache para um app específico, um parâmetro específico ou para todos os apps.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
app_name: Nome do aplicativo (opcional).
|
|
410
|
+
param_name: Nome do parâmetro (opcional).
|
|
411
|
+
"""
|
|
412
|
+
if app_name and param_name:
|
|
413
|
+
# Limpa o cache específico do parâmetro
|
|
414
|
+
param_cache_key = f'{app_name}:{param_name}'
|
|
415
|
+
if param_cache_key in self._param_cache:
|
|
416
|
+
del self._param_cache[param_cache_key]
|
|
417
|
+
if param_cache_key in self._param_cache_timestamp:
|
|
418
|
+
del self._param_cache_timestamp[param_cache_key]
|
|
419
|
+
logger.info(
|
|
420
|
+
f'Cache limpo para o parâmetro {param_name} do app: {app_name}'
|
|
421
|
+
)
|
|
422
|
+
elif app_name:
|
|
423
|
+
# Limpa o cache do app
|
|
424
|
+
if app_name in self._cache:
|
|
425
|
+
del self._cache[app_name]
|
|
426
|
+
if app_name in self._cache_timestamp:
|
|
427
|
+
del self._cache_timestamp[app_name]
|
|
428
|
+
|
|
429
|
+
# Limpa também todos os caches específicos relacionados ao app
|
|
430
|
+
param_cache_keys = [
|
|
431
|
+
k
|
|
432
|
+
for k in self._param_cache.keys()
|
|
433
|
+
if k.startswith(f'{app_name}:')
|
|
434
|
+
]
|
|
435
|
+
for key in param_cache_keys:
|
|
436
|
+
if key in self._param_cache:
|
|
437
|
+
del self._param_cache[key]
|
|
438
|
+
if key in self._param_cache_timestamp:
|
|
439
|
+
del self._param_cache_timestamp[key]
|
|
440
|
+
|
|
441
|
+
logger.info(f'Cache limpo para o app: {app_name}')
|
|
442
|
+
else:
|
|
443
|
+
# Limpa todos os caches
|
|
444
|
+
self._cache = {}
|
|
445
|
+
self._cache_timestamp = {}
|
|
446
|
+
self._param_cache = {}
|
|
447
|
+
self._param_cache_timestamp = {}
|
|
448
|
+
logger.info('Cache limpo para todos os apps e parâmetros')
|
|
449
|
+
|
|
450
|
+
def get_cache_info(self) -> Dict[str, Any]:
|
|
451
|
+
"""
|
|
452
|
+
Retorna informações sobre o cache atual.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Dicionário com informações do cache.
|
|
456
|
+
"""
|
|
457
|
+
info = {
|
|
458
|
+
'apps_cached': list(self._cache.keys()),
|
|
459
|
+
'cache_timestamps': {},
|
|
460
|
+
'cache_valid': {},
|
|
461
|
+
'params_cached': [],
|
|
462
|
+
'param_cache_timestamps': {},
|
|
463
|
+
'param_cache_valid': {},
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
# Informações sobre o cache global
|
|
467
|
+
for app_name, timestamp in self._cache_timestamp.items():
|
|
468
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
469
|
+
expires_at = dt + timedelta(seconds=self._cache_duration)
|
|
470
|
+
is_valid = self._is_cache_valid(app_name)
|
|
471
|
+
|
|
472
|
+
info['cache_timestamps'][app_name] = {
|
|
473
|
+
'cached_at': dt.isoformat(),
|
|
474
|
+
'expires_at': expires_at.isoformat(),
|
|
475
|
+
'seconds_remaining': int(
|
|
476
|
+
timestamp + self._cache_duration - time.time()
|
|
477
|
+
)
|
|
478
|
+
if is_valid
|
|
479
|
+
else 0,
|
|
480
|
+
}
|
|
481
|
+
info['cache_valid'][app_name] = is_valid
|
|
482
|
+
|
|
483
|
+
# Informações sobre o cache específico de parâmetros
|
|
484
|
+
for param_key, timestamp in self._param_cache_timestamp.items():
|
|
485
|
+
info['params_cached'].append(param_key)
|
|
486
|
+
|
|
487
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
488
|
+
expires_at = dt + timedelta(seconds=self._cache_duration)
|
|
489
|
+
|
|
490
|
+
# Extrai app_name e param_name da chave
|
|
491
|
+
app_name, param_name = param_key.split(':', 1)
|
|
492
|
+
is_valid = self._is_param_cache_valid(app_name, param_name)
|
|
493
|
+
|
|
494
|
+
info['param_cache_timestamps'][param_key] = {
|
|
495
|
+
'cached_at': dt.isoformat(),
|
|
496
|
+
'expires_at': expires_at.isoformat(),
|
|
497
|
+
'seconds_remaining': int(
|
|
498
|
+
timestamp + self._cache_duration - time.time()
|
|
499
|
+
)
|
|
500
|
+
if is_valid
|
|
501
|
+
else 0,
|
|
502
|
+
}
|
|
503
|
+
info['param_cache_valid'][param_key] = is_valid
|
|
504
|
+
|
|
505
|
+
return info
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: param-manager
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB.
|
|
5
|
+
Author-email: MatheusLPolidoro <mattpolidoro4@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: tinydb
|
|
9
|
+
Requires-Dist: requests
|
|
10
|
+
|
|
11
|
+
# Biblioteca ParamManager
|
|
12
|
+
|
|
13
|
+
## Descrição
|
|
14
|
+
Biblioteca Python orientada a objetos que implementa o padrão Singleton para interagir com a API de parâmetros. A biblioteca oferece funcionalidades de cache, armazenamento local com TinyDB e fallback automático em caso de indisponibilidade da API.
|
|
15
|
+
|
|
16
|
+
## Funcionalidades
|
|
17
|
+
|
|
18
|
+
- **Padrão Singleton**: Garante que exista apenas uma instância da classe de acesso à API
|
|
19
|
+
- **Cache**: Armazena resultados em memória por até 1 hora para reduzir chamadas à API
|
|
20
|
+
- **Armazenamento Local**: Usa TinyDB para persistir dados localmente
|
|
21
|
+
- **Fallback Automático**: Utiliza dados locais quando a API está indisponível
|
|
22
|
+
- **Recuperação de Parâmetros**: Permite buscar todos os parâmetros de um app ou um parâmetro específico
|
|
23
|
+
|
|
24
|
+
## Instalação
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install param-manager
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Uso Básico
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from param_manager import ParamManager
|
|
34
|
+
|
|
35
|
+
# Obter a instância do gerenciador
|
|
36
|
+
param_manager = ParamManager.get_instance()
|
|
37
|
+
|
|
38
|
+
# Recuperar todos os parâmetros de um app
|
|
39
|
+
params = param_manager.get_all_params('nome_do_app')
|
|
40
|
+
|
|
41
|
+
# Recuperar um parâmetro específico
|
|
42
|
+
param = param_manager.get_param('nome_do_app', 'NOME_PARAMETRO')
|
|
43
|
+
|
|
44
|
+
# Limpar o cache para um app específico
|
|
45
|
+
param_manager.clear_cache('nome_do_app')
|
|
46
|
+
|
|
47
|
+
# Obter informações sobre o cache atual
|
|
48
|
+
cache_info = param_manager.get_cache_info()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Configuração Avançada
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Configurar com URL de API personalizada, duração de cache e timeout
|
|
55
|
+
param_manager = ParamManager.get_instance(
|
|
56
|
+
api_url="http://minha-api.exemplo.com",
|
|
57
|
+
cache_duration=1800, # 30 minutos
|
|
58
|
+
timeout=10 # 10 segundos
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Comportamento de Fallback
|
|
63
|
+
|
|
64
|
+
Quando a API está indisponível, a biblioteca automaticamente:
|
|
65
|
+
1. Tenta acessar a API
|
|
66
|
+
2. Em caso de falha, busca dados do armazenamento local
|
|
67
|
+
3. Retorna os dados mais recentes disponíveis localmente
|
|
68
|
+
|
|
69
|
+
## Estrutura de Arquivos
|
|
70
|
+
|
|
71
|
+
- `param_manager.py`: Implementação principal da biblioteca
|
|
72
|
+
- `test_param_manager.py`: Testes unitários para validar o funcionamento
|
|
73
|
+
- `README.md`: Documentação da biblioteca
|
|
74
|
+
- `requirements.txt`: Dependências necessárias
|
|
75
|
+
|
|
76
|
+
## Dependências
|
|
77
|
+
|
|
78
|
+
- Python 3.6+
|
|
79
|
+
- requests
|
|
80
|
+
- tinydb
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
param_manager/__init__.py
|
|
4
|
+
param_manager/manager.py
|
|
5
|
+
param_manager.egg-info/PKG-INFO
|
|
6
|
+
param_manager.egg-info/SOURCES.txt
|
|
7
|
+
param_manager.egg-info/dependency_links.txt
|
|
8
|
+
param_manager.egg-info/requires.txt
|
|
9
|
+
param_manager.egg-info/top_level.txt
|
|
10
|
+
tests/__init__.py
|
|
11
|
+
tests/conftest.py
|
|
12
|
+
tests/test_param_manager.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "param-manager"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Biblioteca para gerenciamento de parâmetros com padrão Singleton,cache e armazenamento local usando TinyDB."
|
|
5
|
+
authors = [{name = "MatheusLPolidoro", email = "mattpolidoro4@gmail.com"}]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
"tinydb",
|
|
11
|
+
"requests"
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[tool.setuptools.packages.find]
|
|
15
|
+
where = ["."]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["setuptools>=61.0,<70"]
|
|
19
|
+
build-backend = "setuptools.build_meta"
|
|
20
|
+
|
|
21
|
+
[tool.ruff]
|
|
22
|
+
line-length = 79
|
|
23
|
+
|
|
24
|
+
[tool.ruff.lint]
|
|
25
|
+
preview = true
|
|
26
|
+
select = ['I', 'F', 'E', 'W', 'PL', 'PT']
|
|
27
|
+
|
|
28
|
+
[tool.ruff.format]
|
|
29
|
+
preview = true
|
|
30
|
+
quote-style = 'single'
|
|
31
|
+
|
|
32
|
+
[tool.taskipy.tasks]
|
|
33
|
+
lint = "ruff check . --diff"
|
|
34
|
+
format = "ruff format ."
|
|
35
|
+
test = "pytest . -s -x --cov=param_manager -vv"
|
|
36
|
+
post_test = "python -m coverage html"
|
|
37
|
+
|
|
38
|
+
[tool.towncrier]
|
|
39
|
+
package = "param_manager"
|
|
40
|
+
package_dir = "."
|
|
41
|
+
filename = "CHANGELOG.md"
|
|
42
|
+
directory = "chanlogs.d"
|
|
43
|
+
underlines = ["=", "-", "~"]
|
|
44
|
+
title_format = "Versão {version} ({project_date})"
|
|
45
|
+
issue_format = "[#{issue}](https://github.com/seu_usuario/seu_repo/issues/{issue})"
|
|
46
|
+
|
|
47
|
+
[[tool.towncrier.type]]
|
|
48
|
+
directory = "breaking"
|
|
49
|
+
name = ":warning: MUDANÇAS QUEBRADAS"
|
|
50
|
+
showcontent = true
|
|
51
|
+
|
|
52
|
+
[[tool.towncrier.type]]
|
|
53
|
+
directory = "feature"
|
|
54
|
+
name = ":zap: NOVAS FUNCIONALIDADES"
|
|
55
|
+
showcontent = true
|
|
56
|
+
|
|
57
|
+
[[tool.towncrier.type]]
|
|
58
|
+
directory = "enhancement"
|
|
59
|
+
name = ":rocket: OTIMIZAÇÕES"
|
|
60
|
+
showcontent = true
|
|
61
|
+
|
|
62
|
+
[[tool.towncrier.type]]
|
|
63
|
+
directory = "fix"
|
|
64
|
+
name = ":bug: CORREÇÕES"
|
|
65
|
+
showcontent = true
|
|
66
|
+
|
|
67
|
+
[[tool.towncrier.type]]
|
|
68
|
+
directory = "deprecation"
|
|
69
|
+
name = ":wastebasket: OBSOLETOS"
|
|
70
|
+
showcontent = true
|
|
71
|
+
|
|
72
|
+
[[tool.towncrier.type]]
|
|
73
|
+
directory = "security"
|
|
74
|
+
name = ":shield: SEGURANÇA"
|
|
75
|
+
showcontent = true
|
|
76
|
+
|
|
77
|
+
[[tool.towncrier.type]]
|
|
78
|
+
directory = "docs"
|
|
79
|
+
name = ":open_file_folder: DOCUMENTAÇÃO"
|
|
80
|
+
showcontent = true
|
|
81
|
+
|
|
82
|
+
[[tool.towncrier.type]]
|
|
83
|
+
directory = "tests"
|
|
84
|
+
name = ":test_tube: TESTES"
|
|
85
|
+
showcontent = true
|
|
86
|
+
|
|
87
|
+
[[tool.towncrier.type]]
|
|
88
|
+
directory = "infra"
|
|
89
|
+
name = ":classical_building: INFRAESTRUTURA"
|
|
90
|
+
showcontent = true
|
|
91
|
+
|
|
92
|
+
[[tool.towncrier.type]]
|
|
93
|
+
directory = "chore"
|
|
94
|
+
name = ":broom: LIMPEZA"
|
|
95
|
+
showcontent = true
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
from param_manager.manager import ParamManager
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def setup_param_manager():
|
|
9
|
+
"""Fixture para configurar o ambiente de teste para o ParamManager."""
|
|
10
|
+
# Limpa a instância singleton entre os testes
|
|
11
|
+
ParamManager._ParamManager__instance = None
|
|
12
|
+
|
|
13
|
+
# Cria um diretório temporário para o banco de dados de teste
|
|
14
|
+
test_db_dir = os.path.join(os.path.expanduser('~'), '.param_manager_test')
|
|
15
|
+
os.makedirs(test_db_dir, exist_ok=True)
|
|
16
|
+
test_db_path = os.path.join(test_db_dir, 'test_params_db.json')
|
|
17
|
+
|
|
18
|
+
# Mock para o TinyDB
|
|
19
|
+
with patch('param_manager.manager.TinyDB') as mock_db:
|
|
20
|
+
mock_db_instance = MagicMock()
|
|
21
|
+
mock_db.return_value = mock_db_instance
|
|
22
|
+
|
|
23
|
+
# Mock para a tabela do TinyDB
|
|
24
|
+
mock_table = MagicMock()
|
|
25
|
+
mock_db_instance.table.return_value = mock_table
|
|
26
|
+
|
|
27
|
+
# Cria a instância do ParamManager com URL de API de teste
|
|
28
|
+
param_manager = ParamManager.get_instance(
|
|
29
|
+
api_url='http://test-api.example.com',
|
|
30
|
+
cache_duration=60, # 1 minuto para facilitar os testes
|
|
31
|
+
timeout=2,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Retorna os objetos necessários para os testes
|
|
35
|
+
yield param_manager, mock_table
|
|
36
|
+
|
|
37
|
+
# Limpeza após o teste
|
|
38
|
+
if os.path.exists(test_db_path):
|
|
39
|
+
os.remove(test_db_path)
|
|
40
|
+
|
|
41
|
+
# Remove o diretório de teste se estiver vazio
|
|
42
|
+
try:
|
|
43
|
+
os.rmdir(test_db_dir)
|
|
44
|
+
except OSError:
|
|
45
|
+
pass
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import pytest
|
|
3
|
+
from unittest.mock import patch, MagicMock
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
'mock_response_data', [{'param': {'value': 'test_value'}}]
|
|
10
|
+
)
|
|
11
|
+
def test_get_param_from_api_with_individual_cache(
|
|
12
|
+
setup_param_manager, mock_response_data
|
|
13
|
+
):
|
|
14
|
+
"""Testa a recuperação de um parâmetro específico com cache individual."""
|
|
15
|
+
param_manager, _ = setup_param_manager
|
|
16
|
+
|
|
17
|
+
# Configura o mock para requests.get
|
|
18
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
19
|
+
mock_response = MagicMock()
|
|
20
|
+
mock_response.status_code = 200
|
|
21
|
+
mock_response.json.return_value = mock_response_data
|
|
22
|
+
mock_get.return_value = mock_response
|
|
23
|
+
|
|
24
|
+
# Primeira chamada - deve acessar a API
|
|
25
|
+
param1 = param_manager.get_param('test_app', 'PARAM1')
|
|
26
|
+
|
|
27
|
+
# Verifica se a API foi chamada corretamente
|
|
28
|
+
mock_get.assert_called_once_with(
|
|
29
|
+
'http://test-api.example.com/parameters/apps/test_app/params/PARAM1',
|
|
30
|
+
timeout=2,
|
|
31
|
+
verify=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Verifica se o parâmetro foi retornado corretamente
|
|
35
|
+
assert param1 == 'test_value'
|
|
36
|
+
|
|
37
|
+
# Verifica se o cache específico foi atualizado
|
|
38
|
+
assert 'test_app:PARAM1' in param_manager._param_cache
|
|
39
|
+
assert 'test_app:PARAM1' in param_manager._param_cache_timestamp
|
|
40
|
+
|
|
41
|
+
# Reseta o mock para a próxima chamada
|
|
42
|
+
mock_get.reset_mock()
|
|
43
|
+
|
|
44
|
+
# Segunda chamada - deve usar o cache específico
|
|
45
|
+
param2 = param_manager.get_param('test_app', 'PARAM1')
|
|
46
|
+
|
|
47
|
+
# Verifica se a API não foi chamada novamente
|
|
48
|
+
mock_get.assert_not_called()
|
|
49
|
+
|
|
50
|
+
# Verifica se o parâmetro foi retornado corretamente do cache
|
|
51
|
+
assert param2 == 'test_value'
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize(
|
|
55
|
+
'mock_response_data',
|
|
56
|
+
[
|
|
57
|
+
{
|
|
58
|
+
'params': {
|
|
59
|
+
'PARAM1': {'value': 'value1'},
|
|
60
|
+
'PARAM2': {'value': 'value2'},
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
)
|
|
65
|
+
def test_get_param_from_global_cache(setup_param_manager, mock_response_data):
|
|
66
|
+
"""Testa a recuperação de um parâmetro do cache global quando não há cache específico."""
|
|
67
|
+
param_manager, _ = setup_param_manager
|
|
68
|
+
|
|
69
|
+
# Configura o mock para requests.get para a chamada de todos os parâmetros
|
|
70
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
71
|
+
mock_response_all = MagicMock()
|
|
72
|
+
mock_response_all.status_code = 200
|
|
73
|
+
mock_response_all.json.return_value = mock_response_data
|
|
74
|
+
mock_get.return_value = mock_response_all
|
|
75
|
+
|
|
76
|
+
# Primeiro, busca todos os parâmetros para preencher o cache global
|
|
77
|
+
params = param_manager.get_all_params('test_app')
|
|
78
|
+
|
|
79
|
+
# Verifica se a API foi chamada corretamente
|
|
80
|
+
mock_get.assert_called_once_with(
|
|
81
|
+
'http://test-api.example.com/parameters/apps/test_app/params',
|
|
82
|
+
timeout=2,
|
|
83
|
+
verify=False,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Reseta o mock para a próxima chamada
|
|
87
|
+
mock_get.reset_mock()
|
|
88
|
+
|
|
89
|
+
# Agora, busca um parâmetro específico - deve usar o cache global
|
|
90
|
+
param = param_manager.get_param('test_app', 'PARAM1')
|
|
91
|
+
|
|
92
|
+
# Verifica se a API não foi chamada novamente
|
|
93
|
+
mock_get.assert_not_called()
|
|
94
|
+
|
|
95
|
+
# Verifica se o parâmetro foi retornado corretamente do cache global
|
|
96
|
+
assert param == 'value1'
|
|
97
|
+
|
|
98
|
+
# Agora, busca de todos os parametros - deve usar o cache global
|
|
99
|
+
params = param_manager.get_all_params('test_app')
|
|
100
|
+
|
|
101
|
+
# Verifica se o cache específico foi atualizado
|
|
102
|
+
assert 'test_app:PARAM1' in param_manager._param_cache
|
|
103
|
+
assert 'test_app:PARAM1' in param_manager._param_cache_timestamp
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
|
|
107
|
+
def test_cache_expiration_for_individual_param(
|
|
108
|
+
setup_param_manager, mock_response_data
|
|
109
|
+
):
|
|
110
|
+
"""Testa a expiração do cache para um parâmetro individual."""
|
|
111
|
+
param_manager, _ = setup_param_manager
|
|
112
|
+
|
|
113
|
+
# Configura o mock para requests.get
|
|
114
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
115
|
+
mock_response = MagicMock()
|
|
116
|
+
mock_response.status_code = 200
|
|
117
|
+
mock_response.json.return_value = mock_response_data
|
|
118
|
+
mock_get.return_value = mock_response
|
|
119
|
+
|
|
120
|
+
# Define uma duração de cache muito curta para o teste
|
|
121
|
+
param_manager._cache_duration = 0.1 # 100ms
|
|
122
|
+
|
|
123
|
+
# Primeira chamada - deve acessar a API
|
|
124
|
+
param1 = param_manager.get_param('test_app', 'PARAM1')
|
|
125
|
+
|
|
126
|
+
# Verifica se a API foi chamada
|
|
127
|
+
assert mock_get.call_count == 1
|
|
128
|
+
|
|
129
|
+
# Espera o cache expirar
|
|
130
|
+
time.sleep(0.2)
|
|
131
|
+
|
|
132
|
+
# Reseta o mock para a próxima chamada
|
|
133
|
+
mock_get.reset_mock()
|
|
134
|
+
|
|
135
|
+
# Segunda chamada - deve acessar a API novamente
|
|
136
|
+
param2 = param_manager.get_param('test_app', 'PARAM1')
|
|
137
|
+
|
|
138
|
+
# Verifica se a API foi chamada novamente
|
|
139
|
+
assert mock_get.call_count == 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
|
|
143
|
+
def test_clear_cache_for_individual_param(
|
|
144
|
+
setup_param_manager, mock_response_data
|
|
145
|
+
):
|
|
146
|
+
"""Testa a limpeza do cache para um parâmetro individual."""
|
|
147
|
+
param_manager, _ = setup_param_manager
|
|
148
|
+
|
|
149
|
+
# Configura o mock para requests.get
|
|
150
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
151
|
+
mock_response = MagicMock()
|
|
152
|
+
mock_response.status_code = 200
|
|
153
|
+
mock_response.json.return_value = mock_response_data
|
|
154
|
+
mock_get.return_value = mock_response
|
|
155
|
+
|
|
156
|
+
# Primeira chamada - deve acessar a API
|
|
157
|
+
param1 = param_manager.get_param('test_app', 'PARAM1')
|
|
158
|
+
|
|
159
|
+
# Verifica se a API foi chamada
|
|
160
|
+
assert mock_get.call_count == 1
|
|
161
|
+
|
|
162
|
+
# Limpa o cache específico
|
|
163
|
+
param_manager.clear_cache('test_app', 'PARAM1')
|
|
164
|
+
|
|
165
|
+
# Reseta o mock para a próxima chamada
|
|
166
|
+
mock_get.reset_mock()
|
|
167
|
+
|
|
168
|
+
# Segunda chamada - deve acessar a API novamente
|
|
169
|
+
param2 = param_manager.get_param('test_app', 'PARAM1')
|
|
170
|
+
|
|
171
|
+
# Verifica se a API foi chamada novamente
|
|
172
|
+
assert mock_get.call_count == 1
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@pytest.mark.parametrize('mock_response_data', [{'param': 'test_value'}])
|
|
176
|
+
def test_clear_cache_for_app_clears_all_related_params(
|
|
177
|
+
setup_param_manager, mock_response_data
|
|
178
|
+
):
|
|
179
|
+
"""Testa se a limpeza do cache de um app limpa todos os parâmetros relacionados."""
|
|
180
|
+
param_manager, _ = setup_param_manager
|
|
181
|
+
|
|
182
|
+
# Configura o mock para requests.get
|
|
183
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
184
|
+
mock_response = MagicMock()
|
|
185
|
+
mock_response.status_code = 200
|
|
186
|
+
mock_response.json.return_value = mock_response_data
|
|
187
|
+
mock_get.return_value = mock_response
|
|
188
|
+
|
|
189
|
+
# Busca dois parâmetros diferentes para preencher o cache
|
|
190
|
+
param1 = param_manager.get_param('test_app', 'PARAM1')
|
|
191
|
+
param2 = param_manager.get_param('test_app', 'PARAM2')
|
|
192
|
+
|
|
193
|
+
# Verifica se a API foi chamada duas vezes
|
|
194
|
+
assert mock_get.call_count == 2
|
|
195
|
+
|
|
196
|
+
# Verifica se os caches específicos foram criados
|
|
197
|
+
assert 'test_app:PARAM1' in param_manager._param_cache
|
|
198
|
+
assert 'test_app:PARAM2' in param_manager._param_cache
|
|
199
|
+
|
|
200
|
+
# Limpa o cache do app
|
|
201
|
+
param_manager.clear_cache('test_app')
|
|
202
|
+
|
|
203
|
+
# Verifica se os caches específicos foram limpos
|
|
204
|
+
assert 'test_app:PARAM1' not in param_manager._param_cache
|
|
205
|
+
assert 'test_app:PARAM2' not in param_manager._param_cache
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_api_error_fallback_for_individual_param(setup_param_manager):
|
|
209
|
+
"""Testa o fallback para dados locais quando a API falha para um parâmetro individual."""
|
|
210
|
+
param_manager, mock_table = setup_param_manager
|
|
211
|
+
|
|
212
|
+
# Configura o mock para requests.get para lançar uma exceção
|
|
213
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
214
|
+
mock_get.side_effect = requests.exceptions.RequestException(
|
|
215
|
+
'API indisponível'
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Configura o mock para retornar dados locais
|
|
219
|
+
mock_table.all.return_value = [
|
|
220
|
+
{
|
|
221
|
+
'timestamp': time.time(),
|
|
222
|
+
'params': {'PARAM1': {'value': 'local_value'}},
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
# Chama o método para buscar um parâmetro específico
|
|
227
|
+
param = param_manager.get_param('test_app', 'PARAM1')
|
|
228
|
+
|
|
229
|
+
# Verifica se a API foi chamada
|
|
230
|
+
mock_get.assert_called_once()
|
|
231
|
+
|
|
232
|
+
# Verifica se os dados locais foram buscados
|
|
233
|
+
mock_table.all.assert_called_once()
|
|
234
|
+
|
|
235
|
+
# Verifica se o parâmetro local foi retornado
|
|
236
|
+
assert param == 'local_value'
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_api_error_fallback_for_all_params(setup_param_manager):
|
|
240
|
+
"""Testa o fallback para dados locais quando a API falha para um parâmetro individual."""
|
|
241
|
+
param_manager, mock_table = setup_param_manager
|
|
242
|
+
|
|
243
|
+
# Configura o mock para requests.get para lançar uma exceção
|
|
244
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
245
|
+
mock_get.side_effect = requests.exceptions.RequestException(
|
|
246
|
+
'API indisponível'
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
result = {'PARAM1': {'value': 'value1'}, 'PARAM2': {'value': 'value2'}}
|
|
250
|
+
# Configura o mock para retornar dados locais
|
|
251
|
+
mock_table.all.return_value = [
|
|
252
|
+
{
|
|
253
|
+
'timestamp': time.time(),
|
|
254
|
+
'params': result,
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
# Chama o método para buscar um parâmetro específico
|
|
259
|
+
params = param_manager.get_all_params('test_app')
|
|
260
|
+
|
|
261
|
+
# Verifica se a API foi chamada
|
|
262
|
+
mock_get.assert_called_once()
|
|
263
|
+
|
|
264
|
+
# Verifica se os dados locais foram buscados
|
|
265
|
+
mock_table.all.assert_called_once()
|
|
266
|
+
|
|
267
|
+
# Verifica se o parâmetro local foi retornado
|
|
268
|
+
assert params == result
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@pytest.mark.parametrize(
|
|
272
|
+
'mock_response_data', [{'param': {'value': 'test_value'}}]
|
|
273
|
+
)
|
|
274
|
+
def test_api_error_status_code(setup_param_manager, mock_response_data):
|
|
275
|
+
"""Testa se o a chamada da API retorna algo diferente de 200"""
|
|
276
|
+
param_manager, _ = setup_param_manager
|
|
277
|
+
|
|
278
|
+
# Define uma duração de cache muito curta para o teste
|
|
279
|
+
param_manager._cache_duration = 0 # 100ms
|
|
280
|
+
|
|
281
|
+
# Configura o mock para requests.get
|
|
282
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
283
|
+
mock_response = MagicMock()
|
|
284
|
+
mock_response.status_code = 200
|
|
285
|
+
mock_response.json.return_value = mock_response_data
|
|
286
|
+
mock_get.return_value = mock_response
|
|
287
|
+
|
|
288
|
+
# Busca um parâmetro para preencher o cache
|
|
289
|
+
param = param_manager.get_param('test_app', 'PARAM1')
|
|
290
|
+
|
|
291
|
+
# Verifica se o parâmetro local foi retornado
|
|
292
|
+
assert param == 'test_value'
|
|
293
|
+
|
|
294
|
+
mock_response.status_code = 500
|
|
295
|
+
|
|
296
|
+
# Busca um parâmetro para preencher o cache
|
|
297
|
+
param = param_manager.get_param('test_app', 'PARAM1')
|
|
298
|
+
|
|
299
|
+
assert param == None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@pytest.mark.parametrize(
|
|
303
|
+
'mock_response_data', [{'param': {'value': 'test_value'}}]
|
|
304
|
+
)
|
|
305
|
+
def test_get_cache_info_includes_param_cache(
|
|
306
|
+
setup_param_manager, mock_response_data
|
|
307
|
+
):
|
|
308
|
+
"""Testa se o método get_cache_info inclui informações sobre o cache de parâmetros individuais."""
|
|
309
|
+
param_manager, _ = setup_param_manager
|
|
310
|
+
|
|
311
|
+
# Configura o mock para requests.get
|
|
312
|
+
with patch('param_manager.manager.requests.get') as mock_get:
|
|
313
|
+
mock_response = MagicMock()
|
|
314
|
+
mock_response.status_code = 200
|
|
315
|
+
mock_response.json.return_value = mock_response_data
|
|
316
|
+
mock_get.return_value = mock_response
|
|
317
|
+
|
|
318
|
+
# Busca um parâmetro para preencher o cache
|
|
319
|
+
param = param_manager.get_param('test_app', 'PARAM1')
|
|
320
|
+
|
|
321
|
+
# Obtém informações do cache
|
|
322
|
+
cache_info = param_manager.get_cache_info()
|
|
323
|
+
|
|
324
|
+
# Verifica se as informações sobre o cache de parâmetros estão presentes
|
|
325
|
+
assert 'params_cached' in cache_info
|
|
326
|
+
assert 'param_cache_timestamps' in cache_info
|
|
327
|
+
assert 'param_cache_valid' in cache_info
|
|
328
|
+
|
|
329
|
+
# Verifica se o parâmetro específico está nas informações
|
|
330
|
+
assert 'test_app:PARAM1' in cache_info['params_cached']
|
|
331
|
+
assert 'test_app:PARAM1' in cache_info['param_cache_timestamps']
|
|
332
|
+
assert 'test_app:PARAM1' in cache_info['param_cache_valid']
|