d25d482dc2
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
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
780 lines
21 KiB
TypeScript
780 lines
21 KiB
TypeScript
import { isRecordLike } from '@sim/utils/object'
|
|
import type { SubBlockConfig } from '@/blocks/types'
|
|
import type { TriggerOutput } from '@/triggers/types'
|
|
|
|
/**
|
|
* Shared trigger dropdown options for all Jira triggers
|
|
*/
|
|
export const jiraTriggerOptions = [
|
|
{ label: 'Issue Created', id: 'jira_issue_created' },
|
|
{ label: 'Issue Updated', id: 'jira_issue_updated' },
|
|
{ label: 'Issue Deleted', id: 'jira_issue_deleted' },
|
|
{ label: 'Issue Commented', id: 'jira_issue_commented' },
|
|
{ label: 'Comment Updated', id: 'jira_comment_updated' },
|
|
{ label: 'Comment Deleted', id: 'jira_comment_deleted' },
|
|
{ label: 'Worklog Created', id: 'jira_worklog_created' },
|
|
{ label: 'Worklog Updated', id: 'jira_worklog_updated' },
|
|
{ label: 'Worklog Deleted', id: 'jira_worklog_deleted' },
|
|
{ label: 'Sprint Created', id: 'jira_sprint_created' },
|
|
{ label: 'Sprint Started', id: 'jira_sprint_started' },
|
|
{ label: 'Sprint Closed', id: 'jira_sprint_closed' },
|
|
{ label: 'Project Created', id: 'jira_project_created' },
|
|
{ label: 'Version Released', id: 'jira_version_released' },
|
|
{ label: 'Generic Webhook (All Events)', id: 'jira_webhook' },
|
|
]
|
|
|
|
/**
|
|
* Generates setup instructions for Jira webhooks
|
|
*/
|
|
export function jiraSetupInstructions(eventType: string, additionalNotes?: string): string {
|
|
const instructions = [
|
|
'<strong>Note:</strong> You must have admin permissions in your Jira workspace to create webhooks. See the <a href="https://support.atlassian.com/jira-cloud-administration/docs/manage-webhooks/" target="_blank" rel="noopener noreferrer">Jira webhook documentation</a> for details.',
|
|
'In Jira, navigate to <strong>Settings > System > WebHooks</strong>.',
|
|
'Click <strong>"Create a WebHook"</strong> to add a new webhook.',
|
|
'Paste the <strong>Webhook URL</strong> from above into the URL field.',
|
|
'Optionally, enter the <strong>Webhook Secret</strong> from above into the secret field for added security.',
|
|
`Select the events you want to trigger this workflow. For this trigger, select <strong>${eventType}</strong>.`,
|
|
'Click <strong>"Create"</strong> to activate the webhook.',
|
|
]
|
|
|
|
if (additionalNotes) {
|
|
instructions.push(additionalNotes)
|
|
}
|
|
|
|
return instructions
|
|
.map(
|
|
(instruction, index) =>
|
|
`<div class="mb-3">${index === 0 ? instruction : `<strong>${index}.</strong> ${instruction}`}</div>`
|
|
)
|
|
.join('')
|
|
}
|
|
|
|
function jiraWebhookSecretField(triggerId: string): SubBlockConfig {
|
|
return {
|
|
id: 'webhookSecret',
|
|
title: 'Webhook Secret',
|
|
type: 'short-input',
|
|
placeholder: 'Enter a strong secret',
|
|
description: 'Optional secret to validate webhook deliveries from Jira using HMAC signature',
|
|
password: true,
|
|
required: false,
|
|
mode: 'trigger',
|
|
condition: { field: 'selectedTriggerId', value: triggerId },
|
|
}
|
|
}
|
|
|
|
function jiraJqlFilterField(triggerId: string, description: string): SubBlockConfig {
|
|
return {
|
|
id: 'jqlFilter',
|
|
title: 'JQL Filter',
|
|
type: 'long-input',
|
|
placeholder: 'project = PROJ AND issuetype = Bug',
|
|
description,
|
|
required: false,
|
|
mode: 'trigger',
|
|
condition: { field: 'selectedTriggerId', value: triggerId },
|
|
}
|
|
}
|
|
|
|
function jiraFieldFiltersField(triggerId: string): SubBlockConfig {
|
|
return {
|
|
id: 'fieldFilters',
|
|
title: 'Field Filters',
|
|
type: 'long-input',
|
|
placeholder: 'status, assignee, priority',
|
|
description:
|
|
'Comma-separated list of Jira field names. Only trigger when one of these fields changes. Leave empty to trigger on any field change.',
|
|
required: false,
|
|
mode: 'trigger',
|
|
condition: { field: 'selectedTriggerId', value: triggerId },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extra fields for Jira triggers (webhook secret, plus an optional JQL filter
|
|
* for issue-scoped events — project/sprint/version events have no JQL analog).
|
|
* `fieldFilters` is issue_updated-only since it's matched against that
|
|
* event's changelog, which no other Jira webhook carries.
|
|
*/
|
|
export function buildJiraExtraFields(
|
|
triggerId: string,
|
|
jqlFilterDescription?: string
|
|
): SubBlockConfig[] {
|
|
const fields: SubBlockConfig[] = [jiraWebhookSecretField(triggerId)]
|
|
if (jqlFilterDescription) {
|
|
fields.push(jiraJqlFilterField(triggerId, jqlFilterDescription))
|
|
}
|
|
if (triggerId === 'jira_issue_updated') {
|
|
fields.push(jiraFieldFiltersField(triggerId))
|
|
}
|
|
return fields
|
|
}
|
|
|
|
function buildBaseWebhookOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
webhookEvent: {
|
|
type: 'string',
|
|
description:
|
|
'The webhook event type (e.g., jira:issue_created, comment_created, worklog_created)',
|
|
},
|
|
timestamp: {
|
|
type: 'number',
|
|
description: 'Timestamp of the webhook event',
|
|
},
|
|
user: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who triggered the event',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who triggered the event',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Email address of the user who triggered the event',
|
|
},
|
|
},
|
|
|
|
issue: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Jira issue ID',
|
|
},
|
|
key: {
|
|
type: 'string',
|
|
description: 'Jira issue key (e.g., PROJ-123)',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this issue',
|
|
},
|
|
fields: {
|
|
votes: {
|
|
type: 'json',
|
|
description: 'Votes on this issue',
|
|
},
|
|
labels: {
|
|
type: 'array',
|
|
description: 'Array of labels applied to this issue',
|
|
},
|
|
status: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Status name',
|
|
},
|
|
id: {
|
|
type: 'string',
|
|
description: 'Status ID',
|
|
},
|
|
statusCategory: {
|
|
type: 'json',
|
|
description: 'Status category information',
|
|
},
|
|
},
|
|
created: {
|
|
type: 'string',
|
|
description: 'Issue creation date (ISO format)',
|
|
},
|
|
creator: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Creator display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Creator account ID',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description:
|
|
'Email address (Jira Server only — not available in Jira Cloud webhook payloads)',
|
|
},
|
|
},
|
|
duedate: {
|
|
type: 'string',
|
|
description: 'Due date for the issue',
|
|
},
|
|
project: {
|
|
key: {
|
|
type: 'string',
|
|
description: 'Project key',
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
description: 'Project name',
|
|
},
|
|
id: {
|
|
type: 'string',
|
|
description: 'Project ID',
|
|
},
|
|
},
|
|
summary: {
|
|
type: 'string',
|
|
description: 'Issue summary/title',
|
|
},
|
|
description: {
|
|
type: 'json',
|
|
description:
|
|
'Issue description in Atlassian Document Format (ADF). On Jira Server this may be a plain string.',
|
|
},
|
|
updated: {
|
|
type: 'string',
|
|
description: 'Last updated date (ISO format)',
|
|
},
|
|
watches: {
|
|
type: 'json',
|
|
description: 'Watchers information',
|
|
},
|
|
assignee: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Assignee display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Assignee account ID',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description:
|
|
'Email address (Jira Server only — not available in Jira Cloud webhook payloads)',
|
|
},
|
|
},
|
|
priority: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Priority name',
|
|
},
|
|
id: {
|
|
type: 'string',
|
|
description: 'Priority ID',
|
|
},
|
|
},
|
|
progress: {
|
|
type: 'json',
|
|
description: 'Progress tracking information',
|
|
},
|
|
reporter: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Reporter display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Reporter account ID',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description:
|
|
'Email address (Jira Server only — not available in Jira Cloud webhook payloads)',
|
|
},
|
|
},
|
|
security: {
|
|
type: 'string',
|
|
description: 'Security level',
|
|
},
|
|
subtasks: {
|
|
type: 'array',
|
|
description: 'Array of subtask objects',
|
|
},
|
|
versions: {
|
|
type: 'array',
|
|
description: 'Array of affected versions',
|
|
},
|
|
issuetype: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Issue type name',
|
|
},
|
|
id: {
|
|
type: 'string',
|
|
description: 'Issue type ID',
|
|
},
|
|
},
|
|
resolution: {
|
|
name: {
|
|
type: 'string',
|
|
description: 'Resolution name (e.g., Done, Fixed)',
|
|
},
|
|
id: {
|
|
type: 'string',
|
|
description: 'Resolution ID',
|
|
},
|
|
},
|
|
components: {
|
|
type: 'array',
|
|
description: 'Array of component objects associated with this issue',
|
|
},
|
|
fixVersions: {
|
|
type: 'array',
|
|
description: 'Array of fix version objects for this issue',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export function buildIssueOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
issue_event_type_name: {
|
|
type: 'string',
|
|
description: 'Issue event type name from Jira (only present in issue events)',
|
|
},
|
|
}
|
|
}
|
|
|
|
export function buildIssueUpdatedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
issue_event_type_name: {
|
|
type: 'string',
|
|
description: 'Issue event type name from Jira (only present in issue events)',
|
|
},
|
|
changelog: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Changelog ID',
|
|
},
|
|
items: {
|
|
type: 'array',
|
|
description:
|
|
'Array of changed items. Each item contains field, fieldtype, from, fromString, to, toString',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export function buildCommentOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
|
|
comment: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Comment ID',
|
|
},
|
|
body: {
|
|
type: 'json',
|
|
description:
|
|
'Comment body in Atlassian Document Format (ADF). On Jira Server this may be a plain string.',
|
|
},
|
|
author: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Comment author display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Comment author account ID',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Comment author email address',
|
|
},
|
|
},
|
|
updateAuthor: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who last updated the comment',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who last updated the comment',
|
|
},
|
|
},
|
|
created: {
|
|
type: 'string',
|
|
description: 'Comment creation date (ISO format)',
|
|
},
|
|
updated: {
|
|
type: 'string',
|
|
description: 'Comment last updated date (ISO format)',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this comment',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export function buildWorklogOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
...buildBaseWebhookOutputs(),
|
|
|
|
worklog: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Worklog entry ID',
|
|
},
|
|
author: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Worklog author display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Worklog author account ID',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Worklog author email address',
|
|
},
|
|
},
|
|
updateAuthor: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who last updated the worklog',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who last updated the worklog',
|
|
},
|
|
},
|
|
timeSpent: {
|
|
type: 'string',
|
|
description: 'Time spent (e.g., "2h 30m")',
|
|
},
|
|
timeSpentSeconds: {
|
|
type: 'number',
|
|
description: 'Time spent in seconds',
|
|
},
|
|
comment: {
|
|
type: 'string',
|
|
description: 'Worklog comment/description',
|
|
},
|
|
started: {
|
|
type: 'string',
|
|
description: 'When the work was started (ISO format)',
|
|
},
|
|
created: {
|
|
type: 'string',
|
|
description: 'When the worklog entry was created (ISO format)',
|
|
},
|
|
updated: {
|
|
type: 'string',
|
|
description: 'When the worklog entry was last updated (ISO format)',
|
|
},
|
|
issueId: {
|
|
type: 'string',
|
|
description: 'ID of the issue this worklog belongs to',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this worklog entry',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export function isJiraEventMatch(
|
|
triggerId: string,
|
|
webhookEvent: string,
|
|
issueEventTypeName?: string
|
|
): boolean {
|
|
const eventMappings: Record<string, string[]> = {
|
|
jira_issue_created: ['jira:issue_created', 'issue_created'],
|
|
jira_issue_updated: ['jira:issue_updated', 'issue_updated', 'issue_generic'],
|
|
jira_issue_deleted: ['jira:issue_deleted', 'issue_deleted'],
|
|
jira_issue_commented: ['comment_created'],
|
|
jira_comment_updated: ['comment_updated'],
|
|
jira_comment_deleted: ['comment_deleted'],
|
|
jira_worklog_created: ['worklog_created'],
|
|
jira_worklog_updated: ['worklog_updated'],
|
|
jira_worklog_deleted: ['worklog_deleted'],
|
|
jira_sprint_created: ['sprint_created'],
|
|
jira_sprint_started: ['sprint_started'],
|
|
jira_sprint_closed: ['sprint_closed'],
|
|
jira_project_created: ['project_created'],
|
|
jira_version_released: ['jira:version_released'],
|
|
jira_webhook: ['*'],
|
|
}
|
|
|
|
const expectedEvents = eventMappings[triggerId]
|
|
if (!expectedEvents) {
|
|
return false
|
|
}
|
|
|
|
if (expectedEvents.includes('*')) {
|
|
return true
|
|
}
|
|
|
|
return (
|
|
expectedEvents.includes(webhookEvent) ||
|
|
(issueEventTypeName !== undefined && expectedEvents.includes(issueEventTypeName))
|
|
)
|
|
}
|
|
|
|
export function extractIssueData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
issue_event_type_name: obj.issue_event_type_name,
|
|
issue: obj.issue || {},
|
|
changelog: obj.changelog,
|
|
}
|
|
}
|
|
|
|
export function extractCommentData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
issue: obj.issue || {},
|
|
comment: obj.comment || {},
|
|
}
|
|
}
|
|
|
|
export function extractWorklogData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
issue: obj.issue || {},
|
|
worklog: obj.worklog || {},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds output schema for sprint-related webhook events
|
|
*/
|
|
export function buildSprintOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
webhookEvent: {
|
|
type: 'string',
|
|
description: 'The webhook event type (e.g., sprint_started, sprint_closed)',
|
|
},
|
|
timestamp: {
|
|
type: 'number',
|
|
description: 'Timestamp of the webhook event',
|
|
},
|
|
user: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who triggered the event',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who triggered the event',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Email address of the user who triggered the event',
|
|
},
|
|
},
|
|
sprint: {
|
|
id: {
|
|
type: 'number',
|
|
description: 'Sprint ID',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this sprint',
|
|
},
|
|
state: {
|
|
type: 'string',
|
|
description: 'Sprint state (future, active, closed)',
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
description: 'Sprint name',
|
|
},
|
|
startDate: {
|
|
type: 'string',
|
|
description: 'Sprint start date (ISO format)',
|
|
},
|
|
endDate: {
|
|
type: 'string',
|
|
description: 'Sprint end date (ISO format)',
|
|
},
|
|
completeDate: {
|
|
type: 'string',
|
|
description: 'Sprint completion date (ISO format)',
|
|
},
|
|
originBoardId: {
|
|
type: 'number',
|
|
description: 'Board ID the sprint belongs to',
|
|
},
|
|
goal: {
|
|
type: 'string',
|
|
description: 'Sprint goal',
|
|
},
|
|
createdDate: {
|
|
type: 'string',
|
|
description: 'Sprint creation date (ISO format)',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds output schema for project_created webhook events
|
|
*/
|
|
export function buildProjectCreatedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
webhookEvent: {
|
|
type: 'string',
|
|
description: 'The webhook event type (project_created)',
|
|
},
|
|
timestamp: {
|
|
type: 'number',
|
|
description: 'Timestamp of the webhook event',
|
|
},
|
|
user: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who triggered the event',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who triggered the event',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Email address of the user who triggered the event',
|
|
},
|
|
},
|
|
project: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Project ID',
|
|
},
|
|
key: {
|
|
type: 'string',
|
|
description: 'Project key',
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
description: 'Project name',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this project',
|
|
},
|
|
projectTypeKey: {
|
|
type: 'string',
|
|
description: 'Project type (e.g., software, business)',
|
|
},
|
|
lead: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Project lead display name',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Project lead account ID',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds output schema for version_released webhook events
|
|
*/
|
|
export function buildVersionReleasedOutputs(): Record<string, TriggerOutput> {
|
|
return {
|
|
webhookEvent: {
|
|
type: 'string',
|
|
description: 'The webhook event type (jira:version_released)',
|
|
},
|
|
timestamp: {
|
|
type: 'number',
|
|
description: 'Timestamp of the webhook event',
|
|
},
|
|
user: {
|
|
displayName: {
|
|
type: 'string',
|
|
description: 'Display name of the user who triggered the event',
|
|
},
|
|
accountId: {
|
|
type: 'string',
|
|
description: 'Account ID of the user who triggered the event',
|
|
},
|
|
emailAddress: {
|
|
type: 'string',
|
|
description: 'Email address of the user who triggered the event',
|
|
},
|
|
},
|
|
version: {
|
|
id: {
|
|
type: 'string',
|
|
description: 'Version ID',
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
description: 'Version name',
|
|
},
|
|
self: {
|
|
type: 'string',
|
|
description: 'REST API URL for this version',
|
|
},
|
|
released: {
|
|
type: 'boolean',
|
|
description: 'Whether the version is released',
|
|
},
|
|
releaseDate: {
|
|
type: 'string',
|
|
description: 'Release date (ISO format)',
|
|
},
|
|
projectId: {
|
|
type: 'number',
|
|
description: 'Project ID the version belongs to',
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
description: 'Version description',
|
|
},
|
|
archived: {
|
|
type: 'boolean',
|
|
description: 'Whether the version is archived',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts sprint data from a Jira webhook payload
|
|
*/
|
|
export function extractSprintData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
sprint: obj.sprint || {},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts project data from a Jira webhook payload
|
|
*/
|
|
export function extractProjectData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
project: obj.project || {},
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extracts version data from a Jira webhook payload
|
|
*/
|
|
export function extractVersionData(body: unknown) {
|
|
const obj = isRecordLike(body) ? body : {}
|
|
return {
|
|
webhookEvent: obj.webhookEvent,
|
|
timestamp: obj.timestamp,
|
|
user: obj.user || null,
|
|
version: obj.version || {},
|
|
}
|
|
}
|