Référence
API
Breakout Status Webhook

Breakout Status Webhook

Endpoint standalone permettant à des systèmes externes d'interroger l'état du breakout pour un ou plusieurs dictionnaires CSWeb.

Fichier : breakout-status-webhook.php (déployé à la racine de CSWeb)


Authentification

Cet endpoint utilise un Bearer token statique (indépendant d'OAuth2), configuré via la variable d'environnement BREAKOUT_WEBHOOK_TOKEN.

Authorization: Bearer <token>

Configuration du token :

# .env (à la racine du projet)
BREAKOUT_WEBHOOK_TOKEN=<32+ octets aléatoires>

Important : la variable BREAKOUT_WEBHOOK_TOKEN est obligatoire. Si elle n'est pas définie, l'endpoint répond 500 server_misconfigured. Aucune valeur par défaut n'est codée dans les webhooks.

Pour générer un token solide :

openssl rand -hex 32

Endpoint

GET /breakout-status-webhook.php

Paramètres de requête

ParamètreTypeDescription
dictionarystringFiltrer par nom exact d'un dictionnaire (ex: KATS_DICT)
dictionariesstringFiltrer par plusieurs dictionnaires séparés par virgule (limité à 100 entrées)
cron_enabledbooleantrue = seulement ceux avec cron actif
breakout_configuredbooleantrue = seulement ceux avec breakout configuré
pageintegerNuméro de page (défaut: 1)
limitintegerRésultats par page, max 100 (défaut: 20)

Exemples de requêtes

Tous les dictionnaires (paginé)

curl -X GET "http://localhost:8080/breakout-status-webhook.php" \
  -H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"

Un seul dictionnaire

curl -X GET "http://localhost:8080/breakout-status-webhook.php?dictionary=KATS_DICT" \
  -H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"

Plusieurs dictionnaires

curl -X GET "http://localhost:8080/breakout-status-webhook.php?dictionaries=KATS_DICT,EVAL_DICT" \
  -H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"

Uniquement ceux avec breakout configuré et cron actif

curl -X GET "http://localhost:8080/breakout-status-webhook.php?breakout_configured=true&cron_enabled=true" \
  -H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"

Pagination : page 2, 50 résultats

curl -X GET "http://localhost:8080/breakout-status-webhook.php?page=2&limit=50" \
  -H "Authorization: Bearer $BREAKOUT_WEBHOOK_TOKEN"

Format de réponse

Toutes les réponses des webhooks suivent le contrat unifié :

{ "success": true,  "data": ... | null, "error": null,             "meta": {...}? }
{ "success": false, "data": null,        "error": { "code": "...", "message": "..." } }

Succès (200 OK)

{
  "success": true,
  "data": [
    {
      "dictionary": "KATS_DICT",
      "label": "Enquête KATS 2025",
      "breakout_configured": true,
      "target_host": "host.docker.internal:5433",
      "target_schema": "kats_breakout",
      "db_type": "postgresql",
      "cron_enabled": true,
      "cron_expression": "*/30 * * * *",
      "last_run": "2026-04-19 08:30:00",
      "next_run": "2026-04-19 09:00:00",
      "last_exit_code": 0,
      "total_cases": 250,
      "processed_cases": 248,
      "cases_pending": 2,
      "completion_rate": 99.2,
      "is_up_to_date": false,
      "last_processed_time": "2026-04-19 08:29:45"
    },
    {
      "dictionary": "EVAL_DICT",
      "label": "Évaluation terrain",
      "breakout_configured": false,
      "target_host": null,
      "target_schema": null,
      "db_type": null,
      "cron_enabled": false,
      "cron_expression": null,
      "last_run": null,
      "next_run": null,
      "last_exit_code": null,
      "total_cases": 120,
      "processed_cases": null,
      "cases_pending": null,
      "completion_rate": null,
      "is_up_to_date": null,
      "last_processed_time": null
    }
  ],
  "error": null,
  "meta": {
    "total": 3,
    "page": 1,
    "pages": 1,
    "limit": 20
  }
}

Description des champs

meta (pagination)

ChampTypeDescription
totalintegerNombre total de dictionnaires
pageintegerPage courante
pagesintegerNombre total de pages
limitintegerTaille de page utilisée

Items de data

ChampTypeDescription
dictionarystringNom du dictionnaire (ex: KATS_DICT)
labelstringLibellé du dictionnaire
breakout_configuredbooleantrue si une DB cible est configurée
target_hoststring | nullHôte de la DB cible (host:port)
target_schemastring | nullNom de la base/schéma cible
db_typestring | nullType de DB cible : postgresql, mysql, sqlserver
cron_enabledbooleantrue si le scheduler automatique est activé
cron_expressionstring | nullExpression cron du scheduler (ex: */30 * * * *)
last_runstring | nullDernière exécution du cron (YYYY-MM-DD HH:MM:SS)
next_runstring | nullProchaine exécution planifiée
last_exit_codeinteger | nullCode de sortie du dernier cron (0 = succès, -1 = en cours)
total_casesintegerNombre de questionnaires actifs dans CSWeb (source)
processed_casesinteger | nullQuestionnaires présents dans la DB cible (null si inaccessible)
cases_pendinginteger | nulltotal_cases - processed_cases (questionnaires non encore breakés)
completion_ratefloat | nullTaux de complétion en % (processed_cases / total_cases × 100)
is_up_to_dateboolean | nulltrue si cases_pending === 0
last_processed_timestring | nullHorodatage du dernier job traité dans la DB cible

Note : les champs processed_cases, cases_pending, completion_rate, is_up_to_date et last_processed_time sont null si le breakout n'est pas configuré ou si la DB cible est inaccessible au moment de la requête.


Codes d'erreur

Toutes les erreurs renvoient success: false, data: null et un objet error :

{
  "success": false,
  "data": null,
  "error": { "code": "<identifiant>", "message": "<description humaine>" }
}
HTTPerror.codeCause
401missing_tokenHeader Authorization: Bearer <token> absent ou mal formé
401invalid_tokenToken reçu ne correspond pas à BREAKOUT_WEBHOOK_TOKEN
405method_not_allowedVerbe HTTP autre que GET
429rate_limited60 requêtes / 60 s dépassées (header Retry-After fourni)
500server_misconfiguredBREAKOUT_WEBHOOK_TOKEN non défini ou config.php absent
500internal_errorErreur DB ou exception non spécifique

Exemple 401 (missing_token) :

{
  "success": false,
  "data": null,
  "error": {
    "code": "missing_token",
    "message": "Missing or invalid Authorization header. Expected: Bearer <token>."
  }
}

Exemple 429 (rate_limited) :

{
  "success": false,
  "data": null,
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after 42 seconds."
  }
}

