Files
fn_registry/python/functions/infra/build_hoppscotch_collection.py
T
egutierrez eb8dbf66a1 feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 00:16:46 +02:00

136 lines
4.4 KiB
Python

"""Convierte call specs del registry en una coleccion Hoppscotch importable.
Mitad "exportar al GUI" del puente entre el motor de replay del registry y
Hoppscotch. La funcion inversa es parse_hoppscotch_collection.
"""
from urllib.parse import urlparse
def _request_name(call: dict, fallback_index: int) -> str:
"""Deriva un nombre legible para la request a partir del metodo y el path.
Args:
call: call spec con (opcional) method y url.
fallback_index: indice de la call dentro de la lista (no usado en el
nombre derivado, reservado para desambiguar si hiciera falta).
Returns:
nombre del estilo "GET /api/search".
"""
method = str(call.get("method") or "GET").upper()
url = str(call.get("url") or "")
path = urlparse(url).path or "/"
return f"{method} {path}"
def _build_headers(call: dict) -> list[dict]:
"""Construye la lista de headers Hoppscotch desde el dict del call spec.
Convierte el dict headers (preservando orden de insercion) a la lista
[{"key", "value", "active": True}, ...] y, si el call spec trae cookies no
vacias, anade un header extra "Cookie" al final con formato "k1=v1; k2=v2".
Args:
call: call spec con (opcional) headers y cookies.
Returns:
lista de headers Hoppscotch.
"""
headers: list[dict] = []
raw_headers = call.get("headers") or {}
for key, value in raw_headers.items():
headers.append({"key": key, "value": value, "active": True})
cookies = call.get("cookies") or {}
if cookies:
cookie_value = "; ".join(f"{name}={val}" for name, val in cookies.items())
headers.append({"key": "Cookie", "value": cookie_value, "active": True})
return headers
def _build_body(call: dict) -> dict:
"""Construye el objeto body Hoppscotch segun body_type del call spec.
Args:
call: call spec con (opcional) body y body_type.
Returns:
dict con contentType y body. Si no hay body o el body_type es
desconocido/None, ambos campos son None.
"""
body = call.get("body")
body_type = call.get("body_type")
content_types = {
"json": "application/json",
"form": "application/x-www-form-urlencoded",
"raw": "text/plain",
}
if body is None or body_type not in content_types:
return {"contentType": None, "body": None}
return {"contentType": content_types[body_type], "body": body}
def build_hoppscotch_collection(
calls: list[dict],
*,
name: str = "Collection",
request_names: list[str] | None = None,
) -> dict:
"""Convierte una lista de call specs en una coleccion Hoppscotch importable.
Genera el formato canonico estable (coleccion v:1, request v:"2") que
Hoppscotch migra a la ultima version al importar. Pura: sin I/O ni red,
solo stdlib, determinista.
Args:
calls: lista de call specs (salida de har_extract_calls). Cada elemento
es un dict con claves opcionales: method, url, headers, cookies,
body, body_type. Otras claves (status, sets_cookies, ...) se ignoran.
name: nombre de la coleccion Hoppscotch.
request_names: nombres explicitos por request, alineados por indice. Si
se pasa y existe el indice, sobreescribe el nombre derivado. None =
derivar todos los nombres como "<METHOD> <path>".
Returns:
dict con la coleccion Hoppscotch: {"v": 1, "name", "folders": [],
"requests": [...]}. JSON-serializable.
"""
requests: list[dict] = []
for index, call in enumerate(calls):
if request_names is not None and index < len(request_names):
req_name = request_names[index]
else:
req_name = _request_name(call, index)
endpoint = str(call.get("url") or "")
method = str(call.get("method") or "GET").upper()
requests.append(
{
"v": "2",
"endpoint": endpoint,
"name": req_name,
"params": [],
"headers": _build_headers(call),
"method": method,
"auth": {"authType": "none", "authActive": True},
"preRequestScript": "",
"testScript": "",
"body": _build_body(call),
"requestVariables": [],
}
)
return {
"v": 1,
"name": name,
"folders": [],
"requests": requests,
}