nati-log 2.2.7__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.
- nati_log-2.2.7/PKG-INFO +10 -0
- nati_log-2.2.7/nati_log/__init__.py +1 -0
- nati_log-2.2.7/nati_log/client.py +160 -0
- nati_log-2.2.7/nati_log/middleware.py +71 -0
- nati_log-2.2.7/nati_log.egg-info/PKG-INFO +10 -0
- nati_log-2.2.7/nati_log.egg-info/SOURCES.txt +14 -0
- nati_log-2.2.7/nati_log.egg-info/dependency_links.txt +1 -0
- nati_log-2.2.7/nati_log.egg-info/requires.txt +1 -0
- nati_log-2.2.7/nati_log.egg-info/top_level.txt +2 -0
- nati_log-2.2.7/pyproject.toml +11 -0
- nati_log-2.2.7/setup.cfg +4 -0
- nati_log-2.2.7/setup.py +10 -0
- nati_log-2.2.7/tests/__init__.py +0 -0
- nati_log-2.2.7/tests/settings.py +3 -0
- nati_log-2.2.7/tests/test_client.py +220 -0
- nati_log-2.2.7/tests/test_middleware.py +192 -0
nati_log-2.2.7/PKG-INFO
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .client import NatiLogClient
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NatiLogClient:
|
|
6
|
+
def __init__(self, api_url, api_url_login, app_id, username, password):
|
|
7
|
+
self.api_url = api_url.rstrip("/") # Saca la barra final si ya la tiene
|
|
8
|
+
self.api_url_login = api_url_login
|
|
9
|
+
self.app_id = app_id
|
|
10
|
+
self.app_estado = True
|
|
11
|
+
self.username = username
|
|
12
|
+
self.password = password
|
|
13
|
+
self.token = None
|
|
14
|
+
self.get_natilog_token()
|
|
15
|
+
self.actualizar_estado_aplicacion()
|
|
16
|
+
|
|
17
|
+
def get_natilog_token(self):
|
|
18
|
+
payload = {"username": self.username, "password": self.password}
|
|
19
|
+
try:
|
|
20
|
+
response = requests.post(self.api_url_login, json=payload, timeout=10)
|
|
21
|
+
response.raise_for_status() # Lanza un error si el código de estado no es 200
|
|
22
|
+
data = response.json()
|
|
23
|
+
self.token = data.get("access") or data.get("token")
|
|
24
|
+
except requests.exceptions.RequestException as e:
|
|
25
|
+
self.token = None
|
|
26
|
+
|
|
27
|
+
def actualizar_estado_aplicacion(self):
|
|
28
|
+
if not self.token: # si no hay token, no se puede verificar el estado
|
|
29
|
+
return
|
|
30
|
+
headers = {"Authorization": f"Bearer {self.token}"} # encabezado de autorización
|
|
31
|
+
try:
|
|
32
|
+
response = requests.get(
|
|
33
|
+
f"{self.api_url}/aplicaciones/{self.app_id}",
|
|
34
|
+
headers=headers,
|
|
35
|
+
timeout=5,
|
|
36
|
+
) # solicitud para obtener el estado de la aplicación
|
|
37
|
+
if response.status_code == 401:
|
|
38
|
+
self.get_natilog_token() # si el token ha expirado, obtener uno nuevo
|
|
39
|
+
if not self.token:
|
|
40
|
+
return
|
|
41
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
42
|
+
response = requests.get(
|
|
43
|
+
f"{self.api_url}/aplicaciones/{self.app_id}",
|
|
44
|
+
headers=headers,
|
|
45
|
+
timeout=5,
|
|
46
|
+
)
|
|
47
|
+
response.raise_for_status() # Lanza un error si la respuesta no es 200 OK
|
|
48
|
+
data = response.json()
|
|
49
|
+
self.app_estado = data.get("estado", True)
|
|
50
|
+
except requests.exceptions.RequestException: # en caso de error, asumir que la app está activa
|
|
51
|
+
self.app_estado = True
|
|
52
|
+
# si la app está inactiva, no se registrarán eventos
|
|
53
|
+
|
|
54
|
+
def registrar_evento(self, tipo_evento, mensaje, datos=None, fecha=None):
|
|
55
|
+
"""
|
|
56
|
+
Registra un evento en la API de NatiLog
|
|
57
|
+
Args:
|
|
58
|
+
tipo_evento: str - Tipo de evento (e.g., "critical", "error", "warning", "info")
|
|
59
|
+
mensaje: str - Mensaje del evento
|
|
60
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
61
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
62
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
63
|
+
Raises: requests.exceptions.RequestException - Si hay un error en la solicitud
|
|
64
|
+
"""
|
|
65
|
+
self.actualizar_estado_aplicacion()
|
|
66
|
+
if not self.app_estado:
|
|
67
|
+
return {"detail": "Aplicación inactiva. Evento omitido."}
|
|
68
|
+
|
|
69
|
+
if fecha is None:
|
|
70
|
+
fecha = (
|
|
71
|
+
datetime.datetime.now().isoformat()
|
|
72
|
+
) # Fecha y hora actual en formato ISO 8601
|
|
73
|
+
|
|
74
|
+
payload = {
|
|
75
|
+
"aplicacion": self.app_id,
|
|
76
|
+
"tipo_evento": tipo_evento,
|
|
77
|
+
"mensaje": mensaje,
|
|
78
|
+
"datos": datos
|
|
79
|
+
or {}, # Datos adicionales, si no hay datos, envía un diccionario vacío
|
|
80
|
+
"fecha": fecha,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if not self.token:
|
|
84
|
+
self.get_natilog_token()
|
|
85
|
+
|
|
86
|
+
headers = {"Content-Type": "application/json"}
|
|
87
|
+
|
|
88
|
+
if self.token:
|
|
89
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
90
|
+
|
|
91
|
+
response = requests.post(
|
|
92
|
+
f"{self.api_url}/evento/", json=payload, headers=headers, timeout=5
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if response.status_code == 401:
|
|
96
|
+
self.get_natilog_token()
|
|
97
|
+
if not self.token:
|
|
98
|
+
response.raise_for_status()
|
|
99
|
+
headers["Authorization"] = f"Bearer {self.token}"
|
|
100
|
+
response = requests.post(
|
|
101
|
+
f"{self.api_url}/evento/", json=payload, headers=headers, timeout=5
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
response.raise_for_status() # Lanza un error si la respuesta no es 200 OK
|
|
105
|
+
return response.json() # Devuelve la respuesta en formato JSON
|
|
106
|
+
|
|
107
|
+
def debug(self, mensaje, datos=None, fecha=None):
|
|
108
|
+
"""
|
|
109
|
+
Registra un evento de tipo "debug"
|
|
110
|
+
Args:
|
|
111
|
+
mensaje: str - Mensaje del evento
|
|
112
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
113
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
114
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
115
|
+
"""
|
|
116
|
+
return self.registrar_evento("DEBUG", mensaje, datos, fecha)
|
|
117
|
+
|
|
118
|
+
def error(self, mensaje, datos=None, fecha=None):
|
|
119
|
+
"""
|
|
120
|
+
Registra un evento de tipo "error"
|
|
121
|
+
Args:
|
|
122
|
+
mensaje: str - Mensaje del evento
|
|
123
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
124
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
125
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
126
|
+
"""
|
|
127
|
+
return self.registrar_evento("ERROR", mensaje, datos, fecha)
|
|
128
|
+
|
|
129
|
+
def warning(self, mensaje, datos=None, fecha=None):
|
|
130
|
+
"""
|
|
131
|
+
Registra un evento de tipo "warning"
|
|
132
|
+
Args:
|
|
133
|
+
mensaje: str - Mensaje del evento
|
|
134
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
135
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
136
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
137
|
+
"""
|
|
138
|
+
return self.registrar_evento("WARNING", mensaje, datos, fecha)
|
|
139
|
+
|
|
140
|
+
def info(self, mensaje, datos=None, fecha=None):
|
|
141
|
+
"""
|
|
142
|
+
Registra un evento de tipo "info"
|
|
143
|
+
Args:
|
|
144
|
+
mensaje: str - Mensaje del evento
|
|
145
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
146
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
147
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
148
|
+
"""
|
|
149
|
+
return self.registrar_evento("INFO", mensaje, datos, fecha)
|
|
150
|
+
|
|
151
|
+
def critical(self, mensaje, datos=None, fecha=None):
|
|
152
|
+
"""
|
|
153
|
+
Registra un evento de tipo "critical"
|
|
154
|
+
Args:
|
|
155
|
+
mensaje: str - Mensaje del evento
|
|
156
|
+
datos: dict - Datos adicionales del evento (opcional)
|
|
157
|
+
fecha: str - Fecha y hora del evento en formato ISO 8601 (opcional)
|
|
158
|
+
Returns: dict - Respuesta de la API en formato JSON
|
|
159
|
+
"""
|
|
160
|
+
return self.registrar_evento("CRITICAL", mensaje, datos, fecha)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from .client import NatiLogClient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class NatiLogMiddleware:
|
|
6
|
+
def __init__(self, get_response):
|
|
7
|
+
"""
|
|
8
|
+
Middleware para registrar eventos automáticamente en NatiLog.
|
|
9
|
+
Args:
|
|
10
|
+
:param get_response: callable - La función para obtener la respuesta de la vista
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
self.get_response = get_response
|
|
14
|
+
config = getattr(settings, "NATILOG", {})
|
|
15
|
+
self.levels = config.get(
|
|
16
|
+
"EVENT_LEVELS",
|
|
17
|
+
{"DEBUG": True, "INFO": True, "WARNING": True, "ERROR": True, "CRITICAL": True},
|
|
18
|
+
)
|
|
19
|
+
self.natilog = NatiLogClient(
|
|
20
|
+
api_url=config.get("API_URL"),
|
|
21
|
+
api_url_login=config.get("API_URL_LOGIN"),
|
|
22
|
+
app_id=config.get("APP_ID"),
|
|
23
|
+
username=config.get("USERNAME"),
|
|
24
|
+
password=config.get("PASSWORD"),
|
|
25
|
+
)
|
|
26
|
+
print("NatilogClient iniziado en middleware.")
|
|
27
|
+
print(f"NATILOG config: {config}")
|
|
28
|
+
|
|
29
|
+
def __call__(self, request):
|
|
30
|
+
"""
|
|
31
|
+
Procesa la solicitud y registra eventos en NatiLog.
|
|
32
|
+
Args:
|
|
33
|
+
:param request: HttpRequest - La solicitud entrante
|
|
34
|
+
:returns: HttpResponse - La respuesta generada por la vista
|
|
35
|
+
"""
|
|
36
|
+
response = self.get_response(request)
|
|
37
|
+
|
|
38
|
+
if not self.natilog:
|
|
39
|
+
return response
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
if self.levels.get("DEBUG", True):
|
|
43
|
+
self.natilog.debug(
|
|
44
|
+
f"Request recibido: {request.method} {request.path}",
|
|
45
|
+
datos={"usuario": getattr(request.user, "username", None)},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if 200 <= response.status_code < 300 and self.levels.get("INFO", True):
|
|
49
|
+
self.natilog.info(
|
|
50
|
+
f"Request OK: {request.method} {request.path}",
|
|
51
|
+
datos={"status_code": response.status_code},
|
|
52
|
+
)
|
|
53
|
+
elif 300 <= response.status_code < 400 and self.levels.get("WARNING", True):
|
|
54
|
+
self.natilog.warning(
|
|
55
|
+
f"Redirect: {request.method} {request.path}",
|
|
56
|
+
datos={"status_code": response.status_code},
|
|
57
|
+
)
|
|
58
|
+
elif 400 <= response.status_code < 500 and self.levels.get("ERROR", True):
|
|
59
|
+
self.natilog.error(
|
|
60
|
+
f"Client Error {response.status_code}: {request.method} {request.path}",
|
|
61
|
+
datos={"status_code": response.status_code},
|
|
62
|
+
)
|
|
63
|
+
elif response.status_code >= 500 and self.levels.get("CRITICAL", True):
|
|
64
|
+
self.natilog.critical(
|
|
65
|
+
f"Server Error {response.status_code}: {request.method} {request.path}",
|
|
66
|
+
datos={"status_code": response.status_code},
|
|
67
|
+
)
|
|
68
|
+
except Exception as exc:
|
|
69
|
+
print(f"Error al registrar evento en NatiLog: {exc}")
|
|
70
|
+
|
|
71
|
+
return response
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
setup.py
|
|
3
|
+
nati_log/__init__.py
|
|
4
|
+
nati_log/client.py
|
|
5
|
+
nati_log/middleware.py
|
|
6
|
+
nati_log.egg-info/PKG-INFO
|
|
7
|
+
nati_log.egg-info/SOURCES.txt
|
|
8
|
+
nati_log.egg-info/dependency_links.txt
|
|
9
|
+
nati_log.egg-info/requires.txt
|
|
10
|
+
nati_log.egg-info/top_level.txt
|
|
11
|
+
tests/__init__.py
|
|
12
|
+
tests/settings.py
|
|
13
|
+
tests/test_client.py
|
|
14
|
+
tests/test_middleware.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.25.1
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nati_log"
|
|
7
|
+
version = "2.2.7"
|
|
8
|
+
description = "Estadísticas de logs"
|
|
9
|
+
authors = [{name = "Natalí"}]
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = ["requests>=2.25.1"]
|
nati_log-2.2.7/setup.cfg
ADDED
nati_log-2.2.7/setup.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import datetime
|
|
3
|
+
import pytest
|
|
4
|
+
import requests
|
|
5
|
+
from nati_log.client import NatiLogClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def api_urls():
|
|
10
|
+
return {
|
|
11
|
+
"api": "http://fake-api/api",
|
|
12
|
+
"login": "http://fake-api/api/auth/usuarios/login/",
|
|
13
|
+
"app": "http://fake-api/api/aplicaciones/5",
|
|
14
|
+
"evento": "http://fake-api/api/evento/",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _build_client(requests_mock, api_urls, app_estado=True, token="token-1"):
|
|
19
|
+
requests_mock.post(
|
|
20
|
+
api_urls["login"],
|
|
21
|
+
json={"access": token},
|
|
22
|
+
status_code=200,
|
|
23
|
+
)
|
|
24
|
+
requests_mock.get(
|
|
25
|
+
api_urls["app"],
|
|
26
|
+
json={"estado": app_estado},
|
|
27
|
+
status_code=200,
|
|
28
|
+
)
|
|
29
|
+
return NatiLogClient(
|
|
30
|
+
api_url=api_urls["api"],
|
|
31
|
+
api_url_login=api_urls["login"],
|
|
32
|
+
app_id=5,
|
|
33
|
+
username="user",
|
|
34
|
+
password="pass",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_registrar_evento_success(requests_mock, api_urls):
|
|
39
|
+
client = _build_client(requests_mock, api_urls)
|
|
40
|
+
requests_mock.post(
|
|
41
|
+
api_urls["evento"],
|
|
42
|
+
json={"status": "ok"},
|
|
43
|
+
status_code=201,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
resp = client.info("Evento exitoso", datos={"key": "value"})
|
|
47
|
+
assert resp == {"status": "ok"}
|
|
48
|
+
|
|
49
|
+
last = requests_mock.last_request
|
|
50
|
+
assert last.headers["Authorization"] == "Bearer token-1"
|
|
51
|
+
assert last.json()["tipo_evento"] == "INFO"
|
|
52
|
+
assert last.json()["datos"]["key"] == "value"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_registrar_evento_app_inactiva_omite_envio(requests_mock, api_urls):
|
|
56
|
+
client = _build_client(requests_mock, api_urls, app_estado=False)
|
|
57
|
+
|
|
58
|
+
result = client.info("No debe enviarse")
|
|
59
|
+
assert result == {"detail": "Aplicación inactiva. Evento omitido."}
|
|
60
|
+
assert len(requests_mock.request_history) == 2 # login + estado, sin POST de evento
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_registrar_evento_reintenta_en_401(requests_mock, api_urls):
|
|
64
|
+
requests_mock.post(
|
|
65
|
+
api_urls["login"],
|
|
66
|
+
[
|
|
67
|
+
{"json": {"access": "token-1"}, "status_code": 200},
|
|
68
|
+
{"json": {"access": "token-2"}, "status_code": 200},
|
|
69
|
+
],
|
|
70
|
+
)
|
|
71
|
+
requests_mock.get(api_urls["app"], json={"estado": True}, status_code=200)
|
|
72
|
+
requests_mock.post(
|
|
73
|
+
api_urls["evento"],
|
|
74
|
+
[
|
|
75
|
+
{"status_code": 401},
|
|
76
|
+
{"json": {"status": "ok"}, "status_code": 201},
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
client = NatiLogClient(
|
|
81
|
+
api_url=api_urls["api"],
|
|
82
|
+
api_url_login=api_urls["login"],
|
|
83
|
+
app_id=5,
|
|
84
|
+
username="user",
|
|
85
|
+
password="pass",
|
|
86
|
+
)
|
|
87
|
+
resp = client.error("Debe reintentar")
|
|
88
|
+
assert resp == {"status": "ok"}
|
|
89
|
+
|
|
90
|
+
evento_calls = [req for req in requests_mock.request_history if req.url == api_urls["evento"]]
|
|
91
|
+
assert len(evento_calls) == 2
|
|
92
|
+
assert evento_calls[-1].headers["Authorization"] == "Bearer token-2"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_registrar_evento_sin_token_levanta_http_error(requests_mock, api_urls):
|
|
96
|
+
requests_mock.post(api_urls["login"], status_code=401)
|
|
97
|
+
client = NatiLogClient(
|
|
98
|
+
api_url=api_urls["api"],
|
|
99
|
+
api_url_login=api_urls["login"],
|
|
100
|
+
app_id=5,
|
|
101
|
+
username="user",
|
|
102
|
+
password="pass",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
requests_mock.post(api_urls["evento"], status_code=401)
|
|
106
|
+
with pytest.raises(requests.exceptions.HTTPError):
|
|
107
|
+
client.warning("Debe fallar")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_registrar_evento_con_fecha_personalizada(requests_mock, api_urls):
|
|
111
|
+
client = _build_client(requests_mock, api_urls)
|
|
112
|
+
requests_mock.post(
|
|
113
|
+
api_urls["evento"],
|
|
114
|
+
json={"status": "ok"},
|
|
115
|
+
status_code=201,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
fecha = datetime.datetime(2024, 1, 1, 12, 0, 0).isoformat()
|
|
119
|
+
client.debug("Con fecha", fecha=fecha)
|
|
120
|
+
|
|
121
|
+
last = requests_mock.last_request
|
|
122
|
+
assert last.json()["fecha"] == fecha
|
|
123
|
+
assert last.json()["tipo_evento"] == "DEBUG"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_get_token_exception_deja_token_none(requests_mock, api_urls):
|
|
127
|
+
requests_mock.post(
|
|
128
|
+
api_urls["login"],
|
|
129
|
+
exc=requests.exceptions.ConnectTimeout,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
client = NatiLogClient(
|
|
133
|
+
api_url=api_urls["api"],
|
|
134
|
+
api_url_login=api_urls["login"],
|
|
135
|
+
app_id=5,
|
|
136
|
+
username="user",
|
|
137
|
+
password="pass",
|
|
138
|
+
)
|
|
139
|
+
assert client.token is None
|
|
140
|
+
assert client.app_estado is True
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_registrar_evento_retry_sin_token_levanta_error(requests_mock, api_urls):
|
|
144
|
+
requests_mock.post(
|
|
145
|
+
api_urls["login"],
|
|
146
|
+
[
|
|
147
|
+
{"json": {"access": "token-1"}, "status_code": 200},
|
|
148
|
+
{"status_code": 500},
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
requests_mock.get(api_urls["app"], json={"estado": True}, status_code=200)
|
|
152
|
+
requests_mock.post(api_urls["evento"], status_code=401)
|
|
153
|
+
|
|
154
|
+
client = NatiLogClient(
|
|
155
|
+
api_url=api_urls["api"],
|
|
156
|
+
api_url_login=api_urls["login"],
|
|
157
|
+
app_id=5,
|
|
158
|
+
username="user",
|
|
159
|
+
password="pass",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
with pytest.raises(requests.exceptions.HTTPError):
|
|
163
|
+
client.warning("Debe fallar tras reintento sin token")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_registrar_evento_critical(requests_mock, api_urls):
|
|
167
|
+
client = _build_client(requests_mock, api_urls)
|
|
168
|
+
requests_mock.post(
|
|
169
|
+
api_urls["evento"],
|
|
170
|
+
json={"status": "ok"},
|
|
171
|
+
status_code=201,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
client.critical("Falla crítica")
|
|
175
|
+
|
|
176
|
+
last = requests_mock.last_request
|
|
177
|
+
assert last.json()["tipo_evento"] == "CRITICAL"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_actualizar_estado_aplicacion_refresca_token(requests_mock, api_urls):
|
|
181
|
+
requests_mock.post(
|
|
182
|
+
api_urls["login"],
|
|
183
|
+
[
|
|
184
|
+
{"json": {"access": "token-1"}, "status_code": 200},
|
|
185
|
+
{"json": {"access": "token-2"}, "status_code": 200},
|
|
186
|
+
],
|
|
187
|
+
)
|
|
188
|
+
requests_mock.get(
|
|
189
|
+
api_urls["app"],
|
|
190
|
+
[
|
|
191
|
+
{"status_code": 401},
|
|
192
|
+
{"json": {"estado": False}, "status_code": 200},
|
|
193
|
+
],
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
client = NatiLogClient(
|
|
197
|
+
api_url=api_urls["api"],
|
|
198
|
+
api_url_login=api_urls["login"],
|
|
199
|
+
app_id=5,
|
|
200
|
+
username="user",
|
|
201
|
+
password="pass",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
assert client.token == "token-2"
|
|
205
|
+
assert client.estado is False
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_actualizar_estado_aplicacion_error_mantiene_activa(requests_mock, api_urls):
|
|
209
|
+
requests_mock.post(api_urls["login"], json={"access": "token"}, status_code=200)
|
|
210
|
+
requests_mock.get(api_urls["app"], exc=requests.exceptions.ReadTimeout)
|
|
211
|
+
|
|
212
|
+
client = NatiLogClient(
|
|
213
|
+
api_url=api_urls["api"],
|
|
214
|
+
api_url_login=api_urls["login"],
|
|
215
|
+
app_id=5,
|
|
216
|
+
username="user",
|
|
217
|
+
password="pass",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
assert client.estado is True
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
try:
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
except ImportError:
|
|
5
|
+
class SimpleNamespace(object):
|
|
6
|
+
def __init__(self, **kwargs):
|
|
7
|
+
for key, value in kwargs.items():
|
|
8
|
+
setattr(self, key, value)
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
from django.http import HttpResponse
|
|
12
|
+
from django.test import RequestFactory
|
|
13
|
+
from nati_log.middleware import NatiLogMiddleware
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DummyClient(object):
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.calls = []
|
|
19
|
+
|
|
20
|
+
def debug(self, mensaje, datos=None, fecha=None):
|
|
21
|
+
self.calls.append(("debug", mensaje, datos))
|
|
22
|
+
|
|
23
|
+
def info(self, mensaje, datos=None, fecha=None):
|
|
24
|
+
self.calls.append(("info", mensaje, datos))
|
|
25
|
+
|
|
26
|
+
def warning(self, mensaje, datos=None, fecha=None):
|
|
27
|
+
self.calls.append(("warning", mensaje, datos))
|
|
28
|
+
|
|
29
|
+
def error(self, mensaje, datos=None, fecha=None):
|
|
30
|
+
self.calls.append(("error", mensaje, datos))
|
|
31
|
+
|
|
32
|
+
def critical(self, mensaje, datos=None, fecha=None):
|
|
33
|
+
self.calls.append(("critical", mensaje, datos))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def rf():
|
|
38
|
+
return RequestFactory()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_response(status=200):
|
|
42
|
+
resp = HttpResponse("ok")
|
|
43
|
+
resp.status_code = status
|
|
44
|
+
return resp
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_middleware_registra_info_por_defecto(settings, monkeypatch, rf):
|
|
48
|
+
settings.NATILOG = {
|
|
49
|
+
"API_URL": "http://fake/api",
|
|
50
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
51
|
+
"APP_ID": 1,
|
|
52
|
+
"USERNAME": "user",
|
|
53
|
+
"PASSWORD": "pass",
|
|
54
|
+
"EVENT_LEVELS": {
|
|
55
|
+
"DEBUG": True,
|
|
56
|
+
"INFO": True,
|
|
57
|
+
"WARNING": True,
|
|
58
|
+
"ERROR": True,
|
|
59
|
+
"CRITICAL": True,
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
dummy = DummyClient()
|
|
63
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: dummy)
|
|
64
|
+
|
|
65
|
+
middleware = NatiLogMiddleware(lambda req: make_response(200))
|
|
66
|
+
request = rf.get("/path")
|
|
67
|
+
request.user = SimpleNamespace(username="tester")
|
|
68
|
+
|
|
69
|
+
middleware(request)
|
|
70
|
+
|
|
71
|
+
assert ("debug", "Request recibido: GET /path", {"usuario": "tester"}) in dummy.calls
|
|
72
|
+
assert any(call[0] == "info" for call in dummy.calls)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_middleware_filtra_niveles(settings, monkeypatch, rf):
|
|
76
|
+
settings.NATILOG = {
|
|
77
|
+
"API_URL": "http://fake/api",
|
|
78
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
79
|
+
"APP_ID": 1,
|
|
80
|
+
"USERNAME": "user",
|
|
81
|
+
"PASSWORD": "pass",
|
|
82
|
+
"EVENT_LEVELS": {
|
|
83
|
+
"DEBUG": False,
|
|
84
|
+
"INFO": False,
|
|
85
|
+
"WARNING": True,
|
|
86
|
+
"ERROR": True,
|
|
87
|
+
"CRITICAL": True,
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
dummy = DummyClient()
|
|
91
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: dummy)
|
|
92
|
+
|
|
93
|
+
middleware = NatiLogMiddleware(lambda req: make_response(302))
|
|
94
|
+
request = rf.get("/redirect")
|
|
95
|
+
|
|
96
|
+
middleware(request)
|
|
97
|
+
|
|
98
|
+
tipos = set(call[0] for call in dummy.calls)
|
|
99
|
+
assert "debug" not in tipos
|
|
100
|
+
assert "info" not in tipos
|
|
101
|
+
assert "warning" in tipos
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_middleware_registra_error(settings, monkeypatch, rf):
|
|
105
|
+
settings.NATILOG = {
|
|
106
|
+
"API_URL": "http://fake/api",
|
|
107
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
108
|
+
"APP_ID": 1,
|
|
109
|
+
"USERNAME": "user",
|
|
110
|
+
"PASSWORD": "pass",
|
|
111
|
+
"EVENT_LEVELS": {
|
|
112
|
+
"DEBUG": True,
|
|
113
|
+
"INFO": True,
|
|
114
|
+
"WARNING": True,
|
|
115
|
+
"ERROR": True,
|
|
116
|
+
"CRITICAL": True,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
dummy = DummyClient()
|
|
120
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: dummy)
|
|
121
|
+
|
|
122
|
+
middleware = NatiLogMiddleware(lambda req: make_response(404))
|
|
123
|
+
request = rf.get("/not-found")
|
|
124
|
+
request.user = SimpleNamespace(username="tester")
|
|
125
|
+
|
|
126
|
+
middleware(request)
|
|
127
|
+
|
|
128
|
+
assert any(call[0] == "error" for call in dummy.calls)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_middleware_sin_cliente_retorna(settings, monkeypatch, rf):
|
|
132
|
+
settings.NATILOG = {
|
|
133
|
+
"API_URL": "http://fake/api",
|
|
134
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
135
|
+
"APP_ID": 1,
|
|
136
|
+
"USERNAME": "user",
|
|
137
|
+
"PASSWORD": "pass",
|
|
138
|
+
"EVENT_LEVELS": {"DEBUG": True, "INFO": True, "WARNING": True, "ERROR": True, "CRITICAL": True},
|
|
139
|
+
}
|
|
140
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: None)
|
|
141
|
+
|
|
142
|
+
middleware = NatiLogMiddleware(lambda req: make_response(200))
|
|
143
|
+
request = rf.get("/noop")
|
|
144
|
+
middleware(request)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_middleware_registra_critical(settings, monkeypatch, rf):
|
|
148
|
+
settings.NATILOG = {
|
|
149
|
+
"API_URL": "http://fake/api",
|
|
150
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
151
|
+
"APP_ID": 1,
|
|
152
|
+
"USERNAME": "user",
|
|
153
|
+
"PASSWORD": "pass",
|
|
154
|
+
"EVENT_LEVELS": {"DEBUG": True, "INFO": True, "WARNING": True, "ERROR": True, "CRITICAL": True},
|
|
155
|
+
}
|
|
156
|
+
dummy = DummyClient()
|
|
157
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: dummy)
|
|
158
|
+
|
|
159
|
+
middleware = NatiLogMiddleware(lambda req: make_response(503))
|
|
160
|
+
request = rf.get("/boom")
|
|
161
|
+
request.user = SimpleNamespace(username="tester")
|
|
162
|
+
|
|
163
|
+
middleware(request)
|
|
164
|
+
|
|
165
|
+
assert any(call[0] == "critical" for call in dummy.calls)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_middleware_registra_warning(settings, monkeypatch, rf):
|
|
169
|
+
settings.NATILOG = {
|
|
170
|
+
"API_URL": "http://fake/api",
|
|
171
|
+
"API_URL_LOGIN": "http://fake/api/auth/",
|
|
172
|
+
"APP_ID": 1,
|
|
173
|
+
"USERNAME": "user",
|
|
174
|
+
"PASSWORD": "pass",
|
|
175
|
+
"EVENT_LEVELS": {
|
|
176
|
+
"DEBUG": True,
|
|
177
|
+
"INFO": True,
|
|
178
|
+
"WARNING": True,
|
|
179
|
+
"ERROR": True,
|
|
180
|
+
"CRITICAL": True,
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
dummy = DummyClient()
|
|
184
|
+
monkeypatch.setattr("nati_log.middleware.NatiLogClient", lambda **_: dummy)
|
|
185
|
+
|
|
186
|
+
middleware = NatiLogMiddleware(lambda req: make_response(302))
|
|
187
|
+
request = rf.get("/redirect")
|
|
188
|
+
request.user = SimpleNamespace(username="tester")
|
|
189
|
+
|
|
190
|
+
middleware(request)
|
|
191
|
+
|
|
192
|
+
assert any(call[0] == "warning" for call in dummy.calls)
|