Runtime resolution for the AshReplicant sink — the tenant/classification layer
that keeps replicant tenant-blind. Pure functions over compiled resource
metadata (no DB access):
build_index/1— reflect the configured domains into a{source_schema, source_table} => resourceindex, failing closed on a duplicate source key (ambiguous route).resolve_tenant/2— per-row tenant fromtenant_attribute/tenant_mfa, failing closed with:tenant_requiredon a nil/false/blank tenant (any value Ash would treat as unscoped);resolve_tenant!/3is the raising variant every apply path shares.writable_target/2/attrs_for_upsert/2— map source string columns to their real writable targets, routing AshCloak-sensitive columns through the cloak argument while namingencrypted_<col>inupsert_fields. The bulk snapshot path computesupsert_reflection/1once and maps each row viaupsert_input/2(the batch-invariant hoist).primary_key/1/pk_values/2/upsert_identity/1/upsert_action/1/destroy_action/1.
Summary
Types
The batch-invariant upsert reflection: {skip, cloak_attrs, attribute_names}.
Functions
The declared SCD2 business-key values from a string-keyed source record.
The primary destroy action name (mirrors upsert_action/1 for :create).
Look up the mirror resource for a source {schema, table} in an index built by
build_index/1, applying the SAME nil-schema → "public" default the index
keys use (so the convention lives in one place next to the builder). Returns the
resource, or nil for an unmapped table.
A query over resource selecting the CURRENT open version of the business key in
record, whose valid_from_lsn is strictly less than lsn (open-path close) or at
most lsn (delete/terminal close, inclusive?: true). Uses dynamic ^ref/1 because
the window column names are DSL-configured.
The fail-closed bang variant of resolve_tenant/2: returns the per-row tenant, or raises a
value-free AshReplicant.Error (reason: :tenant_required) when the row carries no usable
tenant. op labels the failing sink operation (:upsert / :destroy / ...) in the
structural error. The single tenant-resolution entry point shared by every apply path
(Apply, Apply.Scd2), so :tenant_required fails identically everywhere.
The upsert-capable create action name (the resource's primary create action).
The upsert identity name from the DSL (nil → primary-key upsert).
Map one source record to {inputs, upsert_fields} under a precomputed
upsert_reflection/1. AshCloak-sensitive columns pass plaintext under the cloak
argument while upsert_fields names encrypted_<col>.
Compute the batch-invariant upsert reflection for a resource ONCE — the skip
list, the AshCloak cloak attributes, and the attribute-name set. Thread it into
upsert_input/2 per row of a column-homogeneous batch (the snapshot bulk path)
to avoid re-deriving these for every row. Single-record callers use
attrs_for_upsert/2, which computes the reflection inline.
The {inputs, upsert_fields} for OPENING a version: the source data columns (via the
existing upsert reflection) PLUS the window columns (valid_from_lsn, optional
valid_from_ts, valid_to_lsn: nil, optional is_current: true). upsert_fields
names every window column so a same-lsn re-open coalesces in place.
Types
Functions
@spec build_index([module()]) :: {:ok, %{required(source_key()) => module()}} | {:error, {:duplicate_source, source_key()}} | {:error, {:missing_source_table, module()}}
The declared SCD2 business-key values from a string-keyed source record.
The primary destroy action name (mirrors upsert_action/1 for :create).
@spec lookup(%{required(source_key()) => module()}, String.t() | nil, String.t()) :: module() | nil
Look up the mirror resource for a source {schema, table} in an index built by
build_index/1, applying the SAME nil-schema → "public" default the index
keys use (so the convention lives in one place next to the builder). Returns the
resource, or nil for an unmapped table.
@spec open_version_query(module(), map(), integer(), keyword()) :: Ash.Query.t()
A query over resource selecting the CURRENT open version of the business key in
record, whose valid_from_lsn is strictly less than lsn (open-path close) or at
most lsn (delete/terminal close, inclusive?: true). Uses dynamic ^ref/1 because
the window column names are DSL-configured.
The fail-closed bang variant of resolve_tenant/2: returns the per-row tenant, or raises a
value-free AshReplicant.Error (reason: :tenant_required) when the row carries no usable
tenant. op labels the failing sink operation (:upsert / :destroy / ...) in the
structural error. The single tenant-resolution entry point shared by every apply path
(Apply, Apply.Scd2), so :tenant_required fails identically everywhere.
The upsert-capable create action name (the resource's primary create action).
The upsert identity name from the DSL (nil → primary-key upsert).
@spec upsert_input(upsert_reflection(), map()) :: {map(), [atom()]}
Map one source record to {inputs, upsert_fields} under a precomputed
upsert_reflection/1. AshCloak-sensitive columns pass plaintext under the cloak
argument while upsert_fields names encrypted_<col>.
@spec upsert_reflection(module()) :: upsert_reflection()
Compute the batch-invariant upsert reflection for a resource ONCE — the skip
list, the AshCloak cloak attributes, and the attribute-name set. Thread it into
upsert_input/2 per row of a column-homogeneous batch (the snapshot bulk path)
to avoid re-deriving these for every row. Single-record callers use
attrs_for_upsert/2, which computes the reflection inline.
The {inputs, upsert_fields} for OPENING a version: the source data columns (via the
existing upsert reflection) PLUS the window columns (valid_from_lsn, optional
valid_from_ts, valid_to_lsn: nil, optional is_current: true). upsert_fields
names every window column so a same-lsn re-open coalesces in place.