Skip to content

feat(resources): Bereiche/departments ARE writable via the legacy master-data endpoint — make them a managed resource #108

Closed

Description

@2000game

Bereiche ARE writable — just not through the REST API

src/resolve/resolver.ts currently records departments as a read-only catalog:

GET /departments exists but no POST/PUT/DELETE does (live-probed on eqrm prod, CT 3.135.2,
2026-08-13), so a department is resolvable by name on every host yet can never be declared,
adopted or created.

The first half is right about /api/departments. The conclusion — that a department can never
be created — is not: the admin UI creates them through the legacy AJAX API. Captured from a browser
session against eqrm prod (HAR, 2026-08-13):

POST /index.php?q=churchdb/ajax
Content-Type: application/x-www-form-urlencoded

func=saveMasterData
&table=cdb_bereich
&id=                      # empty = CREATE; an id presumably UPDATEs that row
&col0=bezeichnung&value0=test
&col1=kuerzel&value1=test
&col2=sortkey&value2=2000
&browsertabId=844724310   # UI bookkeeping, almost certainly ignorable

→ 200  {"status":"success","data":null}

Reads come back from the same endpoint with func=getMasterData, which returns every master-data
table keyed by name (cdb_status, and by the same shape cdb_bereich / cdb_station), each row as
{id, bezeichnung, kuerzel, sortkey, …}.

This is the same protocol family ct-cli already speaks — the permission catalog is generated from
POST /index.php?q=churchauth/ajax func=getMasterData.

Why it matters

Right now a department can be referenced but not created, so a config that scopes a grant by
Bereich only plans on hosts where somebody already made that Bereich by hand. Concretely, in
eqrm/ct-structure:

✗ Cannot resolve department:equippers_koblenz referenced at group_role "sll_koblenz_mitglied"
  grant "churchdb:view alldata" scope on https://eqrm-dev.church.tools: no live department at
  /departments matches key "equippers_koblenz".

Prod plans clean; dev fails, because dev has campuses (ct created them — POST /campuses exists)
but not Bereiche. The promotion model assumes dev can be rebuilt from the config, and this breaks
that assumption for any Bereich-scoped grant. The workaround is a manual click in the dev admin UI
before every such adoption, which does not scale to the ~90 groups queued for adoption there.

Ask

Make department a first-class managed resource — declarable, adoptable, created/updated by apply —
backed by saveMasterData while continuing to read through GET /departments (cleaner, paginated,
already wired).

Worth deciding deliberately:

Acceptance

  • department is declarable in the config and created by ct apply on a host that lacks it.
  • A Bereich-scoped grant plans on a fresh host without manual admin-UI setup first.
  • Reads still go through GET /departments; only writes use the legacy endpoint.
  • The capture evidence and a re-probe note live next to the implementation.

Related: #98 (typed scope refs, where the read-only assumption was baked in), #96, #105.

Activity

  1. 2000game commented on Aug 13, 2026

    @2000game
    MemberAuthor

    Superseded in scope by #109: the same endpoint's getMasterData response carries a masterDataTables registry — 22 editable tables, each with its full column schema from DESCRIBE. So this is better solved by one generic driver than by a hand-written department resource. Bereiche remain the motivating case and the first table to support; keeping this open for that, tracking the general mechanism in #109.

  2. 2000game commented on Aug 14, 2026

    @2000game
    MemberAuthor

    Shipped in #112 (merged 2026-08-14). Bereiche are a managed resource, written through the legacy saveMasterData endpoint with reads staying REST and columns validated against the instance's own DESCRIBE. See src/resources/registry.ts and tests/department-resource.test.ts; verified end to end on dev (plan → apply → re-plan no-op → update → delete).

    (Not auto-closed: the PR body's Closes **#108** bolding defeats GitHub's keyword parser.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageUnsorted intake — decide in the weekly sweep

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions