Date: 2026-05-04
Branch: fix/bou-28-create-update-admin-user-in-split-table-setup
Status: Approved
Admin user management silently fails for three operations in the split-table design (auth_users + users):
WHERE deleted_at IS NULL) because :soft-delete true missing from entity configexecute-one! (SELECT-oriented) used for DML UPDATE in split-table transaction; returns nil, 0 rows changed, no error raisedbulk-delete-entities targets table-name (:users) for soft-delete instead of soft-delete-table (:auth_users); :users has no deleted_at columnCreate flow works correctly — :create-redirect-url "/web/users/new" delegates to user/register-user which writes both tables atomically.
:soft-delete true in users.ednBoth resources/conf/dev/admin/users.edn and resources/conf/test/admin/users.edn lack :soft-delete true at the entity level.
Effect: soft-delete? evaluates to false throughout service.clj. List queries include soft-deleted rows. Delete path issues hard DELETE against :users instead of soft-delete against :auth_users.
Fix: Add :soft-delete true to both files.
execute-one! used for DML in split-table update and inline-editIn libs/admin/src/boundary/admin/shell/service.clj, two functions call db/execute-one! for UPDATE DML:
update-entity — split-table UPDATE transaction (primary + secondary table)update-entity-field — inline-edit PATCH path; issues UPDATE against effective-table (either table-name or secondary-table depending on the field)execute-one! is defined as (first (execute-query! ...)) — it executes a SELECT and takes the first row. For an UPDATE statement it returns nil (no rows selected), no exception raised, caller sees apparent success.
execute-update! is the correct function — it executes DML and returns ::jdbc/update-count.
Fix: Replace execute-one! with execute-update! in:
update-entity's split-table branchupdate-entity-fieldbulk-delete-entities uses wrong table for soft-deleteIn service.clj, the soft-delete branch of bulk-delete-entities builds:
{:update table-name ;; :users — WRONG
:set soft-delete-data
:where [:in primary-key id-strings]}
Should be:
{:update soft-delete-table ;; :auth_users — CORRECT
:set soft-delete-data
:where [:in primary-key id-strings]}
Unlike delete-entity (which calls resolve-query-config and correctly binds soft-delete-table), bulk-delete-entities does not call resolve-query-config at all — so soft-delete-table is never bound in its scope.
Fix: Call resolve-query-config inside bulk-delete-entities (or extract soft-delete-table from the entity config inline) to obtain the correct table, then use it in the soft-delete query instead of table-name.
Minimal, targeted fixes. No refactoring beyond what is required to correct the three bugs. Each fix is independently verifiable.
resources/conf/dev/admin/users.ednAdd :soft-delete true alongside :table-name :users:
:table-name :users
:soft-delete true
resources/conf/test/admin/users.ednSame as A — identical change to keep dev/test configs in sync.
libs/admin/src/boundary/admin/shell/service.cljTwo sub-fixes:
C1 — update-entity and update-entity-field DML fix:
Replace db/execute-one! with db/execute-update! in:
update-entity's split-table transactionupdate-entity-field (inline-edit PATCH path)C2 — bulk-delete-entities table fix:
Add a call to resolve-query-config (or inline the extraction) to bind soft-delete-table from the entity config. Replace table-name with soft-delete-table in the soft-delete query.
libs/admin/test/boundary/admin/shell/admin_user_operations_test.cljNew integration test namespace covering the actual auth_users/users schema (not the synthetic test_auth/test_profiles tables used by existing split-table tests).
Tests:
update-user-primary-fields — update :name, :role → confirm persisted in users; return value is full joined entity mapupdate-user-secondary-fields — update :email, :active → confirm persisted in auth_users; return value is full joined entity mapupdate-user-mixed-fields — update fields spanning both tables atomicallyupdate-user-field-inline — inline-edit PATCH via update-entity-field → confirm field persisted (covers update-entity-field DML fix)list-users-excludes-deleted — soft-deleted user does not appear in listbulk-soft-delete-targets-auth-users — bulk delete sets deleted_at on auth_users, not usersHTTP PUT /admin/users/:id
→ update-entity-handler
→ service/update-entity
split-table? → yes
split-update-data → {:primary-fields {name, role, ...}
:secondary-fields {email, active}}
tx:
execute-update! {:update :users ...} ;; primary
execute-update! {:update :auth_users ...} ;; secondary
execute-one! <join SELECT via resolve-query-config> ;; re-fetch full entity
return <full joined entity map>
→ render form with success flash
HTTP DELETE /admin/users (bulk)
→ bulk-delete-entities-handler
→ service/bulk-delete-entities
soft-delete? → yes (because :soft-delete true now present)
soft-delete-table → :auth_users
execute-update! {:update :auth_users :set {:deleted_at now} ...}
execute-update! raises on DB errors (constraint violations, missing column). Silent nil return eliminated. Existing try/catch in HTTP handlers surfaces errors as 500 with error flash.
No new error-handling code required — fixing the wrong function call restores correct behavior.
^:integration — run against H2 in-memory DB via :db/h2 aliasauth_users and users tables — confirmed present in migration filesboundary.test.db-helpers/with-test-db fixture pattern (matches existing integration tests)Note on existing split_table_update_test.clj: This test uses a synthetic schema (test_auth/test_profiles) that does not match production tables. It does not have a deleted_at column on test_profiles and cannot validate the three bugs above. It is not a substitute for the new tests. Passing that suite does not confirm production correctness for the user entity.
wrap-method-override audit — HTML forms work (confirmed by existing tests); not a blocker:name/:role persists to users table:email/:active persists to auth_users tableauth_users.deleted_at/:entity/:id/:field) persists correctly for split-table fieldssplit_table_update_test.clj uses synthetic schema — passing it does not confirm production user entity correctness)Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |