Files
adam-acs/.kiro/steering/feature-references-client.md
T
Kevin L'Hereec e857b4fbe7 feat: reference providers system with Wassup integration
- Pluggable multi-provider architecture (ReferenceEngine)
- Wassup provider implementation (HTTP GET + X_WASSUP_USER_IP)
- Auto-resolve references on Inform (BOOT/BOOTSTRAP events)
- References displayed in device Identity card
- REST API: GET/POST references per device
- Config via config/references.json
- Fix event extraction for single EventStruct and numeric EventCodes
- Mock Wassup provisioned by home-device-simulator on device creation
2026-06-02 15:52:16 +02:00

8.7 KiB

Feature : Références Client — Système de Providers Pluggable

Contexte

Dans adam-acs, on veut un système de références externes (informations client) associées aux devices. Inspiré de Wassup (karma-com) mais conçu comme un système de plugins permettant de brancher N fournisseurs de données.

Principe : N Providers

Chaque device peut être enrichi par plusieurs providers indépendants. Chaque provider est un service externe interrogé via HTTP qui retourne des paires clé-valeur.

                  ┌─────────────────────────────────────┐
                  │           adam-acs                    │
                  │                                     │
                  │  DeviceRecord                       │
                  │    └─ references: [                 │
  Inform ──────►  │         { provider: "wassup", ... } │
                  │         { provider: "crm", ... }    │
                  │         { provider: "noc", ... }    │
                  │       ]                             │
                  │                                     │
                  │  ReferenceEngine                    │
                  │    ├─ WassupProvider                │───► Wassup (HTTP)
                  │    ├─ CRMProvider                   │───► CRM API
                  │    └─ NOCProvider                   │───► NOC API
                  └─────────────────────────────────────┘

Architecture

Interface Provider

Chaque provider implémente une interface commune :

interface ReferenceProvider {
  /** Identifiant unique du provider (ex: "wassup", "crm") */
  name: string

  /** Résout les références pour un device. Retourne null si non applicable. */
  resolve(device: DeviceRecord, context: InformContext): Promise<ResolvedReference | null>

  /** Indique si ce provider est activé */
  enabled: boolean
}

interface ResolvedReference {
  provider: string
  resolvedAt: Date
  resolvedBy: string              // ex: "ip:10.0.0.1"
  properties: Record<string, string>
  status: 'ok' | 'unknown' | 'error'
  error?: string
}

interface InformContext {
  events: string[]
  remoteIp: string
  headers: Record<string, string>
}

ReferenceEngine (orchestrateur)

class ReferenceEngine {
  private providers: ReferenceProvider[] = []

  register(provider: ReferenceProvider): void
  
  /** Résout tous les providers pour un device (en parallèle) */
  async resolveAll(device: DeviceRecord, context: InformContext): Promise<ResolvedReference[]>

  /** Résout un seul provider par nom */
  async resolveOne(providerName: string, device: DeviceRecord, context: InformContext): Promise<ResolvedReference | null>
}

Modèle de données (MongoDB)

// Ajouté à DeviceRecord
interface DeviceRecord {
  // ... champs existants ...
  references?: DeviceReference[]
}

interface DeviceReference {
  provider: string
  resolvedAt: Date
  resolvedBy: string
  status: 'ok' | 'unknown' | 'error'
  properties: Record<string, string>
  error?: string
}

Configuration : config/references.json

Fichier de config qui déclare les providers actifs et leur paramétrage :

{
  "providers": [
    {
      "name": "wassup",
      "type": "wassup",
      "enabled": true,
      "url": "http://wassup-mock:9999",
      "timeout": 3000,
      "trigger": ["BOOT", "BOOTSTRAP"],
      "resolveBy": "ip",
      "requestedProperties": ["sai", "spr", "clo", "puid", "utz"]
    },
    {
      "name": "crm",
      "type": "http-json",
      "enabled": false,
      "url": "http://crm-api:8080/api/customers",
      "timeout": 5000,
      "trigger": ["BOOT"],
      "resolveBy": "serialNumber",
      "mapping": {
        "requestParam": "serial",
        "responseMapping": {
          "customerId": "id",
          "customerName": "name",
          "offer": "subscription.offer"
        }
      }
    }
  ]
}

