@shipfox/client-workflows 15.0.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.css +4 -0
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +41 -0
- package/dist/components/identifier/identifier.js +2 -2
- package/dist/components/identifier/identifier.js.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
- package/dist/components/job-detail/job-context-panel.d.ts +6 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
- package/dist/components/job-detail/job-context-panel.js +224 -0
- package/dist/components/job-detail/job-context-panel.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +10 -1
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-header.js +58 -39
- package/dist/components/job-detail/job-detail-header.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +92 -41
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +19 -10
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-switcher.js +4 -4
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
- package/dist/components/job-detail/json-code.d.ts +14 -0
- package/dist/components/job-detail/json-code.d.ts.map +1 -0
- package/dist/components/job-detail/json-code.js +93 -0
- package/dist/components/job-detail/json-code.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
- package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
- package/dist/components/job-detail/step-troubleshooting.js +493 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +5 -4
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-node.js +3 -3
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/index.d.ts +1 -0
- package/dist/components/step-list/index.d.ts.map +1 -1
- package/dist/components/step-list/index.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +5 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +5 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +117 -82
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +6 -6
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +9 -13
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +3 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js +3 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +39 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js +211 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +90 -25
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +16 -10
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +1 -3
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +217 -59
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +7 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +9 -0
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +29 -0
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +3 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/run-annotation.d.ts +79 -0
- package/dist/core/run-annotation.d.ts.map +1 -0
- package/dist/core/run-annotation.js +125 -0
- package/dist/core/run-annotation.js.map +1 -0
- package/dist/core/workflow-run.d.ts +1 -1
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/annotations.d.ts +63 -0
- package/dist/hooks/api/annotations.d.ts.map +1 -0
- package/dist/hooks/api/annotations.js +145 -0
- package/dist/hooks/api/annotations.js.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.js +22 -0
- package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
- package/dist/hooks/api/run-annotations.d.ts +25 -0
- package/dist/hooks/api/run-annotations.d.ts.map +1 -0
- package/dist/hooks/api/run-annotations.js +78 -0
- package/dist/hooks/api/run-annotations.js.map +1 -0
- package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
- package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
- package/dist/hooks/api/step-attempt-detail.js +43 -0
- package/dist/hooks/api/step-attempt-detail.js.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +47 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/pages/project-workflows-page.js +19 -19
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.js +1 -1
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +7 -2
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +5 -6
- package/dist/routes/inputs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -9
- package/src/components/identifier/identifier.tsx +2 -2
- package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
- package/src/components/job-detail/job-context-panel.test.tsx +72 -0
- package/src/components/job-detail/job-context-panel.tsx +237 -0
- package/src/components/job-detail/job-detail-header.test.tsx +3 -0
- package/src/components/job-detail/job-detail-header.tsx +65 -34
- package/src/components/job-detail/job-detail-view.stories.tsx +555 -48
- package/src/components/job-detail/job-detail-view.tsx +101 -42
- package/src/components/job-detail/job-empty-states.tsx +23 -10
- package/src/components/job-detail/job-execution-switcher.tsx +5 -5
- package/src/components/job-detail/json-code.tsx +120 -0
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
- package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
- package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
- package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
- package/src/components/job-detail/step-troubleshooting.tsx +537 -0
- package/src/components/job-graph/job-graph-content.tsx +4 -3
- package/src/components/job-graph/job-node.tsx +3 -3
- package/src/components/step-list/index.ts +1 -0
- package/src/components/step-list/step-list-model.ts +5 -0
- package/src/components/step-list/step-list.test.tsx +75 -2
- package/src/components/step-list/step-list.tsx +73 -20
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +12 -12
- package/src/components/workflow-run-tabs/index.ts +7 -0
- package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
- package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
- package/src/components/workflow-run-tabs/run-annotation-list.tsx +273 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +95 -20
- package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
- package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +44 -0
- package/src/components/workflow-run-view/run-workspace-nav.tsx +22 -9
- package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +246 -20
- package/src/components/workflow-run-view/workflow-run-view.tsx +255 -37
- package/src/core/entities/job-execution.ts +7 -0
- package/src/core/entities/job.ts +9 -0
- package/src/core/entities/step-attempt.ts +33 -0
- package/src/core/entities/step.ts +3 -1
- package/src/core/run-annotation.test.ts +315 -0
- package/src/core/run-annotation.ts +244 -0
- package/src/core/workflow-run.ts +4 -0
- package/src/hooks/api/annotations.test.ts +84 -0
- package/src/hooks/api/annotations.ts +240 -0
- package/src/hooks/api/run-annotation-mapper.ts +30 -0
- package/src/hooks/api/run-annotations.ts +137 -0
- package/src/hooks/api/step-attempt-detail.ts +67 -0
- package/src/hooks/api/workflow-run-mapper.ts +46 -0
- package/src/hooks/api/workflow-runs.test.tsx +61 -1
- package/src/pages/project-workflows-page.tsx +19 -19
- package/src/pages/workflow-job-detail-page.test.tsx +118 -1
- package/src/pages/workflow-run-list-page.tsx +1 -1
- package/src/routes/inputs.ts +7 -2
- package/test/fixtures/workflow-run.ts +7 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/workflow-run-tabs.d.ts +0 -9
- package/dist/core/workflow-run-tabs.d.ts.map +0 -1
- package/dist/core/workflow-run-tabs.js +0 -3
- package/dist/core/workflow-run-tabs.js.map +0 -1
- package/src/core/workflow-run-tabs.ts +0 -8
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnnotationStyleDto,
|
|
3
|
+
annotationSummaryResponseSchema,
|
|
4
|
+
readAnnotationsResponseSchema,
|
|
5
|
+
} from '@shipfox/annotations-dto';
|
|
6
|
+
import {checkedApiRequest} from '@shipfox/client-api';
|
|
7
|
+
import {
|
|
8
|
+
infiniteQueryOptions,
|
|
9
|
+
queryOptions,
|
|
10
|
+
useInfiniteQuery,
|
|
11
|
+
useQuery,
|
|
12
|
+
} from '@tanstack/react-query';
|
|
13
|
+
import type {RunAnnotationSummary} from '#core/run-annotation.js';
|
|
14
|
+
|
|
15
|
+
const ANNOTATIONS_PAGE_SIZE = 100;
|
|
16
|
+
const ANNOTATIONS_REFRESH_INTERVAL_MS = 5_000;
|
|
17
|
+
|
|
18
|
+
export interface WorkflowRunAnnotation {
|
|
19
|
+
id: string;
|
|
20
|
+
jobId: string;
|
|
21
|
+
jobExecutionId: string;
|
|
22
|
+
originStepId: string;
|
|
23
|
+
originStepAttempt: number;
|
|
24
|
+
context: string;
|
|
25
|
+
style: AnnotationStyleDto;
|
|
26
|
+
sequence: number;
|
|
27
|
+
body: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WorkflowRunAnnotationsPage {
|
|
31
|
+
annotations: WorkflowRunAnnotation[];
|
|
32
|
+
hasMore: boolean;
|
|
33
|
+
nextCursor: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const workflowRunAnnotationsQueryKeys = {
|
|
37
|
+
all: ['workflow-run-annotations'] as const,
|
|
38
|
+
list: (workflowRunId: string, attempt: number, jobExecutionId?: string) =>
|
|
39
|
+
[
|
|
40
|
+
...workflowRunAnnotationsQueryKeys.all,
|
|
41
|
+
'list',
|
|
42
|
+
workflowRunId,
|
|
43
|
+
attempt,
|
|
44
|
+
jobExecutionId ?? null,
|
|
45
|
+
] as const,
|
|
46
|
+
summary: (workflowRunId: string, attempt: number, jobExecutionId?: string) =>
|
|
47
|
+
[
|
|
48
|
+
...workflowRunAnnotationsQueryKeys.all,
|
|
49
|
+
'summary',
|
|
50
|
+
workflowRunId,
|
|
51
|
+
attempt,
|
|
52
|
+
jobExecutionId ?? null,
|
|
53
|
+
] as const,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function annotationParams({
|
|
57
|
+
workflowRunId,
|
|
58
|
+
attempt,
|
|
59
|
+
jobExecutionId,
|
|
60
|
+
cursor,
|
|
61
|
+
}: {
|
|
62
|
+
workflowRunId: string;
|
|
63
|
+
attempt: number;
|
|
64
|
+
jobExecutionId?: string | undefined;
|
|
65
|
+
cursor?: string | null | undefined;
|
|
66
|
+
}): URLSearchParams {
|
|
67
|
+
const params = new URLSearchParams({
|
|
68
|
+
workflow_run_id: workflowRunId,
|
|
69
|
+
attempt: String(attempt),
|
|
70
|
+
limit: String(ANNOTATIONS_PAGE_SIZE),
|
|
71
|
+
});
|
|
72
|
+
if (jobExecutionId) params.set('job_execution_id', jobExecutionId);
|
|
73
|
+
if (cursor) params.set('cursor', cursor);
|
|
74
|
+
return params;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function getWorkflowRunAnnotationsPage({
|
|
78
|
+
workflowRunId,
|
|
79
|
+
attempt,
|
|
80
|
+
jobExecutionId,
|
|
81
|
+
cursor,
|
|
82
|
+
signal,
|
|
83
|
+
}: {
|
|
84
|
+
workflowRunId: string;
|
|
85
|
+
attempt: number;
|
|
86
|
+
jobExecutionId?: string | undefined;
|
|
87
|
+
cursor?: string | null | undefined;
|
|
88
|
+
signal?: AbortSignal;
|
|
89
|
+
}): Promise<WorkflowRunAnnotationsPage> {
|
|
90
|
+
const response = await checkedApiRequest(
|
|
91
|
+
readAnnotationsResponseSchema,
|
|
92
|
+
`/annotations?${annotationParams({workflowRunId, attempt, jobExecutionId, cursor}).toString()}`,
|
|
93
|
+
{signal},
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
annotations: response.annotations.map(toWorkflowRunAnnotation),
|
|
97
|
+
hasMore: response.has_more,
|
|
98
|
+
nextCursor: response.next_cursor,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function getWorkflowRunAnnotationSummary({
|
|
103
|
+
workflowRunId,
|
|
104
|
+
attempt,
|
|
105
|
+
jobExecutionId,
|
|
106
|
+
signal,
|
|
107
|
+
}: {
|
|
108
|
+
workflowRunId: string;
|
|
109
|
+
attempt: number;
|
|
110
|
+
jobExecutionId?: string | undefined;
|
|
111
|
+
signal?: AbortSignal;
|
|
112
|
+
}): Promise<RunAnnotationSummary> {
|
|
113
|
+
const params = annotationParams({workflowRunId, attempt, jobExecutionId});
|
|
114
|
+
params.delete('limit');
|
|
115
|
+
const response = await checkedApiRequest(
|
|
116
|
+
annotationSummaryResponseSchema,
|
|
117
|
+
`/annotations/summary?${params.toString()}`,
|
|
118
|
+
{signal},
|
|
119
|
+
);
|
|
120
|
+
return {
|
|
121
|
+
total: response.total,
|
|
122
|
+
error: response.error,
|
|
123
|
+
warning: response.warning,
|
|
124
|
+
info: response.info,
|
|
125
|
+
success: response.success,
|
|
126
|
+
truncated: false,
|
|
127
|
+
stepCounts: response.step_counts.map((step) => ({
|
|
128
|
+
stepId: step.origin_step_id,
|
|
129
|
+
attempt: step.origin_step_attempt,
|
|
130
|
+
total: step.total,
|
|
131
|
+
})),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function workflowRunAnnotationsQueryOptions(
|
|
136
|
+
workflowRunId: string | undefined,
|
|
137
|
+
attempt: number | undefined,
|
|
138
|
+
jobExecutionId?: string | undefined,
|
|
139
|
+
options?: {enabled?: boolean | undefined; polling?: boolean | undefined},
|
|
140
|
+
) {
|
|
141
|
+
const enabled = Boolean(workflowRunId) && attempt !== undefined && (options?.enabled ?? true);
|
|
142
|
+
const polling = options?.polling ?? true;
|
|
143
|
+
return infiniteQueryOptions({
|
|
144
|
+
queryKey:
|
|
145
|
+
workflowRunId && attempt !== undefined
|
|
146
|
+
? workflowRunAnnotationsQueryKeys.list(workflowRunId, attempt, jobExecutionId)
|
|
147
|
+
: ([...workflowRunAnnotationsQueryKeys.all, 'list'] as const),
|
|
148
|
+
enabled,
|
|
149
|
+
initialPageParam: null as string | null,
|
|
150
|
+
queryFn: ({pageParam, signal}) =>
|
|
151
|
+
getWorkflowRunAnnotationsPage({
|
|
152
|
+
workflowRunId: workflowRunId ?? '',
|
|
153
|
+
attempt: attempt ?? 0,
|
|
154
|
+
...(jobExecutionId ? {jobExecutionId} : {}),
|
|
155
|
+
cursor: pageParam,
|
|
156
|
+
signal,
|
|
157
|
+
}),
|
|
158
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
159
|
+
staleTime: 2_000,
|
|
160
|
+
refetchInterval:
|
|
161
|
+
enabled && polling
|
|
162
|
+
? (query) => {
|
|
163
|
+
const pages = query.state.data?.pages;
|
|
164
|
+
return pages && pages.length > 1 ? false : ANNOTATIONS_REFRESH_INTERVAL_MS;
|
|
165
|
+
}
|
|
166
|
+
: false,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function useWorkflowRunAnnotationsQuery(
|
|
171
|
+
workflowRunId: string | undefined,
|
|
172
|
+
attempt: number | undefined,
|
|
173
|
+
jobExecutionId?: string | undefined,
|
|
174
|
+
options?: {enabled?: boolean | undefined; polling?: boolean | undefined},
|
|
175
|
+
) {
|
|
176
|
+
return useInfiniteQuery(
|
|
177
|
+
workflowRunAnnotationsQueryOptions(workflowRunId, attempt, jobExecutionId, options),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function workflowRunAnnotationSummaryQueryOptions(
|
|
182
|
+
workflowRunId: string | undefined,
|
|
183
|
+
attempt: number | undefined,
|
|
184
|
+
jobExecutionId?: string | undefined,
|
|
185
|
+
options?: {enabled?: boolean | undefined; polling?: boolean | undefined},
|
|
186
|
+
) {
|
|
187
|
+
const enabled = Boolean(workflowRunId) && attempt !== undefined && (options?.enabled ?? true);
|
|
188
|
+
const polling = options?.polling ?? true;
|
|
189
|
+
return queryOptions({
|
|
190
|
+
queryKey:
|
|
191
|
+
workflowRunId && attempt !== undefined
|
|
192
|
+
? workflowRunAnnotationsQueryKeys.summary(workflowRunId, attempt, jobExecutionId)
|
|
193
|
+
: ([...workflowRunAnnotationsQueryKeys.all, 'summary'] as const),
|
|
194
|
+
enabled,
|
|
195
|
+
queryFn: ({signal}) =>
|
|
196
|
+
getWorkflowRunAnnotationSummary({
|
|
197
|
+
workflowRunId: workflowRunId ?? '',
|
|
198
|
+
attempt: attempt ?? 0,
|
|
199
|
+
...(jobExecutionId ? {jobExecutionId} : {}),
|
|
200
|
+
signal,
|
|
201
|
+
}),
|
|
202
|
+
staleTime: 2_000,
|
|
203
|
+
refetchInterval: enabled && polling ? ANNOTATIONS_REFRESH_INTERVAL_MS : false,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function useWorkflowRunAnnotationSummaryQuery(
|
|
208
|
+
workflowRunId: string | undefined,
|
|
209
|
+
attempt: number | undefined,
|
|
210
|
+
jobExecutionId?: string | undefined,
|
|
211
|
+
options?: {enabled?: boolean | undefined; polling?: boolean | undefined},
|
|
212
|
+
) {
|
|
213
|
+
return useQuery(
|
|
214
|
+
workflowRunAnnotationSummaryQueryOptions(workflowRunId, attempt, jobExecutionId, options),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function flattenWorkflowRunAnnotations(
|
|
219
|
+
data: {pages: readonly WorkflowRunAnnotationsPage[]} | undefined,
|
|
220
|
+
): WorkflowRunAnnotation[] {
|
|
221
|
+
return data?.pages.flatMap((page) => page.annotations) ?? [];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function toWorkflowRunAnnotation(
|
|
225
|
+
annotation: Awaited<
|
|
226
|
+
ReturnType<typeof readAnnotationsResponseSchema.parse>
|
|
227
|
+
>['annotations'][number],
|
|
228
|
+
): WorkflowRunAnnotation {
|
|
229
|
+
return {
|
|
230
|
+
id: annotation.id,
|
|
231
|
+
jobId: annotation.job_id,
|
|
232
|
+
jobExecutionId: annotation.job_execution_id,
|
|
233
|
+
originStepId: annotation.origin_step_id,
|
|
234
|
+
originStepAttempt: annotation.origin_step_attempt,
|
|
235
|
+
context: annotation.context,
|
|
236
|
+
style: annotation.style,
|
|
237
|
+
sequence: annotation.sequence,
|
|
238
|
+
body: annotation.body,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {AnnotationDto, ReadAnnotationsResponseDto} from '@shipfox/annotations-dto';
|
|
2
|
+
import type {RunAnnotationRecord} from '#core/run-annotation.js';
|
|
3
|
+
|
|
4
|
+
export interface RunAnnotationPage {
|
|
5
|
+
annotations: RunAnnotationRecord[];
|
|
6
|
+
hasMore: boolean;
|
|
7
|
+
nextCursor: string | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function toRunAnnotation(dto: AnnotationDto): RunAnnotationRecord {
|
|
11
|
+
return {
|
|
12
|
+
id: dto.id,
|
|
13
|
+
jobId: dto.job_id,
|
|
14
|
+
jobExecutionId: dto.job_execution_id,
|
|
15
|
+
originStepId: dto.origin_step_id,
|
|
16
|
+
originStepAttempt: dto.origin_step_attempt,
|
|
17
|
+
context: dto.context,
|
|
18
|
+
style: dto.style,
|
|
19
|
+
sequence: dto.sequence,
|
|
20
|
+
body: dto.body,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function toRunAnnotationPage(response: ReadAnnotationsResponseDto): RunAnnotationPage {
|
|
25
|
+
return {
|
|
26
|
+
annotations: response.annotations.map(toRunAnnotation),
|
|
27
|
+
hasMore: response.has_more,
|
|
28
|
+
nextCursor: response.next_cursor,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {READ_ANNOTATIONS_MAX_LIMIT, readAnnotationsResponseSchema} from '@shipfox/annotations-dto';
|
|
2
|
+
import {checkedApiRequest} from '@shipfox/client-api';
|
|
3
|
+
import {
|
|
4
|
+
type InfiniteData,
|
|
5
|
+
infiniteQueryOptions,
|
|
6
|
+
type UseInfiniteQueryOptions,
|
|
7
|
+
useInfiniteQuery,
|
|
8
|
+
} from '@tanstack/react-query';
|
|
9
|
+
import {useMemo} from 'react';
|
|
10
|
+
import {
|
|
11
|
+
type RunAnnotationRecord,
|
|
12
|
+
type RunAnnotationSummary,
|
|
13
|
+
summarizeRunAnnotations,
|
|
14
|
+
} from '#core/run-annotation.js';
|
|
15
|
+
import {type RunAnnotationPage, toRunAnnotationPage} from './run-annotation-mapper.js';
|
|
16
|
+
|
|
17
|
+
/** Matches the run detail poll, so annotations and run state never disagree by more than a tick. */
|
|
18
|
+
const ACTIVE_POLL_MS = 4_000;
|
|
19
|
+
|
|
20
|
+
export const runAnnotationsQueryKeys = {
|
|
21
|
+
all: ['run-annotations'] as const,
|
|
22
|
+
list: (workflowRunId: string, runAttempt?: number | undefined) =>
|
|
23
|
+
[...runAnnotationsQueryKeys.all, 'list', workflowRunId, runAttempt ?? null] as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type RunAnnotationsQueryKey =
|
|
27
|
+
| ReturnType<typeof runAnnotationsQueryKeys.list>
|
|
28
|
+
| readonly ['run-annotations', 'list'];
|
|
29
|
+
|
|
30
|
+
type RunAnnotationsQueryOptions = UseInfiniteQueryOptions<
|
|
31
|
+
RunAnnotationPage,
|
|
32
|
+
Error,
|
|
33
|
+
InfiniteData<RunAnnotationPage, string | undefined>,
|
|
34
|
+
RunAnnotationsQueryKey,
|
|
35
|
+
string | undefined
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
async function listRunAnnotations({
|
|
39
|
+
workflowRunId,
|
|
40
|
+
runAttempt,
|
|
41
|
+
cursor,
|
|
42
|
+
signal,
|
|
43
|
+
}: {
|
|
44
|
+
workflowRunId: string;
|
|
45
|
+
runAttempt: number;
|
|
46
|
+
cursor?: string | undefined;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}): Promise<RunAnnotationPage> {
|
|
49
|
+
const params = new URLSearchParams({
|
|
50
|
+
workflow_run_id: workflowRunId,
|
|
51
|
+
attempt: String(runAttempt),
|
|
52
|
+
limit: String(READ_ANNOTATIONS_MAX_LIMIT),
|
|
53
|
+
});
|
|
54
|
+
if (cursor) params.set('cursor', cursor);
|
|
55
|
+
|
|
56
|
+
return toRunAnnotationPage(
|
|
57
|
+
await checkedApiRequest(readAnnotationsResponseSchema, `/annotations?${params.toString()}`, {
|
|
58
|
+
signal,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RunAnnotationsQueryInput {
|
|
64
|
+
workflowRunId: string | undefined;
|
|
65
|
+
runAttempt: number | undefined;
|
|
66
|
+
/** Poll while the run attempt is non-terminal, then settle. */
|
|
67
|
+
live?: boolean | undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function runAnnotationsQueryOptions({
|
|
71
|
+
workflowRunId,
|
|
72
|
+
runAttempt,
|
|
73
|
+
live = false,
|
|
74
|
+
}: RunAnnotationsQueryInput): RunAnnotationsQueryOptions {
|
|
75
|
+
const enabled = Boolean(workflowRunId) && runAttempt !== undefined;
|
|
76
|
+
|
|
77
|
+
// Polling stops once the reader has paged past the first page: the cursor bounding page 2 was
|
|
78
|
+
// computed from page 1's last row, so a refetch that shifts that boundary can drop a range of
|
|
79
|
+
// annotations into a between-pages gap.
|
|
80
|
+
return infiniteQueryOptions({
|
|
81
|
+
queryKey: workflowRunId
|
|
82
|
+
? runAnnotationsQueryKeys.list(workflowRunId, runAttempt)
|
|
83
|
+
: ([...runAnnotationsQueryKeys.all, 'list'] as const),
|
|
84
|
+
enabled,
|
|
85
|
+
initialPageParam: undefined as string | undefined,
|
|
86
|
+
queryFn: ({pageParam, signal}) =>
|
|
87
|
+
listRunAnnotations({
|
|
88
|
+
workflowRunId: workflowRunId ?? '',
|
|
89
|
+
runAttempt: runAttempt ?? 1,
|
|
90
|
+
cursor: pageParam,
|
|
91
|
+
signal,
|
|
92
|
+
}),
|
|
93
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
|
|
94
|
+
staleTime: 2_000,
|
|
95
|
+
refetchOnWindowFocus: true,
|
|
96
|
+
refetchInterval: (query) => {
|
|
97
|
+
if (!live) return false;
|
|
98
|
+
const pages = query.state.data?.pages;
|
|
99
|
+
return pages && pages.length > 1 ? false : ACTIVE_POLL_MS;
|
|
100
|
+
},
|
|
101
|
+
refetchIntervalInBackground: false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface RunAnnotationsQueryResult {
|
|
106
|
+
query: ReturnType<
|
|
107
|
+
typeof useInfiniteQuery<
|
|
108
|
+
RunAnnotationPage,
|
|
109
|
+
Error,
|
|
110
|
+
InfiniteData<RunAnnotationPage, string | undefined>,
|
|
111
|
+
RunAnnotationsQueryKey,
|
|
112
|
+
string | undefined
|
|
113
|
+
>
|
|
114
|
+
>;
|
|
115
|
+
/** `undefined` until the first page resolves, so counts never render a speculative zero. */
|
|
116
|
+
annotations: RunAnnotationRecord[] | undefined;
|
|
117
|
+
summary: RunAnnotationSummary | undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function useRunAnnotationsQuery(input: RunAnnotationsQueryInput): RunAnnotationsQueryResult {
|
|
121
|
+
const query = useInfiniteQuery(runAnnotationsQueryOptions(input));
|
|
122
|
+
const pages = query.data?.pages;
|
|
123
|
+
|
|
124
|
+
const annotations = useMemo(
|
|
125
|
+
() => (pages ? pages.flatMap((page) => page.annotations) : undefined),
|
|
126
|
+
[pages],
|
|
127
|
+
);
|
|
128
|
+
const summary = useMemo(
|
|
129
|
+
() =>
|
|
130
|
+
annotations
|
|
131
|
+
? summarizeRunAnnotations(annotations, {truncated: pages?.at(-1)?.hasMore ?? false})
|
|
132
|
+
: undefined,
|
|
133
|
+
[annotations, pages],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return {query, annotations, summary};
|
|
137
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {stepAttemptDetailResponseSchema} from '@shipfox/api-workflows-dto';
|
|
2
|
+
import {checkedApiRequest} from '@shipfox/client-api';
|
|
3
|
+
import {queryOptions, type UseQueryOptions, useQuery} from '@tanstack/react-query';
|
|
4
|
+
import type {StepAttemptDetail} from '#core/workflow-run.js';
|
|
5
|
+
import {toStepAttemptDetail} from './workflow-run-mapper.js';
|
|
6
|
+
|
|
7
|
+
export const stepAttemptDetailQueryKeys = {
|
|
8
|
+
all: ['workflow-step-attempt-details'] as const,
|
|
9
|
+
detail: (stepId: string, attempt: number) =>
|
|
10
|
+
[...stepAttemptDetailQueryKeys.all, stepId, attempt] as const,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type StepAttemptDetailQueryKey =
|
|
14
|
+
| ReturnType<typeof stepAttemptDetailQueryKeys.detail>
|
|
15
|
+
| readonly ['workflow-step-attempt-details'];
|
|
16
|
+
|
|
17
|
+
type StepAttemptDetailQueryOptions = UseQueryOptions<
|
|
18
|
+
StepAttemptDetail,
|
|
19
|
+
Error,
|
|
20
|
+
StepAttemptDetail,
|
|
21
|
+
StepAttemptDetailQueryKey
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
async function getStepAttemptDetail({
|
|
25
|
+
stepId,
|
|
26
|
+
attempt,
|
|
27
|
+
signal,
|
|
28
|
+
}: {
|
|
29
|
+
stepId: string;
|
|
30
|
+
attempt: number;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
}): Promise<StepAttemptDetail> {
|
|
33
|
+
const response = await checkedApiRequest(
|
|
34
|
+
stepAttemptDetailResponseSchema,
|
|
35
|
+
`/workflows/runs/steps/${stepId}/attempts/${attempt}`,
|
|
36
|
+
{signal},
|
|
37
|
+
);
|
|
38
|
+
return toStepAttemptDetail(response);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function stepAttemptDetailQueryOptions(
|
|
42
|
+
stepId: string | undefined,
|
|
43
|
+
attempt: number | undefined,
|
|
44
|
+
): StepAttemptDetailQueryOptions {
|
|
45
|
+
return queryOptions({
|
|
46
|
+
queryKey:
|
|
47
|
+
stepId && attempt !== undefined
|
|
48
|
+
? stepAttemptDetailQueryKeys.detail(stepId, attempt)
|
|
49
|
+
: ([...stepAttemptDetailQueryKeys.all] as const),
|
|
50
|
+
enabled: Boolean(stepId) && attempt !== undefined,
|
|
51
|
+
queryFn: ({signal}) =>
|
|
52
|
+
getStepAttemptDetail({stepId: stepId ?? '', attempt: attempt ?? 0, signal}),
|
|
53
|
+
staleTime: Infinity,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useStepAttemptDetailQuery(
|
|
58
|
+
stepId: string | undefined,
|
|
59
|
+
attempt: number | undefined,
|
|
60
|
+
options?: {enabled?: boolean | undefined},
|
|
61
|
+
) {
|
|
62
|
+
return useQuery({
|
|
63
|
+
...stepAttemptDetailQueryOptions(stepId, attempt),
|
|
64
|
+
...options,
|
|
65
|
+
enabled: Boolean(stepId) && attempt !== undefined && (options?.enabled ?? true),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
EvaluationTraceDto,
|
|
2
3
|
JobListeningDto,
|
|
4
|
+
StepAttemptDetailResponseDto,
|
|
3
5
|
StepAttemptDto,
|
|
4
6
|
StepGateResultDto,
|
|
5
7
|
WorkflowExecutionEventDto,
|
|
@@ -13,6 +15,7 @@ import type {
|
|
|
13
15
|
WorkflowRunStepDetailDto,
|
|
14
16
|
} from '@shipfox/api-workflows-dto';
|
|
15
17
|
import {
|
|
18
|
+
type EvaluationTraceEntry,
|
|
16
19
|
Job,
|
|
17
20
|
JobExecution,
|
|
18
21
|
type JobListening,
|
|
@@ -139,6 +142,10 @@ export function toJob(dto: WorkflowRunJobDetailDto): Job {
|
|
|
139
142
|
status: dto.status,
|
|
140
143
|
statusReason: dto.status_reason,
|
|
141
144
|
carriedOver: dto.carried_over,
|
|
145
|
+
outputs: dto.outputs ?? null,
|
|
146
|
+
success: dto.success ?? null,
|
|
147
|
+
runner: dto.runner ?? null,
|
|
148
|
+
evaluationTrace: toEvaluationTrace(dto.evaluation_trace),
|
|
142
149
|
listening: dto.listening ? toJobListening(dto.listening) : null,
|
|
143
150
|
listenerStatus: dto.listener_status,
|
|
144
151
|
resolutionReason: dto.resolution_reason,
|
|
@@ -158,11 +165,14 @@ export function toJobExecution(dto: WorkflowRunJobExecutionDetailDto): JobExecut
|
|
|
158
165
|
name: dto.name,
|
|
159
166
|
status: dto.status,
|
|
160
167
|
statusReason: dto.status_reason,
|
|
168
|
+
runner: dto.runner ?? null,
|
|
169
|
+
outputs: dto.outputs ?? null,
|
|
161
170
|
triggerEvents: (dto.trigger_events ?? []).map(toWorkflowExecutionEvent),
|
|
162
171
|
queuedAt: dto.queued_at ?? null,
|
|
163
172
|
startedAt: dto.started_at ?? null,
|
|
164
173
|
finishedAt: dto.finished_at ?? null,
|
|
165
174
|
timedOutAt: dto.timed_out_at ?? null,
|
|
175
|
+
evaluationTrace: toEvaluationTrace(dto.evaluation_trace),
|
|
166
176
|
createdAt: dto.created_at,
|
|
167
177
|
updatedAt: dto.updated_at,
|
|
168
178
|
steps: dto.steps.map(toStep),
|
|
@@ -179,8 +189,10 @@ export function toStep(dto: WorkflowRunStepDetailDto): Step {
|
|
|
179
189
|
? {startLine: dto.source_location.start_line, endLine: dto.source_location.end_line}
|
|
180
190
|
: null,
|
|
181
191
|
status: dto.status,
|
|
192
|
+
statusReason: dto.status_reason,
|
|
182
193
|
type: dto.type,
|
|
183
194
|
config: dto.config,
|
|
195
|
+
evaluationTrace: toEvaluationTrace(dto.evaluation_trace),
|
|
184
196
|
agentConfig: toAgentStepConfig(dto),
|
|
185
197
|
error: dto.error
|
|
186
198
|
? {
|
|
@@ -210,6 +222,8 @@ export function toStepAttempt(dto: StepAttemptDto, jobExecutionId: string): Step
|
|
|
210
222
|
status: dto.status,
|
|
211
223
|
exitCode: dto.exit_code ?? null,
|
|
212
224
|
output: dto.output ?? null,
|
|
225
|
+
outputs: dto.outputs ?? dto.output ?? null,
|
|
226
|
+
response: dto.response ?? null,
|
|
213
227
|
error: dto.error ?? null,
|
|
214
228
|
gateResult: toStepGateResult(dto.gate_result),
|
|
215
229
|
restartFeedback: dto.restart_feedback ?? null,
|
|
@@ -218,6 +232,16 @@ export function toStepAttempt(dto: StepAttemptDto, jobExecutionId: string): Step
|
|
|
218
232
|
});
|
|
219
233
|
}
|
|
220
234
|
|
|
235
|
+
export function toStepAttemptDetail(dto: StepAttemptDetailResponseDto) {
|
|
236
|
+
return {
|
|
237
|
+
stepId: dto.step_id,
|
|
238
|
+
attempt: dto.attempt,
|
|
239
|
+
authoredConfig: dto.authored_config,
|
|
240
|
+
config: dto.config,
|
|
241
|
+
evaluationTrace: toEvaluationTrace(dto.evaluation_trace),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
221
245
|
function toJobListening(dto: JobListeningDto): JobListening {
|
|
222
246
|
return {
|
|
223
247
|
on: dto.on,
|
|
@@ -259,6 +283,28 @@ function toStepGateResult(dto: StepGateResultDto): StepGateResult {
|
|
|
259
283
|
return dto;
|
|
260
284
|
}
|
|
261
285
|
|
|
286
|
+
function toEvaluationTrace(trace: EvaluationTraceDto | null): EvaluationTraceEntry[] | null {
|
|
287
|
+
return (
|
|
288
|
+
trace?.map((entry) =>
|
|
289
|
+
'dropped' in entry
|
|
290
|
+
? entry
|
|
291
|
+
: {
|
|
292
|
+
expression: entry.expression,
|
|
293
|
+
roots: entry.roots,
|
|
294
|
+
fillTarget: entry.fill_target,
|
|
295
|
+
evaluatedAt: entry.evaluated_at,
|
|
296
|
+
field: entry.field,
|
|
297
|
+
...(entry.value === undefined ? {} : {value: entry.value}),
|
|
298
|
+
...(entry.truncated === undefined ? {} : {truncated: entry.truncated}),
|
|
299
|
+
...(entry.expr_truncated === undefined ? {} : {exprTruncated: entry.expr_truncated}),
|
|
300
|
+
...(entry.reference === undefined ? {} : {reference: entry.reference}),
|
|
301
|
+
...(entry.degraded === undefined ? {} : {degraded: entry.degraded}),
|
|
302
|
+
...(entry.env_key === undefined ? {} : {envKey: entry.env_key}),
|
|
303
|
+
},
|
|
304
|
+
) ?? null
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
262
308
|
function toAgentStepConfig(dto: WorkflowRunStepDetailDto): Step['agentConfig'] {
|
|
263
309
|
if (dto.type !== 'agent') return null;
|
|
264
310
|
return {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type {StepAttemptDetailResponseDto} from '@shipfox/api-workflows-dto';
|
|
1
2
|
import {configureApiClient} from '@shipfox/client-api';
|
|
2
3
|
import {type InfiniteData, QueryClient, QueryClientProvider} from '@tanstack/react-query';
|
|
3
4
|
import {act, cleanup, renderHook, waitFor} from '@testing-library/react';
|
|
@@ -11,7 +12,8 @@ import {
|
|
|
11
12
|
workflowRunDto,
|
|
12
13
|
workflowRunListResponseDto,
|
|
13
14
|
} from '#test/fixtures/workflow-run.js';
|
|
14
|
-
import {
|
|
15
|
+
import {useStepAttemptDetailQuery} from './step-attempt-detail.js';
|
|
16
|
+
import {toStepAttemptDetail, toWorkflowRun, toWorkflowRunListPage} from './workflow-run-mapper.js';
|
|
15
17
|
import {
|
|
16
18
|
fireManualWorkflow,
|
|
17
19
|
useCancelWorkflowRunMutation,
|
|
@@ -133,6 +135,64 @@ describe('workflow run API hooks', () => {
|
|
|
133
135
|
expect(cached).toHaveProperty('triggerSource', 'manual');
|
|
134
136
|
});
|
|
135
137
|
|
|
138
|
+
test('maps lazy step attempt details to authored and resolved troubleshooting data', () => {
|
|
139
|
+
const dto: StepAttemptDetailResponseDto = {
|
|
140
|
+
step_id: '88888888-8888-4888-8888-888888888888',
|
|
141
|
+
attempt: 2,
|
|
142
|
+
authored_config: {run: 'echo $' + '{{ inputs.message }}'},
|
|
143
|
+
config: {run: 'echo hello'},
|
|
144
|
+
evaluation_trace: [
|
|
145
|
+
{
|
|
146
|
+
expression: 'inputs.message',
|
|
147
|
+
roots: ['inputs.message'],
|
|
148
|
+
fill_target: 'run',
|
|
149
|
+
evaluated_at: '2026-08-05T12:00:00.000Z',
|
|
150
|
+
field: 'run',
|
|
151
|
+
value: 'hello',
|
|
152
|
+
degraded: false,
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
expect(toStepAttemptDetail(dto)).toEqual({
|
|
158
|
+
stepId: dto.step_id,
|
|
159
|
+
attempt: 2,
|
|
160
|
+
authoredConfig: dto.authored_config,
|
|
161
|
+
config: dto.config,
|
|
162
|
+
evaluationTrace: [
|
|
163
|
+
{
|
|
164
|
+
expression: 'inputs.message',
|
|
165
|
+
roots: ['inputs.message'],
|
|
166
|
+
fillTarget: 'run',
|
|
167
|
+
evaluatedAt: '2026-08-05T12:00:00.000Z',
|
|
168
|
+
field: 'run',
|
|
169
|
+
value: 'hello',
|
|
170
|
+
degraded: false,
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('does not fetch step attempt details while the inspector is closed', async () => {
|
|
177
|
+
const fetchImpl = vi.fn(async () =>
|
|
178
|
+
jsonResponse({
|
|
179
|
+
step_id: '88888888-8888-4888-8888-888888888888',
|
|
180
|
+
attempt: 1,
|
|
181
|
+
authored_config: null,
|
|
182
|
+
config: {},
|
|
183
|
+
evaluation_trace: null,
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
configureApiClient({baseUrl: 'https://api.example.test', fetchImpl});
|
|
187
|
+
|
|
188
|
+
renderWithQueryClient(() =>
|
|
189
|
+
useStepAttemptDetailQuery('88888888-8888-4888-8888-888888888888', 1, {enabled: false}),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
await Promise.resolve();
|
|
193
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
|
|
136
196
|
test('rejects malformed detail responses before they reach the query cache', async () => {
|
|
137
197
|
const fetchImpl = vi.fn(async () => jsonResponse({id: RUN_ID, trigger_source: 'manual'}));
|
|
138
198
|
configureApiClient({baseUrl: 'https://api.example.test', fetchImpl});
|