OpenAPI description: refusals some calls do make are missing (409 and 413 on POST /captures, 404 on two creates) #431
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#431
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, comparing the raise sites with whatapi.get_openapi_schema()describes for each call.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atapi/problems.py:303-320(_statuses, which since #248 is handed the scoped guard rather than importing it),api/api.py:415-450(create_capture) andapi/api.py:673-681(the responses_A_RENDERINGdeclares); the_too_bighandler,api/idempotency.py:99and the two routers have not moved, and the probe gives the same lists.problems._statusesworks out each call's refusals from three signs: its guard, a{in its path, and whether it takes input. Its docstring sets the bar: "a description that says a collection may answer 404 is a description that lies". It misses refusals raised in the body of a call, and the one raised before the call reads its body. A client generated fromopenapi.jsonhas no type for those answers, including the two a capturing extension is most likely to meet.api/problems.py:303-322,_statuses: 401 and 429 always, 403 for a scoped guard, 404 onlyif "{" in path, 422 when the call has models.api/api.py:413-448,create_capture:idempotency.onceraisesRefused(409, …, kind="idempotency-key-in-use")(api/idempotency.py:99).api/problems.py:183-207,_too_big, theRequestDataTooBighandler: a 413too-largefor any body overDATA_UPLOAD_MAX_MEMORY_SIZE, on every call that takes one. The wiki names a capture whosehtmlreaches about 2.5 MB as the case that meets it (postulo.wiki/The-capture-API.md:189-190).api/routers/interviews.py:111-113,add_interview, andapi/routers/reminders.py:42-44,add_reminder:raise HttpError(404, "No such application.")for anapplication_idin the body that is not the caller's, on paths without{.api.get_openapi_schema()gives['201', '401', '403', '422', '429']for each ofPOST /api/v1/captures,POST /api/v1/interviewsandPOST /api/v1/reminders, with no 409, no 413 and no 404. ThePUT /captures/{pk}/renderingcall gets its 409/411/413/415 right only because it declares them itself, throughopenapi_extra=_A_RENDERING(api/api.py:671-679).POST /captures. When it is too large, the answer is a typedtoo-largeproblem carryingmax_bytes, but a generated client treats it as an unexpected status and cannot say "send less". The same goes for a retried capture meetingidempotency-key-in-use, which the wiki tells the client to wait out and resend._statusesadd 413 to every operation with a request body, and let an operation add statuses of its own. For example, read an attribute set by a small decorator, or mergeopenapi_extra["responses"]asattach_renderingalready does. Then declare 409 oncreate_captureand 404 onadd_interviewandadd_reminder.Related to #296 (closed), which introduced the per-call description.
Done when
tests/test_api_problems.pyhas a test asserting thatPOST /api/v1/capturesis described with 409 and 413, andPOST /api/v1/interviewsandPOST /api/v1/reminderswith 404. It fails today.