mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-11-08 12:24:45 +00:00
fix(ui): complete grouped Jira dispatch review
This commit is contained in:
26 files changed
+1668
-894
No files matched your search
@@ -0,0 +1,141 @@
|
||||
import type { JiraDispatchTaskResult } from "@/types/integrations";
|
||||
import type { TaskState } from "@/types/tasks";
|
||||
|
||||
export interface JiraDispatchSuccessOutcome {
|
||||
success: true;
|
||||
message: string;
|
||||
warning?: string;
|
||||
failedFindingIds?: string[];
|
||||
}
|
||||
|
||||
export interface JiraDispatchFailureOutcome {
|
||||
success: false;
|
||||
error: string;
|
||||
failedFindingIds?: string[];
|
||||
}
|
||||
|
||||
export type JiraDispatchOutcome =
|
||||
| JiraDispatchSuccessOutcome
|
||||
| JiraDispatchFailureOutcome;
|
||||
|
||||
const getArrayCount = (value: unknown[] | undefined) =>
|
||||
Array.isArray(value) ? value.length : 0;
|
||||
|
||||
const getFailedCount = (result: JiraDispatchTaskResult | undefined) => {
|
||||
if (!result) return 0;
|
||||
return Math.max(
|
||||
result.failed_count ?? 0,
|
||||
getArrayCount(result.failed_groups),
|
||||
getArrayCount(result.failed_batches),
|
||||
getArrayCount(result.failed_finding_ids),
|
||||
);
|
||||
};
|
||||
|
||||
export const getJiraDispatchSuccessCount = (
|
||||
result: JiraDispatchTaskResult | undefined,
|
||||
) => {
|
||||
if (!result) return 0;
|
||||
|
||||
const createdCount = Math.max(
|
||||
result.created_count ?? 0,
|
||||
getArrayCount(result.created_issues),
|
||||
);
|
||||
const updatedCount = Math.max(
|
||||
result.updated_count ?? 0,
|
||||
getArrayCount(result.updated_issues),
|
||||
);
|
||||
|
||||
return Math.max(
|
||||
result.successful_count ?? 0,
|
||||
createdCount + updatedCount,
|
||||
result.issue_key || result.issue_url ? 1 : 0,
|
||||
);
|
||||
};
|
||||
|
||||
const ensureSentence = (message: string) =>
|
||||
/[.!?]$/.test(message.trim()) ? message.trim() : `${message.trim()}.`;
|
||||
|
||||
const buildFailureMessage = (
|
||||
result: JiraDispatchTaskResult | undefined,
|
||||
failedCount: number,
|
||||
) => {
|
||||
const successCount = getJiraDispatchSuccessCount(result);
|
||||
const summary = `Jira dispatch completed with ${failedCount} failed and ${successCount} created/updated issue${successCount === 1 ? "" : "s"}.`;
|
||||
|
||||
return result?.error ? `${ensureSentence(result.error)} ${summary}` : summary;
|
||||
};
|
||||
|
||||
const buildSuccessMessage = (result: JiraDispatchTaskResult | undefined) => {
|
||||
const successCount = getJiraDispatchSuccessCount(result);
|
||||
if (successCount > 1) {
|
||||
return `${successCount} Jira issues were created or updated successfully.`;
|
||||
}
|
||||
|
||||
return "Finding successfully sent to Jira!";
|
||||
};
|
||||
|
||||
const getFailedFindingIds = (result: JiraDispatchTaskResult | undefined) =>
|
||||
Array.from(new Set(result?.failed_finding_ids?.filter(Boolean) ?? []));
|
||||
|
||||
const withFailedFindingIds = (failedFindingIds: string[]) =>
|
||||
failedFindingIds.length > 0 ? { failedFindingIds } : {};
|
||||
|
||||
export const evaluateJiraDispatchTask = (
|
||||
state: TaskState,
|
||||
result: JiraDispatchTaskResult | null | undefined,
|
||||
): JiraDispatchOutcome => {
|
||||
const jiraResult = result ?? undefined;
|
||||
const failedFindingIds = getFailedFindingIds(jiraResult);
|
||||
|
||||
if (state === "completed") {
|
||||
const failedCount = getFailedCount(jiraResult);
|
||||
if (failedCount > 0) {
|
||||
const successCount = getJiraDispatchSuccessCount(jiraResult);
|
||||
if (successCount > 0) {
|
||||
return {
|
||||
success: true,
|
||||
message: buildSuccessMessage(jiraResult),
|
||||
warning: buildFailureMessage(jiraResult, failedCount),
|
||||
...withFailedFindingIds(failedFindingIds),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: buildFailureMessage(jiraResult, failedCount),
|
||||
...withFailedFindingIds(failedFindingIds),
|
||||
};
|
||||
}
|
||||
|
||||
if (jiraResult?.success === false || jiraResult?.error) {
|
||||
return {
|
||||
success: false,
|
||||
error: jiraResult.error || "Failed to create Jira issue.",
|
||||
...withFailedFindingIds(failedFindingIds),
|
||||
};
|
||||
}
|
||||
|
||||
if (!jiraResult || getJiraDispatchSuccessCount(jiraResult) === 0) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
"Jira dispatch completed but did not create or update any issues.",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: buildSuccessMessage(jiraResult),
|
||||
};
|
||||
}
|
||||
|
||||
if (state === "failed") {
|
||||
return {
|
||||
success: false,
|
||||
error: jiraResult?.error || "Task failed.",
|
||||
...withFailedFindingIds(failedFindingIds),
|
||||
};
|
||||
}
|
||||
|
||||
return { success: false, error: `Unknown task state: ${state}` };
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
JIRA_TARGET_SELECTION_KIND,
|
||||
type JiraBatchSelection,
|
||||
type JiraDispatchTarget,
|
||||
type JiraDispatchTargetBatch,
|
||||
type JiraSelection,
|
||||
type NonEmptyStringArray,
|
||||
} from "@/types/integrations";
|
||||
|
||||
export interface JiraDispatchTargetBatchInput {
|
||||
targetIds: string[];
|
||||
targetType: JiraDispatchTarget;
|
||||
dispatchMode?: JiraDispatchTargetBatch["dispatchMode"];
|
||||
}
|
||||
|
||||
export const toNonEmptyStringArray = (
|
||||
values: string[],
|
||||
): NonEmptyStringArray | null => {
|
||||
const [first, ...rest] = values.filter(Boolean);
|
||||
return first ? [first, ...rest] : null;
|
||||
};
|
||||
|
||||
export const createJiraTargetSelection = (
|
||||
targetIds: string[],
|
||||
targetType: JiraDispatchTarget,
|
||||
): JiraSelection | null => {
|
||||
const nonEmptyTargetIds = toNonEmptyStringArray(targetIds);
|
||||
if (!nonEmptyTargetIds) return null;
|
||||
|
||||
if (nonEmptyTargetIds.length === 1) {
|
||||
return {
|
||||
kind: JIRA_TARGET_SELECTION_KIND.SINGLE,
|
||||
targetId: nonEmptyTargetIds[0],
|
||||
targetType,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
kind: JIRA_TARGET_SELECTION_KIND.TARGET_LIST,
|
||||
targetIds: nonEmptyTargetIds,
|
||||
targetType,
|
||||
};
|
||||
};
|
||||
|
||||
export const createJiraBatchSelection = (
|
||||
batches: JiraDispatchTargetBatchInput[],
|
||||
): JiraBatchSelection | null => {
|
||||
const normalizedBatches = batches.flatMap((batch) => {
|
||||
const targetIds = toNonEmptyStringArray(batch.targetIds);
|
||||
return targetIds ? [{ ...batch, targetIds }] : [];
|
||||
});
|
||||
const [firstBatch, ...remainingBatches] = normalizedBatches;
|
||||
|
||||
return firstBatch
|
||||
? {
|
||||
kind: JIRA_TARGET_SELECTION_KIND.BATCHES,
|
||||
batches: [firstBatch, ...remainingBatches],
|
||||
}
|
||||
: null;
|
||||
};
|
||||
|
||||
export const getJiraSelectionBatches = (
|
||||
selection: JiraSelection,
|
||||
): [JiraDispatchTargetBatch, ...JiraDispatchTargetBatch[]] => {
|
||||
if (selection.kind === JIRA_TARGET_SELECTION_KIND.BATCHES) {
|
||||
return selection.batches;
|
||||
}
|
||||
|
||||
if (selection.kind === JIRA_TARGET_SELECTION_KIND.SINGLE) {
|
||||
return [
|
||||
{
|
||||
targetIds: [selection.targetId],
|
||||
targetType: selection.targetType,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
targetIds: selection.targetIds,
|
||||
targetType: selection.targetType,
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { WatchedTask } from "@/store/task-watcher/store";
|
||||
import {
|
||||
JIRA_DISPATCH_MODE,
|
||||
type JiraDispatchMode,
|
||||
} from "@/types/integrations";
|
||||
|
||||
export interface JiraDispatchTaskMeta {
|
||||
integrationId: string;
|
||||
projectKey: string;
|
||||
issueType: string;
|
||||
dispatchMode: JiraDispatchMode;
|
||||
}
|
||||
|
||||
export const buildJiraDispatchTaskMeta = ({
|
||||
integrationId,
|
||||
projectKey,
|
||||
issueType,
|
||||
dispatchMode,
|
||||
}: JiraDispatchTaskMeta): Record<string, string> => ({
|
||||
integrationId,
|
||||
projectKey,
|
||||
issueType,
|
||||
dispatchMode,
|
||||
});
|
||||
|
||||
export const parseJiraDispatchTaskMeta = (
|
||||
task: WatchedTask,
|
||||
): JiraDispatchTaskMeta | null => {
|
||||
const { integrationId, projectKey, issueType, dispatchMode } = task.meta;
|
||||
if (
|
||||
!integrationId ||
|
||||
!projectKey ||
|
||||
!issueType ||
|
||||
(dispatchMode !== JIRA_DISPATCH_MODE.GROUPED &&
|
||||
dispatchMode !== JIRA_DISPATCH_MODE.INDIVIDUAL)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
integrationId,
|
||||
projectKey,
|
||||
issueType,
|
||||
dispatchMode,
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user