"use server"; import { pollTaskUntilSettled } from "@/actions/task/poll"; import { apiBaseUrl, getAuthHeaders } from "@/lib"; import { evaluateJiraDispatchTask } from "@/lib/jira-dispatch-result"; import { handleApiError } from "@/lib/server-actions-helper"; import type { IntegrationProps, JiraDispatchMode, JiraDispatchRequest, JiraDispatchResponse, JiraDispatchTarget, JiraDispatchTaskResult, } from "@/types/integrations"; import { JIRA_DISPATCH_MODE, JIRA_DISPATCH_TARGET } from "@/types/integrations"; interface JiraDispatchInput { integrationId: string; targetIds: string[]; filter: JiraDispatchTarget; projectKey: string; issueType: string; dispatchMode?: JiraDispatchMode; } export const getJiraIssueTypes = async ( integrationId: string, projectKey: string, ): Promise< { success: true; issueTypes: string[] } | { success: false; error: string } > => { const headers = await getAuthHeaders({ contentType: false }); const url = new URL( `${apiBaseUrl}/integrations/${integrationId}/jira/issue_types`, ); url.searchParams.append("project_key", projectKey); try { const response = await fetch(url.toString(), { method: "GET", headers }); if (response.ok) { const data: { data: { type: string; attributes: { issue_types: string[] } }; } = await response.json(); return { success: true, issueTypes: data.data?.attributes?.issue_types ?? [], }; } const errorData: unknown = await response.json().catch(() => ({})); const errorMessage = (errorData as { errors?: { detail?: string }[] }).errors?.[0]?.detail || `Unable to fetch issue types: ${response.statusText}`; return { success: false, error: errorMessage }; } catch (error) { const errorResult = handleApiError(error); return { success: false, error: errorResult.error || "An error occurred" }; } }; export const getJiraIntegrations = async (): Promise< | { success: true; data: IntegrationProps[] } | { success: false; error: string } > => { const headers = await getAuthHeaders({ contentType: false }); const url = new URL(`${apiBaseUrl}/integrations`); // Filter for Jira integrations only url.searchParams.append("filter[integration_type]", "jira"); try { const response = await fetch(url.toString(), { method: "GET", headers }); if (response.ok) { const data: { data: IntegrationProps[] } = await response.json(); // Filter for enabled integrations on the client side const enabledIntegrations = (data.data || []).filter( (integration: IntegrationProps) => integration.attributes.enabled === true, ); return { success: true, data: enabledIntegrations }; } const errorData: unknown = await response.json().catch(() => ({})); const errorMessage = (errorData as { errors?: { detail?: string }[] }).errors?.[0]?.detail || `Unable to fetch Jira integrations: ${response.statusText}`; return { success: false, error: errorMessage }; } catch (error) { const errorResult = handleApiError(error); return { success: false, error: errorResult.error || "An error occurred" }; } }; export const sendFindingToJira = async ( integrationId: string, findingId: string, projectKey: string, issueType: string, ): Promise< | { success: true; taskId: string; message: string } | { success: false; error: string } > => { return sendJiraDispatch({ integrationId, targetIds: [findingId], filter: JIRA_DISPATCH_TARGET.FINDING_ID, projectKey, issueType, }); }; export const sendJiraDispatch = async ({ integrationId, targetIds, filter, projectKey, issueType, dispatchMode = JIRA_DISPATCH_MODE.INDIVIDUAL, }: JiraDispatchInput): Promise< | { success: true; taskId: string; message: string } | { success: false; error: string } > => { const headers = await getAuthHeaders({ contentType: true }); const url = new URL( `${apiBaseUrl}/integrations/${integrationId}/jira/dispatches`, ); if (targetIds.length === 1) { url.searchParams.append(`filter[${filter}]`, targetIds[0]); } else { url.searchParams.append(`filter[${filter}__in]`, targetIds.join(",")); } const payload: JiraDispatchRequest = { data: { type: "integrations-jira-dispatches", attributes: { project_key: projectKey, issue_type: issueType, dispatch_mode: dispatchMode, }, }, }; try { const response = await fetch(url.toString(), { method: "POST", headers, body: JSON.stringify(payload), }); if (response.ok) { const data: JiraDispatchResponse = await response.json(); const taskId = data?.data?.id; if (taskId) { return { success: true, taskId, message: "Jira issue creation started. Processing...", }; } else { return { success: false, error: "Failed to start Jira dispatch. No task ID received.", }; } } const errorData: unknown = await response.json().catch(() => ({})); const errorMessage = (errorData as { errors?: { detail?: string }[] }).errors?.[0]?.detail || `Unable to send finding to Jira: ${response.statusText}`; return { success: false, error: errorMessage }; } catch (error) { const errorResult = handleApiError(error); return { success: false, error: errorResult.error || "An error occurred" }; } }; export const pollJiraDispatchTask = async ( taskId: string, ): Promise< | { success: true; message: string; warning?: string } | { success: false; error: string } > => { const res = await pollTaskUntilSettled(taskId, { maxAttempts: 5, delayMs: 2000, }); if (!res.ok) { return { success: false, error: res.error }; } return evaluateJiraDispatchTask( res.state, res.result as JiraDispatchTaskResult | undefined, ); };