Skip to content

Commit b1d9768

Browse files
committed
Link failed benchmark workflows to their run details
Refactor failedWorkflowNames (string[]) to failedWorkflows (FailedWorkflow[]) to carry runId alongside displayName, enabling direct navigation to failure details from the succeeded_with_failures phase. Made-with: Cursor
1 parent 36f909d commit b1d9768

5 files changed

Lines changed: 63 additions & 24 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const BenchmarkWizard = ({
8787
const {
8888
runPhase,
8989
runningProgress,
90-
failedWorkflowNames,
90+
failedWorkflows,
9191
isRunPending,
9292
handleRunBenchmark,
9393
handleResetRun,
@@ -175,7 +175,7 @@ export const BenchmarkWizard = ({
175175
result={benchmarkCreateResult}
176176
runPhase={runPhase}
177177
runningProgress={runningProgress ?? undefined}
178-
failedWorkflowNames={failedWorkflowNames}
178+
failedWorkflows={failedWorkflows}
179179
/>
180180
)}
181181
</WizardStep>

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,12 @@ describe('useBenchmarkRun', () => {
137137
expect(result.current.isRunPending).toBe(false);
138138
});
139139

140-
it('should start with empty failedWorkflowNames', () => {
140+
it('should start with empty failedWorkflows', () => {
141141
const { result } = renderHook(() =>
142142
useBenchmarkRun(buildBenchmarkCreationResult()),
143143
);
144144

145-
expect(result.current.failedWorkflowNames).toEqual([]);
145+
expect(result.current.failedWorkflows).toEqual([]);
146146
});
147147
});
148148

@@ -263,7 +263,7 @@ describe('useBenchmarkRun', () => {
263263
});
264264

265265
expect(result.current.runPhase).toBe('succeeded');
266-
expect(result.current.failedWorkflowNames).toEqual([]);
266+
expect(result.current.failedWorkflows).toEqual([]);
267267
});
268268

269269
it('should transition to succeeded_with_failures when some sub-workflows failed', async () => {
@@ -302,7 +302,9 @@ describe('useBenchmarkRun', () => {
302302
});
303303

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

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

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +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;
21-
failedWorkflowNames: string[];
26+
failedWorkflows: FailedWorkflow[];
2227
isRunPending: boolean;
2328
handleRunBenchmark: () => Promise<void>;
2429
handleResetRun: () => void;
@@ -44,23 +49,25 @@ function mapStatusToRunPhase(
4449
}
4550
}
4651

47-
function getFailedSubWorkflowNames(data: BenchmarkStatusResponse): string[] {
52+
function getFailedSubWorkflows(
53+
data: BenchmarkStatusResponse,
54+
): FailedWorkflow[] {
4855
return data.workflows
4956
.filter(
5057
(w) =>
5158
!w.isOrchestrator &&
5259
!w.isCleanup &&
5360
w.runStatus === BenchmarkStatus.FAILED,
5461
)
55-
.map((w) => w.displayName);
62+
.map((w) => ({ displayName: w.displayName, runId: w.runId }));
5663
}
5764

5865
export const useBenchmarkRun = (
5966
benchmarkCreateResult: BenchmarkCreationResult | null,
6067
): UseBenchmarkRunResult => {
6168
const [runPhase, setRunPhase] = useState<BenchmarkRunPhase>('idle');
6269
const [lastRunId, setLastRunId] = useState<string | undefined>();
63-
const [failedWorkflowNames, setFailedWorkflowNames] = useState<string[]>([]);
70+
const [failedWorkflows, setFailedWorkflows] = useState<FailedWorkflow[]>([]);
6471
const [runCount, setRunCount] = useState(0);
6572

6673
const benchmarkId = benchmarkCreateResult?.benchmarkId ?? null;
@@ -94,7 +101,7 @@ export const useBenchmarkRun = (
94101
}
95102
const newPhase = mapStatusToRunPhase(statusData);
96103
if (newPhase === 'succeeded_with_failures') {
97-
setFailedWorkflowNames(getFailedSubWorkflowNames(statusData));
104+
setFailedWorkflows(getFailedSubWorkflows(statusData));
98105
}
99106
if (newPhase !== null) {
100107
setRunPhase(newPhase);
@@ -138,7 +145,7 @@ export const useBenchmarkRun = (
138145
const handleResetRun = () => {
139146
setRunPhase('idle');
140147
setLastRunId(undefined);
141-
setFailedWorkflowNames([]);
148+
setFailedWorkflows([]);
142149
};
143150

144151
const runningProgress = useMemo(() => {
@@ -165,7 +172,7 @@ export const useBenchmarkRun = (
165172
return {
166173
runPhase,
167174
runningProgress,
168-
failedWorkflowNames,
175+
failedWorkflows,
169176
isRunPending,
170177
handleRunBenchmark,
171178
handleResetRun,

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

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
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

10-
const FailedWorkflowsList = ({ names }: { names: string[] }) => (
11+
export type FailedWorkflow = {
12+
displayName: string;
13+
runId?: string;
14+
};
15+
16+
const FailedWorkflowsList = ({
17+
workflows,
18+
}: {
19+
workflows: FailedWorkflow[];
20+
}) => (
1121
<div className="flex flex-col gap-1 py-2">
1222
<p className="text-sm dark:text-muted-foreground font-medium">
1323
{t('The following workflows failed:')}
1424
</p>
1525
<ul className="list-disc pl-5 text-sm dark:text-muted-foreground">
16-
{names.map((name) => (
17-
<li key={name}>{name}</li>
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>
1839
))}
1940
</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>
2044
</div>
2145
);
2246

@@ -25,15 +49,15 @@ interface BenchmarkReadyStepProps {
2549
result: BenchmarkCreationResult;
2650
runPhase: BenchmarkRunPhase;
2751
runningProgress?: { completed: number; total: number };
28-
failedWorkflowNames?: string[];
52+
failedWorkflows?: FailedWorkflow[];
2953
}
3054

3155
export const BenchmarkReadyStep = ({
3256
providerName,
3357
result,
3458
runPhase,
3559
runningProgress,
36-
failedWorkflowNames,
60+
failedWorkflows,
3761
}: BenchmarkReadyStepProps) => {
3862
const subWorkflows = result.workflows.filter(
3963
(w) => !w.isOrchestrator && !w.isCleanup,
@@ -71,11 +95,11 @@ export const BenchmarkReadyStep = ({
7195
<BenchmarkAnalyticsPhase
7296
provider={providerName}
7397
message={t(
74-
"You can review your Benchmark Report here (it's not final since there are some failed workflows)",
98+
'You can access the Benchmark Report here. Note: results only cover services from workflows that ran successfully.',
7599
)}
76100
/>
77-
{failedWorkflowNames && failedWorkflowNames.length > 0 && (
78-
<FailedWorkflowsList names={failedWorkflowNames} />
101+
{failedWorkflows && failedWorkflows.length > 0 && (
102+
<FailedWorkflowsList workflows={failedWorkflows} />
79103
)}
80104
</>
81105
)}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,15 @@ export const Failed: ReadyStepStory = { args: { runPhase: 'failed' } };
6969
export const SucceededWithFailures: ReadyStepStory = {
7070
args: {
7171
runPhase: 'succeeded_with_failures',
72-
failedWorkflowNames: [
73-
'EC2 Reserved Instances Analysis',
74-
'S3 Storage Optimization',
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+
},
7581
],
7682
},
7783
};

0 commit comments

Comments
 (0)