feat(T3): validare completa prezentari + 29 teste
- app/validation.py: reguli de continut (VIN ^[A-HJ-NPR-Z0-9]{17}$ fara O/I/Q,
nrInm ^[A-Z0-9]{1,10}$, dataPrestatie ∈ [2024-12-01, azi] TZ Bucuresti,
R-ODO/I-ODO -> odometruInitial obligatoriu, odometruInitial<=odometruFinal,
odometruFinal numeric, prestatii nevide, b64Image base64 valid)
- erori structurate {field, message} (aceeasi forma ca raspunsul RAR), fara exceptii
- modele Pydantic: normalizare strip/upper pe vin/nrInm/coduri
- router /v1/prezentari: validare inainte de enqueue; esec continut -> needs_data
(tinut, vizibil in dashboard cu motiv), NU 422; JSON malformat -> 422 (shape)
- tests/: 29 teste (per regula + rutare API + idempotenta)
Verify: pytest 29 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from fastapi import APIRouter, HTTPException
|
||||
from ...db import get_connection
|
||||
from ...idempotency import idempotency_key
|
||||
from ...models import PrezentareRequest, PrezentariResponse, SubmissionResult
|
||||
from ...validation import validate_prezentare
|
||||
|
||||
router = APIRouter(prefix="/v1", tags=["v1"])
|
||||
|
||||
@@ -26,8 +27,9 @@ router = APIRouter(prefix="/v1", tags=["v1"])
|
||||
def create_prezentari(req: PrezentareRequest) -> PrezentariResponse:
|
||||
"""Enqueue una/mai multe prezentari. Idempotent: continut identic -> acelasi submission.
|
||||
|
||||
TODO(T3): validare Pydantic completa inainte de enqueue (VIN/data/nrInm),
|
||||
ruteaza needs_data/needs_mapping.
|
||||
Validarea de continut (T3, app.validation) ruleaza inainte de enqueue:
|
||||
esecurile NU resping cererea, ci enqueue-aza cu status `needs_data` + motiv
|
||||
(plan.md sect. 3). JSON malformat -> 422 din Pydantic (validare de shape).
|
||||
TODO(auth): rezolva account_id din API key (acum None).
|
||||
Nota: rar_credentials NU se persista (zero-storage) — worker-ul le va primi
|
||||
pe alt canal (T2); in schelet enqueue-ul doar stocheaza prezentarea.
|
||||
@@ -53,12 +55,20 @@ def create_prezentari(req: PrezentareRequest) -> PrezentariResponse:
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
# T3: validare de continut -> queued daca e curat, altfel needs_data + motiv.
|
||||
errors = validate_prezentare(content)
|
||||
if errors:
|
||||
status, rar_error = "needs_data", json.dumps(errors, ensure_ascii=False)
|
||||
else:
|
||||
status, rar_error = "queued", None
|
||||
|
||||
cur = conn.execute(
|
||||
"INSERT INTO submissions (idempotency_key, account_id, status, payload_json) "
|
||||
"VALUES (?, ?, 'queued', ?)",
|
||||
(key, account_id, json.dumps(content, ensure_ascii=False)),
|
||||
"INSERT INTO submissions (idempotency_key, account_id, status, payload_json, rar_error) "
|
||||
"VALUES (?, ?, ?, ?, ?)",
|
||||
(key, account_id, status, json.dumps(content, ensure_ascii=False), rar_error),
|
||||
)
|
||||
results.append(SubmissionResult(submission_id=int(cur.lastrowid), status="queued"))
|
||||
results.append(SubmissionResult(submission_id=int(cur.lastrowid), status=status))
|
||||
finally:
|
||||
conn.close()
|
||||
return PrezentariResponse(results=results)
|
||||
|
||||
Reference in New Issue
Block a user