Ir al contenido

Adjuntos de resultado

Un resultado (un intento de un caso de ejecución) puede llevar hasta 20 archivos de casi cualquier tipo: capturas de pantalla, trazas de Playwright, vídeos, logs. Súbelos en dos pasos: prepara los archivos y luego confirma la lista de adjuntos del resultado. Un adjunto pertenece al resultado completo o, con stepIndex, a uno de los pasos que un reporte de CI envió con él (Adjuntar a un paso reportado); para las imágenes que un tester añade a un paso del caso mientras ejecuta, consulta Adjuntos de resultado de ejecución.

El acceso requiere member+ en el proyecto de la ejecución; un viewer recibe 403 forbidden. Las escrituras siguen la misma regla de ejecución cerrada que el resto de escrituras de resultados: se permiten en una ejecución open, y en una ejecución closed no abortada mientras la configuración de ejecución allowResultsInClosedRuns del proyecto resuelva a true (el valor por defecto). Una ejecución aborted siempre responde 409 conflict. Cuando el proyecto reserva los resultados al asignado (assigneeResultLock), quien no es el tester asignado recibe 403 run_case_assignee_locked; los tokens de API nunca están exentos.

  1. Obtén el resultado. Un reporte de CI contesta cada entrada emparejada con su resultUlid (results[i].resultUlid de POST /api/v1/projects/{projectId}/reports). GET /api/v1/runs/{runUlid}/results lista todos los resultados de una ejecución.
  2. Preparar — POST /api/v1/runs/{runUlid}/results/{resultUlid}/attachments:stage con multipart/form-data y una o más partes file. Devuelve una referencia preparada por archivo. Todavía no se adjunta nada ni se cobra almacenamiento.
  3. Confirmar — PATCH /api/v1/runs/{runUlid}/results/{resultUlid}/attachments con la lista completa del resultado, cada elemento con su position. Devuelve la lista confirmada.
  4. Leer — GET /api/v1/runs/{runUlid}/results/{resultUlid} devuelve resultAttachments, ordenados por position: los archivos confirmados y las imágenes de los textos del resultado (ver la sección Imágenes en el texto).

Un archivo preparado que nunca se confirma caduca por sí solo (unas 48 horas). Por eso reintentar una preparación es seguro; las peticiones de preparación ignoran la cabecera Idempotency-Key. La confirmación y el borrado sí la aceptan.

Mantén el cuerpo completo de la petición en 64 MiB como máximo (ATTACHMENT_UPLOAD_REQUEST_RECOMMENDED_MAX_BYTES) y reparte los lotes más grandes en varias peticiones. Cloudflare rechaza un cuerpo de más de 100 MB con 413 antes de que llegue a la API, permitan lo que permitan los límites por archivo y por petición; ese 413 no lleva el envoltorio de error de la API.

Ventana de terminal
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-F "file=@screenshot.png" \
-F "file=@trace.zip" \
"https://app.probara.net/api/v1/runs/$RUN/results/$RESULT/attachments:stage"
{
"attachments": [
{
"ulid": "01J...ATT1",
"objectKey": "staging/01J...ORG/01J...UPLOAD1.webp",
"thumbKey": "staging/01J...ORG/01J...UPLOAD1_thumb.webp",
"mime": "image/webp",
"byteSize": 8420,
"originalFilename": "screenshot.png",
"disposition": "inline",
"width": 1280,
"height": 720
},
{
"ulid": "01J...ATT2",
"objectKey": "staging/01J...ORG/01J...UPLOAD2.zip",
"thumbKey": null,
"mime": "application/zip",
"byteSize": 1048576,
"originalFilename": "trace.zip",
"disposition": "attachment",
"width": null,
"height": null
}
]
}

Límites:

  • Hasta 20 partes file por petición. Los archivos vacíos se rechazan.
  • Cualquier tipo salvo ejecutables y scripts (application/x-msdownload, application/x-msdos-program, application/x-sh, application/x-bat, application/x-msi, application/x-executable, application/vnd.microsoft.portable-executable), comprobado contra el tipo de contenido declarado de cada parte.
  • Las imágenes PNG, JPEG y WebP (según el tipo de contenido declarado) se convierten a WebP con miniatura y vuelven con disposition: "inline". Tienen un límite de 10 MiB y 8192 px por lado.
  • Cualquier otro archivo se guarda tal cual, hasta 32 MiB, con disposition: "attachment".

La primera parte inválida rechaza la petición entera con 422 validation_failed: no vuelve ninguna referencia, ni siquiera de las partes anteriores (esas caducan como cualquier otro archivo preparado sin referenciar). El message del error es empty_file, denied_type o file_too_large, que también cubre una imagen que supera el límite de píxeles o cuya cabecera no se puede leer. Valida los archivos antes de subirlos, o reintenta los archivos restantes de uno en uno por petición para que un archivo malo no retenga a los demás.

Un cliente que sube evidencias antes de registrar el resultado puede preparar contra el caso de ejecución: POST /api/v1/runs/{runUlid}/cases/{runCaseUlid}/result-attachments:stage. Acepta el mismo cuerpo, tiene los mismos límites y devuelve las mismas referencias. Rechaza cualquier ejecución cerrada con 409 conflict, sea cual sea la configuración. Confirma las referencias en el resultado cuando exista.

