feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
---
|
||||
name: build_hoppscotch_collection
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "def build_hoppscotch_collection(calls: list[dict], *, name: str = \"Collection\", request_names: list[str] | None = None) -> dict"
|
||||
description: "Helper interno de serializacion del grupo hoppscotch: convierte call specs (method/url/headers/body/body_type) en el formato HoppRESTRequest/coleccion Hoppscotch (request v:2). Lo usan hoppscotch_create_request y hoppscotch_update_request para construir el campo request de las mutations GraphQL del self-host. NO uses el dict resultante para escribir un .json e importarlo a mano: el flujo canonico es operar el self-host por la API (ver docs/capabilities/hoppscotch.md). Pura: solo stdlib, sin red."
|
||||
tags: [hoppscotch, flow-replay, http, infra, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
params:
|
||||
- name: calls
|
||||
desc: "lista de call specs (tipicamente la salida de har_extract_calls). Cada elemento es un dict con claves opcionales: method (str), url (str), headers (dict name->value), cookies (dict name->value), body (str|None), body_type (json|form|raw|None). Otras claves como status o sets_cookies se ignoran."
|
||||
- name: name
|
||||
desc: "nombre de la coleccion Hoppscotch resultante. Default 'Collection'."
|
||||
- name: request_names
|
||||
desc: "nombres explicitos por request, alineados por indice con calls. Si se pasa y existe el indice, sobreescribe el nombre derivado. None = derivar todos como '<METHOD> <path>'."
|
||||
output: "dict de coleccion Hoppscotch JSON-serializable: {\"v\": 1, \"name\", \"folders\": [], \"requests\": [...]}. Cada request lleva v:'2', endpoint, name, method (upper), headers (lista key/value/active con header Cookie inyectado si habia cookies), body (contentType+body segun body_type), auth none, params/requestVariables vacios."
|
||||
tested: true
|
||||
tests:
|
||||
- "test_golden_get_simple"
|
||||
- "test_edge_post_json_con_headers_y_cookies"
|
||||
- "test_request_names_sobreescribe_nombre_derivado"
|
||||
- "test_form_body_genera_contenttype_urlencoded"
|
||||
- "test_raw_body_genera_contenttype_text_plain"
|
||||
- "test_body_type_desconocido_da_body_null"
|
||||
- "test_lista_vacia"
|
||||
- "test_call_spec_sin_url_ni_method"
|
||||
- "test_sin_cookies_no_anade_header_cookie"
|
||||
test_file_path: "python/functions/infra/build_hoppscotch_collection_test.py"
|
||||
file_path: "python/functions/infra/build_hoppscotch_collection.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import json
|
||||
from build_hoppscotch_collection import build_hoppscotch_collection
|
||||
|
||||
# Call specs tal cual salen de har_extract_calls.
|
||||
calls = [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "https://api.example.com/api/search?q=foo",
|
||||
"headers": {"Accept": "application/json"},
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "https://api.example.com/login",
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"cookies": {"session": "abc", "csrf": "xyz"},
|
||||
"body": '{"user":"neo","pass":"<<password>>"}',
|
||||
"body_type": "json",
|
||||
},
|
||||
]
|
||||
|
||||
collection = build_hoppscotch_collection(calls, name="Example flow")
|
||||
# {
|
||||
# "v": 1,
|
||||
# "name": "Example flow",
|
||||
# "folders": [],
|
||||
# "requests": [
|
||||
# {
|
||||
# "v": "2",
|
||||
# "endpoint": "https://api.example.com/api/search?q=foo",
|
||||
# "name": "GET /api/search",
|
||||
# "params": [],
|
||||
# "headers": [{"key": "Accept", "value": "application/json", "active": True}],
|
||||
# "method": "GET",
|
||||
# "auth": {"authType": "none", "authActive": True},
|
||||
# "preRequestScript": "",
|
||||
# "testScript": "",
|
||||
# "body": {"contentType": None, "body": None},
|
||||
# "requestVariables": [],
|
||||
# },
|
||||
# {
|
||||
# "v": "2",
|
||||
# "endpoint": "https://api.example.com/login",
|
||||
# "name": "POST /login",
|
||||
# "params": [],
|
||||
# "headers": [
|
||||
# {"key": "Content-Type", "value": "application/json", "active": True},
|
||||
# {"key": "Cookie", "value": "session=abc; csrf=xyz", "active": True},
|
||||
# ],
|
||||
# "method": "POST",
|
||||
# "auth": {"authType": "none", "authActive": True},
|
||||
# "preRequestScript": "",
|
||||
# "testScript": "",
|
||||
# "body": {"contentType": "application/json", "body": '{"user":"neo","pass":"<<password>>"}'},
|
||||
# "requestVariables": [],
|
||||
# },
|
||||
# ],
|
||||
# }
|
||||
|
||||
# Listo para escribir a disco e importar en la app Desktop / hopp CLI.
|
||||
with open("flow.collection.json", "w") as f:
|
||||
json.dump(collection, f, indent=2)
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Usala cuando quieras abrir en la GUI de Hoppscotch unas peticiones que ya grabaste y destilaste con el patron grabar->destilar->reproducir (HAR -> har_filter_flows -> har_extract_calls). Pasas las call specs por esta funcion, guardas el dict resultante como `.json` y lo importas en la app Desktop o con el CLI `hopp`. Es la salida amigable para humanos del flujo de replay: cuando prefieras inspeccionar/tocar las peticiones a mano en el GUI antes de promoverlas a una funcion-accion del registry con http_replay_sequence. La funcion inversa, parse_hoppscotch_collection, reimporta una coleccion editada en el GUI de vuelta a call specs.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Genera el formato canonico estable v1/v2.** La coleccion sale como `v:1` y cada request como `v:"2"` — la forma de los fixtures oficiales de Hoppscotch, garantizada importable. Hoppscotch la migra automaticamente a su ultima version interna al importar; no intentes emitir la version "ultima" a mano.
|
||||
- **Las cookies se inyectan como header Cookie.** Si un call spec trae `cookies` no vacio, se anade un unico header `Cookie` al final con formato `k1=v1; k2=v2`. Hoppscotch no tiene un slot de cookies separado en el request, asi que viajan en headers; al reimportar con parse_hoppscotch_collection se vuelven a separar.
|
||||
- **Los secretos NO se sustituyen.** La funcion copia headers, cookies y body tal cual. Tokens de sesion, `Authorization` y contrasenas viajan en claro en el dict resultante. Si quieres parametrizar, es el caller quien debe marcar los valores con `<<var>>` (referencia a variable de environment de Hoppscotch) antes de llamar a esta funcion. NO commitear el `.json` resultante sin redactar.
|
||||
- **Claves extra del call spec se ignoran.** `status`, `sets_cookies` y cualquier otra clave que no sea method/url/headers/cookies/body/body_type no aparecen en la coleccion (son metadata de la captura, no del request a reproducir).
|
||||
Reference in New Issue
Block a user