Ir al contenido

API de archivo de proyectos

Cada proyecto tiene una marca de tiempo archivedAt (epoch en ms, o null para un proyecto activo) — un estado de ciclo de vida reversible, no una eliminación suave. {projectId} es el código del proyecto (por ejemplo ACME) en todo lo que sigue.

Archivar hace que el contenido de un proyecto sea de solo lectura, mientras su administración sigue siendo accesible:

  • Cada ruta existente sigue siendo totalmente legible: la página de detalle, las suites, casos, ejecuciones, planes, hitos, defectos, adjuntos y exportaciones de un proyecto archivado siguen funcionando exactamente igual que en un proyecto activo. GET /api/v1/projects/{projectId} sigue devolviendo 200, con un archivedAt no nulo.
  • Cada escritura sobre el contenido es rechazada con 409 { code: "project_archived" } — ver Las escrituras de contenido se rechazan mientras está archivado más abajo.
  • No libera un asiento de proyecto. Una organización en su projectLimit que archiva un proyecto sigue siendo rechazada al crear uno nuevo con 409 { code: "project_limit_exceeded" } — eliminar sigue siendo la única forma de liberar un asiento.

Cambio disruptivo: la lista muestra solo activos por defecto

Sección titulada «Cambio disruptivo: la lista muestra solo activos por defecto»

GET /api/v1/projects ahora oculta los proyectos archivados por defecto. Este es un cambio deliberado a un contrato público ya publicado: un integrador que sondea este endpoint empieza a recibir menos filas en cuanto algo se archiva en esa organización. Es observacionalmente inerte el día en que se publica, porque ningún proyecto puede archivarse hasta que esta capacidad exista — pero surte efecto de inmediato en cuanto tú (o alguien de tu organización) archiva un proyecto.

Pasa el parámetro de consulta opcional status para cambiar lo que devuelve la lista:

Valor de statusResultado
ausente, vacío o solo tokens desconocidosSolo activos — el nuevo valor por defecto
activeSolo activos (igual que el valor por defecto)
archivedSolo archivados
active,archived (en cualquier orden)Ambos — el comportamiento previo completo

status es un conjunto de tokens separados por comas, igual que los filtros runs/defects/ milestones/cases que ya existen en este endpoint. Se combina con la paginación, q, memberUlid y cualquier otro filtro, y el total devuelto siempre coincide con el filtro aplicado (nunca un conteo fantasma de una consulta sin filtrar).

Ventana de terminal
# Solo lo que cambió con el flip: proyectos archivados
curl -sS -H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://app.probara.net/api/v1/projects?status=archived"
# Todo, exactamente como antes de este cambio
curl -sS -H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://app.probara.net/api/v1/projects?status=active,archived"

Cualquier otro consumidor de la lista de proyectos hereda este mismo valor por defecto sin ningún trabajo extra de tu parte: el directorio web, el selector de proyectos y cualquier otro lector interno resuelven a solo-activos a menos que también pasen status explícitamente.

POST/api/v1/projects/{projectId}/archive

Sin cuerpo de solicitud. Devuelve 200 con el proyecto actualizado (ver Acceso a proyectos para el recurso hermano; este es el objeto de proyecto plano con su campo archivedAt).

{
"ulid": "01J...PROJECT",
"name": "Acme",
"id": "ACME",
"description": null,
"avatarKey": null,
"avatarVersion": 0,
"createdAt": 1700000000000,
"updatedAt": 1700000005000,
"archivedAt": 1700000005000
}

Idempotente: archivar un proyecto ya archivado responde 200 sin cambiar la marca de tiempo almacenada — un doble clic o una solicitud reintentada se comportan igual que una sola llamada, y este endpoint nunca responde 409.

Misma restricción que PATCH/DELETE sobre un proyecto: solo un propietario o administrador de la organización puede archivar. Un member o viewer es rechazado con 403 forbidden, dejando archivedAt sin cambios. Un {projectId} desconocido, o uno que pertenece a otra organización, responde 404 not_found.

POST/api/v1/projects/{projectId}/unarchive

Sin cuerpo de solicitud. Devuelve 200 con el proyecto actualizado, archivedAt: null. Mismas reglas de idempotencia y autorización que archivar, en espejo: desarchivar un proyecto que no está archivado tiene éxito y deja archivedAt como null.

Ventana de terminal
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://app.probara.net/api/v1/projects/ACME/archive"

Las escrituras de contenido se rechazan mientras está archivado

Sección titulada «Las escrituras de contenido se rechazan mientras está archivado»

El contenido de un proyecto archivado queda congelado. Cualquier escritura sobre él — crear una suite, actualizar un caso de prueba, iniciar una ejecución, guardar el resultado de una ejecución, vincular un defecto, mutar un plan de pruebas, escribir el valor de un campo personalizado, crear un comentario, subir un adjunto o un avatar, y cualquier otra mutación de contenido con alcance de proyecto — es rechazada:

{
"error": {
"code": "project_archived",
"message": "project is archived"
}
}

Estado 409. Esto aplica tanto si la escritura apunta directamente al proyecto (/api/v1/projects/{projectId}/...) como si apunta a una entidad alcanzada por su propio ULID (/api/v1/test-cases/{ulid}, /api/v1/runs/{ulid}, etc.) — el rechazo se aplica donde vive el dato, no donde apunta la URL, así que no existe ruta que lo evite. Una subida de adjunto rechazada de esta forma nunca llega al almacenamiento: no queda ningún objeto parcial u huérfano. Esto aplica a todo tipo de credencial, incluido un token de API de la organización.

El orden del rechazo es fijo: un proyecto que no existe (o pertenece a otra organización) responde 404 antes de consultar el estado de archivado; un llamador sin acceso a un proyecto privado responde 403; solo un llamador que de otro modo habría sido admitido llega al 409. El estado de archivado nunca filtra la existencia de un proyecto que el llamador no puede ver.

Este es un cambio disruptivo a un contrato público ya publicado, y a diferencia del cambio de valor por defecto de la lista anterior, no es observacionalmente inerte: cualquier proyecto archivado antes de este cambio empieza a rechazar escrituras en el momento en que se publica.

Estas operaciones administran el proyecto en sí, no su contenido, y siguen funcionando mientras el proyecto está archivado:

OperaciónRuta
DesarchivarPOST /api/v1/projects/{projectId}/unarchive
EliminarDELETE /api/v1/projects/{projectId}
Archivar (re-archivado idempotente)POST /api/v1/projects/{projectId}/archive
Actualizar (renombrar/redescribir)PATCH /api/v1/projects/{projectId}
Gestión de acceso (modo, membresía, propiedad)PATCH /api/v1/projects/{projectId}/access y sus subrutas de miembro/grupo

El avatar del proyecto es contenido, no administración, y no está exento: POST/DELETE /api/v1/projects/{projectId}/avatar se rechazan con 409 como cualquier otra escritura.

Una escritura rechazada con 409 project_archived bajo una Idempotency-Key se registra como cualquier otra respuesta por debajo de 500, así que un reintento idéntico dentro del TTL de la clave repite el mismo 409 registrado — incluso después de que el proyecto haya sido desarchivado. Envía una clave nueva y sin usar para obtener el resultado posterior al desarchivado. Ver Idempotencia para el contrato general.

La eliminación definitiva (DELETE /api/v1/projects/{projectId}) no tiene relación con archivar y sigue siendo la única vía de eliminación irreversible. Eliminar no libera espacio de almacenamiento — la imagen del proyecto y cada adjunto de paso descendiente sobreviven a la cascada. Esto es preexistente, está fuera del alcance aquí, y se rastrea como su propio cambio futuro.