b3afd44d00
Schema + migration for the template source table, and TemplateRepository.resolve: highest active version for (key,channel,locale), scoped override preferred over the global (scopeId NULL) default. Migration adds a PARTIAL unique index WHERE scopeId IS NULL so two platform defaults for the same key can't coexist (Postgres treats NULL as distinct under a plain UNIQUE — the scoped constraint alone wouldn't catch it). 6 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.8 KiB
SQL
40 lines
1.8 KiB
SQL
-- Reusable message templates (email/SMS/in-app). DB is runtime truth; platform defaults seeded
|
|
-- from repo files on boot. Versions are immutable (a change = a new higher version).
|
|
CREATE TYPE "IiosTemplateChannel" AS ENUM ('EMAIL', 'SMS', 'INTERNAL');
|
|
|
|
CREATE TABLE "IiosMessageTemplate" (
|
|
"id" TEXT NOT NULL,
|
|
"scopeId" TEXT,
|
|
"key" TEXT NOT NULL,
|
|
"channel" "IiosTemplateChannel" NOT NULL,
|
|
"locale" TEXT NOT NULL DEFAULT 'en',
|
|
"version" INTEGER NOT NULL DEFAULT 1,
|
|
"subject" TEXT,
|
|
"bodyHtml" TEXT,
|
|
"bodyText" TEXT,
|
|
"variables" JSONB,
|
|
"active" BOOLEAN NOT NULL DEFAULT true,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
CONSTRAINT "IiosMessageTemplate_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
ALTER TABLE "IiosMessageTemplate"
|
|
ADD CONSTRAINT "IiosMessageTemplate_scopeId_fkey"
|
|
FOREIGN KEY ("scopeId") REFERENCES "IiosScope"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- Uniqueness for SCOPED rows (scopeId NOT NULL).
|
|
CREATE UNIQUE INDEX "IiosMessageTemplate_scopeId_key_channel_locale_version_key"
|
|
ON "IiosMessageTemplate" ("scopeId", "key", "channel", "locale", "version");
|
|
|
|
-- Uniqueness for GLOBAL rows (scopeId IS NULL). Postgres treats NULL as distinct under a normal
|
|
-- UNIQUE, so the constraint above does NOT stop two platform defaults for the same key — this
|
|
-- partial index does. (Same footgun handled in the inbox idempotency migration.)
|
|
CREATE UNIQUE INDEX "IiosMessageTemplate_global_key"
|
|
ON "IiosMessageTemplate" ("key", "channel", "locale", "version")
|
|
WHERE "scopeId" IS NULL;
|
|
|
|
-- Resolution lookup: by key/channel/locale among active rows.
|
|
CREATE INDEX "IiosMessageTemplate_key_channel_locale_active_idx"
|
|
ON "IiosMessageTemplate" ("key", "channel", "locale", "active");
|