diff --git a/apps/sim/app/api/tools/jira/update/route.ts b/apps/sim/app/api/tools/jira/update/route.ts index 8ad96ba3d0c..2c0f5dcb4ab 100644 --- a/apps/sim/app/api/tools/jira/update/route.ts +++ b/apps/sim/app/api/tools/jira/update/route.ts @@ -3,7 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' -import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils' +import { getJiraCloudId, parseAtlassianErrorMessage, toAdf } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' @@ -15,14 +15,14 @@ const jiraUpdateSchema = z.object({ issueKey: z.string().min(1, 'Issue key is required'), summary: z.string().optional(), title: z.string().optional(), - description: z.string().optional(), + description: z.union([z.string(), z.record(z.unknown())]).optional(), priority: z.string().optional(), assignee: z.string().optional(), labels: z.array(z.string()).optional(), components: z.array(z.string()).optional(), duedate: z.string().optional(), fixVersions: z.array(z.string()).optional(), - environment: z.string().optional(), + environment: z.union([z.string(), z.record(z.unknown())]).optional(), customFieldId: z.string().optional(), customFieldValue: z.string().optional(), notifyUsers: z.boolean().optional(), @@ -91,21 +91,7 @@ export async function PUT(request: NextRequest) { } if (description !== undefined && description !== null && description !== '') { - fields.description = { - type: 'doc', - version: 1, - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: description, - }, - ], - }, - ], - } + fields.description = toAdf(description) } if (priority !== undefined && priority !== null && priority !== '') { @@ -136,21 +122,7 @@ export async function PUT(request: NextRequest) { } if (environment !== undefined && environment !== null && environment !== '') { - fields.environment = { - type: 'doc', - version: 1, - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: environment, - }, - ], - }, - ], - } + fields.environment = toAdf(environment) } if ( diff --git a/apps/sim/app/api/tools/jira/write/route.ts b/apps/sim/app/api/tools/jira/write/route.ts index 6ecb49553c6..d63689b267f 100644 --- a/apps/sim/app/api/tools/jira/write/route.ts +++ b/apps/sim/app/api/tools/jira/write/route.ts @@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { getJiraCloudId, parseAtlassianErrorMessage } from '@/tools/jira/utils' +import { getJiraCloudId, parseAtlassianErrorMessage, toAdf } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' @@ -85,21 +85,7 @@ export async function POST(request: NextRequest) { } if (description !== undefined && description !== null && description !== '') { - fields.description = { - type: 'doc', - version: 1, - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: description, - }, - ], - }, - ], - } + fields.description = toAdf(description) } if (parent !== undefined && parent !== null && parent !== '') { @@ -144,21 +130,7 @@ export async function POST(request: NextRequest) { } if (environment !== undefined && environment !== null && environment !== '') { - fields.environment = { - type: 'doc', - version: 1, - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: environment, - }, - ], - }, - ], - } + fields.environment = toAdf(environment) } if ( diff --git a/apps/sim/tools/jira/update.ts b/apps/sim/tools/jira/update.ts index 75f53fc8765..47e8e26693a 100644 --- a/apps/sim/tools/jira/update.ts +++ b/apps/sim/tools/jira/update.ts @@ -42,7 +42,8 @@ export const jiraUpdateTool: ToolConfig = type: 'string', required: false, visibility: 'user-or-llm', - description: 'New description for the issue', + description: + 'New description for the issue. Accepts plain text (auto-wrapped in ADF) or a raw ADF document object', }, priority: { type: 'string', diff --git a/apps/sim/tools/jira/utils.ts b/apps/sim/tools/jira/utils.ts index d29f9c792b7..02f5a28b0b9 100644 --- a/apps/sim/tools/jira/utils.ts +++ b/apps/sim/tools/jira/utils.ts @@ -5,6 +5,51 @@ const logger = createLogger('JiraUtils') const MAX_ATTACHMENT_SIZE = 50 * 1024 * 1024 +/** + * Converts a value to ADF format. If the value is already an ADF document object, + * it is returned as-is. If it is a plain string, it is wrapped in a single-paragraph ADF doc. + */ +export function toAdf(value: string | Record): Record { + if (typeof value === 'object') { + if (value.type === 'doc') { + return value + } + if (value.type && Array.isArray(value.content)) { + return { type: 'doc', version: 1, content: [value] } + } + } + if (typeof value === 'string') { + try { + const parsed = JSON.parse(value) + if (typeof parsed === 'object' && parsed !== null && parsed.type === 'doc') { + return parsed + } + if ( + typeof parsed === 'object' && + parsed !== null && + parsed.type && + Array.isArray(parsed.content) + ) { + return { type: 'doc', version: 1, content: [parsed] } + } + } catch { + // Not JSON — treat as plain text below + } + } + return { + type: 'doc', + version: 1, + content: [ + { + type: 'paragraph', + content: [ + { type: 'text', text: typeof value === 'string' ? value : JSON.stringify(value) }, + ], + }, + ], + } +} + /** * Extracts plain text from Atlassian Document Format (ADF) content. * Returns null if content is falsy. diff --git a/apps/sim/tools/jira/write.ts b/apps/sim/tools/jira/write.ts index 42a5f9391c5..db1fac87f86 100644 --- a/apps/sim/tools/jira/write.ts +++ b/apps/sim/tools/jira/write.ts @@ -42,7 +42,8 @@ export const jiraWriteTool: ToolConfig = { type: 'string', required: false, visibility: 'user-or-llm', - description: 'Description for the issue', + description: + 'Description for the issue. Accepts plain text (auto-wrapped in ADF) or a raw ADF document object', }, priority: { type: 'string',