Types de providers supportés

Type Description Protocole
wassup Protocole Wassup (HTTP GET + headers + text/plain response) Conforme au format Wassup Orange
http-json API REST standard (GET avec query params, réponse JSON) Générique
http-properties HTTP GET avec réponse key=value (Java properties) Générique
static Valeurs statiques déclarées dans la config (pas d'appel externe) Local

Champ resolveBy — Comment identifier le device auprès du provider

Valeur Source dans le device Exemple
ip Header x-forwarded-for de l'Inform ou IP source 10.0.0.1
serialNumber device.deviceId.serialNumber LB4-TEST-001
oui device.deviceId.oui F8B7FF
mac Paramètre MACAddress du device F8:B7:FF:00:11:22
parameter:<path> Valeur d'un paramètre du device Device.ManagementServer.Username

Champ trigger — Quand déclencher la résolution

Valeur Événement
BOOT Event "1 BOOT"
BOOTSTRAP Event "0 BOOTSTRAP"
PERIODIC Event "2 PERIODIC"
SCHEDULED Event "3 SCHEDULED"
CONNECTION_REQUEST Event "6 CONNECTION REQUEST"
MANUAL Uniquement via API (pas automatique)

Intégration dans le flux Inform

// Dans cwmp_controller.ts → handleInform(), après persist device :
const informContext: InformContext = { events, remoteIp: request.ip(), headers: request.headers() }
const references = await referenceEngine.resolveAll(device, informContext)
if (references.length > 0) {
  await DeviceModel.updateReferences(hashId, references)
}

API REST

Méthode Route Description
GET /api/v1/devices/:id/references Toutes les références du device
GET /api/v1/devices/:id/references/:provider Références d'un provider spécifique
POST /api/v1/devices/:id/references/resolve Déclencher la résolution (tous les providers)
POST /api/v1/devices/:id/references/resolve/:provider Résoudre un seul provider
PUT /api/v1/devices/:id/references/:provider Mettre à jour manuellement
DELETE /api/v1/devices/:id/references/:provider Supprimer les références d'un provider

UI (DeviceDetail.vue)

Nouvelle section collapsible "Références" dans la page device :

┌─────────────────────────────────────────────────┐
│ Références (2 providers)                    ▾   │
├─────────────────────────────────────────────────┤
│ 🟢 wassup — résolu le 02/06/2026 via ip:10.0.0.1│
│   SAI: 088913491                                │
│   SPR: EN                                       │
│   CLO: pppLB.user                               │
│   [Rafraîchir]                                  │
│                                                 │
│ ⚪ crm — non résolu (désactivé)                 │
└─────────────────────────────────────────────────┘

Fichiers à créer/modifier dans adam-acs

Fichier Action
config/references.json Créer — configuration des providers
app/domain/device/device_record.ts Modifier — ajouter references?: DeviceReference[]
app/services/references/reference_engine.ts Créer — orchestrateur
app/services/references/providers/wassup_provider.ts Créer — provider Wassup
app/services/references/providers/http_json_provider.ts Créer — provider HTTP JSON générique
app/services/references/types.ts Créer — interfaces et types
app/controllers/references_controller.ts Créer — API REST
app/controllers/cwmp_controller.ts Modifier — appel ReferenceEngine dans handleInform
inertia/pages/DeviceDetail.vue Modifier — section Références
start/routes.ts Modifier — ajouter routes references

Mock Wassup (gogowassupgo)

Déjà implémenté et testé. Le mock tourne dans Docker (wassup-mock:9999) et est provisionné automatiquement par le simulateur lors de la création de devices.

  • Protocole : identique à Wassup (HTTP GET + X_WASSUP_USER_IP)
  • Provisioning : API /admin/entries (POST/GET/DELETE)
  • Stockage : data/entries.json
  • Modes d'erreur : --error http-500, --error-status KO, --delay 2s