InvenioRDM v12.0.3 OpenAPI audit — Records/Drafts and Zenodo compatibility
Scope
This audit compares Terradue/invenio-rest-api-client tag v12.0.3 against:
- the current public
inveniosoftware/invenio-openapispecification; - the actual InvenioRDM v12.0.3 service/resource schemas (
invenio-rdm-recordsplus its v5/v6 draft/record resource dependencies); - Zenodo's current
zenodo-rdmrecord serializer override.
The supplied patch focuses on the Records/Drafts contract because that is where strict generated models currently fail. The remaining API groups still contain generic Success/Created response definitions and should be audited endpoint-by-endpoint before making them strongly typed.
High-confidence corrections included in the patch
| Area | Existing declaration | Runtime/source behavior | Patch |
|---|---|---|---|
Shared Created response |
Forced every 201 through a record-shaped Created model |
Created is used by unrelated endpoints |
Restore generic response; add record-specific responses |
POST /api/records |
201 -> Created |
Returns a record/draft item | 201 -> RecordCreated -> RecordResponse |
Zenodo application/json |
Assumed canonical Invenio record | Zenodo deliberately replaces the default record serializer with ZenodoJSONSerializer |
RecordResponse = oneOf[RDMRecord, ZenodoRecord] |
Zenodo id |
string | integer | ZenodoRecord.id: integer |
Zenodo files |
Files object |
array | ZenodoRecord.files: array[ZenodoFile] |
| Canonical files | Only input options modeled | v12 FilesSchema also emits count, total_bytes, entries |
Add read-only output properties |
| PID | typo type": string; no required fields |
identifier and provider are required |
Fix typo and requirements |
| Draft metadata | Full publish-time required fields | drafts are saved with validation errors (raise_errors=False) |
Remove Metadata.required for draft/general representation |
publisher |
required | optional in v12 MetadataSchema |
no longer required |
| publication/date fields | format: date |
EDTF strings, including intervals/partial dates | remove format: date |
| contributors | reused Creator, role optional |
contributor role required | add Contributor schema |
| locations | array of container objects | object containing features array |
reference the existing location container directly |
| rights | links URI |
v12 has props, singular link, and dump-only icon |
correct properties |
| vocabulary relation labels | title required |
relation input needs ID; display title is output enrichment | make title read-only/non-required |
| draft update example | enabled: "false" |
boolean | enabled: false |
| publish status | 201 |
v5 draft resource returns 202 |
202 -> RecordAccepted |
| delete draft status | 200 |
v5 draft resource returns 204 |
204 -> NoContent |
| latest-version endpoint | 200 |
raises redirect with HTTP 301 + Location |
model 301 redirect |
Important issues intentionally not guessed in this patch
Search/list responses
GET /api/records, GET /api/user/records, and versions searches still use the generic Success response. Runtime responses are search-result envelopes and should get dedicated list/search schemas rather than being inferred from a single payload.
File endpoints
File initialization, commit, metadata, and content endpoints use response shapes distinct from records. The old global record-shaped Created was especially dangerous here. The patch restores generic handling but does not invent detailed file-operation responses without tracing each resource method.
Export/content media types
Several export and content-download endpoints still reuse a JSON Success response even when the real payload is XML, BibTeX, binary content, etc. These should be corrected with operation-specific content media types.
Controlled-vocabulary enums
The hand-written specification turns multiple configurable vocabulary IDs into Python enums (resource type, roles, identifier schemes, relation types, etc.). This is convenient for a stock instance but too strict for a generic InvenioRDM client because several vocabularies/schemes are deployment-configurable. A second hardening pass should replace configurable enums with strings plus examples/descriptions, while keeping genuinely fixed enums such as person/organization type and access protection values.
Missing/under-described routes
The runtime record resource exposes more route semantics than the manual public specification captures. A complete API-wide contract should be generated from configured runtime resources where possible rather than continuing to treat the public manual YAML as authoritative.
Recommended source-of-truth strategy
- Use the public
inveniosoftware/invenio-openapionly as a route/documentation baseline. - Ground strongly typed schemas and status codes in the tagged implementation corresponding to the intended InvenioRDM version.
- Treat Zenodo as a deployment profile: it intentionally changes the JSON record representation.
- Keep operation-specific response components (
RecordCreated,RecordSuccess, etc.) rather than a universal typedCreated/Success. - Add captured runtime payloads as regression tests before regenerating the Python client.