El cuerpo es la lista completa de adjuntos del resultado. Los archivos confirmados que falten se borran, así que reenvía cada archivo existente como { ulid, position } para conservarlo. Un elemento nuevo es una referencia preparada más su position.

{
"attachments": [
{ "ulid": "01J...KEEP", "position": 0 },
{
"ulid": "01J...ATT2",
"position": 1,
"objectKey": "staging/01J...ORG/01J...UPLOAD2.zip",
"mime": "application/zip",
"byteSize": 1048576,
"originalFilename": "trace.zip",
"disposition": "attachment",
"thumbKey": null,
"width": null,
"height": null
}
]
}
  • Un elemento nuevo necesita al menos ulid, position, objectKey y originalFilename; lo más sencillo es enviar la referencia preparada tal cual más position.
  • Un resultado admite como máximo 20 adjuntos. La confirmación es todo o nada: una confirmación rechazada no cambia nada.
  • { "attachments": [] } elimina todos los adjuntos del resultado.
  • Un elemento puede llevar stepIndex para adjuntar el archivo a un paso reportado; ver abajo.
  • Un token de aplicación solo puede añadir: un cuerpo que deja fuera un adjunto que el resultado ya tiene responde 403 forbidden y no cambia nada. Un token de aplicación no puede preparar contra el caso de ejecución ni eliminar un adjunto.
  • Los bytes confirmados cuentan para la cuota de almacenamiento de la organización (categoría result_attachment); los preparados no. Una confirmación cuyo crecimiento superaría la cuota responde 413 storage_quota_exceeded.

Un resultado registrado por un reporte de CI puede llevar los pasos que ejecutó (los steps de la entrada). Para adjuntar un archivo a uno de ellos, añade al elemento stepIndex: el índice del paso en un recorrido en profundidad y en preorden de los pasos tal como se enviaron, desde 0 — la position del paso en resultSteps de la instantánea del intento.

{
"attachments": [
{ "ulid": "01J...KEEP", "position": 0 },
{
"ulid": "01J...SHOT",
"position": 1,
"stepIndex": 3,
"objectKey": "staging/01J...ORG/01J...UPLOAD3.webp",
"originalFilename": "pay.png"
}
]
}
stepIndex en el elementoElemento nuevoAdjunto existente
OmitidoSe adjunta al resultadoConserva su paso actual
nullSe adjunta al resultadoVuelve al resultado
Un númeroSe adjunta a ese pasoSe mueve a ese paso
  • El índice debe nombrar un paso de este resultado. Si no, toda la confirmación responde 422 validation_failed y no se escribe nada.
  • Los adjuntos de pasos cuentan para el mismo límite de 20 archivos por resultado.
  • La respuesta de la confirmación, resultAttachments de la instantánea del intento y el detalle del caso de la ejecución devuelven el stepIndex de cada adjunto (null para un archivo del resultado). Reenvíalo tal cual al confirmar de nuevo la lista, u omítelo para conservarlo.
Ventana de terminal
curl -sS -X DELETE \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://app.probara.net/api/v1/runs/$RUN/results/$RESULT/attachments/$ATT"

204 si tiene éxito. Borrar un adjunto que ya no existe también responde 204.

EstadoCódigoCuándo
403forbiddenRol viewer; o un token de aplicación cuya confirmación eliminaría un adjunto, o que llama a una operación distinta de preparar y confirmar a nivel de resultado
403run_case_assignee_lockedEl proyecto reserva los resultados al asignado y quien llama no es el tester asignado (operaciones sobre el resultado)
403project_lockedEl proyecto está bloqueado
404not_foundEjecución, resultado o caso de ejecución desconocido, o de otra organización
409conflictLa ejecución está abortada, o cerrada con allowResultsInClosedRuns a false; la preparación sobre el caso rechaza toda ejecución cerrada
409staging_already_committedSolo confirmación: una referencia preparada ya se confirmó
409project_archivedEl proyecto está archivado
413—Solo preparación: el cuerpo de la petición supera los 100 MB; Cloudflare lo rechaza antes de que llegue a la API, sin el envoltorio de error
413storage_quota_exceededSolo confirmación: se superaría la cuota de almacenamiento de la organización
415unsupported_media_typeSolo confirmación: el cuerpo no es application/json
422validation_failedPreparación: sin parte file, demasiados archivos, un archivo vacío o demasiado grande, una imagen que supera el límite de píxeles, o un tipo denegado; una parte inválida rechaza la petición entera. Confirmación: cuerpo inválido, más de 20 adjuntos, una referencia preparada inválida o caducada, o un stepIndex que no nombra ningún paso del resultado
429too_many_requestsEl límite de peticiones de API de la organización; espera Retry-After segundos

La lista resultAttachments también incluye las imágenes insertadas en los textos del resultado, junto con sus archivos. Cada imagen tiene kind image y los campos de una subida (ulid, mime, width, height, byteSize, createdAt), más field, que indica en qué texto está:

  • { "kind": "description" }, { "kind": "precondition" } o { "kind": "postcondition" } para los textos del caso que el intento congeló con su encabezado.
  • { "kind": "step_actual_result", "runCaseStepUlid": "01J...", "position": 2 } para el resultado real de un paso.
  • { "kind": "notes" } para las notas del resultado.

Los archivos conservan kind file. Una imagen sale de la lista cuando sale de su texto.