CodamAIDocs
Topicdone

The order within a write operation

Recursion, before hooks, validation, saving, after hooks. Why a before hook may still fill a required field.

Variants
Create, PUT, PATCHDELETEROLLBACKbefore hook fills a required fieldbefore hook sets an invalid valuebefore hook changes a valid field

What this is about

A write operation consists of many steps: check rights, transfer values, check rules, save, read back. The hooks sit at two fixed points in between. Exactly where decides what a hook can do and what CDMS still checks afterwards.

The most important rule: CDMS first walks through the whole object tree of the request and queues the hooks for each object on the way. This pass over the object and all its children is called recursion. Only then do the hooks run, and they run before validation gives its verdict.

The steps of a create, PUT or PATCH

PUT /api/rest/order/update/{id}
  1. 1
    Client→CDMS
    sends the changed object
  2. 2
    CDMS
    check visibility, load the stored object (only with PUT and PATCH)
  3. 3
    CDMS
    recursion: for the object and every child, check the role, transfer values, remember rule violations, queue hooks
    A missing role stops here, before a single hook has run. A dependent child that drops out of a list is already marked for removal here.
  4. 4
    Hook
    before hooks of all queued objects
    They see the object with all transferred values and may still change it.
  5. 5
    CDMS
    validation: check each remembered violation again against the value that is now on the object
  6. 6
    CDMS
    violations left → 422 with all violations
  7. 7
    CDMS→Database
    hands the object to the database, new objects get their id
  8. 8
    Hook
    after hooks of all queued objects
  9. 9
    CDMS→Database
    flush: the SQL is executed, the database checks its rules, for example unique values
  10. 10
    CDMS→Database
    reads the object back with the response of the request
  11. 11
    Hook
    READ hook for every object read back
  12. 12
    CDMS→Client
    commit, then 200 with the object

Three points are fixed here:

  1. Roles before hooks. All role checks of the recursion happen before any hook runs. If a role is missing, not a single hook runs. See Model roles.
  2. Hooks before the verdict. Validation only remembers violations during the recursion. Whether they count, it decides only after the before hooks. See Validation.
  3. After hooks before the flush. When the after hooks run, the database has not yet checked its own rules. A duplicate value in a unique field only shows up afterwards. See One request, one transaction.

Why a before hook may fill a required field

An example: the model order has the required field orderNr. The client does not know the number, a before hook assigns it.

POST /api/rest/order/create without orderNr
  1. 1
    Client→CDMS
    sends { "data": { "customer": { "id": "k-7" } } }
  2. 2
    CDMS
    recursion: orderNr is missing → violation cannot-be-null is remembered, not reported
  3. 3
    Hook
    before hook sets orderNr = "A-2026-0042"
  4. 4
    CDMS
    checks orderNr again: now filled, the violation disappears
  5. 5
    CDMS→Database
    saves the order with the number from the hook
    Result: 200, the response contains orderNr

The second check applies all rules of the field, not only the one that was violated before. If the hook sets an empty text instead of the missing number, CDMS reports cannot-be-empty, because the field now breaks this rule.

What CDMS still checks after the before hooks

The second check only covers fields that had a violation. A field whose value was valid during the recursion is not checked again by CDMS.

Does CDMS check what a before hook writes?
Field had a violation beforeValue after the hookWhat happens
yesvalidviolation disappears, saved
yesinvalid422 with the rule the new value breaks
novalidsaved
noinvalidno check by CDMS, saved as long as the database accepts it at flush

So the validation rules are mainly a protection against wrong input from the client. What your hook writes is your responsibility. This applies even more to PATCH: PATCH only checks the sent fields. A field that the hook sets but the client did not send is not checked by CDMS at all.

The variants per operation

Where the hooks lie per operation

When: POST /create, PUT /update/{id}, PATCH /update/{id}

Exactly as above: recursion, before hooks, validation, saving, after hooks, flush, read-back with READ hook, commit.

Result: The operation in the hook is called CREATE, UPDATE or PATCH, per object: a new child in a PUT gets CREATE.

When: DELETE /delete/{id}

  1. 1
    CDMS
    check visibility, load the object
  2. 2
    CDMS
    recursion: check the delete role per object, mark dependent children for removal, unlink other relations, queue hooks
  3. 3
    Hook
    before hooks (DELETE)
  4. 4
    CDMS→Database
    hands the object over for removal
  5. 5
    Hook
    after hooks (DELETE)
  6. 6
    CDMS→Database
    flush, commit, 200 without body

Result: No validation, no read-back, no READ hook. See How a DELETE runs.

When: POST {basis}/{id}/rollback/{revision}

  1. 1
    CDMS
    check visibility and the rollback role
  2. 2
    Hook
    before hook (ROLLBACK) with the current state
  3. 3
    CDMS→Database
    restores the revision
  4. 4
    Hook
    after hook (ROLLBACK) with the restored state
  5. 5
    CDMS→Database
    flush, read-back with READ hook, commit

Result: No recursion and no validation: the hooks only run for the addressed object. See Rolling back to an old state.

The order of the hook calls among each other

For a single object it is simple: first all before hooks of its model by @Order, later all after hooks by @Order. If the request writes several objects, for example a parent with children, there is a fixed order across all objects. It is described in Hooks for nested objects and cascades.

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractSystemLayer (createObject, updateObject, patchObject, deleteObject, historyRollback: beginValidation, runAllBefore, assertValid, runAllAfter, flush)
  • CDMS/cdms-system-layer – AbstractLayer (recursivePrepare: role check, file; recursiveCreate/Update/Patch: addHooksBeforeDatabase, validateField, addHooksAfterDatabase; recursiveDelete; assertValid)
  • CDMS/cdms-system-layer – session/ValidationRequestContext (add, recheck), session/HookRequestContext (runAllBefore, runAllAfter)
  • CDMS/cdms-rest-api – RequestTransactionCommitter (commit before the response)
  • CDMS/cdms-integrationtest – AbstractHookValidationTest (hookFillsRequiredFieldOnCreate/OnUpdate/OnPatch, hookWritingUnacceptableValueIsRefused, hookOnUnsentFieldRaisesNoViolation)
  • documentation/60-erweiterung/01-hooks.md, 02-validierung.md
Search