Skip to content

Commit d6bb2fb

Browse files
committed
docs(changelog): record auto-injection of organization_id system field
Documents the registry-time multi-tenant field augmentation and the SecurityPlugin field-resolution precedence fix (registry over metadata) shipped in 669f8d5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 669f8d5 commit d6bb2fb

1 file changed

Lines changed: 6 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- **Auto-injected `organization_id` system field on every user object** (`@objectstack/objectql`, `@objectstack/spec`, `@objectstack/plugin-security`) — `SchemaRegistry.registerObject()` now runs `applySystemFields(schema, { multiTenant })` before storing the contributor, splicing in an `organization_id` field (`lookup → sys_organization`, `hidden`/`readonly`/`indexed`) on every registered object that (a) doesn't already declare one and (b) isn't `managedBy: 'better-auth' | 'system' | 'platform'` and (c) hasn't opted out via the new `systemFields: false | { tenant?: false; owner?: false; audit?: false }` field on `ObjectSchema`. The registry honours `OS_MULTI_TENANT` (default `true`) the same way `SecurityPlugin` and the CLI startup banner do, so single-tenant deployments incur zero registration-time cost. Author-declared `organization_id` always wins (no overwrite). Combined with the existing SecurityPlugin auto-fill on insert and the `tenant_isolation` RLS policy, this means: out of the box, every CRM/business object built on ObjectStack is multi-tenant — schema authors no longer have to remember to declare `organization_id` per-object, and the long-standing footgun of "I just inserted a row but it's not visible because organization_id is NULL" is gone. Verified end-to-end with `pnpm dev:crm`: fresh DB → sign up → create org → `POST /api/v1/data/lead` → `organization_id` populated from `session.activeOrganizationId`; switch to a 2nd org → leads created there carry the 2nd org's id. Tests: 7 new cases in `registry.test.ts` covering injection, opt-out (`systemFields: false`), per-key opt-out (`systemFields: { tenant: false }`), `multiTenant: false`, manageBy skip, author-declared override.
12+
13+
### Fixed
14+
- **`SecurityPlugin` saw the un-augmented schema during auto-fill** (`@objectstack/plugin-security`) — `loadObjectFieldNames()` previously consulted the kernel's `metadata` service first and only fell back to `ObjectQL.getSchema()` when metadata had no entry. With the new registry-time `organization_id` injection (above), this ordering caused metadata to serve the original (pre-registration) schema while the registry held the augmented one — so SecurityPlugin's auto-fill on insert thought the column didn't exist and skipped populating `organization_id`. Reversed the order: `ObjectQL.getSchema()` is now the primary source of truth (it always reflects registry-time augmentation), with the metadata service as fallback for objects ObjectQL doesn't know about.
15+
1016
### Fixed
1117
- **Engine: unknown `select` fields silently returned empty rows** (`@objectstack/objectql`) — When a query requested fields that don't exist on the target object's schema (e.g. dashboard's auto-generated card view requesting `name`/`due_date`/`image`/`start_date`/`end_date` against every object regardless of its real shape), `Engine.find` and `Engine.findOne` passed those names straight through to the driver. `SqlDriver` then emitted `SELECT unknown_col FROM lead`, the DB rejected it with "no such column", and `SqlDriver` *swallowed* the error returning `[]` (sql-driver.ts:206-212) — so the API responded `200 { records: [], total: 0 }`. Result: every row of the `lead` object disappeared from `/_dashboard/apps/crm_enterprise/lead` even though the records existed and the user could read them via `?limit=20`. Now both `find()` and `findOne()` filter `ast.fields` against `schema.fields` (keeping relationship paths like `owner.name` by validating only the head segment) before the driver is invoked. If filtering produces an empty list, fall back to `undefined` so the driver projects `*` instead of an empty SELECT. Verified end-to-end via Chrome devtools: dashboard's lead grid now shows all 4 records (3 seeded + 1 user-created) instead of "No Leads Yet".
1218

0 commit comments

Comments
 (0)