diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 55dbb30..9bb2d49 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -27,7 +27,7 @@ | workplan | USER-WP-0017 | finished | — | workplans/USER-WP-0017-durable-store-record-serialization.md | | workplan | USER-WP-0018 | finished | — | workplans/USER-WP-0018-postgres-store-adapter.md | | workplan | USER-WP-0019 | finished | — | workplans/USER-WP-0019-provider-backed-postgres-conformance.md | -| workplan | USER-WP-0020 | ready | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| workplan | USER-WP-0020 | active | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | | task | USER-WP-0001-T1 | done | — | workplans/USER-WP-0001-preparation-and-interface-adoption.md | | task | USER-WP-0001-T2 | done | — | workplans/USER-WP-0001-preparation-and-interface-adoption.md | | task | USER-WP-0001-T3 | done | — | workplans/USER-WP-0001-preparation-and-interface-adoption.md | @@ -145,11 +145,11 @@ | task | USER-WP-0019-T3 | done | — | workplans/USER-WP-0019-provider-backed-postgres-conformance.md | | task | USER-WP-0019-T4 | done | — | workplans/USER-WP-0019-provider-backed-postgres-conformance.md | | task | USER-WP-0019-T5 | done | — | workplans/USER-WP-0019-provider-backed-postgres-conformance.md | -| task | USER-WP-0020-T01 | todo | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | -| task | USER-WP-0020-T02 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | -| task | USER-WP-0020-T03 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | -| task | USER-WP-0020-T04 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | -| task | USER-WP-0020-T05 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| task | USER-WP-0020-T01 | done | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| task | USER-WP-0020-T02 | progress | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| task | USER-WP-0020-T03 | progress | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| task | USER-WP-0020-T04 | progress | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | +| task | USER-WP-0020-T05 | progress | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | | task | USER-WP-0020-T06 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | | task | USER-WP-0020-T07 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | | task | USER-WP-0020-T08 | wait | — | workplans/USER-WP-0020-self-service-and-user-administration-portal.md | diff --git a/pyproject.toml b/pyproject.toml index c486202..11edb67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,9 @@ build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] where = ["src"] +[tool.setuptools.package-data] +user_engine = ["migrations/postgres/*.sql"] + [tool.user-engine] package = "user_engine" workplan = "USER-WP-0006" diff --git a/src/user_engine/adapters/postgres.py b/src/user_engine/adapters/postgres.py index 6b41d27..fe9ef0d 100644 --- a/src/user_engine/adapters/postgres.py +++ b/src/user_engine/adapters/postgres.py @@ -9,7 +9,7 @@ from __future__ import annotations import json from contextlib import contextmanager -from pathlib import Path +from importlib.resources import files from typing import Any, Iterable, Iterator, Mapping, Protocol, cast from user_engine.domain import ( @@ -624,9 +624,10 @@ def _first_column(row: Any) -> Any: def _load_bootstrap_sql() -> str: - repo_root = Path(__file__).resolve().parents[3] - return (repo_root / "migrations/postgres/0001_user_engine_store.sql").read_text( - encoding="utf-8" + return ( + files("user_engine") + .joinpath("migrations/postgres/0001_user_engine_store.sql") + .read_text(encoding="utf-8") ) diff --git a/src/user_engine/migrations/postgres/0001_user_engine_store.sql b/src/user_engine/migrations/postgres/0001_user_engine_store.sql new file mode 100644 index 0000000..2cadba6 --- /dev/null +++ b/src/user_engine/migrations/postgres/0001_user_engine_store.sql @@ -0,0 +1,57 @@ +CREATE TABLE IF NOT EXISTS user_engine_schema_versions ( + version text PRIMARY KEY, + name text NOT NULL, + applied_at timestamptz NOT NULL DEFAULT now(), + checksum text +); +CREATE TABLE IF NOT EXISTS user_engine_records ( + record_type text NOT NULL, + record_key text NOT NULL, + tenant text, + user_id text, + application_id text, + scope_type text, + scope_id text, + payload jsonb NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (record_type, record_key) +); +CREATE INDEX IF NOT EXISTS user_engine_records_tenant_idx ON user_engine_records (tenant, record_type); +CREATE INDEX IF NOT EXISTS user_engine_records_user_idx ON user_engine_records (user_id, record_type); +CREATE INDEX IF NOT EXISTS user_engine_records_application_idx ON user_engine_records (application_id, record_type); +CREATE INDEX IF NOT EXISTS user_engine_records_scope_idx ON user_engine_records (scope_type, scope_id, record_type); +CREATE TABLE IF NOT EXISTS user_engine_audit_records ( + audit_id text PRIMARY KEY, + tenant text NOT NULL, + actor_issuer text NOT NULL, + actor_subject text NOT NULL, + action text NOT NULL, + subject text NOT NULL, + correlation_id text NOT NULL, + summary text, + payload jsonb NOT NULL, + recorded_at timestamptz NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS user_engine_audit_records_tenant_idx ON user_engine_audit_records (tenant, recorded_at); +CREATE INDEX IF NOT EXISTS user_engine_audit_records_subject_idx ON user_engine_audit_records (subject, recorded_at); +CREATE TABLE IF NOT EXISTS user_engine_outbox_events ( + event_id text PRIMARY KEY, + tenant text NOT NULL, + event_type text NOT NULL, + aggregate_id text NOT NULL, + correlation_id text NOT NULL, + payload jsonb NOT NULL, + occurred_at timestamptz NOT NULL DEFAULT now(), + claimed_at timestamptz, + claimed_by text, + delivered_at timestamptz, + failed_at timestamptz, + failure_reason text +); +CREATE INDEX IF NOT EXISTS user_engine_outbox_events_pending_idx + ON user_engine_outbox_events (occurred_at) + WHERE claimed_at IS NULL AND delivered_at IS NULL; +INSERT INTO user_engine_schema_versions (version, name) +VALUES ('0001_initial', 'initial durable store') +ON CONFLICT (version) DO NOTHING;