CodamAIDocs
Topicdone

How a DELETE runs

Check visibility, load, check roles, cascade, hooks, remove: the steps of a delete and the errors at each point. There is only hard deletion.

Variants
regular modelsingletonabstract modelinvisible → 404without delete role → 403deleting twice

What this is about

You delete an object with a single request:

Request
DELETE /api/rest/company/delete/c4…
Response
200, empty body

The request has no body, and neither does the response. There is no response, because after the delete there is nothing left to read.

CDMS deletes hard: the row disappears from the database. There is no “deleted” field and no recycle bin. What is still left afterwards is described in What remains after a delete.

The stations

DELETE /company/delete/{id}
  1. CDMS
    Visibility
    Does the row exist, and may you see it? Same filters as for reading: tenant, own data, attribute filters.
    ↳ no 404 not-found|<Dto>|<id>
  2. CDMS
    Delete role
    Do you have the delete role of the model, here company-delete?
    ↳ no 403 missing-permission|company-delete
  3. CDMS
    Cascade
    Delete dependent children along, unlink all other relations. Every deleted child needs the delete role of its own model.
    ↳ no 403 missing-permission|<role of the child>
  4. Hook
    Hooks
    Before hooks of all affected objects. A hook can prevent the delete by throwing an error.
    ↳ no Error of the hook
  5. Database
    Database
    Can all rows be removed and all unlinked relations be saved?
    ↳ no 400 constraint-violation, e.g. an unlinked child must not exist without its parent
  6. 200 without body, the transaction is committed

If a station fails, CDMS rolls back the whole transaction. The object, its children and all unlinked connections stay as they were.

The flow in detail

sequenceDiagram
    participant C as Client
    participant D as CDMS
    participant H as Hook
    participant DB as Database
    C->>D: DELETE /company/delete/c4…
    D->>DB: counts the row with the read filters
    DB-->>D: 1 (otherwise 404)
    D->>DB: loads the full object
    D->>D: check the company's delete role
    D->>D: cascade: check and mark children, unlink relations
    D->>H: before hooks (company and every child)
    D->>DB: remove
    D->>H: after hooks (company and every child)
    D->>DB: flush, then commit
    D-->>C: 200, empty body

Three things stand out:

  1. Visibility comes before the role. If the row is invisible to you, you get 404, even if you lack the delete role. This way CDMS does not reveal whether a foreign id exists. See Why invisible objects return 404.
  2. You do not need a read role. The visibility check uses the read filters, but not the read role. order-delete alone is enough to delete a visible order.
  3. Nothing is read back. Unlike create, PUT and PATCH, there is no read-back after a delete and no READ hook.

Hooks during a delete

For every object that gets deleted, the hooks run with the method DELETE: for the addressed object and for every dependent child, each with the hooks of its own model.

  1. 1
    CDMS
    walks through the object and its dependent children and registers the DELETE hooks for each
  2. 2
    Hook
    before hooks run, the object still has all its own fields
    Here you can check whether the delete is allowed, or clean up dependent data outside of CDMS. If the hook throws an error, the request fails and the database stays unchanged.
  3. 3
    CDMS→Database
    removes the rows
  4. 4
    Hook
    after hooks run
  5. 5
    CDMS→Database
    writes everything to the database and commits

An object that is only unlinked (relation without the DELETE flag) is not deleted. No DELETE hooks run for it.

Variants

How deletion works

When: DELETE {basis}/delete/{id}

  1. 1
    Client→CDMS
    sends DELETE /company/delete/c4…, without body
  2. 2
    CDMS
    checks visibility, then the delete role
  3. 3
    CDMS→Database
    deletes the object and its dependent children, and unlinks all other relations

Result: 200 without body.

When: DELETE {basis}/delete, without id

  1. 1
    CDMS→Database
    finds the one object itself
  2. 2
    CDMS
    none there → nothing to do, 200
  3. 3
    CDMS
    exists → checks the delete role and deletes it like a regular object, with cascade and hooks

Result: Afterwards a new create is possible again. See Singletons.

When: DELETE {basis}/delete/{id} on the hub API of an abstract model

  1. 1
    Client→CDMS
    sends only the id, no @type
  2. 2
    CDMS→Database
    looks up the stored type for the id
  3. 3
    CDMS
    the id does not exist → 404
  4. 4
    CDMS
    passes the request on to the API of the subtype: visibility, delete role, cascade and hooks are those of the subtype

Result: To delete a private customer through /crm/kunde/delete/{id}, you need the delete role of the private customer. See Abstract models.

Decision table

Result of a DELETE
Row visible to youDelete role of the modelDelete role of every dependent childResult
no––404 not-found|<Dto>|<id>, nothing deleted
yesno–403 missing-permission|<role>, nothing deleted
yesyesmissing for one child403 with the role of the child, nothing deleted
yesyesyes200, object and dependent children deleted

With strict mode switched off, the key for a missing role is missing-delete-role, and the status stays 403. So a delete is never silently skipped.

Deleting twice

A deleted row no longer exists. A second DELETE with the same id finds nothing and returns 404. For a client that repeats a request because the first response never arrived, 404 on the second attempt usually means: the object is already gone. See May the client retry?

Pitfalls

What comes next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractSystemLayer.deleteObject, AbstractSystemSingletonLayer.deleteObject, AbstractLayer.assertVisibleForWrite, recursiveDelete
  • CDMS/cdms-authorization – AbstractAuthorizationLayer.deleteAccessAllowedByClass, classAccess
  • CDMS/cdms-rest-api – AbstractRestApi.deleteObject, AbstractRestSingletonApi.deleteObject, AbstractHubApi.delete
  • CDMS/cdms-generator – ApiProcessor, ApiSingletonProcessor, ApiHubProcessor (getDeleteMethod)
  • CDMS/cdms-persistence-database – AbstractDatabasePersistence.deleteObject, flush
  • CDMS/cdms-integrationtest – AbstractRoleDenialTest, AbstractDataFilterTest, AbstractAuditTrailTest, AbstractRecursiveDelete
  • documentation/05-api-guide/06-schreiben.md, 20-api/04-schreibsemantik.md
Search