Skip to content

Commit 583f5c4

Browse files
authored
fix(webflow): fix collection & site dropdown in webflow triggers (#2849)
* fix(webflow): fix collection & site dropdown in webflow triggers * added form submission trigger to webflow * fix(webflow): added form submission trigger and scope * fixed function signatures
1 parent 6ff68b3 commit 583f5c4

File tree

9 files changed

+476
-36
lines changed

9 files changed

+476
-36
lines changed

apps/sim/blocks/blocks/webflow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export const WebflowBlock: BlockConfig<WebflowResponse> = {
127127
...getTrigger('webflow_collection_item_created').subBlocks,
128128
...getTrigger('webflow_collection_item_changed').subBlocks,
129129
...getTrigger('webflow_collection_item_deleted').subBlocks,
130+
...getTrigger('webflow_form_submission').subBlocks,
130131
],
131132
tools: {
132133
access: [

apps/sim/lib/auth/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1858,7 +1858,7 @@ export const auth = betterAuth({
18581858
authorizationUrl: 'https://webflow.com/oauth/authorize',
18591859
tokenUrl: 'https://api.webflow.com/oauth/access_token',
18601860
userInfoUrl: 'https://api.webflow.com/v2/token/introspect',
1861-
scopes: ['sites:read', 'sites:write', 'cms:read', 'cms:write'],
1861+
scopes: ['sites:read', 'sites:write', 'cms:read', 'cms:write', 'forms:read'],
18621862
responseType: 'code',
18631863
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/webflow`,
18641864
getUserInfo: async (tokens) => {

apps/sim/lib/webhooks/processor.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,20 @@ export function shouldSkipWebhookEvent(webhook: any, body: any, requestId: strin
251251
}
252252
}
253253

254+
// Webflow collection filtering - filter by collectionId if configured
255+
if (webhook.provider === 'webflow') {
256+
const configuredCollectionId = providerConfig.collectionId
257+
if (configuredCollectionId) {
258+
const payloadCollectionId = body?.payload?.collectionId || body?.collectionId
259+
if (payloadCollectionId && payloadCollectionId !== configuredCollectionId) {
260+
logger.info(
261+
`[${requestId}] Webflow collection '${payloadCollectionId}' doesn't match configured collection '${configuredCollectionId}' for webhook ${webhook.id}, skipping`
262+
)
263+
return true
264+
}
265+
}
266+
}
267+
254268
return false
255269
}
256270

