diff --git a/src/route-handlers/list-workflows/helpers/__tests__/get-list-workflow-executions-query.test.ts b/src/route-handlers/list-workflows/helpers/__tests__/get-list-workflow-executions-query.test.ts index 5a60b721e..47d85a7e1 100644 --- a/src/route-handlers/list-workflows/helpers/__tests__/get-list-workflow-executions-query.test.ts +++ b/src/route-handlers/list-workflows/helpers/__tests__/get-list-workflow-executions-query.test.ts @@ -7,6 +7,7 @@ describe('getListWorkflowExecutionsQuery', () => { workflowStatus: 'WORKFLOW_EXECUTION_CLOSE_STATUS_TERMINATED', sortColumn: 'CloseTime', sortOrder: 'ASC', + timeColumn: 'StartTime', timeRangeStart: '1712066100000000', timeRangeEnd: '1712096100000000', }); @@ -18,6 +19,7 @@ describe('getListWorkflowExecutionsQuery', () => { it('should return query for running status', () => { const query = getListWorkflowExecutionsQuery({ search: 'mocksearchterm', + timeColumn: 'StartTime', workflowStatus: 'WORKFLOW_EXECUTION_CLOSE_STATUS_INVALID', }); expect(query).toEqual( @@ -25,8 +27,8 @@ describe('getListWorkflowExecutionsQuery', () => { ); }); - it('should return default query with no params', () => { - const query = getListWorkflowExecutionsQuery({}); + it('should return default query with no params except for time column', () => { + const query = getListWorkflowExecutionsQuery({ timeColumn: 'StartTime' }); expect(query).toEqual('ORDER BY StartTime DESC'); }); }); diff --git a/src/route-handlers/list-workflows/helpers/get-list-workflow-executions-query.ts b/src/route-handlers/list-workflows/helpers/get-list-workflow-executions-query.ts index 2c9f32ab4..3ab9a333e 100644 --- a/src/route-handlers/list-workflows/helpers/get-list-workflow-executions-query.ts +++ b/src/route-handlers/list-workflows/helpers/get-list-workflow-executions-query.ts @@ -2,11 +2,14 @@ import { type SortOrder } from '@/utils/sort-by'; import { WORKFLOW_STATUSES } from '@/views/shared/workflow-status-tag/workflow-status-tag.constants'; import type { WorkflowStatus } from '@/views/shared/workflow-status-tag/workflow-status-tag.types'; +import { type TimeColumn } from '../list-workflows.types'; + export default function getListWorkflowExecutionsQuery({ search, workflowStatus, sortColumn, sortOrder, + timeColumn, timeRangeStart, timeRangeEnd, }: { @@ -14,6 +17,7 @@ export default function getListWorkflowExecutionsQuery({ workflowStatus?: WorkflowStatus; sortColumn?: string; sortOrder?: SortOrder; + timeColumn: TimeColumn; timeRangeStart?: string; timeRangeEnd?: string; }) { @@ -37,11 +41,11 @@ export default function getListWorkflowExecutionsQuery({ } if (timeRangeStart) { - searchQueries.push(`StartTime > "${timeRangeStart}"`); + searchQueries.push(`${timeColumn} > "${timeRangeStart}"`); } if (timeRangeEnd) { - searchQueries.push(`StartTime <= "${timeRangeEnd}"`); + searchQueries.push(`${timeColumn} <= "${timeRangeEnd}"`); } return ( diff --git a/src/route-handlers/list-workflows/list-workflows.ts b/src/route-handlers/list-workflows/list-workflows.ts index 6bc76e604..0d35a46a2 100644 --- a/src/route-handlers/list-workflows/list-workflows.ts +++ b/src/route-handlers/list-workflows/list-workflows.ts @@ -36,23 +36,29 @@ export async function listWorkflows( ); } + const listWorkflowsParams = { + domain: decodedParams.domain, + pageSize: queryParams.pageSize, + nextPageToken: queryParams.nextPage, + query: + queryParams.inputType === 'query' + ? queryParams.query + : getListWorkflowExecutionsQuery({ + search: queryParams.search, + workflowStatus: queryParams.status, + sortColumn: queryParams.sortColumn, + sortOrder: queryParams.sortOrder, + timeColumn: queryParams.timeColumn, + timeRangeStart: queryParams.timeRangeStart, + timeRangeEnd: queryParams.timeRangeEnd, + }), + }; + try { - const res = await ctx.grpcClusterMethods.listWorkflows({ - domain: decodedParams.domain, - pageSize: queryParams.pageSize, - nextPageToken: queryParams.nextPage, - query: - queryParams.inputType === 'query' - ? queryParams.query - : getListWorkflowExecutionsQuery({ - search: queryParams.search, - workflowStatus: queryParams.status, - sortColumn: queryParams.sortColumn, - sortOrder: queryParams.sortOrder, - timeRangeStart: queryParams.timeRangeStart, - timeRangeEnd: queryParams.timeRangeEnd, - }), - }); + const res = + queryParams.listType === 'archived' + ? await ctx.grpcClusterMethods.archivedWorkflows(listWorkflowsParams) + : await ctx.grpcClusterMethods.listWorkflows(listWorkflowsParams); const response: ListWorkflowsResponse = { workflows: mapExecutionsToWorkflows(res.executions), diff --git a/src/route-handlers/list-workflows/list-workflows.types.ts b/src/route-handlers/list-workflows/list-workflows.types.ts index cbd935109..4cf6a61b7 100644 --- a/src/route-handlers/list-workflows/list-workflows.types.ts +++ b/src/route-handlers/list-workflows/list-workflows.types.ts @@ -18,6 +18,8 @@ export type ListWorkflowsRequestQueryParams = z.input< typeof listWorkflowsQueryParamSchema >; +export type TimeColumn = ListWorkflowsRequestQueryParams['timeColumn']; + export type ListWorkflowsResponse = { workflows: Array; nextPage: string; diff --git a/src/route-handlers/list-workflows/schemas/list-workflows-query-params-schema.ts b/src/route-handlers/list-workflows/schemas/list-workflows-query-params-schema.ts index 8a2d5160a..8ded99d98 100644 --- a/src/route-handlers/list-workflows/schemas/list-workflows-query-params-schema.ts +++ b/src/route-handlers/list-workflows/schemas/list-workflows-query-params-schema.ts @@ -1,38 +1,58 @@ import { z } from 'zod'; -import getTimestampNsFromISO from '@/utils/datetime/get-timestamp-ns-from-iso'; import { SORT_ORDERS } from '@/utils/sort-by'; import isWorkflowStatus from '@/views/shared/workflow-status-tag/helpers/is-workflow-status'; import { type WorkflowStatus } from '@/views/shared/workflow-status-tag/workflow-status-tag.types'; -const listWorkflowsQueryParamSchema = z.object({ - pageSize: z - .string() - .transform((val) => parseInt(val, 10)) - .pipe( - z.number().positive({ message: 'Page size must be a positive integer' }) - ), - inputType: z.enum(['search', 'query']), - search: z.string().optional(), - query: z.string().optional(), - status: z - .custom(isWorkflowStatus, { - message: 'Invalid workflow status', - }) - .optional(), - timeRangeStart: z - .string() - .datetime() - .transform(getTimestampNsFromISO) - .optional(), - timeRangeEnd: z - .string() - .datetime() - .transform(getTimestampNsFromISO) - .optional(), - sortColumn: z.string().optional(), - sortOrder: z.enum(SORT_ORDERS, { message: 'Invalid sort order' }).optional(), - nextPage: z.string().optional(), -}); +const listWorkflowsQueryParamSchema = z + .object({ + pageSize: z + .string() + .transform((val) => parseInt(val, 10)) + .pipe( + z.number().positive({ message: 'Page size must be a positive integer' }) + ), + listType: z.enum(['default', 'archived']), + inputType: z.enum(['search', 'query']), + search: z.string().optional(), + query: z.string().optional(), + status: z + .custom(isWorkflowStatus, { + message: 'Invalid workflow status', + }) + .optional(), + timeColumn: z + .enum(['StartTime', 'CloseTime']) + .optional() + .default('StartTime'), + timeRangeStart: z.string().datetime().optional(), + timeRangeEnd: z.string().datetime().optional(), + sortColumn: z.string().optional(), + sortOrder: z + .enum(SORT_ORDERS, { message: 'Invalid sort order' }) + .optional(), + nextPage: z.string().optional(), + }) + .superRefine((queryParams, ctx) => { + if ( + queryParams.listType === 'archived' && + queryParams.inputType === 'search' + ) { + if (queryParams.timeColumn === 'StartTime') { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Cannot search for archived workflows by start time', + }); + } + + if (!queryParams.timeRangeStart || !queryParams.timeRangeEnd) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: + 'Start and End time need to be passed for searching archived workflows', + }); + } + } + }); export default listWorkflowsQueryParamSchema; diff --git a/src/views/domain-workflows/hooks/use-list-workflows.ts b/src/views/domain-workflows/hooks/use-list-workflows.ts index 3105bd727..26c7f6f47 100644 --- a/src/views/domain-workflows/hooks/use-list-workflows.ts +++ b/src/views/domain-workflows/hooks/use-list-workflows.ts @@ -50,6 +50,7 @@ export default function useListWorkflows({ url: `/api/domains/${domain}/${cluster}/workflows`, query: { ...requestQueryParams, + listType: 'default', pageSize: pageSize.toString(), nextPage: pageParam as string, } as const satisfies ListWorkflowsRequestQueryParams,