Interprétation des résultats

Évaluer si un dictionnaire est à jour

is_up_to_date = true   → Tous les questionnaires ont été breakés
is_up_to_date = false  → cases_pending > 0, le breakout a du retard
is_up_to_date = null   → Breakout non configuré ou DB cible inaccessible

Surveiller la santé du cron

cron_enabled = true + last_exit_code = 0   → Cron actif et dernière exécution OK
cron_enabled = true + last_exit_code = -1  → Breakout en cours d'exécution
cron_enabled = true + last_exit_code ≠ 0   → Erreur lors du dernier run
cron_enabled = false                       → Breakout manuel uniquement

Exemple de logique de décision (Python)

import os, requests
 
token = os.environ["BREAKOUT_WEBHOOK_TOKEN"]
response = requests.get(
    "http://csweb.example.com/breakout-status-webhook.php",
    headers={"Authorization": f"Bearer {token}"},
    params={"breakout_configured": "true", "cron_enabled": "true"},
)
payload = response.json()
 
if not payload["success"]:
    err = payload["error"]
    raise SystemExit(f"[{err['code']}] {err['message']}")
 
print(f"Page {payload['meta']['page']}/{payload['meta']['pages']} "
      f"({payload['meta']['total']} dictionnaires)")
 
for d in payload["data"]:
    name = d["dictionary"]
    rate = d["completion_rate"]
    pending = d["cases_pending"]
    exit_code = d["last_exit_code"]
 
    if not d["is_up_to_date"]:
        print(f"[ALERTE] {name}: {pending} questionnaires en attente ({rate}% traités)")
    if exit_code is not None and exit_code != 0 and exit_code != -1:
        print(f"[ERREUR] {name}: dernier cron en échec (exit code {exit_code})")

Limites et garde-fous

  • Body POST max : non applicable (endpoint GET).
  • Rate-limit : 60 requêtes par fenêtre glissante de 60 secondes par couple (token, IP). Au-delà : 429 avec header Retry-After.
  • Liste dictionaries : capée à 100 entrées côté serveur pour borner la requête SQL.

Déploiement

Le fichier breakout-status-webhook.php est déployé à la racine de l'application CSWeb (même niveau que index.php). Il se connecte à la base MySQL de CSWeb via le fichier src/AppBundle/config.php.

Variables d'environnement requises

# docker-compose.yml
environment:
  BREAKOUT_WEBHOOK_TOKEN: "${BREAKOUT_WEBHOOK_TOKEN}"
  CSWEB_ROOT: "/var/www/html"

Générer un token sécurisé

# Linux / macOS
openssl rand -hex 32
 
# PHP
php -r "echo bin2hex(random_bytes(32));"

Ressources


CSWeb Community Platform v2.0 - Breakout Status Webhook Architecte : Bouna DRAME | Portfolio (opens in a new tab)