8d320edfa7
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
4.9 KiB
Bash
Executable File
100 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Pruebas de SOLO LECTURA contra la instancia real de Jira (FAC) — Bloques 2,
|
||
# 3 (inventario), 4 y 5 de Investigacion-API-Jira.md. Ninguna llamada muta nada.
|
||
#
|
||
# Requisitos: el token en propuesta/token-jira.txt (gitignoreado) o en $JIRA_TOKEN.
|
||
# Uso: ./pruebas-jira-lectura.sh [directorio_salida]
|
||
# La salida VA FUERA DEL REPO por default (los tickets traen datos reales de
|
||
# clientes): /tmp/jira-probes-<fecha>. Al repo solo se llevan los hallazgos
|
||
# agregados, transcritos a mano a Investigacion-API-Jira.md.
|
||
set -euo pipefail
|
||
|
||
SITE="https://balam-jsm-temp.atlassian.net"
|
||
EMAIL="pedro.ayala@balamtalentoestrategico.com"
|
||
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||
TOKEN="${JIRA_TOKEN:-$(cat "$REPO_DIR/propuesta/token-jira.txt" 2>/dev/null || true)}"
|
||
if [ -z "$TOKEN" ]; then
|
||
echo "ERROR: token no encontrado (propuesta/token-jira.txt o \$JIRA_TOKEN)." >&2
|
||
exit 1
|
||
fi
|
||
|
||
OUT="${1:-/tmp/jira-probes-$(date +%Y%m%d-%H%M%S)}"
|
||
mkdir -p "$OUT"
|
||
AUTH="$EMAIL:$TOKEN"
|
||
|
||
req() { # req <nombre> <ruta> — guarda body y headers, imprime status + rate limit
|
||
local name="$1" path="$2"
|
||
local hdr="$OUT/$name.headers" body="$OUT/$name.json"
|
||
local status
|
||
status=$(curl -sS -u "$AUTH" -H "Accept: application/json" \
|
||
-D "$hdr" -o "$body" -w "%{http_code}" "$SITE$path")
|
||
local rate
|
||
rate=$(grep -i "x-ratelimit" "$hdr" | tr -d '\r' | paste -sd' ' - || true)
|
||
echo "[$status] $name ${rate:-(sin headers de rate limit)}"
|
||
}
|
||
|
||
echo "== Bloque 0 · auth =="
|
||
req myself "/rest/api/3/myself"
|
||
|
||
echo "== Bloque 4 · búsqueda vigente, fields acotados y tope de maxResults =="
|
||
req search-recientes "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC ORDER BY updated DESC"))')&maxResults=10&fields=summary,status,updated,created,issuetype,reporter"
|
||
req search-tope "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC"))')&maxResults=5000&fields=id"
|
||
req search-delta "/rest/api/3/search/jql?jql=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("project = FAC AND updated >= \"-20m\" ORDER BY updated ASC"))')&fields=updated"
|
||
|
||
echo "== Bloque 5 · origen de los tickets (changelog de los 8 más recientes) =="
|
||
python3 - "$OUT" <<'PY'
|
||
import json, sys, subprocess, os
|
||
out = sys.argv[1]
|
||
data = json.load(open(f"{out}/search-recientes.json"))
|
||
keys = [(i["id"], i["key"]) for i in data.get("issues", [])][:8]
|
||
open(f"{out}/ids-muestra.txt", "w").write("\n".join(f"{i} {k}" for i, k in keys))
|
||
print("muestra:", ", ".join(k for _, k in keys))
|
||
PY
|
||
while read -r id key; do
|
||
req "issue-$key" "/rest/api/3/issue/$id?expand=changelog&fields=summary,status,issuetype,created,reporter,attachment,description,resolution"
|
||
done < "$OUT/ids-muestra.txt"
|
||
|
||
echo "== Bloque 3 · transiciones con campos obligatorios (2 tickets de la muestra) =="
|
||
head -2 "$OUT/ids-muestra.txt" | while read -r id key; do
|
||
req "transitions-$key" "/rest/api/3/issue/$id/transitions?expand=transitions.fields"
|
||
done
|
||
|
||
echo "== Bloque 2 · request types faltantes (12 Facturación, 389 Automatización) =="
|
||
req requesttype-12 "/rest/servicedeskapi/servicedesk/35/requesttype/12/field"
|
||
req requesttype-389 "/rest/servicedeskapi/servicedesk/35/requesttype/389/field"
|
||
|
||
echo "== Resumen para transcribir a Investigacion-API-Jira.md =="
|
||
python3 - "$OUT" <<'PY'
|
||
import json, sys, glob, os
|
||
out = sys.argv[1]
|
||
|
||
def load(n):
|
||
try: return json.load(open(f"{out}/{n}.json"))
|
||
except Exception: return None
|
||
|
||
tope = load("search-tope")
|
||
if tope: print(f"- maxResults efectivo con 5000 pedidos: {len(tope.get('issues', []))} issues devueltos; isLast={tope.get('isLast')}")
|
||
delta = load("search-delta")
|
||
if delta is not None: print(f"- delta 20m: {len(delta.get('issues', []))} tickets actualizados en la ventana")
|
||
|
||
moves = 0
|
||
for f in sorted(glob.glob(f"{out}/issue-*.json")):
|
||
d = load(os.path.basename(f)[:-5])
|
||
if not d: continue
|
||
key = d.get("key", "?")
|
||
fields = d.get("fields", {})
|
||
hist = (d.get("changelog") or {}).get("histories", []) or (d.get("changelog") or {}).get("values", [])
|
||
key_changes = [i for h in hist for i in h.get("items", []) if i.get("field") in ("Key", "project")]
|
||
if key_changes: moves += 1
|
||
att = fields.get("attachment") or []
|
||
desc = fields.get("description")
|
||
desc_len = len(json.dumps(desc)) if desc else 0
|
||
total_hist = (d.get("changelog") or {}).get("total", len(hist))
|
||
print(f"- {key}: type={ (fields.get('issuetype') or {}).get('name') } · status={(fields.get('status') or {}).get('name')} · "
|
||
f"resolution={(fields.get('resolution') or {}).get('name') if fields.get('resolution') else None} · "
|
||
f"adjuntos={len(att)} · descripcion_adf={desc_len}B · changelog(total={total_hist}, en_expand={len(hist)})"
|
||
f"{' · ⚠️ MOVE/KEY-CHANGE detectado' if key_changes else ''}")
|
||
print(f"- tickets con move/cambio de llave en la muestra: {moves}")
|
||
print(f"\nBodies completos en: {out} (NO copiar al repo: contienen datos reales)")
|
||
PY
|