mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
feat(triggers): add Linear v2 triggers with automatic webhook registration (#3991)
* feat(triggers): add Linear v2 triggers with automatic webhook registration * fix(triggers): preserve specific Linear API error messages in catch block * fix(triggers): check response.ok before JSON parsing, replace as any with as unknown * fix linear subscription params * fix build --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
This commit is contained in:
@@ -8,8 +8,9 @@ import { getTrigger } from '@/triggers'
|
||||
|
||||
export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
type: 'linear',
|
||||
name: 'Linear',
|
||||
name: 'Linear (Legacy)',
|
||||
description: 'Interact with Linear issues, projects, and more',
|
||||
hideFromToolbar: true,
|
||||
authMode: AuthMode.OAuth,
|
||||
triggerAllowed: true,
|
||||
longDescription:
|
||||
@@ -2543,3 +2544,62 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear V2 Block
|
||||
*
|
||||
* Uses automatic webhook registration via the Linear GraphQL API.
|
||||
* Inherits all tool operations from the legacy block.
|
||||
*/
|
||||
export const LinearV2Block: BlockConfig<LinearResponse> = {
|
||||
...LinearBlock,
|
||||
type: 'linear_v2',
|
||||
name: 'Linear',
|
||||
hideFromToolbar: false,
|
||||
subBlocks: [
|
||||
...LinearBlock.subBlocks.filter(
|
||||
(sb) =>
|
||||
!sb.id?.startsWith('webhookUrlDisplay') &&
|
||||
!sb.id?.startsWith('webhookSecret') &&
|
||||
!sb.id?.startsWith('triggerSave') &&
|
||||
!sb.id?.startsWith('triggerInstructions') &&
|
||||
!sb.id?.startsWith('selectedTriggerId')
|
||||
),
|
||||
// V2 Trigger SubBlocks
|
||||
...getTrigger('linear_issue_created_v2').subBlocks,
|
||||
...getTrigger('linear_issue_updated_v2').subBlocks,
|
||||
...getTrigger('linear_issue_removed_v2').subBlocks,
|
||||
...getTrigger('linear_comment_created_v2').subBlocks,
|
||||
...getTrigger('linear_comment_updated_v2').subBlocks,
|
||||
...getTrigger('linear_project_created_v2').subBlocks,
|
||||
...getTrigger('linear_project_updated_v2').subBlocks,
|
||||
...getTrigger('linear_cycle_created_v2').subBlocks,
|
||||
...getTrigger('linear_cycle_updated_v2').subBlocks,
|
||||
...getTrigger('linear_label_created_v2').subBlocks,
|
||||
...getTrigger('linear_label_updated_v2').subBlocks,
|
||||
...getTrigger('linear_project_update_created_v2').subBlocks,
|
||||
...getTrigger('linear_customer_request_created_v2').subBlocks,
|
||||
...getTrigger('linear_customer_request_updated_v2').subBlocks,
|
||||
...getTrigger('linear_webhook_v2').subBlocks,
|
||||
],
|
||||
triggers: {
|
||||
enabled: true,
|
||||
available: [
|
||||
'linear_issue_created_v2',
|
||||
'linear_issue_updated_v2',
|
||||
'linear_issue_removed_v2',
|
||||
'linear_comment_created_v2',
|
||||
'linear_comment_updated_v2',
|
||||
'linear_project_created_v2',
|
||||
'linear_project_updated_v2',
|
||||
'linear_cycle_created_v2',
|
||||
'linear_cycle_updated_v2',
|
||||
'linear_label_created_v2',
|
||||
'linear_label_updated_v2',
|
||||
'linear_project_update_created_v2',
|
||||
'linear_customer_request_created_v2',
|
||||
'linear_customer_request_updated_v2',
|
||||
'linear_webhook_v2',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
|
||||
import { LangsmithBlock } from '@/blocks/blocks/langsmith'
|
||||
import { LaunchDarklyBlock } from '@/blocks/blocks/launchdarkly'
|
||||
import { LemlistBlock } from '@/blocks/blocks/lemlist'
|
||||
import { LinearBlock } from '@/blocks/blocks/linear'
|
||||
import { LinearBlock, LinearV2Block } from '@/blocks/blocks/linear'
|
||||
import { LinkedInBlock } from '@/blocks/blocks/linkedin'
|
||||
import { LinkupBlock } from '@/blocks/blocks/linkup'
|
||||
import { LoopsBlock } from '@/blocks/blocks/loops'
|
||||
@@ -338,6 +338,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
launchdarkly: LaunchDarklyBlock,
|
||||
lemlist: LemlistBlock,
|
||||
linear: LinearBlock,
|
||||
linear_v2: LinearV2Block,
|
||||
linkedin: LinkedInBlock,
|
||||
linkup: LinkupBlock,
|
||||
loops: LoopsBlock,
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import crypto from 'crypto'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { safeCompare } from '@/lib/core/security/encryption'
|
||||
import { generateId } from '@/lib/core/utils/uuid'
|
||||
import { getNotificationUrl, getProviderConfig } from '@/lib/webhooks/providers/subscription-utils'
|
||||
import type {
|
||||
DeleteSubscriptionContext,
|
||||
EventMatchContext,
|
||||
FormatInputContext,
|
||||
FormatInputResult,
|
||||
SubscriptionContext,
|
||||
SubscriptionResult,
|
||||
WebhookProviderHandler,
|
||||
} from '@/lib/webhooks/providers/types'
|
||||
import { createHmacVerifier } from '@/lib/webhooks/providers/utils'
|
||||
@@ -60,6 +66,169 @@ export const linearHandler: WebhookProviderHandler = {
|
||||
}
|
||||
},
|
||||
|
||||
async matchEvent({ body, requestId, providerConfig }: EventMatchContext) {
|
||||
const triggerId = providerConfig.triggerId as string | undefined
|
||||
if (triggerId && !triggerId.endsWith('_webhook') && !triggerId.endsWith('_webhook_v2')) {
|
||||
const { isLinearEventMatch } = await import('@/triggers/linear/utils')
|
||||
const obj = body as Record<string, unknown>
|
||||
const action = obj.action as string | undefined
|
||||
const type = obj.type as string | undefined
|
||||
if (!isLinearEventMatch(triggerId, type || '', action)) {
|
||||
logger.debug(
|
||||
`[${requestId}] Linear event mismatch for trigger ${triggerId}. Type: ${type}, Action: ${action}. Skipping.`
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
async createSubscription(ctx: SubscriptionContext): Promise<SubscriptionResult | undefined> {
|
||||
const config = getProviderConfig(ctx.webhook)
|
||||
const triggerId = config.triggerId as string | undefined
|
||||
|
||||
if (!triggerId || !triggerId.endsWith('_v2')) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const apiKey = config.apiKey as string | undefined
|
||||
if (!apiKey) {
|
||||
logger.warn(`[${ctx.requestId}] Missing API key for Linear webhook ${ctx.webhook.id}`)
|
||||
throw new Error(
|
||||
'Linear API key is required. Please provide a valid API key in the trigger configuration.'
|
||||
)
|
||||
}
|
||||
|
||||
const { LINEAR_RESOURCE_TYPE_MAP } = await import('@/triggers/linear/utils')
|
||||
const resourceTypes = LINEAR_RESOURCE_TYPE_MAP[triggerId]
|
||||
if (!resourceTypes) {
|
||||
logger.warn(`[${ctx.requestId}] Unknown Linear trigger ID: ${triggerId}`)
|
||||
throw new Error(`Unknown Linear trigger type: ${triggerId}`)
|
||||
}
|
||||
|
||||
const notificationUrl = getNotificationUrl(ctx.webhook)
|
||||
const webhookSecret = generateId()
|
||||
const teamId = config.teamId as string | undefined
|
||||
|
||||
const input: Record<string, unknown> = {
|
||||
url: notificationUrl,
|
||||
resourceTypes,
|
||||
secret: webhookSecret,
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
if (teamId) {
|
||||
input.teamId = teamId
|
||||
} else {
|
||||
input.allPublicTeams = true
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('https://api.linear.app/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: apiKey,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `mutation WebhookCreate($input: WebhookCreateInput!) {
|
||||
webhookCreate(input: $input) {
|
||||
success
|
||||
webhook { id enabled }
|
||||
}
|
||||
}`,
|
||||
variables: { input },
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Linear API returned HTTP ${response.status}. Please verify your API key and try again.`
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const result = data?.data?.webhookCreate
|
||||
|
||||
if (!result?.success) {
|
||||
const errors = data?.errors?.map((e: { message: string }) => e.message).join(', ')
|
||||
logger.error(`[${ctx.requestId}] Failed to create Linear webhook`, {
|
||||
errors,
|
||||
webhookId: ctx.webhook.id,
|
||||
})
|
||||
throw new Error(errors || 'Failed to create Linear webhook. Please verify your API key.')
|
||||
}
|
||||
|
||||
const externalId = result.webhook?.id
|
||||
logger.info(
|
||||
`[${ctx.requestId}] Created Linear webhook ${externalId} for webhook ${ctx.webhook.id}`
|
||||
)
|
||||
|
||||
return {
|
||||
providerConfigUpdates: {
|
||||
externalId,
|
||||
webhookSecret,
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message !== 'fetch failed') {
|
||||
throw error
|
||||
}
|
||||
logger.error(`[${ctx.requestId}] Error creating Linear webhook`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
throw new Error('Failed to create Linear webhook. Please verify your API key and try again.')
|
||||
}
|
||||
},
|
||||
|
||||
async deleteSubscription(ctx: DeleteSubscriptionContext): Promise<void> {
|
||||
const config = getProviderConfig(ctx.webhook)
|
||||
const externalId = config.externalId as string | undefined
|
||||
const apiKey = config.apiKey as string | undefined
|
||||
|
||||
if (!externalId || !apiKey) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('https://api.linear.app/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: apiKey,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `mutation WebhookDelete($id: String!) {
|
||||
webhookDelete(id: $id) { success }
|
||||
}`,
|
||||
variables: { id: externalId },
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.warn(
|
||||
`[${ctx.requestId}] Linear API returned HTTP ${response.status} during webhook deletion for ${externalId}`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
if (data?.data?.webhookDelete?.success) {
|
||||
logger.info(
|
||||
`[${ctx.requestId}] Deleted Linear webhook ${externalId} for webhook ${ctx.webhook.id}`
|
||||
)
|
||||
} else {
|
||||
logger.warn(
|
||||
`[${ctx.requestId}] Linear webhook deletion returned unsuccessful for ${externalId}`
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`[${ctx.requestId}] Error deleting Linear webhook ${externalId} (non-fatal)`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
extractIdempotencyId(body: unknown) {
|
||||
const obj = body as Record<string, unknown>
|
||||
const data = obj.data as Record<string, unknown> | undefined
|
||||
|
||||
30
apps/sim/triggers/linear/comment_created_v2.ts
Normal file
30
apps/sim/triggers/linear/comment_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCommentOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCommentCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_comment_created_v2',
|
||||
name: 'Linear Comment Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new comment is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_comment_created_v2',
|
||||
eventType: 'Comment (create)',
|
||||
}),
|
||||
|
||||
outputs: buildCommentOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Comment',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/comment_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/comment_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCommentOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCommentUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_comment_updated_v2',
|
||||
name: 'Linear Comment Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a comment is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_comment_updated_v2',
|
||||
eventType: 'Comment (update)',
|
||||
}),
|
||||
|
||||
outputs: buildCommentOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Comment',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/customer_request_created_v2.ts
Normal file
30
apps/sim/triggers/linear/customer_request_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCustomerRequestOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCustomerRequestCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_customer_request_created_v2',
|
||||
name: 'Linear Customer Request Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new customer request is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_customer_request_created_v2',
|
||||
eventType: 'Customer Requests',
|
||||
}),
|
||||
|
||||
outputs: buildCustomerRequestOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'CustomerNeed',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/customer_request_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/customer_request_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCustomerRequestOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCustomerRequestUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_customer_request_updated_v2',
|
||||
name: 'Linear Customer Request Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a customer request is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_customer_request_updated_v2',
|
||||
eventType: 'CustomerNeed (update)',
|
||||
}),
|
||||
|
||||
outputs: buildCustomerRequestOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'CustomerNeed',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/cycle_created_v2.ts
Normal file
30
apps/sim/triggers/linear/cycle_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCycleOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCycleCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_cycle_created_v2',
|
||||
name: 'Linear Cycle Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new cycle is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_cycle_created_v2',
|
||||
eventType: 'Cycle (create)',
|
||||
}),
|
||||
|
||||
outputs: buildCycleOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Cycle',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/cycle_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/cycle_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildCycleOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearCycleUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_cycle_updated_v2',
|
||||
name: 'Linear Cycle Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a cycle is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_cycle_updated_v2',
|
||||
eventType: 'Cycle (update)',
|
||||
}),
|
||||
|
||||
outputs: buildCycleOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Cycle',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,15 +1,30 @@
|
||||
export { linearCommentCreatedTrigger } from './comment_created'
|
||||
export { linearCommentCreatedV2Trigger } from './comment_created_v2'
|
||||
export { linearCommentUpdatedTrigger } from './comment_updated'
|
||||
export { linearCommentUpdatedV2Trigger } from './comment_updated_v2'
|
||||
export { linearCustomerRequestCreatedTrigger } from './customer_request_created'
|
||||
export { linearCustomerRequestCreatedV2Trigger } from './customer_request_created_v2'
|
||||
export { linearCustomerRequestUpdatedTrigger } from './customer_request_updated'
|
||||
export { linearCustomerRequestUpdatedV2Trigger } from './customer_request_updated_v2'
|
||||
export { linearCycleCreatedTrigger } from './cycle_created'
|
||||
export { linearCycleCreatedV2Trigger } from './cycle_created_v2'
|
||||
export { linearCycleUpdatedTrigger } from './cycle_updated'
|
||||
export { linearCycleUpdatedV2Trigger } from './cycle_updated_v2'
|
||||
export { linearIssueCreatedTrigger } from './issue_created'
|
||||
export { linearIssueCreatedV2Trigger } from './issue_created_v2'
|
||||
export { linearIssueRemovedTrigger } from './issue_removed'
|
||||
export { linearIssueRemovedV2Trigger } from './issue_removed_v2'
|
||||
export { linearIssueUpdatedTrigger } from './issue_updated'
|
||||
export { linearIssueUpdatedV2Trigger } from './issue_updated_v2'
|
||||
export { linearLabelCreatedTrigger } from './label_created'
|
||||
export { linearLabelCreatedV2Trigger } from './label_created_v2'
|
||||
export { linearLabelUpdatedTrigger } from './label_updated'
|
||||
export { linearLabelUpdatedV2Trigger } from './label_updated_v2'
|
||||
export { linearProjectCreatedTrigger } from './project_created'
|
||||
export { linearProjectCreatedV2Trigger } from './project_created_v2'
|
||||
export { linearProjectUpdateCreatedTrigger } from './project_update_created'
|
||||
export { linearProjectUpdateCreatedV2Trigger } from './project_update_created_v2'
|
||||
export { linearProjectUpdatedTrigger } from './project_updated'
|
||||
export { linearProjectUpdatedV2Trigger } from './project_updated_v2'
|
||||
export { linearWebhookTrigger } from './webhook'
|
||||
export { linearWebhookV2Trigger } from './webhook_v2'
|
||||
|
||||
37
apps/sim/triggers/linear/issue_created_v2.ts
Normal file
37
apps/sim/triggers/linear/issue_created_v2.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Linear Issue Created Trigger (v2)
|
||||
*
|
||||
* Primary trigger - includes the dropdown for selecting trigger type.
|
||||
* Uses automatic webhook registration via the Linear GraphQL API.
|
||||
*/
|
||||
export const linearIssueCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_issue_created_v2',
|
||||
name: 'Linear Issue Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new issue is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_issue_created_v2',
|
||||
eventType: 'Issue (create)',
|
||||
includeDropdown: true,
|
||||
}),
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Issue',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/issue_removed_v2.ts
Normal file
30
apps/sim/triggers/linear/issue_removed_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearIssueRemovedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_issue_removed_v2',
|
||||
name: 'Linear Issue Removed',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when an issue is removed/deleted in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_issue_removed_v2',
|
||||
eventType: 'Issue (remove)',
|
||||
}),
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Issue',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/issue_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/issue_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildIssueOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearIssueUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_issue_updated_v2',
|
||||
name: 'Linear Issue Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when an issue is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_issue_updated_v2',
|
||||
eventType: 'Issue (update)',
|
||||
}),
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Issue',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/label_created_v2.ts
Normal file
30
apps/sim/triggers/linear/label_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLabelOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearLabelCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_label_created_v2',
|
||||
name: 'Linear Label Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new label is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_label_created_v2',
|
||||
eventType: 'IssueLabel (create)',
|
||||
}),
|
||||
|
||||
outputs: buildLabelOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'IssueLabel',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/label_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/label_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLabelOutputs, buildLinearV2SubBlocks } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearLabelUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_label_updated_v2',
|
||||
name: 'Linear Label Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a label is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_label_updated_v2',
|
||||
eventType: 'IssueLabel (update)',
|
||||
}),
|
||||
|
||||
outputs: buildLabelOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'IssueLabel',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/project_created_v2.ts
Normal file
30
apps/sim/triggers/linear/project_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLinearV2SubBlocks, buildProjectOutputs } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearProjectCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_project_created_v2',
|
||||
name: 'Linear Project Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new project is created in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_project_created_v2',
|
||||
eventType: 'Project (create)',
|
||||
}),
|
||||
|
||||
outputs: buildProjectOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Project',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/project_update_created_v2.ts
Normal file
30
apps/sim/triggers/linear/project_update_created_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLinearV2SubBlocks, buildProjectUpdateOutputs } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearProjectUpdateCreatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_project_update_created_v2',
|
||||
name: 'Linear Project Update Created',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a new project update is posted in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_project_update_created_v2',
|
||||
eventType: 'ProjectUpdate (create)',
|
||||
}),
|
||||
|
||||
outputs: buildProjectUpdateOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'ProjectUpdate',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
30
apps/sim/triggers/linear/project_updated_v2.ts
Normal file
30
apps/sim/triggers/linear/project_updated_v2.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLinearV2SubBlocks, buildProjectOutputs } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearProjectUpdatedV2Trigger: TriggerConfig = {
|
||||
id: 'linear_project_updated_v2',
|
||||
name: 'Linear Project Updated',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow when a project is updated in Linear',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_project_updated_v2',
|
||||
eventType: 'Project (update)',
|
||||
}),
|
||||
|
||||
outputs: buildProjectOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Project',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { TriggerOutput } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,37 @@ export const linearTriggerOptions = [
|
||||
]
|
||||
|
||||
/**
|
||||
* Generate setup instructions for a specific Linear event type
|
||||
* Maps trigger IDs to Linear resource types for webhook creation.
|
||||
* Used by the automatic webhook registration in provider-subscriptions.
|
||||
*/
|
||||
export const LINEAR_RESOURCE_TYPE_MAP: Record<string, string[]> = {
|
||||
linear_issue_created_v2: ['Issue'],
|
||||
linear_issue_updated_v2: ['Issue'],
|
||||
linear_issue_removed_v2: ['Issue'],
|
||||
linear_comment_created_v2: ['Comment'],
|
||||
linear_comment_updated_v2: ['Comment'],
|
||||
linear_project_created_v2: ['Project'],
|
||||
linear_project_updated_v2: ['Project'],
|
||||
linear_cycle_created_v2: ['Cycle'],
|
||||
linear_cycle_updated_v2: ['Cycle'],
|
||||
linear_label_created_v2: ['IssueLabel'],
|
||||
linear_label_updated_v2: ['IssueLabel'],
|
||||
linear_project_update_created_v2: ['ProjectUpdate'],
|
||||
linear_customer_request_created_v2: ['CustomerNeed'],
|
||||
linear_customer_request_updated_v2: ['CustomerNeed'],
|
||||
linear_webhook_v2: [
|
||||
'Issue',
|
||||
'Comment',
|
||||
'Project',
|
||||
'Cycle',
|
||||
'IssueLabel',
|
||||
'ProjectUpdate',
|
||||
'CustomerNeed',
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate setup instructions for manual Linear webhook configuration (v1 triggers)
|
||||
*/
|
||||
export function linearSetupInstructions(eventType: string, additionalNotes?: string): string {
|
||||
const instructions = [
|
||||
@@ -47,6 +78,121 @@ export function linearSetupInstructions(eventType: string, additionalNotes?: str
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate setup instructions for automatic Linear webhook creation (v2 triggers)
|
||||
*/
|
||||
export function linearV2SetupInstructions(eventType: string, additionalNotes?: string): string {
|
||||
const instructions = [
|
||||
'Enter your Linear API Key above. You can create one in Linear at <a href="https://linear.app/settings/api" target="_blank" rel="noopener noreferrer">Settings > API > Personal API keys</a>.',
|
||||
'Optionally enter a <strong>Team ID</strong> to scope the webhook to a single team. Leave it empty to receive events from all public teams. You can find Team IDs in Linear under <a href="https://linear.app/settings" target="_blank" rel="noopener noreferrer">Settings > Teams</a> or via the API.',
|
||||
`Click <strong>"Save Configuration"</strong> to automatically create the webhook in Linear for <strong>${eventType}</strong> events.`,
|
||||
'The webhook will be automatically deleted when you remove this trigger.',
|
||||
]
|
||||
|
||||
if (additionalNotes) {
|
||||
instructions.push(additionalNotes)
|
||||
}
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 trigger dropdown options with _v2 suffixed IDs
|
||||
*/
|
||||
export const linearV2TriggerOptions = [
|
||||
{ label: 'Issue Created', id: 'linear_issue_created_v2' },
|
||||
{ label: 'Issue Updated', id: 'linear_issue_updated_v2' },
|
||||
{ label: 'Issue Removed', id: 'linear_issue_removed_v2' },
|
||||
{ label: 'Comment Created', id: 'linear_comment_created_v2' },
|
||||
{ label: 'Comment Updated', id: 'linear_comment_updated_v2' },
|
||||
{ label: 'Project Created', id: 'linear_project_created_v2' },
|
||||
{ label: 'Project Updated', id: 'linear_project_updated_v2' },
|
||||
{ label: 'Cycle Created', id: 'linear_cycle_created_v2' },
|
||||
{ label: 'Cycle Updated', id: 'linear_cycle_updated_v2' },
|
||||
{ label: 'Label Created', id: 'linear_label_created_v2' },
|
||||
{ label: 'Label Updated', id: 'linear_label_updated_v2' },
|
||||
{ label: 'Project Update Created', id: 'linear_project_update_created_v2' },
|
||||
{ label: 'Customer Request Created', id: 'linear_customer_request_created_v2' },
|
||||
{ label: 'Customer Request Updated', id: 'linear_customer_request_updated_v2' },
|
||||
{ label: 'General Webhook (All Events)', id: 'linear_webhook_v2' },
|
||||
]
|
||||
|
||||
/**
|
||||
* Builds the complete subBlocks array for a v2 Linear trigger.
|
||||
* Webhooks are managed via API, so no webhook URL is displayed.
|
||||
*
|
||||
* Structure: [dropdown?] -> apiKey -> triggerSave -> instructions
|
||||
*/
|
||||
export function buildLinearV2SubBlocks(options: {
|
||||
triggerId: string
|
||||
eventType: string
|
||||
includeDropdown?: boolean
|
||||
additionalNotes?: string
|
||||
}): SubBlockConfig[] {
|
||||
const { triggerId, eventType, includeDropdown = false, additionalNotes } = options
|
||||
const blocks: SubBlockConfig[] = []
|
||||
|
||||
if (includeDropdown) {
|
||||
blocks.push({
|
||||
id: 'selectedTriggerId',
|
||||
title: 'Trigger Type',
|
||||
type: 'dropdown',
|
||||
mode: 'trigger',
|
||||
options: linearV2TriggerOptions,
|
||||
value: () => triggerId,
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
|
||||
blocks.push({
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Linear API key',
|
||||
password: true,
|
||||
required: true,
|
||||
paramVisibility: 'user-only',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
blocks.push({
|
||||
id: 'teamId',
|
||||
title: 'Team ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'All teams (optional)',
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
blocks.push({
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId,
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
blocks.push({
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearV2SetupInstructions(eventType, additionalNotes),
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared user/actor output schema
|
||||
* Note: Linear webhooks only include id, name, and type in actor objects
|
||||
@@ -298,7 +444,7 @@ export function buildIssueOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +531,7 @@ export function buildCommentOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,7 +674,7 @@ export function buildProjectOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,7 +777,7 @@ export function buildCycleOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -718,7 +864,7 @@ export function buildLabelOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -793,7 +939,7 @@ export function buildProjectUpdateOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,7 +1022,7 @@ export function buildCustomerRequestOutputs(): Record<string, TriggerOutput> {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
} as any
|
||||
} as unknown as Record<string, TriggerOutput>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -900,7 +1046,8 @@ export function isLinearEventMatch(triggerId: string, eventType: string, action?
|
||||
linear_customer_request_updated: { type: 'CustomerNeed', actions: ['update'] },
|
||||
}
|
||||
|
||||
const config = eventMap[triggerId]
|
||||
const normalizedId = triggerId.replace(/_v2$/, '')
|
||||
const config = eventMap[normalizedId]
|
||||
if (!config) {
|
||||
return true // Unknown trigger, allow through
|
||||
}
|
||||
|
||||
66
apps/sim/triggers/linear/webhook_v2.ts
Normal file
66
apps/sim/triggers/linear/webhook_v2.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { LinearIcon } from '@/components/icons'
|
||||
import { buildLinearV2SubBlocks, userOutputs } from '@/triggers/linear/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
export const linearWebhookV2Trigger: TriggerConfig = {
|
||||
id: 'linear_webhook_v2',
|
||||
name: 'Linear Webhook',
|
||||
provider: 'linear',
|
||||
description: 'Trigger workflow from any Linear webhook event',
|
||||
version: '2.0.0',
|
||||
icon: LinearIcon,
|
||||
|
||||
subBlocks: buildLinearV2SubBlocks({
|
||||
triggerId: 'linear_webhook_v2',
|
||||
eventType: 'All Events',
|
||||
additionalNotes:
|
||||
'This webhook will receive all Linear events. Use the <code>type</code> and <code>action</code> fields in the payload to filter and handle different event types.',
|
||||
}),
|
||||
|
||||
outputs: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action performed (create, update, remove)',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Entity type (Issue, Comment, Project, Cycle, IssueLabel, ProjectUpdate, etc.)',
|
||||
},
|
||||
webhookId: {
|
||||
type: 'string',
|
||||
description: 'Webhook ID',
|
||||
},
|
||||
webhookTimestamp: {
|
||||
type: 'number',
|
||||
description: 'Webhook timestamp (milliseconds)',
|
||||
},
|
||||
organizationId: {
|
||||
type: 'string',
|
||||
description: 'Organization ID',
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
description: 'Event creation timestamp',
|
||||
},
|
||||
actor: userOutputs,
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'Complete entity data object',
|
||||
},
|
||||
updatedFrom: {
|
||||
type: 'object',
|
||||
description: 'Previous values for changed fields (only present on update)',
|
||||
},
|
||||
},
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Linear-Event': 'Issue',
|
||||
'Linear-Delivery': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
||||
'Linear-Signature': 'sha256...',
|
||||
'User-Agent': 'Linear-Webhook',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -170,20 +170,35 @@ import {
|
||||
} from '@/triggers/lemlist'
|
||||
import {
|
||||
linearCommentCreatedTrigger,
|
||||
linearCommentCreatedV2Trigger,
|
||||
linearCommentUpdatedTrigger,
|
||||
linearCommentUpdatedV2Trigger,
|
||||
linearCustomerRequestCreatedTrigger,
|
||||
linearCustomerRequestCreatedV2Trigger,
|
||||
linearCustomerRequestUpdatedTrigger,
|
||||
linearCustomerRequestUpdatedV2Trigger,
|
||||
linearCycleCreatedTrigger,
|
||||
linearCycleCreatedV2Trigger,
|
||||
linearCycleUpdatedTrigger,
|
||||
linearCycleUpdatedV2Trigger,
|
||||
linearIssueCreatedTrigger,
|
||||
linearIssueCreatedV2Trigger,
|
||||
linearIssueRemovedTrigger,
|
||||
linearIssueRemovedV2Trigger,
|
||||
linearIssueUpdatedTrigger,
|
||||
linearIssueUpdatedV2Trigger,
|
||||
linearLabelCreatedTrigger,
|
||||
linearLabelCreatedV2Trigger,
|
||||
linearLabelUpdatedTrigger,
|
||||
linearLabelUpdatedV2Trigger,
|
||||
linearProjectCreatedTrigger,
|
||||
linearProjectCreatedV2Trigger,
|
||||
linearProjectUpdateCreatedTrigger,
|
||||
linearProjectUpdateCreatedV2Trigger,
|
||||
linearProjectUpdatedTrigger,
|
||||
linearProjectUpdatedV2Trigger,
|
||||
linearWebhookTrigger,
|
||||
linearWebhookV2Trigger,
|
||||
} from '@/triggers/linear'
|
||||
import {
|
||||
microsoftTeamsChatSubscriptionTrigger,
|
||||
@@ -380,6 +395,21 @@ export const TRIGGER_REGISTRY: TriggerRegistry = {
|
||||
linear_project_update_created: linearProjectUpdateCreatedTrigger,
|
||||
linear_customer_request_created: linearCustomerRequestCreatedTrigger,
|
||||
linear_customer_request_updated: linearCustomerRequestUpdatedTrigger,
|
||||
linear_webhook_v2: linearWebhookV2Trigger,
|
||||
linear_issue_created_v2: linearIssueCreatedV2Trigger,
|
||||
linear_issue_updated_v2: linearIssueUpdatedV2Trigger,
|
||||
linear_issue_removed_v2: linearIssueRemovedV2Trigger,
|
||||
linear_comment_created_v2: linearCommentCreatedV2Trigger,
|
||||
linear_comment_updated_v2: linearCommentUpdatedV2Trigger,
|
||||
linear_project_created_v2: linearProjectCreatedV2Trigger,
|
||||
linear_project_updated_v2: linearProjectUpdatedV2Trigger,
|
||||
linear_cycle_created_v2: linearCycleCreatedV2Trigger,
|
||||
linear_cycle_updated_v2: linearCycleUpdatedV2Trigger,
|
||||
linear_label_created_v2: linearLabelCreatedV2Trigger,
|
||||
linear_label_updated_v2: linearLabelUpdatedV2Trigger,
|
||||
linear_project_update_created_v2: linearProjectUpdateCreatedV2Trigger,
|
||||
linear_customer_request_created_v2: linearCustomerRequestCreatedV2Trigger,
|
||||
linear_customer_request_updated_v2: linearCustomerRequestUpdatedV2Trigger,
|
||||
microsoftteams_webhook: microsoftTeamsWebhookTrigger,
|
||||
microsoftteams_chat_subscription: microsoftTeamsChatSubscriptionTrigger,
|
||||
notion_page_created: notionPageCreatedTrigger,
|
||||
|
||||
Reference in New Issue
Block a user