Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/docs/content/docs/en/tools/jira_service_management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,11 @@ Create a new service request in Jira Service Management
| `cloudId` | string | No | Jira Cloud ID for the instance |
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
| `requestTypeId` | string | Yes | Request Type ID \(e.g., "10", "15"\) |
| `summary` | string | Yes | Summary/title for the service request |
| `summary` | string | No | Summary/title for the service request \(required unless using Form Answers\) |
| `description` | string | No | Description for the service request |
| `raiseOnBehalfOf` | string | No | Account ID of customer to raise request on behalf of |
| `requestFieldValues` | json | No | Request field values as key-value pairs \(overrides summary/description if provided\) |
| `formAnswers` | json | No | Form answers for form-based request types \(e.g., \{"summary": \{"text": "Title"\}, "customfield_10010": \{"choices": \["10320"\]\}\}\) |
| `requestParticipants` | string | No | Comma-separated account IDs to add as request participants |
| `channel` | string | No | Channel the request originates from \(e.g., portal, email\) |

Expand Down
52 changes: 44 additions & 8 deletions apps/sim/app/api/tools/jsm/request/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ export const dynamic = 'force-dynamic'

const logger = createLogger('JsmRequestAPI')

function parseJsmErrorMessage(status: number, statusText: string, errorText: string): string {
try {
const errorData = JSON.parse(errorText)
if (errorData.errorMessage) {
return `JSM API error: ${errorData.errorMessage}`
}
} catch {
if (errorText) {
return `JSM API error: ${errorText}`
}
}
return `JSM API error: ${status} ${statusText}`
}

