# Hoppscotch — consola de APIs + colecciones versionadas GUI para explorar/probar APIs a mano (Hoppscotch Desktop) con las consultas guardadas como JSON versionado en este sub-repo, y un puente bidireccional con el motor de replay del registry para **lanzar y automatizar** esas mismas peticiones desde terminal o scripts. Grupo de capacidad: `hoppscotch`. Página madre: `docs/capabilities/hoppscotch.md`. ## El ciclo ``` GUI Hoppscotch ──export .json──▶ parse_hoppscotch_collection ──call specs──▶ run_hoppscotch_collection ──▶ http_replay_sequence ▲ (automatiza, headless) └──── build_hoppscotch_collection ◀── call specs (de HAR destilado, o a mano) ────────────────────────────┘ ``` - **GUI → automatización**: editas/creas requests en la app, exportas la colección a `collections/.json`, y `run_hoppscotch_collection` las ejecuta sin tocar la GUI. - **Automatización → GUI**: tienes call specs (p.ej. de un flujo grabado con HAR y destilado con `har_extract_calls`), `build_hoppscotch_collection` te da un `.json` importable en la app para inspeccionarlas visualmente. ## Estructura ``` hoppscotch/ collections/ # Colecciones .json VERSIONADAS (sin secretos) registry_api.json # ejemplo: 4 endpoints GET del registry_api environments/ # Variables de entorno Hoppscotch .gitignore # ignora *.json (secretos) salvo *.example.json registry_api.example.json # plantilla VERSIONADA (valores placeholder) registry_api.json # real, GITIGNORED (puede llevar tokens) README.md ``` Regla de secretos: las **colecciones** se versionan; los **environments reales** no (llevan tokens/basicAuth). Solo la plantilla `*.example.json` viaja en git. En las requests, los valores sensibles van como variable de environment con la sintaxis Hoppscotch `<>`, nunca hardcodeados. ## Lanzar la app Desktop Binario instalado por el `.deb`: `/usr/bin/hoppscotch-desktop` (menú de aplicaciones: "Hoppscotch"). Lanzado desde una sesión gráfica normal o, de forma aislada: ```bash systemd-run --user --unit=hoppscotch-gui --setenv=DISPLAY=:0 /usr/bin/hoppscotch-desktop ``` ### Importar una colección en la GUI 1. Panel **Collections** (izquierda) → menú **Import / Export**. 2. **Import** → **Hoppscotch** → **Import from File**. 3. Selecciona `collections/registry_api.json`. 4. (Opcional) Importa también el environment: panel **Environments** → Import → `environments/registry_api.example.json` y rellena los valores reales (no se versionan). 5. Selecciona el environment activo arriba a la derecha y pulsa **Send** en cualquier request. ### Exportar / ver el JSON que genera la GUI Collections → menú → **Export** → **Hoppscotch** → guarda en `collections/`. Así se ve el esquema nativo de la versión instalada (la app migra al esquema más reciente al importar/exportar). `parse_hoppscotch_collection` lee cualquier versión v1..v12 por campos estables. ## Automatizar desde el registry Lanzar toda una colección o un subconjunto, headless, con sustitución de variables: ```python import sys, os sys.path.insert(0, os.path.join(os.path.expanduser("~/fn_registry"), "python", "functions")) from infra.run_hoppscotch_collection import run_hoppscotch_collection res = run_hoppscotch_collection( "projects/web_scraping/hoppscotch/collections/registry_api.json", environment_path="projects/web_scraping/hoppscotch/environments/registry_api.json", params={"baseURL": "https://registry.organic-machine.com"}, # pisa el environment only=["status", "locations"], # filtra por nombre de request; None = todas ) print(res["status"]) for s in res["steps"]: print(s["method"], s["url"], "->", s["status_code"], "ok" if s["ok"] else "FAIL") ``` `run_hoppscotch_collection` convierte la sintaxis Hoppscotch `<>` a la del motor de replay `{{var}}`, mergea environment + params (params del caller ganan), filtra por `only`, y reproduce las peticiones sobre una sesión HTTP compartida (cookie jar entre pasos). ## Añadir peticiones rápido Sin editar el `.json` a mano, con `add_hoppscotch_request` (reusa el mapeo de `build`, no destruye el archivo si el JSON es inválido): ```python from infra.add_hoppscotch_request import add_hoppscotch_request r = add_hoppscotch_request( "projects/web_scraping/hoppscotch/collections/registry_api.json", "GET", "<>/api/search?q=metabase&kind=function", name="search metabase", headers={"Accept": "application/json"}) print(r["status"], r["total_requests"]) # ok 8 ``` Tras añadir, re-importa la colección en la app Desktop (no recarga el `.json` en caliente) o córrela headless con `run_hoppscotch_collection` / `hopp test`. ## Validar la colección Dos motores independientes, ambos verde sobre `registry_api.json` (7 requests → 7×200): ```bash # Motor nativo Hoppscotch (corre también preRequestScript/testScript JS): hopp test collections/registry_api.json -e environments/registry_api.cli.json # Motor del registry (integrado, telemetría, params/extract): python3 -c 'from infra.run_hoppscotch_collection import run_hoppscotch_collection as r; \ print(r("collections/registry_api.json", environment_path="environments/registry_api.json")["status"])' ``` ## Funciones del registry | ID | Qué hace | |---|---| | `build_hoppscotch_collection_py_infra` | call specs → dict de colección Hoppscotch (importable en la GUI) | | `parse_hoppscotch_collection_py_infra` | dict de colección Hoppscotch (v1..v12) → call specs | | `run_hoppscotch_collection_py_infra` | lee colección + environment .json y ejecuta vía `http_replay_sequence` | | `add_hoppscotch_request_py_infra` | añade una petición a una colección `.json` existente (reusa `build`, no destructivo) | | `http_replay_sequence_py_infra` | motor de replay HTTP (secuencia con sesión, `{{param}}`, extract) | | `har_extract_calls_py_cybersecurity` | HAR destilado → call specs (origen del lado izquierdo del ciclo) | ## Gotchas - **Sintaxis de variables**: la GUI usa `<>`; el motor de replay usa `{{var}}`. `run_hoppscotch_collection` traduce automáticamente. Si compones a mano con `http_replay_sequence`, traduce tú (`<>` → `{{x}}`). - **Endpoints con efecto** (POST/DELETE destructivos): filtra con `only` para no ejecutar a ciegas toda la colección. El replay reproduce lo que haya; la responsabilidad de no disparar acciones irreversibles es del caller. - **Esquema de versión**: `build_*` genera el formato canónico estable v1/v2 que la app migra al importar. `parse_*` lee cualquier versión por campos. No fijamos v12/v17 a mano para no romper con cambios de esquema upstream. - **multipart/form-data**: `parse_*` lo marca `body_type="raw"` y no reconstruye el cuerpo binario. - **Formato dual de environment**: la GUI Desktop importa el array `[{v,name,variables:[{key,value,secret}]}]` (`registry_api.json` / `.example.json`); el CLI `hopp` v0.31.2 quiere un **objeto plano** `{name,variables:[{key,value}]}` SIN `v` ni `secret` (`registry_api.cli.json`). Pasar el array a `hopp test -e` da `MALFORMED_ENV_FILE`. `run_hoppscotch_collection` (registry) usa el array.