Skip to content

Commit 6678f62

Browse files
fix[backend](compilance_reports): migrated compilance reports from ol… (#2232)
* fix[backend](compilance_reports): migrated compilance reports from old table to new one * fix[backend](compilance_reports): added rollback marker robustness and unconditional sentinel deletion
1 parent 8badb0d commit 6678f62

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<databaseChangeLog
3+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
6+
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
7+
8+
<changeSet id="20260623001" author="Alex">
9+
<sql dbms="postgresql" splitStatements="true" stripComments="false">
10+
<![CDATA[
11+
-- 0. Persistent audit log so rollback can identify the rows this
12+
-- changeset created without depending on any user-editable column.
13+
-- Shared across future data migrations; PK keyed by (changeset_id, new_control_id).
14+
CREATE TABLE IF NOT EXISTS utm_compliance_migration_log (
15+
changeset_id VARCHAR(50) NOT NULL,
16+
new_control_id BIGINT NOT NULL,
17+
old_report_id BIGINT,
18+
migrated_at TIMESTAMP NOT NULL DEFAULT now(),
19+
PRIMARY KEY (changeset_id, new_control_id)
20+
);
21+
22+
-- 1. Sentinel "Legacy Reports" standard + section. The COALESCE in the
23+
-- control insert falls back to id=9000 when a legacy row's section is
24+
-- NULL. Range 9000+ is unused by existing seeds (which cap at 812).
25+
INSERT INTO utm_compliance_standard (id, standard_name, standard_description, system_owner)
26+
SELECT 9000, 'Legacy Reports', 'Auto-created landing standard for reports migrated from utm_compliance_report_config.', false
27+
WHERE NOT EXISTS (SELECT 1 FROM utm_compliance_standard WHERE id = 9000);
28+
29+
INSERT INTO utm_compliance_standard_section (id, standard_id, standard_section_name, standard_section_description)
30+
SELECT 9000, 9000, 'Unassigned', 'Migrated reports whose original section was NULL.'
31+
WHERE NOT EXISTS (SELECT 1 FROM utm_compliance_standard_section WHERE id = 9000);
32+
33+
-- 2. Pre-allocate one control id per legacy report and record it in the
34+
-- audit log. nextval() is atomic in PostgreSQL and produces one fresh
35+
-- value per source row in the SELECT — no collisions possible even
36+
-- under concurrent transactions, though this changeset runs at boot
37+
-- and has no concurrent writers in practice.
38+
INSERT INTO utm_compliance_migration_log (changeset_id, new_control_id, old_report_id)
39+
SELECT
40+
'20260623001',
41+
nextval(pg_get_serial_sequence('utm_compliance_control_config', 'id')),
42+
r.id
43+
FROM utm_compliance_report_config r
44+
WHERE NOT EXISTS (
45+
SELECT 1 FROM utm_compliance_control_config c
46+
WHERE c.control_name = COALESCE(r.config_report_name, 'Report ' || r.id::text)
47+
AND c.standard_section_id = COALESCE(r.standard_section_id, 9000)
48+
)
49+
AND NOT EXISTS (
50+
SELECT 1 FROM utm_compliance_migration_log m
51+
WHERE m.changeset_id = '20260623001' AND m.old_report_id = r.id
52+
);
53+
54+
-- 3. Insert controls with the reserved ids.
55+
INSERT INTO utm_compliance_control_config
56+
(id, standard_section_id, control_name, control_solution, control_remediation, control_strategy)
57+
SELECT
58+
m.new_control_id,
59+
COALESCE(r.standard_section_id, 9000),
60+
COALESCE(r.config_report_name, 'Report ' || r.id::text),
61+
r.config_solution,
62+
r.config_report_remediation,
63+
'ALL'
64+
FROM utm_compliance_migration_log m
65+
JOIN utm_compliance_report_config r ON r.id = m.old_report_id
66+
WHERE m.changeset_id = '20260623001'
67+
AND NOT EXISTS (
68+
SELECT 1 FROM utm_compliance_control_config c WHERE c.id = m.new_control_id
69+
);
70+
71+
-- 4. For each control whose legacy dashboard has visualizations, copy
72+
-- one query row per visualization. Actual SQL lives in
73+
-- utm_visualization.sql_query (column added in 20251203001).
74+
INSERT INTO utm_compliance_query_config
75+
(query_name, query_description, sql_query, evaluation_rule, rule_value,
76+
index_pattern_id, control_config_id)
77+
SELECT
78+
COALESCE(NULLIF(v.name, ''), 'Query for control ' || m.new_control_id),
79+
COALESCE(NULLIF(v.description, ''), 'Migrated from utm_visualization id=' || v.id),
80+
COALESCE(NULLIF(v.sql_query, ''), '-- TODO: define SQL query (placeholder from legacy migration)'),
81+
'NO_HITS_ALLOWED',
82+
NULL,
83+
COALESCE(v.id_pattern, (SELECT id FROM utm_index_pattern ORDER BY id LIMIT 1)),
84+
m.new_control_id
85+
FROM utm_compliance_migration_log m
86+
JOIN utm_compliance_report_config r ON r.id = m.old_report_id
87+
JOIN utm_dashboard_visualization dv ON dv.id_dashboard = r.dashboard_id
88+
JOIN utm_visualization v ON v.id = dv.id_visualization
89+
WHERE m.changeset_id = '20260623001';
90+
91+
-- 5. Placeholder query for controls whose dashboard had no
92+
-- visualizations (or whose dashboard_id was NULL), so the UI lists
93+
-- every migrated control with at least one editable query row.
94+
INSERT INTO utm_compliance_query_config
95+
(query_name, query_description, sql_query, evaluation_rule, rule_value,
96+
index_pattern_id, control_config_id)
97+
SELECT
98+
'Query for control ' || m.new_control_id,
99+
'Migrated from utm_compliance_report_config — no source visualization, query to be defined',
100+
'-- TODO: define SQL query (placeholder from legacy migration)',
101+
'NO_HITS_ALLOWED',
102+
NULL,
103+
(SELECT id FROM utm_index_pattern ORDER BY id LIMIT 1),
104+
m.new_control_id
105+
FROM utm_compliance_migration_log m
106+
WHERE m.changeset_id = '20260623001'
107+
AND NOT EXISTS (
108+
SELECT 1 FROM utm_compliance_query_config q
109+
WHERE q.control_config_id = m.new_control_id
110+
);
111+
]]>
112+
</sql>
113+
<rollback>
114+
<sql dbms="postgresql" splitStatements="true" stripComments="false">
115+
<![CDATA[
116+
-- Delete controls created by this changeset. The audit log is the
117+
-- source of truth — independent of any column the application may
118+
-- have edited after migration. Cascade FK from utm_compliance_query_config
119+
-- (defined in 20260112003) removes child query rows automatically.
120+
DELETE FROM utm_compliance_control_config
121+
WHERE id IN (
122+
SELECT new_control_id FROM utm_compliance_migration_log
123+
WHERE changeset_id = '20260623001'
124+
);
125+
126+
-- Conditionally remove the sentinel section/standard: only if
127+
-- nothing else now references id=9000 (so we don't orphan rows
128+
-- that may have been attached manually post-migration).
129+
DELETE FROM utm_compliance_standard_section
130+
WHERE id = 9000
131+
AND NOT EXISTS (
132+
SELECT 1 FROM utm_compliance_control_config c WHERE c.standard_section_id = 9000
133+
);
134+
135+
DELETE FROM utm_compliance_standard
136+
WHERE id = 9000
137+
AND NOT EXISTS (
138+
SELECT 1 FROM utm_compliance_standard_section s WHERE s.standard_id = 9000
139+
);
140+
141+
-- Clear this changeset's audit rows; keep the log table itself
142+
-- intact for future migrations to reuse.
143+
DELETE FROM utm_compliance_migration_log WHERE changeset_id = '20260623001';
144+
]]>
145+
</sql>
146+
</rollback>
147+
</changeSet>
148+
</databaseChangeLog>

backend/src/main/resources/config/liquibase/master.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,6 @@
601601

602602
<include file="/config/liquibase/changelog/20260615001_fix_response_action_template_syntax.xml" relativeToChangelogFile="false"/>
603603

604+
<include file="/config/liquibase/changelog/20260623001_migrate_compliance_report_config_to_control_config.xml" relativeToChangelogFile="false"/>
605+
604606
</databaseChangeLog>

0 commit comments

Comments
 (0)