API de comentarios en casos de prueba
Los comentarios de casos de prueba se vinculan a un caso específico usando un almacén genérico de comentarios con clave (entity_type='test_case', entity_id), compartido con los comentarios de defectos — ambas superficies son estructuralmente idénticas y solo difieren en la ruta de la entidad. Los comentarios están delimitados a la organización a través del ULID del caso de prueba.
Los comentarios admiten un nivel de respuestas en hilo, fijado y resolución de menciones @. Consulta Adjuntos en comentarios para el flujo de adjuntos de imágenes.
Endpoints
Sección titulada «Endpoints»| Método | Ruta | Descripción |
|---|---|---|
GET | /api/v1/test-cases/{caseUlid}/comments | Listar comentarios de primer nivel (paginación por cursor) |
POST | /api/v1/test-cases/{caseUlid}/comments | Agregar un comentario, o una respuesta usando parentUlid |
PATCH | /api/v1/test-cases/{caseUlid}/comments/{commentUlid} | Editar un comentario o respuesta |
DELETE | /api/v1/test-cases/{caseUlid}/comments/{commentUlid} | Eliminar un comentario (elimina en cascada sus respuestas) |
POST | /api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pin | Fijar un comentario de primer nivel |
DELETE | /api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pin | Desfijar un comentario de primer nivel |
{caseUlid} debe pertenecer a la organización activa. El acceso cruzado entre organizaciones devuelve 404 not_found.
Autorización
Sección titulada «Autorización»Todos los endpoints requieren una sesión autenticada o un token Bearer con al menos acceso viewer.
| Operación | Rol mínimo |
|---|---|
GET | Visualizador |
POST (comentario o respuesta) | Miembro |
PATCH | Miembro (propio comentario únicamente) |
DELETE | Miembro (propio) o Administrador/Propietario (cualquiera) |
POST / DELETE .../pin | Miembro (cualquier comentario de primer nivel) |
GET — listar comentarios
Sección titulada «GET — listar comentarios»/api/v1/test-cases/{caseUlid}/commentsDevuelve comentarios de primer nivel (parentUlid: null) con paginación por cursor.
Parámetros de consulta
Sección titulada «Parámetros de consulta»| Parámetro | Tipo | Descripción |
|---|---|---|
cursor | string | Cursor ULID de la página anterior (nextCursor). |
limit | integer | Tamaño de página. Predeterminado 50, máximo 200. |
sort | oldest | newest | Orden de los comentarios de primer nivel. Predeterminado oldest (sin cambios respecto al comportamiento público previo). Las respuestas siempre se mantienen en orden cronológico bajo su comentario padre, sin importar este valor. |
Respuesta (200 OK)
Sección titulada «Respuesta (200 OK)»{ "items": [ { "ulid": "01J…", "body": "Reproducido en staging con una sesión nueva del navegador.", "author": { "ulid": "01J…", "displayName": "Ada Lovelace", "avatarKey": null, "avatarVersion": 0 }, "createdAt": 1700000000000, "editedAt": null, "attachments": [], "parentUlid": null, "pinnedAt": null, "pinnedBy": null, "mentions": [], "replyCount": 1, "replies": [ { "ulid": "01J…RPLY", "body": "Confirmado — mismo resultado de mi lado. @[01J…GRACE]", "author": { "ulid": "01J…", "displayName": "Grace Hopper", "avatarKey": null, "avatarVersion": 0 }, "createdAt": 1700000100000, "editedAt": null, "attachments": [], "parentUlid": "01J…", "pinnedAt": null, "pinnedBy": null, "mentions": [{ "ulid": "01J…GRACE", "displayName": "Grace Hopper" }] } ] } ], "nextCursor": "01J…", "pinned": []}authoresnullcuando el miembro fue removido de la organización.editedAtesnullcuando el comentario nunca fue editado.nextCursoresnullen la última página.pinnedes el conjunto completo de comentarios de primer nivel fijados para este caso de prueba, ordenado porpinnedAtascendente, independiente del cursor paginado deitems— represéntalo por encima del feed. Es idéntico sin importar el valor desort.- Un objeto de respuesta nunca lleva
replyCountnireplies— la profundidad del hilo está limitada a un nivel. - El arreglo
repliesde un elemento de primer nivel está en orden cronológico ascendente y limitado a 100 entradas en línea.
Importante: las respuestas no aparecen en la enumeración plana de items[] — solo son accesibles a través de replies[] de su comentario padre. Este es un cambio aditivo pero no compatible byte a byte con clientes que asumían que items[] enumeraba todos los comentarios: itera replies en cada elemento si necesitas el contenido de las respuestas, y usa replyCount si solo necesitas un conteo.
POST — agregar un comentario o respuesta
Sección titulada «POST — agregar un comentario o respuesta»/api/v1/test-cases/{caseUlid}/commentsCuerpo de la solicitud
Sección titulada «Cuerpo de la solicitud»| Campo | Requerido | Notas |
|---|---|---|
body | sí | Cadena no vacía, máximo 5000 caracteres. Los valores que solo contienen espacios son rechazados. Puede contener tokens de mención @[ulid] (ver Menciones). |
parentUlid | no | ULID de un comentario de primer nivel existente en el mismo caso de prueba. Crea una respuesta de profundidad 1. Omítelo para crear un comentario de primer nivel (comportamiento sin cambios). |
attachments | no | Ver Adjuntos en comentarios. |
Errores:
422 validation_failed—bodyestá vacío, contiene solo espacios, supera los 5000 caracteres, oparentUlidapunta a un comentario que es en sí mismo una respuesta (responder a una respuesta es rechazado).404 not_found—parentUlidno resuelve a un comentario de primer nivel en este caso de prueba (entidad incorrecta u organización incorrecta).403— rol visualizador.
Respuesta (201 Created)
Sección titulada «Respuesta (201 Created)»{ "ulid": "01J…", "body": "Se ve bien en móvil también.", "author": { "ulid": "01J…", "displayName": "Ada", "avatarKey": null, "avatarVersion": 0 }, "createdAt": 1700000000000, "editedAt": null, "attachments": [], "parentUlid": null, "pinnedAt": null, "pinnedBy": null, "mentions": [], "replyCount": 0, "replies": []}Auditoría: emite test_case.commented con metadata.excerpt (primeros 240 caracteres del cuerpo).
PATCH — editar un comentario
Sección titulada «PATCH — editar un comentario»/api/v1/test-cases/{caseUlid}/comments/{commentUlid}Solo el autor original del comentario puede editarlo (exclusivo del autor, ya sea de primer nivel o respuesta). Los administradores y propietarios no pueden editar el comentario de otro miembro. Las menciones se resuelven de nuevo a partir del cuerpo actualizado en cada edición.
Cuerpo de la solicitud
Sección titulada «Cuerpo de la solicitud»| Campo | Requerido | Notas |
|---|---|---|
body | sí | Cuerpo de reemplazo, misma validación que POST. |
attachments | no | Lista completa de adjuntos deseada; ver Adjuntos en comentarios. |
Respuesta (200 OK)
Sección titulada «Respuesta (200 OK)»CommentResponse actualizado con editedAt establecido en el timestamp actual y mentions[] reflejando el nuevo cuerpo.
Errores: 403 si no es el autor. 404 si el comentario no existe en este caso (caseUlid incorrecto / org incorrecta).
Auditoría: emite test_case.comment_updated con metadata.excerpt.
DELETE — eliminar un comentario
Sección titulada «DELETE — eliminar un comentario»/api/v1/test-cases/{caseUlid}/comments/{commentUlid}Los autores pueden eliminar su propio comentario. Los administradores y propietarios pueden eliminar cualquier comentario. Eliminar un comentario de primer nivel elimina en cascada todas sus respuestas, purgando los adjuntos de cada respuesta en cascada y reembolsando el almacenamiento antes de eliminar las filas.
Respuesta (204 No Content)
Sección titulada «Respuesta (204 No Content)»Errores: 403 si el solicitante es un miembro que intenta eliminar el comentario de otro miembro.
Auditoría: emite test_case.comment_deleted para el comentario eliminado, más un test_case.comment_deleted adicional por cada respuesta eliminada en cascada.
POST — fijar un comentario
Sección titulada «POST — fijar un comentario»/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pinFija un comentario de primer nivel para que siempre aparezca en el arreglo pinned[] de la respuesta de listado, mostrado antes del feed paginado sin importar sort. Cualquier miembro o rol superior puede fijar cualquier comentario de primer nivel (no solo el propio). No hay límite en la cantidad de comentarios fijados por caso de prueba.
Errores: 422 validation_failed si commentUlid corresponde a una respuesta — solo los comentarios de primer nivel pueden fijarse. 403 para el rol visualizador. 404 si el comentario no existe en este caso.
Respuesta (200 OK)
Sección titulada «Respuesta (200 OK)»Comentario actualizado con pinnedAt (epoch ms) y pinnedBy (resumen del actor que lo fijó) establecidos.
Auditoría: emite test_case.comment_pinned.
DELETE — desfijar un comentario
Sección titulada «DELETE — desfijar un comentario»/api/v1/test-cases/{caseUlid}/comments/{commentUlid}/pinLimpia pinnedAt/pinnedBy. Cualquier miembro o rol superior puede desfijar.
Respuesta (200 OK)
Sección titulada «Respuesta (200 OK)»Comentario actualizado con pinnedAt: null, pinnedBy: null.
Errores: 403 para el rol visualizador. 404 si el comentario no existe en este caso.
Auditoría: emite test_case.comment_unpinned.
Menciones
Sección titulada «Menciones»El cuerpo de un comentario puede incluir tokens @[ulid] que referencian a un miembro de la organización por su ULID (la forma ULID, no una cadena de nombre visible, para que renombrar a alguien nunca rompa una mención). Al crear o editar, el servidor:
- Extrae cada token
@[ulid]del cuerpo. - Resuelve cada token únicamente contra la membresía de la organización activa — un ulid de otra organización, o uno que no resuelve a un miembro, se deja silenciosamente como texto plano sin estilo y no crea ninguna fila de mención.
- Persiste una fila por cada mención válida distinta, y reconcilia el conjunto en cada edición (las menciones eliminadas se limpian, las nuevas se agregan).
El arreglo mentions[] de la respuesta siempre refleja el nombre visible actual del miembro mencionado — renombrar a un miembro actualiza toda mención pasada que lo referencie. Las menciones nunca disparan una notificación ni un correo.
Eliminación en cascada
Sección titulada «Eliminación en cascada»Cuando se elimina un caso de prueba (DELETE /api/v1/test-cases/{caseUlid}), todos sus comentarios y respuestas se eliminan primero, junto con sus adjuntos. No se requiere ninguna llamada API adicional; la cascada es atómica dentro del manejador de eliminación.
Ejemplos
Sección titulada «Ejemplos»# Listar la página de comentarios más recientes primerocurl -sS \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/test-cases/01J...CASE/comments?sort=newest"
# Agregar un comentario de primer nivelcurl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"body":"Verificado en Safari 17 — aprueba."}' \ "https://probara.net/api/v1/test-cases/01J...CASE/comments"
# Responder a un comentario de primer nivel, mencionando a un compañerocurl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"body":"Gracias @[01J...GRACE] — confirmado también de mi lado.","parentUlid":"01J...CMT"}' \ "https://probara.net/api/v1/test-cases/01J...CASE/comments"
# Fijar un comentariocurl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT/pin"
# Desfijar un comentariocurl -sS -X DELETE \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT/pin"
# Editar un comentariocurl -sS -X PATCH \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"body":"Nota actualizada luego de re-probar."}' \ "https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT"
# Eliminar un comentariocurl -sS -X DELETE \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ "https://probara.net/api/v1/test-cases/01J...CASE/comments/01J...CMT"