From ff03041cd6aaeb94777b433cdb6bfec7118e4860 Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Mon, 15 Jun 2026 11:56:16 +0000 Subject: [PATCH] docs: contract RAR actualizat cu T1 verificat live (postPrezentare) - WAF cere User-Agent pe toate apelurile (altfel 403); fara UA -> blocat - format eroare validare: data=[{field,message}], 3 mesaje exacte capturate - raspuns success live: data.id=68514, idPrezentare==id, idAgent server-side - sistemReparat="null" acceptat, b64Image/odometruInitial omise OK - odometruFinal string -> intors numar; camp extra listaPrestatii - Open Q #5 (mesaje eroare) + WAF inchise Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/api-rar-contract.md | 90 +++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/docs/api-rar-contract.md b/docs/api-rar-contract.md index c84b70c..0ca1576 100644 --- a/docs/api-rar-contract.md +++ b/docs/api-rar-contract.md @@ -33,6 +33,17 @@ se face din cod / Postman cu credențialele de test. > **calea reală e `/nomenclator/getNomenclatorPrestatii`** (cea din VFP). `/nomenclator/getPrestatiiNom` > întoarce 403 — nu o folosi. operationId ≠ path. +## ⚠️ Header obligatoriu pe TOATE apelurile: `User-Agent` + +WAF-ul RAR întoarce **`403 Forbidden` („Request forbidden by administrative rules")** +la orice request **fără header `User-Agent` de browser** — inclusiv `/public/login` și +chiar `swagger-ui`. curl/clienți fără UA = blocați la WAF înainte de a ajunge la aplicație. +MSXML2.XMLHTTP din VFP trimite un UA implicit, de aceea VFP-ul merge. + +→ Gateway-ul (`app/rar_client.py`) **trebuie** să seteze `User-Agent` pe fiecare apel +(ex. `Mozilla/5.0`). Confirmat live 2026-06-15: fără UA → 403; cu UA + creds greșite → 401; +cu UA + creds corecte → 200. + ## Autentificare `POST /public/login` body `{"email": "...", "password": "..."}` → 200, JSON cu câmpuri: @@ -85,34 +96,71 @@ Câmpurile marcate OPTIONAL pot lipsi; restul sunt obligatorii. `tipPrestatie` * | `obs` | NU (OPTIONAL) | text liber | | `b64Image` | NU (OPTIONAL) | poza odometrului; **nu mai e obligatorie**; dacă se atașează, trebuie format base64 valid | -### postPrezentare — răspuns (success) +### postPrezentare — răspuns (success) — VERIFICAT LIVE 2026-06-15 + +Răspuns real pe contul nostru de test (record creat `data.id=68514`): ```json { "statusCode": 200, "message": "Prezentare adaugata cu succes", "data": { - "id": 59950, - "dataPrestatie": "2024-07-25", - "vin": "...", - "odometruFinal": 9999999, - "idAgent": 1587, + "id": 68514, + "dataPrestatie": "2026-06-15", + "vin": "WVWZZZ1KZAW000123", + "odometruFinal": 123456, + "idAgent": 40, "tipPrestatie": "GENERIC", "odometruInitial": null, - "idUser": 22, + "idUser": 6766, "sistemReparat": "null", - "obs": "TEST", - "nrInmatriculare": "B999GEN", + "obs": "TEST GATEWAY", + "nrInmatriculare": "B999TST", + "listaPrestatii": null, "status": "FINALIZATA", - "prestatii": [ { "idPrezentare": 599950, "codPrestatie": "OE-1" }, ... ], - "b64Image": "..." + "prestatii": [ + { "idPrezentare": 68514, "codPrestatie": "OE-1" }, + { "idPrezentare": 68514, "codPrestatie": "OE-2" } + ], + "b64Image": null } } ``` - `data.id` = ID-ul prezentării la RAR (de reținut ca `idPrezentare` în submission). +- **`prestatii[].idPrezentare == data.id`** (live: ambele 68514). Exemplul vechi sugera un + număr separat mai mare (599950 vs 59950) — **fals**, e același id. +- `idAgent` = **atribuit de server** (login a întors `null`, răspunsul are `40`). +- `odometruFinal` trimis ca string `"123456"` → **întors ca număr** `123456` (server normalizează). +- `sistemReparat:"null"` acceptat; `b64Image` omis → `null`; `odometruInitial:null` OK. +- Câmp extra nedocumentat în răspuns: **`listaPrestatii: null`** (prezent lângă `prestatii`). - `tipPrestatie` = `"GENERIC"` — **generat de server**, nu input client. -- Validările eșuate sunt raportate **în răspunsul API** (mesaj de eroare) când nu sunt respectate constrângerile. + +### postPrezentare — răspuns (eroare validare) — VERIFICAT LIVE 2026-06-15 + +HTTP **400**. `data` este un **ARRAY** de `{field, message}` (NU string), un element per eroare: + +```json +{ + "statusCode": 400, + "message": "Validare eșuată pentru cererea de prezentare.", + "data": [ + { "field": "vin", "message": "VIN trebuie să aibă exact 17 caractere, fara spatii sau caractere speciale, litere invalide : O, I, Q." }, + { "field": "vin", "message": "VIN conține caractere invalide, spatii, sau O, I, Q." } + ] +} +``` + +Mesaje exacte capturate live (de mapat în UI/loguri gateway): + +| Constrângere încălcată | field | message (exact) | +|---|---|---| +| VIN cu O/I/Q | `vin` | `VIN trebuie să aibă exact 17 caractere, fara spatii sau caractere speciale, litere invalide : O, I, Q.` + `VIN conține caractere invalide, spatii, sau O, I, Q.` | +| dataPrestatie < 2024-12-01 | `dataPrestatie` | `Data prestatiei nu poate fi anterioara datei de 01.12.2024.` | +| dataPrestatie în viitor | `dataPrestatie` | `Data prestatiei nu poate fi în viitor.` | + +→ Gateway: parsează `data` ca listă de erori de câmp (nu citi `data.message`). Pe success `data` +e obiect; pe 400 `data` e array — discriminează după `statusCode`/HTTP code. ## Reguli de validare (server-side RAR, de aplicat și în gateway) @@ -201,14 +249,18 @@ Aplicate deja pe ambele medii (test + producție): 2. `sistemReparat` — ce valori reale acceptă (în afară de `"null"`). De probat la postPrezentare test. 3. Un singur user RAR per agent economic sau mai mulți (`idUser`/`idAgent` — afectează filtrarea monitorizării). 4. ~~JWT TTL~~ — **închis: 30h.** Decizie nouă: simplifică worker-ul; reconsideră necesitatea re-push ROAAUTO. -5. Comportamentul exact al răspunsului de eroare la fiecare constrângere (de capturat la primul postPrezentare test). +5. ~~Comportamentul răspunsului de eroare~~ — **închis** (format `data:[{field,message}]`, mesaje capturate mai sus). +6. WAF cere `User-Agent` — **închis/confirmat** (vezi secțiunea de sus). -## Rămas de verificat live (postPrezentare real pe test) +## ~~Rămas de verificat live (postPrezentare real pe test)~~ — ✅ FĂCUT 2026-06-15 -Login + nomenclator + JWT TTL = ✅ făcute. Mai rămâne **un singur POST real pe test** ca să confirmi: -- mesajele de eroare exacte pentru fiecare constrângere (VIN cu O/I/Q, dată în afara intervalului etc.); -- valoarea `data.id` întoarsă și forma exactă a răspunsului pe contul nostru; -- acceptarea `sistemReparat: "null"` și omiterea `b64Image`/`odometruInitial`. -(Creează un record pe mediul de test — nu pe producție.) +Login + nomenclator + JWT TTL + **postPrezentare** = ✅ toate verificate live. +Record de test creat: `data.id = 68514` (FINALIZATA, permanent pe test). Confirmat: +- mesajele de eroare exacte (VIN O/I/Q, dată prea veche, dată viitoare) — vezi tabelul de erori; +- forma răspunsului success pe contul nostru + `data.id`; +- `sistemReparat:"null"` acceptat, `b64Image`/`odometruInitial` omise OK; +- header `User-Agent` obligatoriu (altfel 403 WAF). + +Rămas neprobat: ce alte valori `sistemReparat` (în afară de `"null"`) acceptă (Open Q #2).