Current section
Files
Jump to
Current section
Files
attr_engine
CHANGELOG.md
CHANGELOG.md
# Changelog
## v0.5.0 — Render-time transforms + relation attributes
### Added
- **`AttrEngine.Transform`** — render-time value transforms declared via
`data_config["transform"]` (a string or a list/pipeline). Built-ins: `redact`,
`redact_secret`. Custom transforms via the `:transforms` application config
(modules implementing `apply/2`, or 1-/2-arity functions). Transforms now run
automatically inside `Render.Block.render/4` and `Cascade.build_context/3`.
- **`AttrEngine.Relation`** — relation resolution runtime for reference attributes
(`data_config.type = "relation"`). Stores an opaque ref in the ASD while the value
lives in an external store reached through pluggable source adapters
(`:relation_sources` config). `ref_only` mode keeps raw values out of the ASD;
ships read (`resolve_relations/3`), write (`process_writes/2`), and cleanup
(`cleanup/2`) paths.
### Notes
- No schema or migration changes. Backward compatible.
## v0.3.0 — BREAKING: ASA primary key restructure
### What changed
`AttrEngine.Schema.AttributeSetAttribute` (ASA) now uses a surrogate
`bigserial` primary key (`{:id, :id, autogenerate: true}`) instead of
a composite PK on `(attribute_set_id, attribute_id)`.
This allows a single Attribute to appear multiple times within the same
AttributeSet, distinguished by `handle` — enabling structural primitives
(string, integer, currency, etc.) to be reused across named fields.
### New constraints
- **`handle` is required** — `validate_required([:handle])` in changeset,
`NOT NULL` and `CHECK (handle <> '')` at the database level
- **`UNIQUE(attribute_set_id, handle)`** — each handle appears at most
once per set (index name: `attribute_set_attributes_unique_set_handle`)
### PK column shape
- Column: `id`, type: `bigserial` (Postgres `bigint` with `GENERATED BY DEFAULT AS IDENTITY`)
- Matches `AttrEngine.Schema.Attribute` and `AttrEngine.Schema.AttributeSet`
### Consumer migration recipes
**Category (a): Consumers with composite PK**
> Before adding NOT NULL / CHECK constraints on `handle`, either backfill
> handles for existing ASA rows or `DELETE FROM attribute_set_attributes
> WHERE handle IS NULL`. For staging consumers where seeds re-create the
> rows with handles, the delete is the right call.
```elixir
def up do
# 1. Clean up rows without handles (seeds will re-create them)
execute "DELETE FROM attribute_set_attributes WHERE handle IS NULL OR handle = ''"
# 2. Drop composite PK
execute "ALTER TABLE attribute_set_attributes DROP CONSTRAINT attribute_set_attributes_pkey"
# 3. Add surrogate PK
execute "ALTER TABLE attribute_set_attributes ADD COLUMN id bigserial PRIMARY KEY"
# 4. Add unique index on (set, handle)
create unique_index(:attribute_set_attributes, [:attribute_set_id, :handle],
name: :attribute_set_attributes_unique_set_handle
)
# 5. Add NOT NULL + non-empty constraint on handle
execute """
ALTER TABLE attribute_set_attributes
ALTER COLUMN handle SET NOT NULL,
ADD CONSTRAINT asa_handle_non_empty CHECK (handle <> '')
"""
end
```
**Category (b): Consumers with surrogate id + old composite UNIQUE**
```elixir
def up do
# Drop old unique constraint if present
drop_if_exists unique_index(:attribute_set_attributes, [:attribute_set_id, :attribute_id])
# Add new unique on (set, handle)
create unique_index(:attribute_set_attributes, [:attribute_set_id, :handle],
name: :attribute_set_attributes_unique_set_handle
)
end
```
**Category (c): Consumers already on this shape**
No migration needed.
## v0.2.2
- Thread `:prefix` through all Repo calls for multi-tenant support.
## v0.2.1
- AttributeSet: conditional `@derive Flop.Schema`, `setup_changeset/3`,
schema default `"Standalone"`, easy_mode alignment across schema modules.
## v0.2.0
- Attribute: `data_config`/`ui_config` converted to `embeds_one` with
`Attribute.DataConfig` and `Attribute.UIConfig` embedded schemas.
- Added `draft_config`, virtual fields (`sample`, `data_preview`,
`ui_preview`, `ui_state`), conditional `@derive Flop.Schema`.
- Aligned easy_mode across schema modules. Added identifier locking.
- Cascade: `normalize_config` handles embedded structs transparently.