Skip to content

Commit 0b39f55

Browse files
committed
fix(confluence): use payload-based filtering instead of nonexistent event field
Confluence Cloud webhooks don't include an event/webhookEvent field in the body (unlike Jira). Replaced broken event string matching with structural payload filtering that checks which entity key is present.
1 parent 59ea2fe commit 0b39f55

File tree

2 files changed

+71
-125
lines changed

2 files changed

+71
-125
lines changed

apps/sim/lib/webhooks/processor.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { getWorkspaceBilledAccountUserId } from '@/lib/workspaces/utils'
2828
import { resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
2929
import { executeWebhookJob } from '@/background/webhook-execution'
3030
import { resolveEnvVarReferences } from '@/executor/utils/reference-validation'
31-
import { isConfluenceEventMatch } from '@/triggers/confluence/utils'
31+
import { isConfluencePayloadMatch } from '@/triggers/confluence/utils'
3232
import { isGitHubEventMatch } from '@/triggers/github/utils'
3333
import { isHubSpotContactEventMatch } from '@/triggers/hubspot/utils'
3434
import { isJiraEventMatch } from '@/triggers/jira/utils'
@@ -956,31 +956,24 @@ export async function queueWebhookExecution(
956956
}
957957
}
958958

959-
// Confluence event filtering for event-specific triggers
960959
if (foundWebhook.provider === 'confluence') {
961960
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
962961
const triggerId = providerConfig.triggerId as string | undefined
963962

964-
if (triggerId && triggerId !== 'confluence_webhook') {
965-
// Confluence may use `event`, `webhookEvent`, or neither — check both
966-
const event = (body.event || body.webhookEvent) as string | undefined
967-
968-
if (event && !isConfluenceEventMatch(triggerId, event)) {
969-
logger.debug(
970-
`[${options.requestId}] Confluence event mismatch for trigger ${triggerId}. Event: ${event}. Skipping execution.`,
971-
{
972-
webhookId: foundWebhook.id,
973-
workflowId: foundWorkflow.id,
974-
triggerId,
975-
receivedEvent: event,
976-
}
977-
)
963+
if (triggerId && !isConfluencePayloadMatch(triggerId, body)) {
964+
logger.debug(
965+
`[${options.requestId}] Confluence payload mismatch for trigger ${triggerId}. Skipping execution.`,
966+
{
967+
webhookId: foundWebhook.id,
968+
workflowId: foundWorkflow.id,
969+
triggerId,
970+
bodyKeys: Object.keys(body),
971+
}
972+
)
978973

979-
// Return 200 OK to prevent Confluence from retrying
980-
return NextResponse.json({
981-
message: 'Event type does not match trigger configuration. Ignoring.',
982-
})
983-
}
974+
return NextResponse.json({
975+
message: 'Payload does not match trigger configuration. Ignoring.',
976+
})
984977
}
985978
}
986979

apps/sim/triggers/confluence/utils.ts

Lines changed: 57 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,25 @@
11
import type { SubBlockConfig } from '@/blocks/types'
22
import type { TriggerOutput } from '@/triggers/types'
33

4-
/**
5-
* Shared trigger dropdown options for all Confluence triggers
6-
*/
74
export const confluenceTriggerOptions = [
8-
// Page Events
95
{ label: 'Page Created', id: 'confluence_page_created' },
106
{ label: 'Page Updated', id: 'confluence_page_updated' },
117
{ label: 'Page Removed', id: 'confluence_page_removed' },
128
{ label: 'Page Moved', id: 'confluence_page_moved' },
13-
// Comment Events
149
{ label: 'Comment Created', id: 'confluence_comment_created' },
1510
{ label: 'Comment Removed', id: 'confluence_comment_removed' },
16-
// Blog Events
1711
{ label: 'Blog Post Created', id: 'confluence_blog_created' },
1812
{ label: 'Blog Post Updated', id: 'confluence_blog_updated' },
1913
{ label: 'Blog Post Removed', id: 'confluence_blog_removed' },
20-
// Attachment Events
2114
{ label: 'Attachment Created', id: 'confluence_attachment_created' },
2215
{ label: 'Attachment Removed', id: 'confluence_attachment_removed' },
23-
// Space Events
2416
{ label: 'Space Created', id: 'confluence_space_created' },
2517
{ label: 'Space Updated', id: 'confluence_space_updated' },
26-
// Label Events
2718
{ label: 'Label Added', id: 'confluence_label_added' },
2819
{ label: 'Label Removed', id: 'confluence_label_removed' },
29-
// Generic
3020
{ label: 'Generic Webhook (All Events)', id: 'confluence_webhook' },
3121
]
3222

33-
/**
34-
* Generates setup instructions for Confluence webhooks
35-
*/
3623
export function confluenceSetupInstructions(eventType: string): string {
3724
const instructions = [
3825
'<strong>Note:</strong> You must have admin permissions in your Confluence workspace to create webhooks. See the <a href="https://developer.atlassian.com/cloud/confluence/modules/webhook/" target="_blank" rel="noopener noreferrer">Confluence webhook documentation</a> for details.',
@@ -52,9 +39,6 @@ export function confluenceSetupInstructions(eventType: string): string {
5239
.join('')
5340
}
5441

55-
/**
56-
* Extra fields shared across Confluence triggers (webhook secret + optional domain)
57-
*/
5842
export function buildConfluenceExtraFields(triggerId: string): SubBlockConfig[] {
5943
return [
6044
{
@@ -82,10 +66,6 @@ export function buildConfluenceExtraFields(triggerId: string): SubBlockConfig[]
8266
]
8367
}
8468

85-
/**
86-
* Extra fields for attachment triggers that support file downloads.
87-
* Adds email, API token, and include toggle on top of the base fields.
88-
*/
8969
export function buildConfluenceAttachmentExtraFields(triggerId: string): SubBlockConfig[] {
9070
return [
9171
...buildConfluenceExtraFields(triggerId),
@@ -128,7 +108,6 @@ export function buildConfluenceAttachmentExtraFields(triggerId: string): SubBloc
128108

129109
/**
130110
* Base webhook outputs common to all Confluence triggers.
131-
* Maps to the actual top-level fields in the Confluence webhook payload.
132111
*/
133112
function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
134113
return {
@@ -148,40 +127,38 @@ function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
148127
}
149128

150129
/**
151-
* Shared content-entity output fields present on page, blog, comment, and attachment objects
152-
* in the actual Confluence webhook payload.
130+
* Shared content-entity output fields present on page, blog, comment, and attachment objects.
153131
*/
154132
function buildContentEntityFields(): Record<string, TriggerOutput> {
155133
return {
156134
id: { type: 'number', description: 'Content ID' },
157135
title: { type: 'string', description: 'Content title' },
158-
contentType: { type: 'string', description: 'Content type (page, blogpost, comment, attachment)' },
136+
contentType: {
137+
type: 'string',
138+
description: 'Content type (page, blogpost, comment, attachment)',
139+
},
159140
version: { type: 'number', description: 'Version number' },
160141
spaceKey: { type: 'string', description: 'Space key the content belongs to' },
161142
creatorAccountId: { type: 'string', description: 'Account ID of the creator' },
162143
lastModifierAccountId: { type: 'string', description: 'Account ID of the last modifier' },
163144
self: { type: 'string', description: 'URL link to the content' },
164145
creationDate: { type: 'number', description: 'Creation timestamp (Unix epoch milliseconds)' },
165-
modificationDate: { type: 'number', description: 'Last modification timestamp (Unix epoch milliseconds)' },
146+
modificationDate: {
147+
type: 'number',
148+
description: 'Last modification timestamp (Unix epoch milliseconds)',
149+
},
166150
}
167151
}
168152

169-
/**
170-
* Page-related outputs for page events.
171-
* Matches the actual Confluence webhook payload for page_created, page_updated, etc.
172-
*/
153+
/** Page-related outputs for page events. */
173154
export function buildPageOutputs(): Record<string, TriggerOutput> {
174155
return {
175156
...buildBaseWebhookOutputs(),
176157
page: buildContentEntityFields(),
177158
}
178159
}
179160

180-
/**
181-
* Comment-related outputs for comment events.
182-
* Matches the actual Confluence webhook payload for comment_created, comment_removed.
183-
* The comment object contains a `parent` field with the full parent page/blog object.
184-
*/
161+
/** Comment-related outputs for comment events. */
185162
export function buildCommentOutputs(): Record<string, TriggerOutput> {
186163
return {
187164
...buildBaseWebhookOutputs(),
@@ -198,21 +175,15 @@ export function buildCommentOutputs(): Record<string, TriggerOutput> {
198175
}
199176
}
200177

201-
/**
202-
* Blog post outputs for blog events.
203-
* Matches the actual Confluence webhook payload for blog_created, blog_updated, etc.
204-
*/
178+
/** Blog post outputs for blog events. */
205179
export function buildBlogOutputs(): Record<string, TriggerOutput> {
206180
return {
207181
...buildBaseWebhookOutputs(),
208182
blog: buildContentEntityFields(),
209183
}
210184
}
211185

212-
/**
213-
* Attachment-related outputs for attachment events.
214-
* Matches the actual Confluence webhook payload for attachment_created, attachment_removed.
215-
*/
186+
/** Attachment-related outputs for attachment events. */
216187
export function buildAttachmentOutputs(): Record<string, TriggerOutput> {
217188
return {
218189
...buildBaseWebhookOutputs(),
@@ -234,10 +205,7 @@ export function buildAttachmentOutputs(): Record<string, TriggerOutput> {
234205
}
235206
}
236207

237-
/**
238-
* Space-related outputs for space events.
239-
* Matches the actual Confluence webhook payload for space_created, space_updated.
240-
*/
208+
/** Space-related outputs for space events. */
241209
export function buildSpaceOutputs(): Record<string, TriggerOutput> {
242210
return {
243211
...buildBaseWebhookOutputs(),
@@ -249,10 +217,7 @@ export function buildSpaceOutputs(): Record<string, TriggerOutput> {
249217
}
250218
}
251219

252-
/**
253-
* Label-related outputs for label events.
254-
* Matches the actual Confluence webhook payload for label_added, label_removed.
255-
*/
220+
/** Label-related outputs for label events. */
256221
export function buildLabelOutputs(): Record<string, TriggerOutput> {
257222
return {
258223
...buildBaseWebhookOutputs(),
@@ -269,10 +234,7 @@ export function buildLabelOutputs(): Record<string, TriggerOutput> {
269234
}
270235
}
271236

272-
/**
273-
* Combined outputs for the generic webhook trigger (all events).
274-
* Uses json type for entity fields since the shape varies by event type.
275-
*/
237+
/** Combined outputs for the generic webhook trigger (all events). */
276238
export function buildGenericWebhookOutputs(): Record<string, TriggerOutput> {
277239
return {
278240
...buildBaseWebhookOutputs(),
@@ -291,9 +253,6 @@ export function buildGenericWebhookOutputs(): Record<string, TriggerOutput> {
291253
}
292254
}
293255

294-
/**
295-
* Extracts page data from a Confluence webhook payload.
296-
*/
297256
export function extractPageData(body: any) {
298257
return {
299258
timestamp: body.timestamp,
@@ -303,9 +262,6 @@ export function extractPageData(body: any) {
303262
}
304263
}
305264

306-
/**
307-
* Extracts comment data from a Confluence webhook payload.
308-
*/
309265
export function extractCommentData(body: any) {
310266
return {
311267
timestamp: body.timestamp,
@@ -315,9 +271,6 @@ export function extractCommentData(body: any) {
315271
}
316272
}
317273

318-
/**
319-
* Extracts blog post data from a Confluence webhook payload.
320-
*/
321274
export function extractBlogData(body: any) {
322275
return {
323276
timestamp: body.timestamp,
@@ -327,9 +280,6 @@ export function extractBlogData(body: any) {
327280
}
328281
}
329282

330-
/**
331-
* Extracts attachment data from a Confluence webhook payload.
332-
*/
333283
export function extractAttachmentData(body: any) {
334284
return {
335285
timestamp: body.timestamp,
@@ -339,9 +289,6 @@ export function extractAttachmentData(body: any) {
339289
}
340290
}
341291

342-
/**
343-
* Extracts space data from a Confluence webhook payload.
344-
*/
345292
export function extractSpaceData(body: any) {
346293
return {
347294
timestamp: body.timestamp,
@@ -351,9 +298,6 @@ export function extractSpaceData(body: any) {
351298
}
352299
}
353300

354-
/**
355-
* Extracts label data from a Confluence webhook payload.
356-
*/
357301
export function extractLabelData(body: any) {
358302
return {
359303
timestamp: body.timestamp,
@@ -365,43 +309,52 @@ export function extractLabelData(body: any) {
365309
}
366310

367311
/**
368-
* Checks if a Confluence webhook event matches a specific trigger
312+
* Infers the entity category from a Confluence webhook payload.
313+
* Unlike Jira, Confluence payloads have no `event`/`webhookEvent` field —
314+
* we detect the category by which entity key is present in the body.
369315
*/
370-
export function isConfluenceEventMatch(triggerId: string, event: string): boolean {
371-
const eventMappings: Record<string, string[]> = {
372-
// Page events
373-
confluence_page_created: ['page_created'],
374-
confluence_page_updated: ['page_updated'],
375-
confluence_page_removed: ['page_removed', 'page_trashed'],
376-
confluence_page_moved: ['page_moved'],
377-
// Comment events
378-
confluence_comment_created: ['comment_created'],
379-
confluence_comment_removed: ['comment_removed'],
380-
// Blog events
381-
confluence_blog_created: ['blog_created'],
382-
confluence_blog_updated: ['blog_updated'],
383-
confluence_blog_removed: ['blog_removed', 'blog_trashed'],
384-
// Attachment events
385-
confluence_attachment_created: ['attachment_created'],
386-
confluence_attachment_removed: ['attachment_removed', 'attachment_trashed'],
387-
// Space events
388-
confluence_space_created: ['space_created'],
389-
confluence_space_updated: ['space_updated'],
390-
// Label events
391-
confluence_label_added: ['label_added', 'label_created'],
392-
confluence_label_removed: ['label_removed', 'label_deleted'],
393-
// Generic webhook accepts all events
394-
confluence_webhook: ['*'],
316+
export function inferConfluenceEntityCategory(body: Record<string, unknown>): string | null {
317+
if (body.comment) return 'comment'
318+
if (body.attachment) return 'attachment'
319+
if (body.blog || body.blogpost) return 'blog'
320+
if (body.label) return 'label'
321+
if (body.page) return 'page'
322+
if (body.space) return 'space'
323+
return null
324+
}
325+
326+
/** Checks if a Confluence webhook payload matches a trigger's expected entity category. */
327+
export function isConfluencePayloadMatch(
328+
triggerId: string,
329+
body: Record<string, unknown>
330+
): boolean {
331+
if (triggerId === 'confluence_webhook') {
332+
return true
395333
}
396334

397-
const expectedEvents = eventMappings[triggerId]
398-
if (!expectedEvents) {
399-
return false
335+
const triggerCategoryMap: Record<string, string> = {
336+
confluence_page_created: 'page',
337+
confluence_page_updated: 'page',
338+
confluence_page_removed: 'page',
339+
confluence_page_moved: 'page',
340+
confluence_comment_created: 'comment',
341+
confluence_comment_removed: 'comment',
342+
confluence_blog_created: 'blog',
343+
confluence_blog_updated: 'blog',
344+
confluence_blog_removed: 'blog',
345+
confluence_attachment_created: 'attachment',
346+
confluence_attachment_removed: 'attachment',
347+
confluence_space_created: 'space',
348+
confluence_space_updated: 'space',
349+
confluence_label_added: 'label',
350+
confluence_label_removed: 'label',
400351
}
401352

402-
if (expectedEvents.includes('*')) {
403-
return true
353+
const expectedCategory = triggerCategoryMap[triggerId]
354+
if (!expectedCategory) {
355+
return false
404356
}
405357

406-
return expectedEvents.includes(event)
358+
const actualCategory = inferConfluenceEntityCategory(body)
359+
return actualCategory === expectedCategory
407360
}

0 commit comments

Comments
 (0)