Skip to content

Commit 767fde7

Browse files
Add support for multiple statuses to ListWorkflows route handler (#842)
* Use queryString to parse query params (Object.fromEntries() does not work with array query params) * Modify query params schema to support multiple statuses (and also transform the input in case there is only one query param) * Modify the ListWorkflowExecutions query to support multiple workflow statuses * Temporary: Pass an array of single status in useListWorkflows hook
1 parent 0c077f8 commit 767fde7

File tree

5 files changed

+37
-29
lines changed

5 files changed

+37
-29
lines changed

src/route-handlers/list-workflows/helpers/__tests__/get-list-workflow-executions-query.test.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
11
import getListWorkflowExecutionsQuery from '../get-list-workflow-executions-query';
22

33
describe('getListWorkflowExecutionsQuery', () => {
4-
it('should return query', () => {
4+
it('should return query to show various open and closed workflows', () => {
55
const query = getListWorkflowExecutionsQuery({
66
search: 'mocksearchterm',
7-
workflowStatus: 'WORKFLOW_EXECUTION_CLOSE_STATUS_TERMINATED',
7+
workflowStatuses: [
8+
'WORKFLOW_EXECUTION_CLOSE_STATUS_TERMINATED',
9+
'WORKFLOW_EXECUTION_CLOSE_STATUS_CANCELED',
10+
'WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID',
11+
],
812
sortColumn: 'CloseTime',
913
sortOrder: 'ASC',
1014
timeColumn: 'StartTime',
1115
timeRangeStart: '1712066100000000',
1216
timeRangeEnd: '1712096100000000',
1317
});
1418
expect(query).toEqual(
15-
'(WorkflowType = "mocksearchterm" OR WorkflowID = "mocksearchterm" OR RunID = "mocksearchterm") AND CloseStatus = 3 AND StartTime > "1712066100000000" AND StartTime <= "1712096100000000" ORDER BY CloseTime ASC'
16-
);
17-
});
18-
19-
it('should return query for running status', () => {
20-
const query = getListWorkflowExecutionsQuery({
21-
search: 'mocksearchterm',
22-
timeColumn: 'StartTime',
23-
workflowStatus: 'WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID',
24-
});
25-
expect(query).toEqual(
26-
'(WorkflowType = "mocksearchterm" OR WorkflowID = "mocksearchterm" OR RunID = "mocksearchterm") AND CloseTime = missing ORDER BY StartTime DESC'
19+
'(WorkflowType = "mocksearchterm" OR WorkflowID = "mocksearchterm" OR RunID = "mocksearchterm") AND ' +
20+
'(CloseStatus = 3 OR CloseStatus = 2 OR CloseTime = missing) AND ' +
21+
'StartTime > "1712066100000000" AND StartTime <= "1712096100000000" ORDER BY CloseTime ASC'
2722
);
2823
});
2924

src/route-handlers/list-workflows/helpers/get-list-workflow-executions-query.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import { type TimeColumn } from '../list-workflows.types';
66

77
export default function getListWorkflowExecutionsQuery({
88
search,
9-
workflowStatus,
9+
workflowStatuses,
1010
sortColumn,
1111
sortOrder,
1212
timeColumn,
1313
timeRangeStart,
1414
timeRangeEnd,
1515
}: {
1616
search?: string;
17-
workflowStatus?: WorkflowStatus;
17+
workflowStatuses?: Array<WorkflowStatus>;
1818
sortColumn?: string;
1919
sortOrder?: SortOrder;
2020
timeColumn: TimeColumn;
@@ -28,16 +28,21 @@ export default function getListWorkflowExecutionsQuery({
2828
);
2929
}
3030

31-
if (workflowStatus) {
32-
if (workflowStatus === WORKFLOW_STATUSES.running) {
33-
searchQueries.push('CloseTime = missing');
31+
const workflowStatusQueries: Array<string> = [];
32+
workflowStatuses?.forEach((status) => {
33+
if (status === WORKFLOW_STATUSES.running) {
34+
workflowStatusQueries.push('CloseTime = missing');
3435
} else {
35-
searchQueries.push(
36-
// Query CloseStatus is 0-indexed (and excludes INVALID)
36+
workflowStatusQueries.push(
37+
// Numerical query CloseStatus is 0-indexed (and excludes INVALID)
3738
// https://cadenceworkflow.io/docs/concepts/search-workflows/#query-capabilities
38-
`CloseStatus = ${Object.values(WORKFLOW_STATUSES).indexOf(workflowStatus) - 1}`
39+
`CloseStatus = ${Object.values(WORKFLOW_STATUSES).indexOf(status) - 1}`
3940
);
4041
}
42+
});
43+
44+
if (workflowStatusQueries.length > 0) {
45+
searchQueries.push(`(${workflowStatusQueries.join(' OR ')})`);
4146
}
4247

4348
if (timeRangeStart) {

src/route-handlers/list-workflows/list-workflows.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { type NextRequest, NextResponse } from 'next/server';
2+
import queryString from 'query-string';
23

34
import decodeUrlParams from '@/utils/decode-url-params';
45
import { getHTTPStatusCode, GRPCError } from '@/utils/grpc/grpc-error';
@@ -22,7 +23,7 @@ export async function listWorkflows(
2223
const decodedParams = decodeUrlParams(requestParams.params) as RouteParams;
2324

2425
const { data: queryParams, error } = listWorkflowsQueryParamSchema.safeParse(
25-
Object.fromEntries(request.nextUrl.searchParams)
26+
queryString.parse(request.nextUrl.searchParams.toString())
2627
);
2728
if (error) {
2829
return NextResponse.json(
@@ -45,7 +46,7 @@ export async function listWorkflows(
4546
? queryParams.query
4647
: getListWorkflowExecutionsQuery({
4748
search: queryParams.search,
48-
workflowStatus: queryParams.status,
49+
workflowStatuses: queryParams.statuses,
4950
sortColumn: queryParams.sortColumn,
5051
sortOrder: queryParams.sortOrder,
5152
timeColumn: queryParams.timeColumn,

src/route-handlers/list-workflows/schemas/list-workflows-query-params-schema.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { SORT_ORDERS } from '@/utils/sort-by';
44
import isWorkflowStatus from '@/views/shared/workflow-status-tag/helpers/is-workflow-status';
55
import { type WorkflowStatus } from '@/views/shared/workflow-status-tag/workflow-status-tag.types';
66

7+
// TODO: this should go in a shared location
8+
const workflowStatusSchema = z.custom<WorkflowStatus>(isWorkflowStatus, {
9+
message: 'Invalid workflow status',
10+
});
11+
712
const listWorkflowsQueryParamSchema = z
813
.object({
914
pageSize: z
@@ -16,10 +21,11 @@ const listWorkflowsQueryParamSchema = z
1621
inputType: z.enum(['search', 'query']),
1722
search: z.string().trim().optional(),
1823
query: z.string().optional(),
19-
status: z
20-
.custom<WorkflowStatus>(isWorkflowStatus, {
21-
message: 'Invalid workflow status',
22-
})
24+
statuses: z
25+
.union([
26+
workflowStatusSchema.transform((status) => [status]),
27+
z.array(workflowStatusSchema),
28+
])
2329
.optional(),
2430
timeColumn: z
2531
.enum(['StartTime', 'CloseTime'])

src/views/shared/hooks/use-list-workflows.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ export default function useListWorkflows({
3939
}
4040
: {
4141
search,
42-
status,
42+
// Temporary change, this will be removed in a follow-up
43+
statuses: status ? [status] : [],
4344
sortColumn,
4445
sortOrder,
4546
timeRangeStart: timeRangeStart?.toISOString(),

0 commit comments

Comments
 (0)