apps/sim/lib/webhooks/provider-subscriptions.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,7 @@ export async function createWebflowWebhookSubscription(
14001400
): Promise<string | undefined> {
14011401
try {
14021402
const { path, providerConfig } = webhookData
1403-
const { siteId, triggerId, collectionId, formId } = providerConfig || {}
1403+
const { siteId, triggerId, collectionId, formName } = providerConfig || {}
14041404

14051405
if (!siteId) {
14061406
webflowLogger.warn(`[${requestId}] Missing siteId for Webflow webhook creation.`, {
@@ -1455,17 +1455,10 @@ export async function createWebflowWebhookSubscription(
14551455
url: notificationUrl,
14561456
}
14571457

1458-
if (collectionId && webflowTriggerType.startsWith('collection_item_')) {
1458+
// Note: Webflow API only supports 'filter' for form_submission triggers.
1459+
if (formName && webflowTriggerType === 'form_submission') {
14591460
requestBody.filter = {
1460-
resource_type: 'collection',
1461-
resource_id: collectionId,
1462-
}
1463-
}
1464-
1465-
if (formId && webflowTriggerType === 'form_submission') {
1466-
requestBody.filter = {
1467-
resource_type: 'form',
1468-
resource_id: formId,
1461+
name: formName,
14691462
}
14701463
}
14711464

apps/sim/lib/webhooks/utils.server.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -800,15 +800,39 @@ export async function formatWebhookInput(
800800
}
801801

802802
if (foundWebhook.provider === 'webflow') {
803+
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}
804+
const triggerId = providerConfig.triggerId as string | undefined
805+
806+
// Form submission trigger
807+
if (triggerId === 'webflow_form_submission') {
808+
return {
809+
siteId: body?.siteId || '',
810+
formId: body?.formId || '',
811+
name: body?.name || '',
812+
id: body?.id || '',
813+
submittedAt: body?.submittedAt || '',
814+
data: body?.data || {},
815+
schema: body?.schema || {},
816+
formElementId: body?.formElementId || '',
817+
}
818+
}
819+
820+
// Collection item triggers (created, changed, deleted)
821+
// Webflow uses _cid for collection ID and _id for item ID
822+
const { _cid, _id, ...itemFields } = body || {}
803823
return {
804824
siteId: body?.siteId || '',
805-
formId: body?.formId || '',
806-
name: body?.name || '',
807-
id: body?.id || '',
808-
submittedAt: body?.submittedAt || '',
809-
data: body?.data || {},
810-
schema: body?.schema || {},
811-
formElementId: body?.formElementId || '',
825+
collectionId: _cid || body?.collectionId || '',
826+
payload: {
827+
id: _id || '',
828+
cmsLocaleId: itemFields?.cmsLocaleId || '',
829+
lastPublished: itemFields?.lastPublished || itemFields?.['last-published'] || '',
830+
lastUpdated: itemFields?.lastUpdated || itemFields?.['last-updated'] || '',
831+
createdOn: itemFields?.createdOn || itemFields?.['created-on'] || '',
832+
isArchived: itemFields?.isArchived || itemFields?._archived || false,
833+
isDraft: itemFields?.isDraft || itemFields?._draft || false,
834+
fieldData: itemFields,
835+
},
812836
}
813837
}
814838

apps/sim/triggers/webflow/collection_item_changed.ts

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import { createLogger } from '@sim/logger'
12
import { WebflowIcon } from '@/components/icons'
3+
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
24
import type { TriggerConfig } from '../types'
35

6+
const logger = createLogger('webflow-collection-item-changed-trigger')
7+
48
export const webflowCollectionItemChangedTrigger: TriggerConfig = {
59
id: 'webflow_collection_item_changed',
610
name: 'Collection Item Changed',
@@ -38,6 +42,58 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
3842
field: 'selectedTriggerId',
3943
value: 'webflow_collection_item_changed',
4044
},
45+
fetchOptions: async (blockId: string, _subBlockId: string) => {
46+
const credentialId = useSubBlockStore.getState().getValue(blockId, 'triggerCredentials') as
47+
| string
48+
| null
49+
if (!credentialId) {
50+
throw new Error('No Webflow credential selected')
51+
}
52+
try {
53+
const response = await fetch('/api/tools/webflow/sites', {
54+
method: 'POST',
55+
headers: { 'Content-Type': 'application/json' },
56+
body: JSON.stringify({ credential: credentialId }),
57+
})
58+
if (!response.ok) {
59+
throw new Error('Failed to fetch Webflow sites')
60+
}
61+
const data = await response.json()
62+
if (data.sites && Array.isArray(data.sites)) {
63+
return data.sites.map((site: { id: string; name: string }) => ({
64+
id: site.id,
65+
label: site.name,
66+
}))
67+
}
68+
return []
69+
} catch (error) {
70+
logger.error('Error fetching Webflow sites:', error)
71+
throw error
72+
}
73+
},
74+
fetchOptionById: async (blockId: string, _subBlockId: string, optionId: string) => {
75+
const credentialId = useSubBlockStore.getState().getValue(blockId, 'triggerCredentials') as
76+
| string
77+
| null
78+
if (!credentialId) return null
79+
try {
80+
const response = await fetch('/api/tools/webflow/sites', {
81+
method: 'POST',
82+
headers: { 'Content-Type': 'application/json' },
83+
body: JSON.stringify({ credential: credentialId, siteId: optionId }),
84+
})
85+
if (!response.ok) return null
86+
const data = await response.json()
87+
const site = data.sites?.find((s: { id: string }) => s.id === optionId)
88+
if (site) {
89+
return { id: site.id, label: site.name }
90+
}
91+
return null
92+
} catch {
93+
return null
94+
}
95+
},
96+
dependsOn: ['triggerCredentials'],
4197
},
4298
{
4399
id: 'collectionId',
@@ -52,6 +108,60 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
52108
field: 'selectedTriggerId',
53109
value: 'webflow_collection_item_changed',
54110
},
111+
fetchOptions: async (blockId: string, _subBlockId: string) => {
112+
const credentialId = useSubBlockStore.getState().getValue(blockId, 'triggerCredentials') as
113+
| string
114+
| null
115+
const siteId = useSubBlockStore.getState().getValue(blockId, 'siteId') as string | null
116+
if (!credentialId || !siteId) {
117+
return []
118+
}
119+
try {
120+
const response = await fetch('/api/tools/webflow/collections', {
121+
method: 'POST',
122+
headers: { 'Content-Type': 'application/json' },
123+
body: JSON.stringify({ credential: credentialId, siteId }),
124+
})
125+
if (!response.ok) {
126+
throw new Error('Failed to fetch Webflow collections')
127+
}
128+
const data = await response.json()
129+
if (data.collections && Array.isArray(data.collections)) {
130+
return data.collections.map((collection: { id: string; name: string }) => ({
131+
id: collection.id,
132+
label: collection.name,
133+
}))
134+
}
135+
return []
136+
} catch (error) {
137+
logger.error('Error fetching Webflow collections:', error)
138+
throw error
139+
}
140+
},
141+
fetchOptionById: async (blockId: string, _subBlockId: string, optionId: string) => {
142+
const credentialId = useSubBlockStore.getState().getValue(blockId, 'triggerCredentials') as
143+
| string
144+
| null
145+
const siteId = useSubBlockStore.getState().getValue(blockId, 'siteId') as string | null
146+
if (!credentialId || !siteId) return null
147+
try {
148+
const response = await fetch('/api/tools/webflow/collections', {
149+
method: 'POST',
150+
headers: { 'Content-Type': 'application/json' },
151+
body: JSON.stringify({ credential: credentialId, siteId }),
152+
})
153+
if (!response.ok) return null
154+
const data = await response.json()
155+
const collection = data.collections?.find((c: { id: string }) => c.id === optionId)
156+
if (collection) {
157+
return { id: collection.id, label: collection.name }
158+
}
159+
return null
160+
} catch {
161+
return null
162+
}
163+
},
164+
dependsOn: ['triggerCredentials', 'siteId'],
55165
},
56166
{
57167
id: 'triggerSave',
@@ -72,9 +182,9 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
72182
type: 'text',
73183
defaultValue: [
74184
'Connect your Webflow account using the "Select Webflow credential" button above.',
75-
'Enter your Webflow Site ID (found in the site URL or site settings).',
76-
'Optionally enter a Collection ID to monitor only specific collections.',
77-
'If no Collection ID is provided, the trigger will fire for items changed in any collection on the site.',
185+
'Select your Webflow site from the dropdown.',
186+
'Optionally select a collection to monitor only specific collections.',
187+
'If no collection is selected, the trigger will fire for items changed in any collection on the site.',
78188
'The webhook will trigger whenever an existing item is updated in the specified collection(s).',
79189
'Make sure your Webflow account has appropriate permissions for the specified site.',
80190
]

0 commit comments

Comments
 (0)