Skip to content

feat(tenant): shared canonical-first organisationId ownership filter - #13

Merged
KilianBoute merged 3 commits into
mainfrom
feat/canonical-organisation-id
Aug 12, 2026
Merged

KilianBoute merged 3 commits into
mainfrom
feat/canonical-organisation-id

Conversation

@KilianBoute

@KilianBoute KilianBoute commented Aug 12, 2026 •

Copy link
Copy Markdown
Member

Description

This change introduces a shared, canonical-first tenant ownership filter to support the ongoing organisationId migration while preserving backward compatibility with legacy ownership fields.

Previously, services needed to reimplement dual-read ownership logic independently, increasing the risk of inconsistent tenant isolation rules during the migration. By centralising this logic in pkg/database, all services can now build the same ownership query shape from a single, tested implementation.

The new helpers:

  • Prefer the canonical organisationId field for migrated documents.
  • Safely fall back to legacy owner fields only for documents that have not yet been backfilled.
  • Treat missing, empty, and null organisationId values consistently to avoid partial-migration edge cases.
  • Provide a lightweight TenantField abstraction so collections define legacy ownership fields once instead of duplicating string literals across query sites.

This improves the project by making tenant scoping consistent, reducing migration risk, and creating a clear path to eventually remove legacy ownership handling once the backfill is complete. Comprehensive unit tests were added to lock down the expected query behaviour and prevent regressions.

Add a models-free, strings-only home for the organisationId migration's
canonical-first dual-read filter so hub-api and the pipeline services build the
exact same $or shape instead of each hardcoding it:

- CanonicalOrganisationField constant ("organisationId").
- MissingCanonicalOrganisation() predicate (reusable by index/backfill tooling).
- CanonicalFirstOwnership(organisationId, legacyField, legacyValue).
- TenantField{Legacy} per-collection descriptor with .Scope(org, legacyValue).

Callers resolve the ids via their own models helpers and pass strings, keeping
this package a dependency-light leaf. Additive; no existing consumer is affected
until it bumps the dependency and delegates its local helper here.
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Add a models-free, strings-only home for the organisationId migration's
canonical-first dual-read filter so hub-api and the pipeline services build the
exact same $or shape instead of each hardcoding it:

- CanonicalOrganisationField constant ("organisationId").
- MissingCanonicalOrganisation() predicate (reusable by index/backfill tooling).
- CanonicalFirstOwnership(organisationId, legacyField, legacyValue).
- TenantField{Legacy} per-collection descriptor with .Scope(org, legacyValue).

Callers resolve the ids via their own models helpers and pass strings, keeping
this package a dependency-light leaf. Additive; no existing consumer is affected
until it bumps the dependency and delegates its local helper here.
…tabase into feat/canonical-organisation-id

# Conflicts:
#	pkg/database/tenant.go
#	pkg/database/tenant_test.go
@KilianBoute
KilianBoute merged commit a7ff223 into main Aug 12, 2026
6 checks passed
@KilianBoute
KilianBoute deleted the feat/canonical-organisation-id branch August 12, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant