Skip to content

Commit 7364b3b

Browse files
authored
List the names of the failing benchmark subworkflows (#2144)
Fixes OPS-3888. <img width="573" height="914" alt="Screenshot 2026-03-17 at 14 58 50" src="https://github.com/user-attachments/assets/c2b1c510-7e7a-47af-ab98-7fa533e8978b" />
1 parent 6ee6158 commit 7364b3b

6 files changed

Lines changed: 102 additions & 7 deletions

File tree

packages/react-ui/src/app/features/benchmark/components/benchmark-wizard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export const BenchmarkWizard = ({
8787
const {
8888
runPhase,
8989
runningProgress,
90+
failedWorkflows,
9091
isRunPending,
9192
handleRunBenchmark,
9293
handleResetRun,
@@ -174,6 +175,7 @@ export const BenchmarkWizard = ({
174175
result={benchmarkCreateResult}
175176
runPhase={runPhase}
176177
runningProgress={runningProgress ?? undefined}
178+
failedWorkflows={failedWorkflows}
177179
/>
178180
)}
179181
</WizardStep>

packages/react-ui/src/app/features/benchmark/tests/use-benchmark-run.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ describe('useBenchmarkRun', () => {
136136

137137
expect(result.current.isRunPending).toBe(false);
138138
});
139+
140+
it('should start with empty failedWorkflows', () => {
141+
const { result } = renderHook(() =>
142+
useBenchmarkRun(buildBenchmarkCreationResult()),
143+
);
144+
145+
expect(result.current.failedWorkflows).toEqual([]);
146+
});
139147
});
140148

141149
describe('handleRunBenchmark', () => {
@@ -255,6 +263,7 @@ describe('useBenchmarkRun', () => {
255263
});
256264

257265
expect(result.current.runPhase).toBe('succeeded');
266+
expect(result.current.failedWorkflows).toEqual([]);
258267
});
259268

260269
it('should transition to succeeded_with_failures when some sub-workflows failed', async () => {
@@ -293,6 +302,9 @@ describe('useBenchmarkRun', () => {
293302
});
294303

295304
expect(result.current.runPhase).toBe('succeeded_with_failures');
305+
expect(result.current.failedWorkflows).toEqual([
306+
{ displayName: 'Sub', runId: 'run-2' },
307+
]);
296308
});
297309

298310
it('should transition to failed when status is FAILED', async () => {

packages/react-ui/src/app/features/benchmark/use-benchmark-run.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ import { useEffect, useMemo, useState } from 'react';
1515

1616
import { benchmarkApi } from './benchmark-api';
1717

18+
export type FailedWorkflow = {
19+
displayName: string;
20+
runId?: string;
21+
};
22+
1823
export type UseBenchmarkRunResult = {
1924
runPhase: BenchmarkRunPhase;
2025
runningProgress: { completed: number; total: number } | null;
26+
failedWorkflows: FailedWorkflow[];
2127
isRunPending: boolean;
2228
handleRunBenchmark: () => Promise<void>;
2329
handleResetRun: () => void;
@@ -43,11 +49,25 @@ function mapStatusToRunPhase(
4349
}
4450
}
4551

52+
function getFailedSubWorkflows(
53+
data: BenchmarkStatusResponse,
54+
): FailedWorkflow[] {
55+
return data.workflows
56+
.filter(
57+
(w) =>
58+
!w.isOrchestrator &&
59+
!w.isCleanup &&
60+
w.runStatus === BenchmarkStatus.FAILED,
61+
)
62+
.map((w) => ({ displayName: w.displayName, runId: w.runId }));
63+
}
64+
4665
export const useBenchmarkRun = (
4766
benchmarkCreateResult: BenchmarkCreationResult | null,
4867
): UseBenchmarkRunResult => {
4968
const [runPhase, setRunPhase] = useState<BenchmarkRunPhase>('idle');
5069
const [lastRunId, setLastRunId] = useState<string | undefined>();
70+
const [failedWorkflows, setFailedWorkflows] = useState<FailedWorkflow[]>([]);
5171
const [runCount, setRunCount] = useState(0);
5272

5373
const benchmarkId = benchmarkCreateResult?.benchmarkId ?? null;
@@ -80,6 +100,9 @@ export const useBenchmarkRun = (
80100
setLastRunId(statusData.lastRunId);
81101
}
82102
const newPhase = mapStatusToRunPhase(statusData);
103+
if (newPhase === 'succeeded_with_failures') {
104+
setFailedWorkflows(getFailedSubWorkflows(statusData));
105+
}
83106
if (newPhase !== null) {
84107
setRunPhase(newPhase);
85108
}
@@ -122,6 +145,7 @@ export const useBenchmarkRun = (
122145
const handleResetRun = () => {
123146
setRunPhase('idle');
124147
setLastRunId(undefined);
148+
setFailedWorkflows([]);
125149
};
126150

127151
const runningProgress = useMemo(() => {
@@ -148,6 +172,7 @@ export const useBenchmarkRun = (
148172
return {
149173
runPhase,
150174
runningProgress,
175+
failedWorkflows,
151176
isRunPending,
152177
handleRunBenchmark,
153178
handleResetRun,

packages/ui-components/src/components/benchmark/benchmark-analytics-phase.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const BenchmarkAnalyticsPhase = ({
88
message: string;
99
provider: string;
1010
}) => (
11-
<div className="flex flex-col gap-3 py-2">
11+
<div className="flex flex-col gap-1 py-2">
1212
<p className="text-sm dark:text-muted-foreground">{message}</p>
1313
<Link
1414
className="text-sm text-primary-200"

packages/ui-components/src/components/benchmark/benchmark-ready-step.tsx

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,63 @@
11
import { BenchmarkRunPhase } from '@/lib/types';
22
import { BenchmarkCreationResult } from '@openops/shared';
33
import { t } from 'i18next';
4+
import { Link } from 'react-router-dom';
45
import { StepDescription } from '../../ui/wizard/wizard-step';
56
import { BenchmarkAnalyticsPhase } from './benchmark-analytics-phase';
67
import { BenchmarkFailedPhase } from './benchmark-failed-phase';
78
import { BenchmarkRunningPhase } from './benchmark-running-phase';
89
import { BenchmarkWorkflowList } from './benchmark-workflow-list';
910

11+
export type FailedWorkflow = {
12+
displayName: string;
13+
runId?: string;
14+
};
15+
16+
const FailedWorkflowsList = ({
17+
workflows,
18+
}: {
19+
workflows: FailedWorkflow[];
20+
}) => (
21+
<div className="flex flex-col gap-1 py-2">
22+
<p className="text-sm dark:text-muted-foreground font-medium">
23+
{t('The following workflows failed:')}
24+
</p>
25+
<ul className="list-disc pl-5 text-sm dark:text-muted-foreground">
26+
{workflows.map((workflow) => (
27+
<li key={workflow.displayName}>
28+
{workflow.runId ? (
29+
<Link
30+
to={`/runs/${workflow.runId}`}
31+
className="text-primary hover:underline"
32+
>
33+
{workflow.displayName}
34+
</Link>
35+
) : (
36+
workflow.displayName
37+
)}
38+
</li>
39+
))}
40+
</ul>
41+
<p className="text-sm dark:text-muted-foreground mt-1">
42+
{t('Click on a workflow name to review failure details.')}
43+
</p>
44+
</div>
45+
);
46+
1047
interface BenchmarkReadyStepProps {
1148
providerName: string;
1249
result: BenchmarkCreationResult;
1350
runPhase: BenchmarkRunPhase;
1451
runningProgress?: { completed: number; total: number };
52+
failedWorkflows?: FailedWorkflow[];
1553
}
1654

1755
export const BenchmarkReadyStep = ({
1856
providerName,
1957
result,
2058
runPhase,
2159
runningProgress,
60+
failedWorkflows,
2261
}: BenchmarkReadyStepProps) => {
2362
const subWorkflows = result.workflows.filter(
2463
(w) => !w.isOrchestrator && !w.isCleanup,
@@ -52,12 +91,17 @@ export const BenchmarkReadyStep = ({
5291
{runPhase === 'failed' && <BenchmarkFailedPhase />}
5392

5493
{runPhase === 'succeeded_with_failures' && (
55-
<BenchmarkAnalyticsPhase
56-
provider={providerName}
57-
message={t(
58-
"You can review your Benchmark Report here (it's not final since there are some failed workflows)",
94+
<>
95+
<BenchmarkAnalyticsPhase
96+
provider={providerName}
97+
message={t(
98+
'You can access the Benchmark Report here. Note: results only cover services from workflows that ran successfully.',
99+
)}
100+
/>
101+
{failedWorkflows && failedWorkflows.length > 0 && (
102+
<FailedWorkflowsList workflows={failedWorkflows} />
59103
)}
60-
/>
104+
</>
61105
)}
62106

63107
{runPhase === 'succeeded' && (

packages/ui-components/src/stories/benchmark/benchmark-ready-step.stories.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ export const RunningWithProgress: ReadyStepStory = {
6767
};
6868
export const Failed: ReadyStepStory = { args: { runPhase: 'failed' } };
6969
export const SucceededWithFailures: ReadyStepStory = {
70-
args: { runPhase: 'succeeded_with_failures' },
70+
args: {
71+
runPhase: 'succeeded_with_failures',
72+
failedWorkflows: [
73+
{
74+
displayName: 'EC2 Reserved Instances Analysis',
75+
runId: 'run-001',
76+
},
77+
{
78+
displayName: 'S3 Storage Optimization',
79+
runId: 'run-002',
80+
},
81+
],
82+
},
7183
};
7284
export const Succeeded: ReadyStepStory = { args: { runPhase: 'succeeded' } };

0 commit comments

Comments
 (0)