AshHooks.Tenancy (AshHooks v1.2.0)

Copy Markdown View Source

The tenancy contract resolver: every public entry family resolves the resources it touches BEFORE any data access.

  • a single-tenant set (no resource declares multitenancy) passes the tenant through unchanged — inert by construction: Ash applies a tenant only when a strategy is declared (reads no-op without one, and the data layer ignores tenants except under :context), so threading tenant: unconditionally cannot change single-tenant behavior;
  • a consistent attribute-multitenant set — every touched resource declares multitenancy :attribute over the SAME attribute with global?: false — requires a tenant: none given is the package's named {:error, :tenant_required} (Ash's own TenantRequired stays the backstop on paths this pre-flight cannot see — the bang-path heads this guard converts to tuples would otherwise crash);
  • anything else is {:error, :tenancy_mismatch} before any data access: some-but-not-all declared (an undeclared resource reads globally next to tenant-scoped ones), differing attributes, a non-:attribute strategy (not portable across the package's supported data layers), or global?: true (which silently disables Ash's fail-closed reads — it passes a same-attribute consistency check while permitting tenant-less access).

The cross-tenant hazard the mismatch guard closes is concrete: an inconsistent set reached through the delivery runtime would resolve an endpoint GLOBALLY under a tenant-scoped row and deliver one tenant's payload bytes to another tenant's URL.

Three Ash.Resource.Info introspections per resource per call, deliberately uncached: a compile-time cache would race Spark's cross-resource compilation.

Summary

Types

The named pre-flight errors, returned by every entry family before any data access.

Functions

Resolves the entry family's declaration set against the caller's tenant.

Types

error()

@type error() :: :tenant_required | :tenancy_mismatch

The named pre-flight errors, returned by every entry family before any data access.

Functions

resolve(modules, tenant)

@spec resolve([module()], term()) :: {:ok, term()} | {:error, error()}

Resolves the entry family's declaration set against the caller's tenant.

modules are the resources the entry family is about to touch (data access through each follows immediately after); tenant is the caller-supplied tenant, nil when none was given.