feat(t-paliad-070): rename Department → PartnerUnit on the Go side
Backend rename (frontend lands in next commit): - Migration 026: rename paliad.departments → paliad.partner_units, paliad.department_members → paliad.partner_unit_members, junction FK department_id → partner_unit_id, plus all constraints/indexes/policies. Pre-drop seed re-runs migration 019's logic to capture any users.dezernat drift, then DROP COLUMN. Adds paliad.partner_unit_events audit table with RLS (any-authenticated read, global_admin write). - models.User.Dezernat dropped. Department / DepartmentMember → PartnerUnit / PartnerUnitMember. - DepartmentService → PartnerUnitService (file renamed via git mv to preserve blame). Every mutation now opens a tx and emits a partner_unit_events row in the same tx (created/updated/deleted/ member_added/member_removed). Update emits before/after snapshots; Delete emits BEFORE the cascade so the FK still resolves, then ON DELETE SET NULL keeps the historical row. - /api/departments/* → /api/partner-units/*. Handlers renamed. - New /admin/partner-units page handler stub. - AuditService UNIONs the new partner_unit_events source as a 4th branch; handler accepts AuditSourcePartnerUnitEvents. - user_service: drop dezernat from CreateUserInput / UpdateProfileInput / AdminCreateInput / AdminUpdateInput. CreateUserInput gains PartnerUnitID *uuid.UUID — onboarding can pick an initial unit and the membership row + audit event are inserted in the same tx. - Settings tab aliases drop dezernat/department. - Legacy /dezernate and /departments now redirect to /admin/partner-units (admins only see it; non-admins land on the forbidden bounce). go build / vet / test compile clean.
This commit is contained in:
48
internal/db/migrations/026_rename_to_partner_units.down.sql
Normal file
48
internal/db/migrations/026_rename_to_partner_units.down.sql
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Down migration for 026: revert the partner_units rename.
|
||||
--
|
||||
-- Note: paliad.users.dezernat values cannot be perfectly restored. The
|
||||
-- column is recreated as NULL; structural data (membership rows) is
|
||||
-- preserved. Per design doc §7.3 — if a true free-text rollback is ever
|
||||
-- needed, an admin script can reconstruct values from
|
||||
-- partner_unit_members:
|
||||
--
|
||||
-- UPDATE paliad.users u SET dezernat = (
|
||||
-- SELECT pu.name FROM paliad.partner_units pu
|
||||
-- JOIN paliad.partner_unit_members pum ON pum.partner_unit_id = pu.id
|
||||
-- WHERE pum.user_id = u.id LIMIT 1)
|
||||
-- WHERE u.dezernat IS NULL;
|
||||
--
|
||||
-- That step is not auto-run because most rollbacks are recoveries, not
|
||||
-- data restorations.
|
||||
|
||||
-- 1. Drop the audit table.
|
||||
DROP TABLE IF EXISTS paliad.partner_unit_events;
|
||||
|
||||
-- 2. Rename RLS policies back.
|
||||
DO $$ BEGIN ALTER POLICY partner_units_select ON paliad.partner_units RENAME TO departments_select; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY partner_units_write ON paliad.partner_units RENAME TO departments_write; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY partner_unit_members_select ON paliad.partner_unit_members RENAME TO department_members_select; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY partner_unit_members_write ON paliad.partner_unit_members RENAME TO department_members_write; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- 3. Rename indexes back.
|
||||
DO $$ BEGIN ALTER INDEX paliad.partner_units_office_idx RENAME TO departments_office_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER INDEX paliad.partner_units_lead_idx RENAME TO departments_lead_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER INDEX paliad.partner_unit_members_user_idx RENAME TO department_members_user_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- 4. Rename constraints back.
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT partner_units_pkey TO departments_pkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT partner_units_lead_user_id_fkey TO departments_lead_user_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT partner_units_office_check TO departments_office_check; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT partner_unit_members_pkey TO department_members_pkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT partner_unit_members_partner_unit_id_fkey TO department_members_department_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT partner_unit_members_user_id_fkey TO department_members_user_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- 5. Rename junction column back.
|
||||
ALTER TABLE paliad.partner_unit_members RENAME COLUMN partner_unit_id TO department_id;
|
||||
|
||||
-- 6. Rename tables back.
|
||||
ALTER TABLE paliad.partner_unit_members RENAME TO department_members;
|
||||
ALTER TABLE paliad.partner_units RENAME TO departments;
|
||||
|
||||
-- 7. Recreate the legacy free-text column. Values are NULL.
|
||||
ALTER TABLE paliad.users ADD COLUMN IF NOT EXISTS dezernat text;
|
||||
131
internal/db/migrations/026_rename_to_partner_units.up.sql
Normal file
131
internal/db/migrations/026_rename_to_partner_units.up.sql
Normal file
@@ -0,0 +1,131 @@
|
||||
-- t-paliad-070: Rename departments → partner_units across the schema, drop
|
||||
-- the legacy users.dezernat free-text column, and add the
|
||||
-- partner_unit_events audit table.
|
||||
--
|
||||
-- Order of operations matters because constraint names are owned by their
|
||||
-- owning table; we rename the table first, then the columns/constraints/
|
||||
-- policies that postgres did not auto-rename.
|
||||
--
|
||||
-- Idempotent renames (DO $$ EXCEPTION WHEN undefined_object $$) are used
|
||||
-- for constraint/index/policy steps so re-runs after a partial apply on
|
||||
-- freshly-provisioned DBs (e.g. test DBs that may have run earlier
|
||||
-- migrations under different names) do not abort the chain.
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 1. Best-effort second seed of department_members from the legacy
|
||||
-- users.dezernat free-text field. Mirror of migration 019 — re-run before
|
||||
-- DROP COLUMN to capture any drift since 019 ran. Idempotent via
|
||||
-- ON CONFLICT DO NOTHING.
|
||||
-- ---------------------------------------------------------------------------
|
||||
INSERT INTO paliad.departments (id, name, lead_user_id, office, created_at, updated_at)
|
||||
SELECT gen_random_uuid(),
|
||||
btrim(u.dezernat),
|
||||
NULL,
|
||||
MIN(u.office),
|
||||
now(),
|
||||
now()
|
||||
FROM paliad.users u
|
||||
WHERE u.dezernat IS NOT NULL
|
||||
AND btrim(u.dezernat) <> ''
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM paliad.departments d2 WHERE d2.name = btrim(u.dezernat)
|
||||
)
|
||||
GROUP BY btrim(u.dezernat)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO paliad.department_members (department_id, user_id, created_at)
|
||||
SELECT d.id, u.id, now()
|
||||
FROM paliad.users u
|
||||
JOIN paliad.departments d
|
||||
ON d.name = btrim(u.dezernat)
|
||||
WHERE u.dezernat IS NOT NULL
|
||||
AND btrim(u.dezernat) <> ''
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 2. Drop the legacy free-text column. The structured side
|
||||
-- (department_members) is the source of truth from here on.
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE paliad.users DROP COLUMN IF EXISTS dezernat;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 3. Rename tables.
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE paliad.departments RENAME TO partner_units;
|
||||
ALTER TABLE paliad.department_members RENAME TO partner_unit_members;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 4. Rename junction column.
|
||||
-- ---------------------------------------------------------------------------
|
||||
ALTER TABLE paliad.partner_unit_members RENAME COLUMN department_id TO partner_unit_id;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 5. Rename constraints. Postgres auto-renames the underlying index for
|
||||
-- pkey/uniq constraints; standalone indexes are renamed in step 6.
|
||||
-- ---------------------------------------------------------------------------
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT departments_pkey TO partner_units_pkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT departments_lead_user_id_fkey TO partner_units_lead_user_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_units RENAME CONSTRAINT departments_office_check TO partner_units_office_check; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT department_members_pkey TO partner_unit_members_pkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT department_members_department_id_fkey TO partner_unit_members_partner_unit_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER TABLE paliad.partner_unit_members RENAME CONSTRAINT department_members_user_id_fkey TO partner_unit_members_user_id_fkey; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 6. Rename non-pkey indexes.
|
||||
-- ---------------------------------------------------------------------------
|
||||
DO $$ BEGIN ALTER INDEX paliad.departments_office_idx RENAME TO partner_units_office_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER INDEX paliad.departments_lead_idx RENAME TO partner_units_lead_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER INDEX paliad.department_members_user_idx RENAME TO partner_unit_members_user_idx; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 7. Rename RLS policies.
|
||||
-- ---------------------------------------------------------------------------
|
||||
DO $$ BEGIN ALTER POLICY departments_select ON paliad.partner_units RENAME TO partner_units_select; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY departments_write ON paliad.partner_units RENAME TO partner_units_write; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY department_members_select ON paliad.partner_unit_members RENAME TO partner_unit_members_select; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
DO $$ BEGIN ALTER POLICY department_members_write ON paliad.partner_unit_members RENAME TO partner_unit_members_write; EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 8. Audit table for partner-unit events. Mutations on partner_units +
|
||||
-- partner_unit_members emit one row each, written in the same tx by
|
||||
-- PartnerUnitService. The viewer in audit_service.go unions this source
|
||||
-- in alongside project_events / caldav_sync_log / reminder_log.
|
||||
--
|
||||
-- partner_unit_id is nullable + ON DELETE SET NULL so the historical
|
||||
-- 'deleted' event survives the cascade that removes the unit row.
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE paliad.partner_unit_events (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
partner_unit_id uuid NULL REFERENCES paliad.partner_units(id) ON DELETE SET NULL,
|
||||
actor_id uuid NOT NULL REFERENCES auth.users(id),
|
||||
event_type text NOT NULL CHECK (event_type IN (
|
||||
'created', 'updated', 'deleted', 'member_added', 'member_removed'
|
||||
)),
|
||||
-- Snapshot of the unit's name at event time so deleted units still show
|
||||
-- a human-readable label in the audit timeline (partner_unit_id is NULL
|
||||
-- on deleted, so we can't JOIN through to partner_units.name).
|
||||
unit_name text NOT NULL,
|
||||
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX partner_unit_events_unit_idx ON paliad.partner_unit_events(partner_unit_id, created_at DESC);
|
||||
CREATE INDEX partner_unit_events_actor_idx ON paliad.partner_unit_events(actor_id, created_at DESC);
|
||||
CREATE INDEX partner_unit_events_time_idx ON paliad.partner_unit_events(created_at DESC);
|
||||
|
||||
-- RLS: read access matches /api/partner-units (any authenticated user);
|
||||
-- writes only by global_admin (defence-in-depth — the service already
|
||||
-- gates with requireAdmin).
|
||||
ALTER TABLE paliad.partner_unit_events ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY partner_unit_events_select ON paliad.partner_unit_events
|
||||
FOR SELECT USING (auth.uid() IS NOT NULL);
|
||||
|
||||
CREATE POLICY partner_unit_events_write ON paliad.partner_unit_events
|
||||
FOR INSERT WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM paliad.users u
|
||||
WHERE u.id = auth.uid()
|
||||
AND u.global_role = 'global_admin'
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user