d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
528 lines
17 KiB
TypeScript
528 lines
17 KiB
TypeScript
import type { SubBlockConfig } from '@/blocks/types'
|
|
import type { TriggerOutput } from '@/triggers/types'
|
|
|
|
export const attioTriggerOptions = [
|
|
{ label: 'Record Created', id: 'attio_record_created' },
|
|
{ label: 'Record Updated', id: 'attio_record_updated' },
|
|
{ label: 'Record Deleted', id: 'attio_record_deleted' },
|
|
{ label: 'Record Merged', id: 'attio_record_merged' },
|
|
{ label: 'Note Created', id: 'attio_note_created' },
|
|
{ label: 'Note Updated', id: 'attio_note_updated' },
|
|
{ label: 'Note Deleted', id: 'attio_note_deleted' },
|
|
{ label: 'Task Created', id: 'attio_task_created' },
|
|
{ label: 'Task Updated', id: 'attio_task_updated' },
|
|
{ label: 'Task Deleted', id: 'attio_task_deleted' },
|
|
{ label: 'Comment Created', id: 'attio_comment_created' },
|
|
{ label: 'Comment Resolved', id: 'attio_comment_resolved' },
|
|
{ label: 'Comment Unresolved', id: 'attio_comment_unresolved' },
|
|
{ label: 'Comment Deleted', id: 'attio_comment_deleted' },
|
|
{ label: 'List Entry Created', id: 'attio_list_entry_created' },
|
|
{ label: 'List Entry Updated', id: 'attio_list_entry_updated' },
|
|
{ label: 'List Entry Deleted', id: 'attio_list_entry_deleted' },
|
|
{ label: 'List Created', id: 'attio_list_created' },
|
|
{ label: 'List Updated', id: 'attio_list_updated' },
|
|
{ label: 'List Deleted', id: 'attio_list_deleted' },
|
|
{ label: 'Workspace Member Created', id: 'attio_workspace_member_created' },
|
|
{ label: 'Generic Webhook (All Events)', id: 'attio_webhook' },
|
|
]
|
|
|
|
export function attioSetupInstructions(): string {
|
|
const instructions = [
|
|
'<strong>Note:</strong> Webhooks are automatically created in Attio when you deploy this workflow, and deleted when you undeploy. See the <a href="https://docs.attio.com/rest-api/guides/webhooks" target="_blank" rel="noopener noreferrer">Attio webhook documentation</a> for details.',
|
|
'Connect your <strong>Attio account</strong> using the credential selector above.',
|
|
'<strong>Deploy</strong> the workflow — a webhook will be created automatically in your Attio workspace.',
|
|
]
|
|
|
|
return instructions
|
|
.map(
|
|
(instruction, index) =>
|
|
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
|
|
)
|
|
.join('')
|
|
}
|
|
|
|
/**
|
|
* Builds subBlocks for an Attio trigger with OAuth credentials and automatic webhook lifecycle.
|
|
* Used by both the primary trigger (with dropdown) and secondary triggers.
|
|
*/
|
|
export function buildAttioTriggerSubBlocks(triggerId: string): SubBlockConfig[] {
|
|
return [
|
|
{
|
|
id: 'triggerCredentials',
|
|
title: 'Attio Account',
|
|
type: 'oauth-input',
|
|
serviceId: 'attio',
|
|
mode: 'trigger',
|
|
required: true,
|
|
condition: { field: 'selectedTriggerId', value: triggerId },
|
|
},
|
|
{
|
|
id: 'triggerInstructions',
|
|
title: 'Setup Instructions',
|
|
hideFromPreview: true,
|
|
type: 'text',
|
|
defaultValue: attioSetupInstructions(),
|
|
mode: 'trigger',
|
|
condition: { field: 'selectedTriggerId', value: triggerId },
|
|
},
|
|
]
|
|
}
|
|
|
|
/**
|
|
* Base webhook outputs common to all Attio triggers.
|
|
*/
|
|
function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
eventType: {
|
|
type: 'string',
|
|
description: 'The type of event (e.g. record.created, note.created)',
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record event outputs for record triggers.
|
|
*/
|
|
function buildRecordIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
objectId: { type: 'string', description: 'The object type ID (e.g. people, companies)' },
|
|
recordId: { type: 'string', description: 'The record ID' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record updated event outputs (includes attributeId).
|
|
*/
|
|
function buildRecordUpdatedIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildRecordIdOutputs(),
|
|
attributeId: {
|
|
type: 'string',
|
|
description: 'The ID of the attribute that was updated on the record',
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Record merged event outputs.
|
|
* Attio payload: id.record_id (winner), duplicate_object_id, duplicate_record_id (loser).
|
|
*/
|
|
function buildRecordMergedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
objectId: { type: 'string', description: 'The object type ID of the surviving record' },
|
|
recordId: { type: 'string', description: 'The surviving record ID after merge' },
|
|
duplicateObjectId: {
|
|
type: 'string',
|
|
description: 'The object type ID of the merged-away record',
|
|
},
|
|
duplicateRecordId: { type: 'string', description: 'The record ID that was merged away' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Note event outputs.
|
|
*/
|
|
function buildNoteIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
noteId: { type: 'string', description: 'The note ID' },
|
|
parentObjectId: { type: 'string', description: 'The parent object type ID' },
|
|
parentRecordId: { type: 'string', description: 'The parent record ID' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Task event outputs.
|
|
* Attio task webhook payloads only contain workspace_id and task_id.
|
|
*/
|
|
function buildTaskIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
taskId: { type: 'string', description: 'The task ID' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Comment event outputs.
|
|
* Attio payload uses object_id/record_id (not parent_*), plus list_id/entry_id.
|
|
*/
|
|
function buildCommentIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
threadId: { type: 'string', description: 'The thread ID' },
|
|
commentId: { type: 'string', description: 'The comment ID' },
|
|
objectId: { type: 'string', description: 'The object type ID' },
|
|
recordId: { type: 'string', description: 'The record ID' },
|
|
listId: { type: 'string', description: 'The list ID (if comment is on a list entry)' },
|
|
entryId: { type: 'string', description: 'The list entry ID (if comment is on a list entry)' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List entry event outputs.
|
|
*/
|
|
function buildListEntryIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
listId: { type: 'string', description: 'The list ID' },
|
|
entryId: { type: 'string', description: 'The list entry ID' },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List entry updated event outputs (includes attributeId).
|
|
*/
|
|
function buildListEntryUpdatedIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildListEntryIdOutputs(),
|
|
attributeId: {
|
|
type: 'string',
|
|
description: 'The ID of the attribute that was updated on the list entry',
|
|
},
|
|
}
|
|
}
|
|
|
|
/** Record created/deleted outputs. */
|
|
export function buildRecordOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildRecordIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Record updated outputs (includes attributeId). */
|
|
export function buildRecordUpdatedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildRecordUpdatedIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Record merged outputs. */
|
|
export function buildRecordMergedEventOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildRecordMergedOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Note event outputs. */
|
|
export function buildNoteOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildNoteIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Task event outputs. */
|
|
export function buildTaskOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildTaskIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Comment event outputs. */
|
|
export function buildCommentOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildCommentIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List event outputs.
|
|
*/
|
|
function buildListIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
listId: { type: 'string', description: 'The list ID' },
|
|
}
|
|
}
|
|
|
|
/** List created/updated/deleted outputs. */
|
|
export function buildListOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildListIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Workspace member event outputs.
|
|
*/
|
|
function buildWorkspaceMemberIdOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
workspaceId: { type: 'string', description: 'The workspace ID' },
|
|
workspaceMemberId: { type: 'string', description: 'The workspace member ID' },
|
|
}
|
|
}
|
|
|
|
/** Workspace member outputs. */
|
|
export function buildWorkspaceMemberOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildWorkspaceMemberIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** List entry created/deleted outputs. */
|
|
export function buildListEntryOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildListEntryIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** List entry updated outputs (includes attributeId). */
|
|
export function buildListEntryUpdatedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
...buildListEntryUpdatedIdOutputs(),
|
|
}
|
|
}
|
|
|
|
/** Generic webhook outputs covering all event types. */
|
|
export function buildGenericWebhookOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
id: { type: 'json', description: 'The event ID object containing resource identifiers' },
|
|
parentObjectId: {
|
|
type: 'string',
|
|
description: 'The parent object type ID (if applicable)',
|
|
},
|
|
parentRecordId: {
|
|
type: 'string',
|
|
description: 'The parent record ID (if applicable)',
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maps trigger IDs to the exact Attio event type strings.
|
|
*/
|
|
export const TRIGGER_EVENT_MAP: Record<string, string[]> = {
|
|
attio_record_created: ['record.created'],
|
|
attio_record_updated: ['record.updated'],
|
|
attio_record_deleted: ['record.deleted'],
|
|
attio_record_merged: ['record.merged'],
|
|
attio_note_created: ['note.created'],
|
|
attio_note_updated: ['note.updated', 'note-content.updated'],
|
|
attio_note_deleted: ['note.deleted'],
|
|
attio_task_created: ['task.created'],
|
|
attio_task_updated: ['task.updated'],
|
|
attio_task_deleted: ['task.deleted'],
|
|
attio_comment_created: ['comment.created'],
|
|
attio_comment_resolved: ['comment.resolved'],
|
|
attio_comment_unresolved: ['comment.unresolved'],
|
|
attio_comment_deleted: ['comment.deleted'],
|
|
attio_list_entry_created: ['list-entry.created'],
|
|
attio_list_entry_updated: ['list-entry.updated'],
|
|
attio_list_entry_deleted: ['list-entry.deleted'],
|
|
attio_list_created: ['list.created'],
|
|
attio_list_updated: ['list.updated'],
|
|
attio_list_deleted: ['list.deleted'],
|
|
attio_workspace_member_created: ['workspace-member.created'],
|
|
}
|
|
|
|
/**
|
|
* Extracts the first event from an Attio webhook payload.
|
|
* Attio wraps events in an `events` array: `{ webhook_id, events: [{ event_type, id, ... }] }`.
|
|
*/
|
|
export function getAttioEvent(body: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
const events = body.events as Array<Record<string, unknown>> | undefined
|
|
return events?.[0]
|
|
}
|
|
|
|
/**
|
|
* Checks if an Attio webhook payload matches a trigger.
|
|
*/
|
|
export function isAttioPayloadMatch(triggerId: string, body: Record<string, unknown>): boolean {
|
|
if (triggerId === 'attio_webhook') {
|
|
return true
|
|
}
|
|
|
|
const event = getAttioEvent(body)
|
|
const eventType = event?.event_type as string | undefined
|
|
if (!eventType) {
|
|
return false
|
|
}
|
|
|
|
const acceptedEvents = TRIGGER_EVENT_MAP[triggerId]
|
|
return acceptedEvents ? acceptedEvents.includes(eventType) : false
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio record event payload.
|
|
* Used for record.created, record.deleted triggers.
|
|
*/
|
|
export function extractAttioRecordData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
objectId: id.object_id ?? null,
|
|
recordId: id.record_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio record.updated event payload.
|
|
*/
|
|
export function extractAttioRecordUpdatedData(
|
|
body: Record<string, unknown>
|
|
): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
objectId: id.object_id ?? null,
|
|
recordId: id.record_id ?? null,
|
|
attributeId: id.attribute_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio record.merged event payload.
|
|
*/
|
|
export function extractAttioRecordMergedData(
|
|
body: Record<string, unknown>
|
|
): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
objectId: id.object_id ?? null,
|
|
recordId: id.record_id ?? null,
|
|
duplicateObjectId: (event.duplicate_object_id as string) ?? null,
|
|
duplicateRecordId: (event.duplicate_record_id as string) ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio note event payload.
|
|
*/
|
|
export function extractAttioNoteData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
noteId: id.note_id ?? null,
|
|
parentObjectId: (event.parent_object_id as string) ?? null,
|
|
parentRecordId: (event.parent_record_id as string) ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio task event payload.
|
|
*/
|
|
export function extractAttioTaskData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
taskId: id.task_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio comment event payload.
|
|
*/
|
|
export function extractAttioCommentData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
threadId: (event.thread_id as string) ?? null,
|
|
commentId: id.comment_id ?? null,
|
|
objectId: (event.object_id as string) ?? null,
|
|
recordId: (event.record_id as string) ?? null,
|
|
listId: (event.list_id as string) ?? null,
|
|
entryId: (event.entry_id as string) ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio list-entry event payload.
|
|
* Used for list-entry.created, list-entry.deleted triggers.
|
|
*/
|
|
export function extractAttioListEntryData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
listId: id.list_id ?? null,
|
|
entryId: id.entry_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio list-entry.updated event payload.
|
|
*/
|
|
export function extractAttioListEntryUpdatedData(
|
|
body: Record<string, unknown>
|
|
): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
listId: id.list_id ?? null,
|
|
entryId: id.entry_id ?? null,
|
|
attributeId: id.attribute_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio list event payload.
|
|
* Used for list.created, list.updated, list.deleted triggers.
|
|
*/
|
|
export function extractAttioListData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
listId: id.list_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from an Attio workspace-member.created event payload.
|
|
*/
|
|
export function extractAttioWorkspaceMemberData(
|
|
body: Record<string, unknown>
|
|
): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
workspaceId: id.workspace_id ?? null,
|
|
workspaceMemberId: id.workspace_member_id ?? null,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts formatted data from a generic Attio webhook payload.
|
|
* Passes through the first event with camelCase field mapping.
|
|
*/
|
|
export function extractAttioGenericData(body: Record<string, unknown>): Record<string, unknown> {
|
|
const event = getAttioEvent(body) ?? {}
|
|
const id = (event.id as Record<string, unknown>) ?? {}
|
|
return {
|
|
eventType: event.event_type ?? null,
|
|
id,
|
|
parentObjectId: (event.parent_object_id as string) ?? null,
|
|
parentRecordId: (event.parent_record_id as string) ?? null,
|
|
}
|
|
}
|