|
| 1 | +/** |
| 2 | + * Provider-specific unique identifier extractors for webhook idempotency |
| 3 | + */ |
| 4 | + |
| 5 | +function extractSlackIdentifier(body: any): string | null { |
| 6 | + if (body.event_id) { |
| 7 | + return body.event_id |
| 8 | + } |
| 9 | + |
| 10 | + if (body.event?.ts && body.team_id) { |
| 11 | + return `${body.team_id}:${body.event.ts}` |
| 12 | + } |
| 13 | + |
| 14 | + return null |
| 15 | +} |
| 16 | + |
| 17 | +function extractTwilioIdentifier(body: any): string | null { |
| 18 | + return body.MessageSid || body.CallSid || null |
| 19 | +} |
| 20 | + |
| 21 | +function extractStripeIdentifier(body: any): string | null { |
| 22 | + if (body.id && body.object === 'event') { |
| 23 | + return body.id |
| 24 | + } |
| 25 | + return null |
| 26 | +} |
| 27 | + |
| 28 | +function extractHubSpotIdentifier(body: any): string | null { |
| 29 | + if (Array.isArray(body) && body.length > 0 && body[0]?.eventId) { |
| 30 | + return String(body[0].eventId) |
| 31 | + } |
| 32 | + return null |
| 33 | +} |
| 34 | + |
| 35 | +function extractLinearIdentifier(body: any): string | null { |
| 36 | + if (body.action && body.data?.id) { |
| 37 | + return `${body.action}:${body.data.id}` |
| 38 | + } |
| 39 | + return null |
| 40 | +} |
| 41 | + |
| 42 | +function extractJiraIdentifier(body: any): string | null { |
| 43 | + if (body.webhookEvent && (body.issue?.id || body.project?.id)) { |
| 44 | + return `${body.webhookEvent}:${body.issue?.id || body.project?.id}` |
| 45 | + } |
| 46 | + return null |
| 47 | +} |
| 48 | + |
| 49 | +function extractMicrosoftTeamsIdentifier(body: any): string | null { |
| 50 | + if (body.value && Array.isArray(body.value) && body.value.length > 0) { |
| 51 | + const notification = body.value[0] |
| 52 | + if (notification.subscriptionId && notification.resourceData?.id) { |
| 53 | + return `${notification.subscriptionId}:${notification.resourceData.id}` |
| 54 | + } |
| 55 | + } |
| 56 | + return null |
| 57 | +} |
| 58 | + |
| 59 | +function extractAirtableIdentifier(body: any): string | null { |
| 60 | + if (body.cursor && typeof body.cursor === 'string') { |
| 61 | + return body.cursor |
| 62 | + } |
| 63 | + return null |
| 64 | +} |
| 65 | + |
| 66 | +const PROVIDER_EXTRACTORS: Record<string, (body: any) => string | null> = { |
| 67 | + slack: extractSlackIdentifier, |
| 68 | + twilio: extractTwilioIdentifier, |
| 69 | + twilio_voice: extractTwilioIdentifier, |
| 70 | + stripe: extractStripeIdentifier, |
| 71 | + hubspot: extractHubSpotIdentifier, |
| 72 | + linear: extractLinearIdentifier, |
| 73 | + jira: extractJiraIdentifier, |
| 74 | + 'microsoft-teams': extractMicrosoftTeamsIdentifier, |
| 75 | + airtable: extractAirtableIdentifier, |
| 76 | +} |
| 77 | + |
| 78 | +export function extractProviderIdentifierFromBody(provider: string, body: any): string | null { |
| 79 | + if (!body || typeof body !== 'object') { |
| 80 | + return null |
| 81 | + } |
| 82 | + |
| 83 | + const extractor = PROVIDER_EXTRACTORS[provider] |
| 84 | + return extractor ? extractor(body) : null |
| 85 | +} |
0 commit comments