CodamAIDocs
Topicdone

The four cases in nested writing

Two questions decide whether a child object is created, changed, only linked or rejected: Does it have an id? Is the matching flag set?

Variants
without id + CREATE → createwithout id, without CREATE → 400with id + UPDATE → update alongwith id, without UPDATE → only linkunknown id → 404Lists with PATCHRoles of the childrenStrict mode off

What this is about

In one request you can write not only one object, but also its children: the objects in its relation fields. Create a company with new employees, change an invoice together with its items, assign an employee to an existing department. This is called nested writing.

For each child in the request, CDMS asks two questions:

  1. Does the child have an id?
  2. Does the relation allow the matching action, that is, the flag CREATE or UPDATE?

The decision tree

flowchart TB
    K["Child in the request"] --> I{"has an id?"}
    I -->|no| C{"Flag CREATE?"}
    C -->|yes| A1["1 · create"]
    C -->|no| A4["4 · rejected: 400"]
    I -->|yes| E{"does the object exist?"}
    E -->|no| A5["404"]
    E -->|yes| U{"Flag UPDATE?"}
    U -->|yes| A2["2 · update along"]
    U -->|no| A3["3 · only link"]

All four cases in one request

A department whose relation employees has the flags CREATE and UPDATE, and whose relation location has no flags:

PUT with all cases
Request
PUT /api/rest/department/update/d1…
{
  "data": {
    "id": "d1…",
    "name": "IT",
    "employees": [
      { "firstname": "Ada", "lastname": "Lovelace" },
      { "id": "e2…", "firstname": "Alan", "lastname": "Turing" }
    ],
    "location": { "id": "l7…", "city": "Wiesbaden" }
  },
  "response": ["+"]
}
What CDMS does
Ada      → case 1: is created (no id, CREATE)
Alan     → case 2: is changed (id, UPDATE)
location → case 3: is only linked (id, no UPDATE),
           "city" is ignored

If location had a child without an id, that would be case 4: location has no CREATE, so the whole request fails with 400 recursive-create-not-allowed|location.

The four cases in detail

A child in the request

When: Child without id, relation with CREATE.

  1. 1
    CDMS
    checks the create role of the child model (or a field role on the relation field)
  2. 2
    CDMS
    creates the child: its own id, _createdOn, default values, CREATE rules
  3. 3
    CDMS
    connects it to the parent object, on both sides

Result: The child exists and belongs to the parent object. This works the same for Create, PUT and PATCH.

When: Child with id, relation with UPDATE.

  1. 1
    CDMS→Database
    looks up the child
  2. 2
    CDMS
    checks the update role of the child model (or a field role)
  3. 3
    CDMS
    changes the child according to the rules of the verb: with Create and PUT it replaces the child, with PATCH it changes only the fields you sent
  4. 4
    CDMS
    connects it to the parent object

Result: Watch out with PUT: { "id": "e2…" } alone clears all fields of the child. For required fields you get 422 employees[0].firstname cannot-be-null.

When: Child with id, relation without UPDATE.

  1. 1
    CDMS→Database
    looks up the child
  2. 2
    CDMS→Client
    does not exist → 404
  3. 3
    CDMS
    checks whether you could read the child: read role (or read field role) and row filters
  4. 4
    CDMS→Client
    you may not see it → 404, as if the id did not exist
  5. 5
    CDMS
    connects it to the parent object; other fields of the child are ignored

Result: The child stays as it is. Only the connection is new. What is required is read permission for the child, not its update role — the child does not change.

When: Child without id, relation without CREATE.

  1. 1
    CDMS
    flag CREATE is missing
  2. 2
    CDMS→Client
    400 recursive-create-not-allowed|<feld>, nothing is saved

Result: This is how CDMS prevents new objects from being created by accident through a reference.

Lists with PATCH

In a list, PATCH follows the same four cases as Create and PUT, and as a single reference. The only difference is how a child with UPDATE is changed: PATCH changes only the fields you sent, PUT replaces the child.

A list entry with PUT and with PATCH
Create and PUTPATCH
Entry with id, without UPDATEis only linked, fields you sent are ignoredis only linked, fields you sent are ignored
Entry without id, without CREATE400 recursive-create-not-allowed|<feld>400 recursive-create-not-allowed|<feld>
Entry with id, with UPDATEchild is replacedonly the fields you sent change

Decision table

Result per child (strict mode on)
idObject existsFlagVerb, field kindResult
no–CREATEallcreate
no–no CREATEall400 recursive-create-not-allowed|<feld>
yesno–Create, PUT (single reference)404 missing-object|<id>|<feld>
yesno–PATCH404 missing-object-for-field|<id>|<feld>
yesyesUPDATEallupdate along (PUT replaces, PATCH changes)
yesyesno UPDATEallonly link

If the target model is abstract, a new child also needs @type. Otherwise CDMS does not know which subtype to create. If it is missing, you get 400 missing-type-for-abstract-field with the path of the field: …|mainPhone with PATCH, …|data.mainPhone with Create and PUT. An unknown type returns 400 unknown-type-for-abstract-field|<pfad>|<typ>.

Roles of the children

Every child that CDMS creates, changes or deletes is checked against the roles of its own model. A field role on the relation field can replace the role of the child model, but only for this path. If both are missing, the whole request fails with 403 missing-permission|<rolle>. See Relation types and recursive flags and Permissions on relations (field roles).

Strict mode off

Strict mode is the default: Anything that is not allowed is rejected with an error. An installation can turn it off. Then CDMS rejects less and leaves things out instead:

CaseStrict mode onStrict mode off
new child in a list, no CREATE400entry is skipped
new child as a single reference, no CREATE400the field becomes empty
role of the child is missing403 missing-permission|<rolle>403 missing-create-role / missing-update-role / missing-delete-role

See Strict mode: error or silently ignore.

Pitfalls

What comes next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractLayer.setModel, recursiveCreate (lists), recursiveUpdate (lists), recursivePatch (single reference, lists), recursivePrepare (roles), enterField (field roles)
  • CDMS/cdms-authorization – AbstractAuthorizationLayer (classAccess, fieldRole)
  • commons – RequestContext (STRICT_MODE)
  • CDMS/cdms-integrationtest – AbstractRecursiveCreate, AbstractRecursiveUpdate, AbstractRecursivePatch, AbstractUpdateTest, AbstractFieldRoleTest, AbstractRoleDenialTest, AbstractPatchListRecursionTest; probe against Group, Department, Contact, SponsorInvoice
  • documentation/05-api-guide/07-verschachtelt-schreiben.md
Search