export async function POST(request: NextRequest) {
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
Expand All @@ -31,6 +45,7 @@ export async function POST(request: NextRequest) {
description,
raiseOnBehalfOf,
requestFieldValues,
formAnswers,
requestParticipants,
channel,
expand,
Expand All @@ -55,7 +70,7 @@ export async function POST(request: NextRequest) {

const baseUrl = getJsmApiBaseUrl(cloudId)

const isCreateOperation = serviceDeskId && requestTypeId && summary
const isCreateOperation = serviceDeskId && requestTypeId && (summary || formAnswers)

if (isCreateOperation) {
const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
Expand All @@ -69,15 +84,30 @@ export async function POST(request: NextRequest) {
}
const url = `${baseUrl}/request`

logger.info('Creating request at:', url)
logger.info('Creating request at:', { url, serviceDeskId, requestTypeId })

const requestBody: Record<string, unknown> = {
serviceDeskId,
requestTypeId,
requestFieldValues: requestFieldValues || {
summary,
...(description && { description }),
},
}

if (summary || description || requestFieldValues) {
const fieldValues =
requestFieldValues && typeof requestFieldValues === 'object'
? {
...(!requestFieldValues.summary && summary ? { summary } : {}),
...(!requestFieldValues.description && description ? { description } : {}),
...requestFieldValues,
}
: {
...(summary && { summary }),
...(description && { description }),
}
requestBody.requestFieldValues = fieldValues
}

if (formAnswers && typeof formAnswers === 'object') {
requestBody.form = { answers: formAnswers }
}

if (raiseOnBehalfOf) {
Expand Down Expand Up @@ -112,7 +142,10 @@ export async function POST(request: NextRequest) {
})

return NextResponse.json(
{ error: `JSM API error: ${response.status} ${response.statusText}`, details: errorText },
{
error: parseJsmErrorMessage(response.status, response.statusText, errorText),
details: errorText,
},
{ status: response.status }
)
}
Expand Down Expand Up @@ -178,7 +211,10 @@ export async function POST(request: NextRequest) {
})

return NextResponse.json(
{ error: `JSM API error: ${response.status} ${response.statusText}`, details: errorText },
{
error: parseJsmErrorMessage(response.status, response.statusText, errorText),
details: errorText,
},
{ status: response.status }
)
}
Expand Down
39 changes: 35 additions & 4 deletions apps/sim/blocks/blocks/jira_service_management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
id: 'summary',
title: 'Summary',
type: 'short-input',
required: true,
placeholder: 'Enter request summary',
condition: { field: 'operation', value: 'create_request' },
wandConfig: {
Expand Down Expand Up @@ -238,20 +237,23 @@ Return ONLY the description text - no explanations.`,
title: 'Raise on Behalf Of',
type: 'short-input',
placeholder: 'Account ID to raise request on behalf of',
mode: 'advanced',
condition: { field: 'operation', value: 'create_request' },
},
{
id: 'requestParticipants',
title: 'Request Participants',
type: 'short-input',
placeholder: 'Comma-separated account IDs to add as participants',
mode: 'advanced',
condition: { field: 'operation', value: 'create_request' },
},
{
id: 'channel',
title: 'Channel',
type: 'short-input',
placeholder: 'Channel (e.g., portal, email)',
mode: 'advanced',
condition: { field: 'operation', value: 'create_request' },
},
{
Expand All @@ -260,6 +262,16 @@ Return ONLY the description text - no explanations.`,
type: 'long-input',
placeholder:
'JSON object of field values (e.g., {"summary": "Title", "customfield_10010": "value"})',
mode: 'advanced',
condition: { field: 'operation', value: 'create_request' },
},
{
id: 'formAnswers',
title: 'Form Answers',
type: 'long-input',
placeholder:
'JSON object for form-based request types (e.g., {"summary": {"text": "Title"}, "customfield_10010": {"choices": ["10320"]}})',
mode: 'advanced',
condition: { field: 'operation', value: 'create_request' },
},
{
Expand Down Expand Up @@ -571,8 +583,8 @@ Return ONLY the comment text - no explanations.`,
if (!params.requestTypeId) {
throw new Error('Request Type ID is required')
}
if (!params.summary) {
throw new Error('Summary is required')
if (!params.summary && !params.formAnswers) {
throw new Error('Summary is required (unless using Form Answers)')
}
return {
...baseParams,
Expand All @@ -584,7 +596,22 @@ Return ONLY the comment text - no explanations.`,
requestParticipants: params.requestParticipants,
channel: params.channel,
requestFieldValues: params.requestFieldValues
? JSON.parse(params.requestFieldValues)
? (() => {
try {
return JSON.parse(params.requestFieldValues)
} catch {
throw new Error('requestFieldValues must be valid JSON')
}
})()
: undefined,
formAnswers: params.formAnswers
? (() => {
try {
return JSON.parse(params.formAnswers)
} catch {
throw new Error('formAnswers must be valid JSON')
}
})()
: undefined,
}
case 'get_request':
Expand Down Expand Up @@ -826,6 +853,10 @@ Return ONLY the comment text - no explanations.`,
},
channel: { type: 'string', description: 'Channel (e.g., portal, email)' },
requestFieldValues: { type: 'string', description: 'JSON object of request field values' },
formAnswers: {
type: 'string',
description: 'JSON object of form answers for form-based request types',
},
searchQuery: { type: 'string', description: 'Filter request types by name' },
groupId: { type: 'string', description: 'Filter by request type group ID' },
expand: { type: 'string', description: 'Comma-separated fields to expand' },
Expand Down
7 changes: 7 additions & 0 deletions apps/sim/tools/error-extractors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export interface ErrorExtractorConfig {
}

const ERROR_EXTRACTORS: ErrorExtractorConfig[] = [
{
id: 'atlassian-errors',
description: 'Atlassian REST API errorMessage field',
examples: ['Jira', 'Jira Service Management', 'Confluence'],
extract: (errorInfo) => errorInfo?.data?.errorMessage,
},
{
id: 'graphql-errors',
description: 'GraphQL errors array with message field',
Expand Down Expand Up @@ -221,6 +227,7 @@ export function extractErrorMessage(errorInfo?: ErrorInfo, extractorId?: string)
}

export const ErrorExtractorId = {
ATLASSIAN_ERRORS: 'atlassian-errors',
GRAPHQL_ERRORS: 'graphql-errors',
TWITTER_ERRORS: 'twitter-errors',
DETAILS_ARRAY: 'details-array',
Expand Down
12 changes: 10 additions & 2 deletions apps/sim/tools/jsm/create_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
},
summary: {
type: 'string',
required: true,
required: false,
visibility: 'user-or-llm',
description: 'Summary/title for the service request',
description: 'Summary/title for the service request (required unless using Form Answers)',
},
description: {
type: 'string',
Expand All @@ -68,6 +68,13 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
description:
'Request field values as key-value pairs (overrides summary/description if provided)',
},
formAnswers: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Form answers for form-based request types (e.g., {"summary": {"text": "Title"}, "customfield_10010": {"choices": ["10320"]}})',
},
requestParticipants: {
type: 'string',
required: false,
Expand Down Expand Up @@ -98,6 +105,7 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
description: params.description,
raiseOnBehalfOf: params.raiseOnBehalfOf,
requestFieldValues: params.requestFieldValues,
formAnswers: params.formAnswers,
requestParticipants: params.requestParticipants,
channel: params.channel,
}),
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/tools/jsm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,10 @@ export interface JsmGetRequestTypesParams extends JsmBaseParams {
export interface JsmCreateRequestParams extends JsmBaseParams {
serviceDeskId: string
requestTypeId: string
summary: string
summary?: string
description?: string
requestFieldValues?: Record<string, unknown>
formAnswers?: Record<string, unknown>
raiseOnBehalfOf?: string
requestParticipants?: string[]
channel?: string
Expand Down
Loading