---
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).
