feat(openapi): declare the full response contract (401/404/409/413) on every v1 route
Honors portability safeguard (c) -- the committed docs/openapi-v1.json was
enforced to stay in sync with app.openapi() (test_openapi_committed.py),
but the app declared almost no error responses: POST /v1/emissoes showed
only 200/422 (no 201), and no route declared 401/404/409/413 or the
structured 409 codes the spec names as contract. A Go reimplementation
reading only the committed OpenAPI as source of truth wouldn't learn them.
Adds responses={...} to every v1 router (emissao, certificados, series,
documentos GET/xml) covering the status codes each route actually returns
-- 201 as the default on POST /v1/emissoes (with 200 documented for the
Idempotency-Key replay case), 401 on every authenticated route, 404 where
the anti-oracle boundary applies, 409 naming the structured codes each
route raises, 413 on the certificate upload's size cap. Regenerated
docs/openapi-v1.json from app.openapi() (uv run python -c '...json.dump...'
per test_openapi_committed.py's own docstring) so the committed==generated
assertion stays green with FIX 1-4's new 409 codes included.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
260644c961
commit
c2d2d30b70
@@ -41,7 +41,22 @@ async def _read_upload_capped(file: UploadFile, max_bytes: int) -> bytes:
|
||||
return b"".join(chunks)
|
||||
|
||||
|
||||
@router.post("", response_model=FiscalCertificateRead, status_code=status.HTTP_201_CREATED)
|
||||
@router.post(
|
||||
"",
|
||||
response_model=FiscalCertificateRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={
|
||||
401: {"description": "API key ausente ou inválida"},
|
||||
409: {"description": "`detail.code`=`certificate_upload_conflict` -- upload concorrente venceu a corrida"},
|
||||
413: {"description": f"Certificado excede o tamanho máximo permitido ({_MAX_PFX_UPLOAD_BYTES} bytes)"},
|
||||
422: {
|
||||
"description": (
|
||||
"PFX inválido/senha incorreta, CNPJ do certificado diverge do declarado, "
|
||||
"certificado vencido ou ainda não vigente"
|
||||
)
|
||||
},
|
||||
},
|
||||
)
|
||||
async def upload_certificate_endpoint(
|
||||
tenant_ref: str = Query(..., max_length=64),
|
||||
branch_ref: str = Query(..., max_length=64),
|
||||
@@ -77,7 +92,14 @@ async def upload_certificate_endpoint(
|
||||
return FiscalCertificateRead.model_validate(certificate)
|
||||
|
||||
|
||||
@router.get("", response_model=FiscalCertificateRead)
|
||||
@router.get(
|
||||
"",
|
||||
response_model=FiscalCertificateRead,
|
||||
responses={
|
||||
401: {"description": "API key ausente ou inválida"},
|
||||
404: {"description": "Nenhum certificado vivo para este (tenant_ref, branch_ref) -- anti-oracle"},
|
||||
},
|
||||
)
|
||||
async def read_certificate_endpoint(
|
||||
tenant_ref: str = Query(..., max_length=64),
|
||||
branch_ref: str = Query(..., max_length=64),
|
||||
@@ -96,7 +118,14 @@ async def read_certificate_endpoint(
|
||||
return FiscalCertificateRead.model_validate(certificate)
|
||||
|
||||
|
||||
@router.delete("", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete(
|
||||
"",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={
|
||||
401: {"description": "API key ausente ou inválida"},
|
||||
404: {"description": "Nenhum certificado vivo para este (tenant_ref, branch_ref) -- anti-oracle"},
|
||||
},
|
||||
)
|
||||
async def deactivate_certificate_endpoint(
|
||||
tenant_ref: str = Query(..., max_length=64),
|
||||
branch_ref: str = Query(..., max_length=64),
|
||||
|
||||
Reference in New Issue
Block a user