CodamAIDocs
Topicdone

Reading the history

How POST /{id}/history returns revisions page by page, which permissions you need, and why deleted objects have a history too.

Variants
normal modelSingletondeleted objectwithout history role → 403invisible object → 404unknown id → empty list (model without a filter)paging with page and limit

What this is about

The history is the list of all revisions of an object, newest first. You read it with an endpoint of its own, which only exists for audited models and only if it is listed in the model’s endpoint list.

Request and response

History of an order, two entries per page
Request
POST /api/rest/order/7e1…/history
{
  "response": ["orderNr", "price"],
  "parameter": { "page": 0, "limit": 2 }
}
Response
{ "data": [
    { "revision": { "id": "7e1…", "orderNr": "A-1000", "price": 99.0 },
      "revisionMeta": { "ref": 51, "ts": 1790000300000, "username": "Ben Beispiel", … },
      "revisionType": "MOD" },
    { "revision": { "id": "7e1…", "orderNr": "A-1000", "price": 120.5 },
      "revisionMeta": { "ref": 42, "ts": 1790000200000, "username": "Anna Muster", … },
      "revisionType": "MOD" }
  ],
  "meta": { "error": false, "totalCount": 0, "currentPage": 0, … } }

Every entry in data is a revision with three parts: revision (the state of the object), revisionMeta (who, when, from where) and revisionType (ADD, MOD, DEL). The fields of revisionMeta are explained in What a revision records.

What the request controls

Part of the requestEffect in the history
responseRequired. Which simple fields each revision shows. + and * return all simple fields you may read. A field with a read role of its own is missing from every revision without this role, see Protected values.
references and lists in responseare not expanded. The history only shows the fields of the object itself.
parameter.page, parameter.limitPaging. Without limit all revisions come at once.
filters and sorting in parameterare not evaluated. The order is always: newest revision first.

+ and * do not bring the system fields _createdOn and _updatedOn in the history. You read the time of a revision from revisionMeta.ts. See Audit is not the same as system fields.

The checks

The path of POST /{id}/history
  1. CDMS
    Endpoint
    Is the model audited, and is the history endpoint in its endpoint list?
    ↳ no The endpoint does not exist
  2. CDMS
    Read role
    Does the person have the read role of the model?
    ↳ no 403
  3. CDMS
    History role
    Does the person have the history role of the model?
    ↳ no 403
  4. CDMS
    Visibility
    Would the person have been allowed to read the object – owner filter and attribute filters?
    ↳ no 404
  5. The revisions for the id are returned, newest first

Both roles are defined in the model. Their names are explained in Model roles. In strict mode the error is missing-permission|<role>, without strict mode missing-read-role or missing-history-role. See Strict mode.

The roles say whether someone may read histories of this model – not which ones. That is decided by the same row filters as reading and searching: the owner filter and the attribute filters. Whoever cannot read an object does not get its history either.

For a deleted object there is no row left to check this against. CDMS uses the last revision before the deletion instead: it still carries the owner and the attribute values, and the filters are applied to it. The DEL revision is of no use for this, its fields are empty.

If a model has neither an owner nor an attribute filter, there is nothing to check – there the two roles decide on their own.

The variants

History in four cases

When: POST /{base}/{id}/history for an existing object

All revisions of the object, newest first. The first one is an ADD, followed by MOD revisions, including those of a rollback.

When: POST /{base}/history, without id in the path

  1. 1
    Client→CDMS
    sends POST /api/rest/tenant/preferences/history with response
  2. 2
    CDMS
    looks for the one object that belongs to the person or the tenant
  3. 3
    CDMS→Client
    no object → 404 data-not-found
  4. 4
    CDMS→Client
    object exists → checks the roles and returns its history like for a normal model

When: POST /{base}/{id}/history with the id of a deleted object

The history stays readable – for those who were allowed to read the object. CDMS decides that on the last revision before the deletion, because the row itself is gone. The newest revision is a DEL. It states person and time of the deletion, its fields are empty except the id. You find the last content in the revision before it. This does not work for a singleton: without an object CDMS finds no id and answers with 404.

Result: See What remains after a delete.

When: The person has the read role, but not the history role.

403. Nothing is returned, not even partially. The same applies without the read role.

Decision table

What does POST /{id}/history return?
Endpoint exists?Read role?History role?Visible?Revisions for the id?Response
no––––The endpoint does not exist. Audit the model and list the endpoint.
yesno–––403
yesyesno––403
yesyesyesno–404 missing-object
yesyesyesno filterno200, data is an empty list. On a model without a filter, an unknown id does not result in 404.
yesyesyesyesyes200 with the revisions, newest first

On a model with a filter, an unknown id cannot be told apart from a hidden one: both result in 404. Otherwise the difference between the answers would tell a caller which foreign ids exist.

Paging

With page and limit you fetch the history in pages, just like a search. page starts at 0. If you leave out limit, all revisions come at once.

Unlike a search, the history does not fill meta: totalCount, currentPage and currentLimit are always 0. So you do not learn the total in advance. Keep paging until a page has fewer entries than limit.

Fetching the whole history in pages of 20
  1. 1
    Client→CDMS
    page: 0, limit: 20 → 20 entries
  2. 2
    Client→CDMS
    page: 1, limit: 20 → 20 entries
  3. 3
    Client→CDMS
    page: 2, limit: 20 → 7 entries
    Result: Fewer than 20, so this is the last page. 47 revisions in total.

Pitfalls

What comes next

Sources in the code and the knowledge base
  • CDMS/cdms-rest-api – AbstractRestApi.getHistory (only fields, parameter), AbstractRestSingletonApi.getHistory (getExistingData, data-not-found), Expander.expandResponse
  • CDMS/cdms-system-layer – AbstractLayer.queryHistory (read role, history role, assertHistoryVisible)
  • CDMS/cdms-persistence-database – AuditHistoryReader.queryHistory (forRevisionsOfEntity including deleted, newest first, page/limit only with limit > 0, recursiveRemoveObjects)
  • CDMS/cdms-commons – AuditQueryResponse, AuditRevision, AuditRevisionMeta, QueryMetaResponse
  • CDMS/cdms-generator – ApiProcessor.getHistoryMethod, ApiSingletonProcessor.getHistoryMethod
  • CDMS/cdms-integrationtest – AbstractAuditTrailTest (history with order-history + order-read), AbstractSingletonRollbackTest.history
Search