Files
balam/planeacion/Investigacion-API-Jira.md
T
JohannVelazquez 980baf0a0a Documenta la API de Jira FAC y protege su token
Consolida los hallazgos y decisiones pendientes para continuar el desarrollo sin exponer credenciales.
2026-08-01 12:38:39 -06:00

293 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Investigación de la API de Jira — qué probar exactamente
**Fecha:** 30-jul-2026
**Propósito:** lista cerrada de pruebas y preguntas para habilitar la integración **Jira → cotización BIND** (estimada en ~10 h, [REGISTRO #51](../bitacora/REGISTRO.md)). Documento de trabajo interno; sirve también como briefing para el agente/chat especializado en la API de Jira.
**Estado validado (30-jul):**
- Sitio: `https://balam-jsm-temp.atlassian.net`. El token regenerado de Pedro, usado con Basic auth y `pedro.ayala@balamtalentoestrategico.com`, respondió **200** a `GET /rest/api/3/myself`.
- Facturación es el proyecto JSM **`FAC`** (`projectId: 10034`, `serviceDeskId: 35`), de tipo `service_desk` y estilo `classic` (company-managed).
- La búsqueda de FAC devolvió **66 tickets**. La API vigente es `GET /rest/api/3/search/jql`, con paginación por `nextPageToken`; el endpoint clásico `/rest/api/3/search` responde **410 Gone**.
- Estatus confirmados: `Open`, `En proceso de facturación`, `En espera por colaborador`, `En validación nacional`, `En validación extranjera`, `Facturado` y `Cancelado`. No se observó un estatus llamado “Resuelto”.
- Límites de consumo ya aclarados por Pedro (sin tope mensual; 3 límites de velocidad; telemetría solo en headers `X-RateLimit-*`).
- **Pendiente antes de escribir código productivo:** decisión de negocio sobre el estatus disparador, definición de qué request type debe observarse, fuente de monto y conceptos para la cotización, y aprobación explícita de cada prueba de escritura.
---
## Bloque 0 — Acceso autenticado ✅
El 401 del 29-jul quedó explicado por la combinación de token previo y datos de conexión incompletos. Pedro confirmó el correo, regeneró el token y compartió el dominio del sitio.
**Configuración que funcionó:**
```text
GET https://balam-jsm-temp.atlassian.net/rest/api/3/myself
Authorization: Basic base64(pedro.ayala@balamtalentoestrategico.com:<token>)
Accept: application/json
```
El resultado fue **HTTP 200**. Por tanto, para esta integración se usará la API directa del sitio con Basic auth; no hay evidencia de que se requiera una ruta `api.atlassian.com/ex/jira/{cloudId}`.
**Prueba canónica de auth (la primera que debe correrse siempre):**
```
GET /rest/api/3/myself
```
Interpretación de la respuesta:
- **200** → auth OK; además devuelve `accountId` (identidad con la que la plataforma va a escribir — dato de gobierno, ver Bloque 7).
- **401** → credencial inválida/inactiva o esquema mal armado.
- **403** → credencial VÁLIDA pero sin permiso → problema de permisos, no de token. Distinguir 401 de 403 es el diagnóstico más barato que existe.
> ⚠️ **Registrar siempre los headers de la respuesta 401 completos.** Ahí vive la pista (`WWW-Authenticate`, `X-Seraph-LoginReason`, `X-Failure-Category`).
---
## Bloque 1 — Topología: Jira Service Management confirmado ✅
FAC está confirmado como **Jira Service Management (JSM)**. Por ello la integración requiere dos familias de API:
| API | Para qué | Cuándo usarla |
|---|---|---|
| `/rest/api/3/...` | Issues genéricos: JQL, campos, transiciones, adjuntos, changelog | Lectura/sync, transiciones, trazabilidad |
| `/rest/servicedeskapi/...` | Requests de portal: request types, campos del formulario, aprobaciones, SLA, comentarios públicos vs internos | Crear tickets como los crea un humano; leer aprobaciones y SLA |
**Resultados:**
```
GET /rest/api/3/project/FAC
```
`projectTypeKey: service_desk`, `style: classic`, `id: 10034`.
```
GET /rest/servicedeskapi/servicedesk
```
→ FAC corresponde a `serviceDeskId: 35`.
Request types disponibles:
| requestTypeId | Nombre |
|---:|---|
| 84 | Bajas |
| 83 | Facturación adicional |
| 389 | Automatización |
| 12 | Facturación |
Contratos ya consultados:
| Request type | Hallazgo |
|---|---|
| **Bajas** (`84`) | Exige Empresa origen, Summary, Cálculo de Finiquito // VoBo de Finiquito (adjunto), Nombre del Cliente, Nombre del Colaborador, Fecha de la Baja y Motivo de la Baja. |
| **Facturación adicional** (`83`) | Exige Empresa origen, Summary, Cliente Nuevo, Nombre del Cliente, cotización/CSF adjuntos, Tipo de Moneda, Días de Crédito, Periodo de Incidencias, Facturación recurrente y Periodo de recurrencia. Es el contrato más completo, pero no expone monto ni conceptos como campos estructurados. |
| **Automatización** (`389`) | Solo exige Summary. |
| **Facturación** (`12`) | Solo exige Summary. |
**Implicación:** la integración no debe asumir que “Facturación” (`12`) es el formulario origen de datos; `Facturación adicional` (`83`) es el candidato más completo, pero todavía no basta para crear una cotización en BIND. Falta confirmar con Balam qué request type entra en la automatización y si monto/conceptos se leerán de la cotización adjunta o se incorporarán como campos de Jira. Mantener una cotización adjunta como entrada mientras la integración genera otra en BIND puede duplicar el proceso.
---
## Bloque 2 — Modelo de datos del ticket FAC (el mapeo) 🔴
Esto es lo que decide si la integración es de 10 h o de 25. **La pregunta de fondo: ¿los datos de facturación vienen en campos estructurados o en texto libre?** Si vienen en texto libre, hay que negociar campos nuevos con Pedro o meter parsing frágil.
**Lo que la plataforma necesita de cada ticket para armar una cotización en BIND:**
| Dato requerido | Por qué | Qué verificar |
|---|---|---|
| Cliente | Match contra `Clients` de BIND (por RFC o nombre normalizado) | ¿Campo custom? ¿Lista desplegable? ¿Texto libre? ¿Trae RFC? |
| Monto y moneda | Cotización BIND; la cartera **jamás mezcla monedas** | ¿Campo numérico? ¿Viene la moneda separada o embebida en el texto? |
| Nacional vs internacional | Determina PDF+XML (nacional) vs solo PDF (extranjero) | ¿Se deduce del cliente o hay campo/etiqueta? |
| Concepto / descripción | Línea de la cotización | Ver formato (ver ⚠️ ADF abajo) |
| Tipo de solicitud | adicional / recurrente / baja / headhunting / staff augmentation | ¿Request type, issue type, o campo? |
| Folio `FAC-nnn` | Trazabilidad ticket ↔ cotización ↔ factura (persistir en el modelo) | Confirmar formato y estabilidad (ver Bloque 5, ⚠️ moves) |
| Solicitante / área | Auditoría | `reporter` vs `requester` de JSM (no son lo mismo) |
| Adjuntos | Constancia fiscal, Excel de horas (CEMEX), estado de cuenta | Cómo se descargan y con qué auth |
**Resultados de lectura:**
```
GET /rest/api/3/issue/FAC-98?expand=names,renderedFields
```
→ caso real de tipo Bajas: cliente Axians, colaborador, fecha y motivo de baja en campos estructurados; una evidencia adjunta; sin descripción. Los valores de texto enriquecido regresan como ADF.
Sigue pendiente una muestra deliberada de facturación nacional, extranjera, recurrente y el caso ACUNTIA (un ticket con Excel de 4048 facturas, [REGISTRO #55](../bitacora/REGISTRO.md) punto 6). Esto debe confirmar dónde viven monto y conceptos, cómo se usa la cotización adjunta y si el supuesto contractual de un ticket → una cotización se cumple en los casos reales.
> ⚠️ **ADF (Atlassian Document Format).** En Jira Cloud v3, `description` y los comentarios **no son texto plano ni wiki markup: son JSON estructurado**. Hay que decidir ya si se parsea ADF o se pide `expand=renderedFields` (HTML). Y al **escribir** comentarios/descripciones hay que **construir ADF válido**, no mandar un string. Es la trampa que más tiempo cuesta si se descubre tarde.
> ⚠️ **Adjuntos:** confirmar si `content` (URL de descarga) requiere el mismo Basic auth y si redirige. Y aplica la misma regla que con BIND: los adjuntos traen **datos reales de clientes** (constancias, estados de cuenta) — no van a disco del repo ni a documentos.
---
## Bloque 3 — Workflow, estatus y transiciones (el disparador) 🟡
La **regla de negocio** (qué estatus dispara qué) sigue bloqueada por la definición interna de Balam. Pero el **inventario técnico se puede levantar hoy** y es justo lo que hace que esa sesión dure 20 minutos en vez de una hora: llegar con la lista real de estatus en pantalla.
**Inventario confirmado:**
```
GET /rest/api/3/project/FAC/statuses → estatus por tipo de issue, con id y statusCategory
```
| Estatus | ID | Categoría |
|---|---:|---|
| Open | 1 | To Do |
| En proceso de facturación | 10305 | In Progress |
| Facturado | 10306 | Done |
| En espera por colaborador | 10339 | In Progress |
| Cancelado | 10372 | Done |
| En validación nacional | 10635 | In Progress |
| En validación extranjera | 10669 | In Progress |
**Transiciones de lectura confirmadas** (son contextuales al estatus actual):
| Ticket / estatus actual | transitionId | Destino |
|---|---:|---|
| FAC-98 / En proceso de facturación | 6 | En espera por colaborador |
| FAC-98 / En proceso de facturación | 8 | En validación nacional |
| FAC-98 / En proceso de facturación | 9 | En validación extranjera |
| FAC-98 / En proceso de facturación | 2 | Cancelado |
| FAC-91 / En validación nacional | 2 | Cancelado |
| FAC-89 / Cancelado | 12 | Open |
`FAC-97` ya está Facturado y no expone transiciones. Las respuestas consultadas no devolvieron campos obligatorios para las transiciones anteriores. Aun así, no se ejecutará ninguna transición sin la sesión acompañada.
**Diagrama de workflow compartido por Balam:** confirma la ruta completa y los nombres de transición:
```text
Open --[Iniciar facturación]--> En proceso de facturación
En proceso de facturación --[Pausar actividad]--> En espera por colaborador
En espera por colaborador --[Retomar actividad]--> En proceso de facturación
En proceso de facturación --[Validar documentación nacional]--> En validación nacional
En proceso de facturación --[Validar documentación extranjera]--> En validación extranjera
En validación nacional o extranjera --[Facturación completa]--> Facturado
En validación nacional o extranjera --[Rechazar actividad]--> En proceso de facturación
```
El diagrama también contempla cancelación desde los estados de trabajo y reapertura; la API confirmó al menos `Cancelado --[Reabrir Ticket]--> Open` en FAC-89. **No existe un estado “Resuelto” en el workflow.** El diagrama describe el movimiento del ticket, pero no establece en qué transición se debe crear la cotización BIND. Esa regla debe ser confirmada por Balam: la candidata natural es la entrada a `En proceso de facturación` mediante `Iniciar facturación`; `Facturado` es la confirmación final de que la factura se emitió.
⚠️ **Dos trampas conocidas:**
1. **`Facturado` como estatus ≠ campo `resolution`.** En Jira son cosas distintas: un ticket puede estar en Facturado con `resolution: null`, o cerrarse con `resolution: Done`. Hay que confirmar cuál de los dos es la señal confiable de “esta factura sí se emitió”, aunque el workflow muestra que `Facturado` es la salida de `Facturación completa`.
2. **Las transiciones son contextuales**: `/transitions` solo devuelve las salidas del estado actual, y pueden tener **pantallas con campos obligatorios**. Probar con `expand=transitions.fields` para saber si transicionar por API exige llenar algo.
**Changelog = la fuente de la detección.** `expand=changelog` da cada cambio de estatus con timestamp y autor. Es el equivalente en Jira del enfoque ya usado con BIND para detectar pagos por transición (ADR-004): la plataforma detecta el cruce de estado, no lo infiere del estado actual. Verificar que el changelog venga completo o si pagina (`/rest/api/3/issue/{key}/changelog`).
---
## Bloque 4 — Estrategia de sincronización: polling vs webhooks 🟡
El scheduler de la plataforma ya existe (`BackgroundService` + `PeriodicTimer` a 15 min). La pregunta es qué le pega a Jira.
**a) Búsqueda por JQL — validado.**
El endpoint clásico `GET /rest/api/3/search` respondió **410 Gone**. La integración debe usar `GET /rest/api/3/search/jql`, con `nextPageToken`. La lectura de FAC recuperó 66 tickets con ese mecanismo.
JQL de delta a validar:
```
project = FAC AND updated >= "-20m" ORDER BY updated ASC
```
⚠️ **`updated` en JQL tiene granularidad de minuto**, no de segundo → el checkpoint necesita **ventana de solape** (igual que el re-barrido de facturas abiertas de BIND) o se pierden tickets en el borde.
Validar también: `fields=` para pedir solo lo necesario (menos payload, menos puntos), y el tope real de `maxResults`.
**b) Webhooks.** Tres caminos, con dueños distintos:
| Opción | Quién la configura | Nota |
|---|---|---|
| Webhook de sitio (admin) | **Pedro** (requiere admin de Jira) | El más limpio; exige endpoint público |
| **Regla de Jira Automation** con "Send web request" | **Pedro, sin escribir código** | La más realista a corto plazo; se puede acotar a FAC y a un estatus |
| Webhook dinámico vía OAuth app | Desarrollo | **Expiran a los 30 días**, hay que refrescarlos |
**Recomendación:** **arrancar con polling** y dejar el webhook para después. Ningún webhook sirve hasta que exista un endpoint público, y Azure sigue pendiente del lado de Balam. El polling además es reversible y no depende de que Pedro toque su instancia.
**c) Rate limits.** Ya documentados por Pedro. Lo que falta es **verificar en la práctica** que los headers `X-RateLimit-Limit` / `Remaining` / `NearLimit` efectivamente vienen en las respuestas (no todos los endpoints los emiten) y capturar un 429 real si se puede, para confirmar `Retry-After` y `RateLimit-Reason`.
---
## Bloque 5 — La bandeja padre: de dónde nacen realmente los tickets 🟡
Pedro dijo que procesos que **se cierran en RH o Administración General caen en Facturación**. Técnicamente eso puede ser cuatro cosas distintas, y cada una se sincroniza diferente:
| Mecanismo | Cómo detectarlo | Implicación |
|---|---|---|
| **Issue link** (RH-45 relacionado con FAC-89) | `issuelinks` en el issue | Escuchar FAC basta; el link da contexto |
| **Clon / creación por Automation** | `changelog` + campo `creator` (usuario de automation) | Escuchar FAC basta |
| **Subtarea / hijo** | `parent` / `subtasks` | Relevante para ACUNTIA (1 ticket ↔ 40 facturas) |
| **Move entre proyectos** | `changelog` con cambio del campo `project` | 🔴 **La llave del ticket CAMBIA** (RH-45 → FAC-89). Cualquier folio persistido se rompe |
**Prueba:** tomar 510 tickets FAC recientes con `expand=changelog` y clasificar cómo llegaron ahí. Si aparecen moves, **hay que persistir el `id` numérico del issue (inmutable), no solo la llave `FAC-nnn`** — decisión de modelo de datos que conviene tomar antes de escribir la entidad.
**Pregunta ligada, ya identificada el 27-jul:** ¿basta escuchar FAC o hay que rastrear también el origen en RH/AG?
---
## Bloque 6 — Escritura (CRUD): qué probar, cómo aprobarlo y dónde 🔴
Noe autorizó una prueba de creación **en vivo y acompañada**. La recomendación es conservar esa condición: el sitio es producción y el token efectivo tiene permisos para crear, editar, transicionar, comentar, adjuntar e incluso borrar tickets en FAC.
**Protocolo obligatorio para cualquier prueba de escritura:**
1. Preparar un script idempotente y de una sola operación; sin bucles, sin búsquedas masivas y sin credenciales embebidas.
2. Compartir el script y el payload de ejemplo con Pedro/Arturo antes de la sesión; documentar endpoint, ticket destino, efecto esperado y reversión.
3. Acordar el ticket de prueba y una ventana de ejecución. Preferir un proyecto sandbox (`FACTEST`) o un ticket creado expresamente para la prueba.
4. Ejecutarlo acompañado, registrar el `HTTP status`, el `issue.id`/`key` y verificar el resultado por API.
5. No probar `DELETE`; no es una capacidad necesaria para la plataforma y no es reversible en producción.
**Operaciones a validar, en este orden (de menos a más invasivo):**
1. **Comentar**`POST /rest/api/3/issue/{key}/comment` (cuerpo en ADF).
⚠️ En JSM hay **comentarios públicos (los ve el cliente) vs internos**: `POST /rest/servicedeskapi/request/{id}/comment` con `public: true|false`. **La plataforma debe escribir SIEMPRE internos.** Publicar por error un comentario visible al cliente es el error más caro y menos reversible de este bloque.
2. **Adjuntar**`POST /rest/api/3/issue/{key}/attachments`.
⚠️ Exige el header `X-Atlassian-Token: no-check` y `multipart/form-data`. Sin ese header falla con un error que no explica nada. Es la vía para dejar el PDF/XML como evidencia en el ticket.
3. **Transicionar**`POST /rest/api/3/issue/{key}/transitions` con el `transition.id` del Bloque 3.
4. **Crear** — decisión real de diseño: `POST /rest/api/3/issue` (crudo, se salta el request type y el ticket queda "raro" en el portal) vs `POST /rest/servicedeskapi/request` (respeta request type y se ve como uno normal). **Si es JSM, la segunda es la correcta.**
5. **Editar**`PUT /rest/api/3/issue/{key}`.
6. **Borrar**`DELETE /rest/api/3/issue/{key}`. Requiere permiso de admin de proyecto. **Probablemente ni se tenga ni convenga tenerlo**; en producción el borrado no debe ser una capacidad de la plataforma.
> 🔴 **Dónde probar — la recomendación más importante de este documento.**
> La instancia de Pedro es **producción**, igual que BIND, y ya se pagó el costo de esa restricción una vez. Dos salidas, no excluyentes:
> 1. **Pedir a Pedro un proyecto de pruebas** (p. ej. `FACTEST`) que replique el workflow de FAC. Barato para él, elimina el riesgo.
> 2. **Crear un sitio propio y gratuito de Jira Cloud** (plan Free, hasta 10 usuarios) con un proyecto JSM. Sirve para desarrollar el cliente, el parser de ADF, la paginación y las transiciones **sin tocar nada de Balam** ni depender de que respondan. Es el sandbox que BIND nunca tuvo. Contra producción solo se corre lectura y, al final, la prueba acompañada.
>
> Esta segunda opción sigue siendo útil para desarrollar el cliente y probar ADF sin tocar datos de Balam. El 401 ya está resuelto, pero el riesgo de escribir en producción permanece.
---
## Bloque 7 — Gobierno y seguridad 🟡
- 🔴 **`propuesta/token-jira.txt` NO está en `.gitignore`** (aparece como no rastreado, no como ignorado): un `git add .` lo commitea al historial. Agregarlo al `.gitignore` y mover el token a user-secrets / Key Vault. Mismo pendiente que arrastra `bind_token_api.txt`.
- **Cuenta de servicio vs cuenta personal de Pedro** (ya planteado en el borrador de correo del 29-jul): hoy todo lo que la plataforma escriba en Jira queda firmado con el nombre de Pedro, y una rotación suya deja la integración muerta. Verificar con `GET /rest/api/3/myself` con qué identidad se está actuando.
- 🔴 **Alcance de permisos:** validado con `GET /rest/api/3/mypermissions` para FAC. El token puede navegar, crear, editar, transicionar, comentar, adjuntar y borrar tickets. Es un alcance excesivo para producción; la integración no debe implementar borrado y conviene migrar a una cuenta de servicio con permisos mínimos.
- **Vigencia 27-jul-2027** → dejar registrado el vencimiento y el plan de rotación desde ahora.
- **Datos de clientes en adjuntos y descripciones** — misma regla que BIND: nunca a disco del repo ni a documentos compartidos.
---
## Bloque 8 — Preguntas para Pedro / Balam (no se resuelven con la API)
Estas van por correo o en la siguiente sesión; ninguna se contesta probando:
1. **¿Puede crear un proyecto de pruebas `FACTEST`** con el mismo workflow de FAC?
2. **¿Quién puede crear webhooks o reglas de Automation** apuntando a una URL nuestra, y bajo qué proceso?
3. **¿Debe observarse/automatizarse `Facturación adicional` (83), `Facturación` (12) u otro request type?** El 83 contiene campos estructurados útiles; el 12 solo exige Summary.
4. **¿Cuál estatus dispara la cotización y cuál confirma la factura?** Los nombres reales y las transiciones de salida ya se conocen, pero la regla de negocio no.
5. **¿De dónde deben obtenerse monto y conceptos?** No existen como campos estructurados en el request type 83; definir si se extraen del adjunto o se agregan a Jira.
6. **PUE vs PPD** — la pregunta que no se ha alcanzado a hacer en tres sesiones ([REGISTRO #52](../bitacora/REGISTRO.md)).
---
## Orden de ataque sugerido
| # | Acción | Bloqueado por |
|---|---|---|
| 1 | Blindar el token (`.gitignore` + secrets) | — |
| 2 | Completar la muestra de tickets y el mapeo a BIND | Definición del caso de negocio |
| 3 | Implementar lectura con `/search/jql`, cursor y ventana de solape | — |
| 4 | Preparar y compartir el script de escritura mínimo para revisión | Definición de flujo de Balam |
| 5 | Levantar un sandbox Jira propio o `FACTEST` | Apoyo de Pedro si se usa FACTEST |
| 6 | Ejecutar una única prueba de escritura acompañada | Aprobación, script revisado y ticket de prueba |
**Lo que puede avanzar ya:** el cliente de lectura, manejo de ADF, paginación por cursor, mapeo de campos y detección incremental. La escritura queda deliberadamente fuera del flujo automático hasta que haya sandbox o una sesión de validación acompañada.
---
## Briefing corto para desarrollo / revisión técnica
> Contexto validado: Jira Cloud JSM en `balam-jsm-temp.atlassian.net`; proyecto FAC company-managed (`projectId: 10034`, `serviceDeskId: 35`). La autenticación directa con Basic `email:token` funciona. El token tiene permisos de escritura amplios, pero **no se debe ejecutar ningún POST, PUT o DELETE fuera de una prueba acompañada y aprobada**.
>
> Para la capa de lectura, usar `GET /rest/api/3/search/jql`, paginar por `nextPageToken` y aplicar una ventana de solape al checkpoint de `updated`. No usar `/rest/api/3/search`: responde 410.
>
> Para JSM, usar `/rest/servicedeskapi` al consultar request types y su contrato de campos. FAC tiene los request types Bajas (84), Facturación adicional (83), Automatización (389) y Facturación (12). Bajas y Facturación adicional ya tienen su contrato verificado; el segundo incluye cliente, moneda, crédito, periodo y recurrencia, pero no monto ni conceptos estructurados.
>
> Pendientes técnicos: revisar changelog y origen de tickets; muestrear tickets nacionales, extranjeros y recurrentes; y diseñar un script de escritura de una sola operación para revisión previa. El script debe ser idempotente, no incluir credenciales y ejecutarse únicamente en sandbox o acompañado en un ticket de prueba acordado.