@@ -44,40 +44,51 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
4444 url := quoteSQLString (* opts .UpdateEntityURL )
4545 caseStatements += fmt .Sprintf (`
4646 WHEN 'entity.update.version' THEN
47- -- Deduplicate: only fire for the first audit row for this entity UUID
48- IF EXISTS (
49- SELECT 1
50- FROM %s a
51- WHERE a.action = 'entity.update.version'
52- AND a.details->'entity'->>'uuid' = NEW.details->'entity'->>'uuid'
53- AND a.details ? 'webhook_sent'
54- ) THEN
55- -- Already processed this entity
47+ DECLARE
48+ entity_data jsonb;
49+ entity_payload jsonb;
50+ BEGIN
51+ -- Deduplicate: only fire for the first audit row for this entity UUID
52+ IF EXISTS (
53+ SELECT 1
54+ FROM %s a
55+ WHERE a.action = 'entity.update.version'
56+ AND a.details->'entity'->>'uuid' = NEW.details->'entity'->>'uuid'
57+ AND a.details ? 'webhook_sent'
58+ ) THEN
59+ -- Already processed this entity
60+ NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
61+ RETURN NEW;
62+ END IF;
63+
64+ SELECT entity_defs."data"
65+ INTO entity_data
66+ FROM entity_defs
67+ WHERE entity_defs.id = (NEW.details->>'entityDefId')::int;
68+
69+ IF entity_data IS NULL THEN
70+ RAISE WARNING 'Entity def not found for entityDefId: %%', NEW.details->>'entityDefId';
71+ NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
72+ RETURN NEW;
73+ END IF;
74+
75+ entity_payload := jsonb_build_object(
76+ 'type', 'entity.update.version',
77+ 'id', (NEW.details->'entity'->>'uuid'),
78+ 'data', entity_data
79+ );
80+
81+ PERFORM http((
82+ 'POST',
83+ %s,
84+ http_headers(%s),
85+ 'application/json',
86+ entity_payload::text
87+ )::http_request);
88+
89+ -- Mark as processed
5690 NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
57- RETURN NEW;
58- END IF;
59-
60- SELECT entity_defs."data"
61- INTO result_data
62- FROM entity_defs
63- WHERE entity_defs.id = (NEW.details->>'entityDefId')::int;
64-
65- webhook_payload := jsonb_build_object(
66- 'type', 'entity.update.version',
67- 'id', (NEW.details->'entity'->>'uuid'),
68- 'data', result_data
69- );
70-
71- PERFORM http((
72- 'POST',
73- %s,
74- http_headers(%s),
75- 'application/json',
76- webhook_payload::text
77- )::http_request);
78-
79- -- Mark as processed
80- NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
91+ END;
8192 ` , tableName , url , headersSQL )
8293 }
8394
@@ -88,40 +99,51 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
8899 url := quoteSQLString (* opts .NewSubmissionURL )
89100 caseStatements += fmt .Sprintf (`
90101 WHEN 'submission.create' THEN
91- -- Deduplicate by instanceId
92- IF EXISTS (
93- SELECT 1
94- FROM %s a
95- WHERE a.action = 'submission.create'
96- AND a.details->>'instanceId' = NEW.details->>'instanceId'
97- AND a.details ? 'webhook_sent'
98- ) THEN
99- -- Already processed
102+ DECLARE
103+ submission_data jsonb;
104+ submission_payload jsonb;
105+ BEGIN
106+ -- Deduplicate by instanceId
107+ IF EXISTS (
108+ SELECT 1
109+ FROM %s a
110+ WHERE a.action = 'submission.create'
111+ AND a.details->>'instanceId' = NEW.details->>'instanceId'
112+ AND a.details ? 'webhook_sent'
113+ ) THEN
114+ -- Already processed
115+ NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
116+ RETURN NEW;
117+ END IF;
118+
119+ SELECT jsonb_build_object('xml', submission_defs.xml)
120+ INTO submission_data
121+ FROM submission_defs
122+ WHERE submission_defs.id = (NEW.details->>'submissionDefId')::int;
123+
124+ IF submission_data IS NULL THEN
125+ RAISE WARNING 'Submission def not found for submissionDefId: %%', NEW.details->>'submissionDefId';
126+ NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
127+ RETURN NEW;
128+ END IF;
129+
130+ submission_payload := jsonb_build_object(
131+ 'type', 'submission.create',
132+ 'id', (NEW.details->>'instanceId'),
133+ 'data', submission_data
134+ );
135+
136+ PERFORM http((
137+ 'POST',
138+ %s,
139+ http_headers(%s),
140+ 'application/json',
141+ submission_payload::text
142+ )::http_request);
143+
144+ -- Mark as processed
100145 NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
101- RETURN NEW;
102- END IF;
103-
104- SELECT jsonb_build_object('xml', submission_defs.xml)
105- INTO result_data
106- FROM submission_defs
107- WHERE submission_defs.id = (NEW.details->>'submissionDefId')::int;
108-
109- webhook_payload := jsonb_build_object(
110- 'type', 'submission.create',
111- 'id', (NEW.details->>'instanceId'),
112- 'data', result_data
113- );
114-
115- PERFORM http((
116- 'POST',
117- %s,
118- http_headers(%s),
119- 'application/json',
120- webhook_payload::text
121- )::http_request);
122-
123- -- Mark as processed
124- NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
146+ END;
125147 ` , tableName , url , headersSQL )
126148 }
127149
@@ -132,38 +154,42 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
132154 url := quoteSQLString (* opts .ReviewSubmissionURL )
133155 caseStatements += fmt .Sprintf (`
134156 WHEN 'submission.update' THEN
135- -- Deduplicate by instanceId + reviewState
136- IF EXISTS (
137- SELECT 1
138- FROM %s a
139- WHERE a.action = 'submission.update'
140- AND a.details->>'instanceId' = NEW.details->>'instanceId'
141- AND a.details->>'reviewState' = NEW.details->>'reviewState'
142- AND a.details ? 'webhook_sent'
143- ) THEN
144- -- Already processed
157+ DECLARE
158+ review_payload jsonb;
159+ BEGIN
160+ -- Deduplicate by instanceId + reviewState
161+ IF EXISTS (
162+ SELECT 1
163+ FROM %s a
164+ WHERE a.action = 'submission.update'
165+ AND a.details->>'instanceId' = NEW.details->>'instanceId'
166+ AND a.details->>'reviewState' = NEW.details->>'reviewState'
167+ AND a.details ? 'webhook_sent'
168+ ) THEN
169+ -- Already processed
170+ NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
171+ RETURN NEW;
172+ END IF;
173+
174+ review_payload := jsonb_build_object(
175+ 'type', 'submission.update',
176+ 'id', (NEW.details->>'instanceId'),
177+ 'data', jsonb_build_object(
178+ 'reviewState', NEW.details->>'reviewState'
179+ )
180+ );
181+
182+ PERFORM http((
183+ 'POST',
184+ %s,
185+ http_headers(%s),
186+ 'application/json',
187+ review_payload::text
188+ )::http_request);
189+
190+ -- Mark as processed
145191 NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
146- RETURN NEW;
147- END IF;
148-
149- webhook_payload := jsonb_build_object(
150- 'type', 'submission.update',
151- 'id', (NEW.details->>'instanceId'),
152- 'data', jsonb_build_object(
153- 'reviewState', NEW.details->>'reviewState'
154- )
155- );
156-
157- PERFORM http((
158- 'POST',
159- %s,
160- http_headers(%s),
161- 'application/json',
162- webhook_payload::text
163- )::http_request);
164-
165- -- Mark as processed
166- NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
192+ END;
167193 ` , tableName , url , headersSQL )
168194 }
169195
@@ -178,8 +204,6 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
178204 $$
179205 DECLARE
180206 action_type text;
181- result_data jsonb;
182- webhook_payload jsonb;
183207 BEGIN
184208 action_type := NEW.action;
185209
0 commit comments