Skip to content

Commit

Permalink
[UI v2] feat: Adds api hooks for work queues and updates routing
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa committed Jan 20, 2025
1 parent 155e947 commit d7d2e9e
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 2 deletions.
5 changes: 5 additions & 0 deletions ui-v2/src/api/work-queues/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export {
type WorkQueue,
type WorkQueuesFilter,
buildFilterWorkQueuesQuery,
} from "./work-queues";
33 changes: 33 additions & 0 deletions ui-v2/src/api/work-queues/work-queues.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createFakeWorkQueue } from "@/mocks";
import { QueryClient, useSuspenseQuery } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { buildApiUrl, createWrapper, server } from "@tests/utils";
import { http, HttpResponse } from "msw";
import { describe, expect, it } from "vitest";
import { type WorkQueue, buildFilterWorkQueuesQuery } from "./work-queues";

describe("work queues api", () => {
const mockFetchWorkQueuesAPI = (workPools: Array<WorkQueue>) => {
server.use(
http.post(buildApiUrl("/work_queues/filter"), () => {
return HttpResponse.json(workPools);
}),
);
};

describe("buildFilterWorkPoolsQuery", () => {
it("fetches filtered workpools", async () => {
const workQueue = createFakeWorkQueue();
mockFetchWorkQueuesAPI([workQueue]);

const queryClient = new QueryClient();
const { result } = renderHook(
() => useSuspenseQuery(buildFilterWorkQueuesQuery()),
{ wrapper: createWrapper({ queryClient }) },
);

await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual([workQueue]);
});
});
});
65 changes: 65 additions & 0 deletions ui-v2/src/api/work-queues/work-queues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { components } from "@/api/prefect";
import { getQueryService } from "@/api/service";
import { queryOptions } from "@tanstack/react-query";

export type WorkQueue = components["schemas"]["WorkQueue"];
export type WorkQueuesFilter =
components["schemas"]["Body_read_work_queues_work_queues_filter_post"];

/**
* Query key factory for work queues-related queries
*
* @property {function} all - Returns base key for all work queue queries
* @property {function} lists - Returns key for all list-type work queue queries
* @property {function} list - Generates key for specific filtered work queue queries
*
* ```
* all => ['work-queues']
* lists => ['work-queues', 'list']
* list => ['work-queues', 'list', { ...filter }]
* ```
*/
export const queryKeyFactory = {
all: () => ["work-queues"] as const,
lists: () => [...queryKeyFactory.all(), "list"] as const,
list: (filter: WorkQueuesFilter) =>
[...queryKeyFactory.lists(), filter] as const,
};

// ----------------------------
// Query Options Factories
// ----------------------------

/**
* Builds a query configuration for fetching filtered work queues
*
* @param filter - Filter options including:
* - limit: Number of items per page (default: 10)
* - offset: Offset of results based on the limit
* - work_queues: Optional work queues-specific filters
* @returns Query configuration object for use with TanStack Query
*
* @example
* ```ts
* const query = useQuery(buildFilterWorkQueuesQuery({
* limit: 100,
* offset: 0
* }));
* ```
*/
export const buildFilterWorkQueuesQuery = (
filter: WorkQueuesFilter = { offset: 0 },
) =>
queryOptions({
queryKey: queryKeyFactory.list(filter),
queryFn: async () => {
const res = await getQueryService().POST("/work_queues/filter", {
body: filter,
});
if (!res.data) {
throw new Error("'data' expected");
}
return res.data;
},
});
20 changes: 20 additions & 0 deletions ui-v2/src/mocks/create-fake-work-queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { components } from "@/api/prefect";
import { faker } from "@faker-js/faker";

export const createFakeWorkQueue = (
overrides?: Partial<components["schemas"]["WorkQueue"]>,
): components["schemas"]["WorkQueue"] => {
return {
created: faker.date.past().toISOString(),
description: `${faker.word.adjective()} ${faker.word.noun()}`,
id: faker.string.uuid(),
name: `${faker.word.adjective()} work queue`,
updated: faker.date.past().toISOString(),
concurrency_limit: faker.number.int({ min: 0, max: 1_000 }),
is_paused: faker.datatype.boolean(),
last_polled: faker.date.past().toISOString(),
work_pool_id: `${faker.word.adjective()} work pool`,
priority: faker.number.int({ min: 1, max: 5 }),
...overrides,
};
};
1 change: 1 addition & 0 deletions ui-v2/src/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { createFakeState } from "./create-fake-state";
export { createFakeTaskRunConcurrencyLimit } from "./create-fake-take-run-concurrency-limit";
export { createFakeTaskRun } from "./create-fake-task-run";
export { createFakeWorkPool } from "./create-fake-work-pool";
export { createFakeWorkQueue } from "./create-fake-work-queue";
73 changes: 72 additions & 1 deletion ui-v2/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Route as DeploymentsIndexImport } from './routes/deployments/index'
import { Route as ConcurrencyLimitsIndexImport } from './routes/concurrency-limits/index'
import { Route as AutomationsIndexImport } from './routes/automations/index'
import { Route as AutomationsCreateImport } from './routes/automations/create'
import { Route as WorkPoolsWorkPoolWorkPoolIdImport } from './routes/work-pools/work-pool.$workPoolId'
import { Route as WorkPoolsWorkPoolIdImport } from './routes/work-pools/work-pool.$id'
import { Route as RunsTaskRunIdImport } from './routes/runs/task-run.$id'
import { Route as RunsFlowRunIdImport } from './routes/runs/flow-run.$id'
Expand All @@ -33,6 +34,7 @@ import { Route as DeploymentsDeploymentIdImport } from './routes/deployments/dep
import { Route as ConcurrencyLimitsConcurrencyLimitIdImport } from './routes/concurrency-limits/concurrency-limit.$id'
import { Route as AutomationsAutomationIdImport } from './routes/automations/automation.$id'
import { Route as AutomationsAutomationIdEditImport } from './routes/automations/automation.$id.edit'
import { Route as WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdImport } from './routes/work-pools/work-pool.$workPoolId.queue.$workQueueId'

// Create/Update Routes

Expand Down Expand Up @@ -120,6 +122,13 @@ const AutomationsCreateRoute = AutomationsCreateImport.update({
getParentRoute: () => rootRoute,
} as any)

const WorkPoolsWorkPoolWorkPoolIdRoute =
WorkPoolsWorkPoolWorkPoolIdImport.update({
id: '/work-pools/work-pool/$workPoolId',
path: '/work-pools/work-pool/$workPoolId',
getParentRoute: () => rootRoute,
} as any)

const WorkPoolsWorkPoolIdRoute = WorkPoolsWorkPoolIdImport.update({
id: '/work-pools/work-pool/$id',
path: '/work-pools/work-pool/$id',
Expand Down Expand Up @@ -170,6 +179,13 @@ const AutomationsAutomationIdEditRoute =
getParentRoute: () => AutomationsAutomationIdRoute,
} as any)

const WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute =
WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdImport.update({
id: '/queue/$workQueueId',
path: '/queue/$workQueueId',
getParentRoute: () => WorkPoolsWorkPoolWorkPoolIdRoute,
} as any)

// Populate the FileRoutesByPath interface

declare module '@tanstack/react-router' {
Expand Down Expand Up @@ -321,13 +337,27 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof WorkPoolsWorkPoolIdImport
parentRoute: typeof rootRoute
}
'/work-pools/work-pool/$workPoolId': {
id: '/work-pools/work-pool/$workPoolId'
path: '/work-pools/work-pool/$workPoolId'
fullPath: '/work-pools/work-pool/$workPoolId'
preLoaderRoute: typeof WorkPoolsWorkPoolWorkPoolIdImport
parentRoute: typeof rootRoute
}
'/automations/automation/$id/edit': {
id: '/automations/automation/$id/edit'
path: '/edit'
fullPath: '/automations/automation/$id/edit'
preLoaderRoute: typeof AutomationsAutomationIdEditImport
parentRoute: typeof AutomationsAutomationIdImport
}
'/work-pools/work-pool/$workPoolId/queue/$workQueueId': {
id: '/work-pools/work-pool/$workPoolId/queue/$workQueueId'
path: '/queue/$workQueueId'
fullPath: '/work-pools/work-pool/$workPoolId/queue/$workQueueId'
preLoaderRoute: typeof WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdImport
parentRoute: typeof WorkPoolsWorkPoolWorkPoolIdImport
}
}
}

Expand All @@ -347,6 +377,21 @@ const AutomationsAutomationIdRouteWithChildren =
AutomationsAutomationIdRouteChildren,
)

interface WorkPoolsWorkPoolWorkPoolIdRouteChildren {
WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute: typeof WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute
}

const WorkPoolsWorkPoolWorkPoolIdRouteChildren: WorkPoolsWorkPoolWorkPoolIdRouteChildren =
{
WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute:
WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute,
}

const WorkPoolsWorkPoolWorkPoolIdRouteWithChildren =
WorkPoolsWorkPoolWorkPoolIdRoute._addFileChildren(
WorkPoolsWorkPoolWorkPoolIdRouteChildren,
)

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/blocks': typeof BlocksRoute
Expand All @@ -369,7 +414,9 @@ export interface FileRoutesByFullPath {
'/runs/flow-run/$id': typeof RunsFlowRunIdRoute
'/runs/task-run/$id': typeof RunsTaskRunIdRoute
'/work-pools/work-pool/$id': typeof WorkPoolsWorkPoolIdRoute
'/work-pools/work-pool/$workPoolId': typeof WorkPoolsWorkPoolWorkPoolIdRouteWithChildren
'/automations/automation/$id/edit': typeof AutomationsAutomationIdEditRoute
'/work-pools/work-pool/$workPoolId/queue/$workQueueId': typeof WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute
}

export interface FileRoutesByTo {
Expand All @@ -394,7 +441,9 @@ export interface FileRoutesByTo {
'/runs/flow-run/$id': typeof RunsFlowRunIdRoute
'/runs/task-run/$id': typeof RunsTaskRunIdRoute
'/work-pools/work-pool/$id': typeof WorkPoolsWorkPoolIdRoute
'/work-pools/work-pool/$workPoolId': typeof WorkPoolsWorkPoolWorkPoolIdRouteWithChildren
'/automations/automation/$id/edit': typeof AutomationsAutomationIdEditRoute
'/work-pools/work-pool/$workPoolId/queue/$workQueueId': typeof WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute
}

export interface FileRoutesById {
Expand All @@ -420,7 +469,9 @@ export interface FileRoutesById {
'/runs/flow-run/$id': typeof RunsFlowRunIdRoute
'/runs/task-run/$id': typeof RunsTaskRunIdRoute
'/work-pools/work-pool/$id': typeof WorkPoolsWorkPoolIdRoute
'/work-pools/work-pool/$workPoolId': typeof WorkPoolsWorkPoolWorkPoolIdRouteWithChildren
'/automations/automation/$id/edit': typeof AutomationsAutomationIdEditRoute
'/work-pools/work-pool/$workPoolId/queue/$workQueueId': typeof WorkPoolsWorkPoolWorkPoolIdQueueWorkQueueIdRoute
}

export interface FileRouteTypes {
Expand All @@ -447,7 +498,9 @@ export interface FileRouteTypes {
| '/runs/flow-run/$id'
| '/runs/task-run/$id'
| '/work-pools/work-pool/$id'
| '/work-pools/work-pool/$workPoolId'
| '/automations/automation/$id/edit'
| '/work-pools/work-pool/$workPoolId/queue/$workQueueId'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
Expand All @@ -471,7 +524,9 @@ export interface FileRouteTypes {
| '/runs/flow-run/$id'
| '/runs/task-run/$id'
| '/work-pools/work-pool/$id'
| '/work-pools/work-pool/$workPoolId'
| '/automations/automation/$id/edit'
| '/work-pools/work-pool/$workPoolId/queue/$workQueueId'
id:
| '__root__'
| '/'
Expand All @@ -495,7 +550,9 @@ export interface FileRouteTypes {
| '/runs/flow-run/$id'
| '/runs/task-run/$id'
| '/work-pools/work-pool/$id'
| '/work-pools/work-pool/$workPoolId'
| '/automations/automation/$id/edit'
| '/work-pools/work-pool/$workPoolId/queue/$workQueueId'
fileRoutesById: FileRoutesById
}

Expand All @@ -521,6 +578,7 @@ export interface RootRouteChildren {
RunsFlowRunIdRoute: typeof RunsFlowRunIdRoute
RunsTaskRunIdRoute: typeof RunsTaskRunIdRoute
WorkPoolsWorkPoolIdRoute: typeof WorkPoolsWorkPoolIdRoute
WorkPoolsWorkPoolWorkPoolIdRoute: typeof WorkPoolsWorkPoolWorkPoolIdRouteWithChildren
}

const rootRouteChildren: RootRouteChildren = {
Expand All @@ -546,6 +604,8 @@ const rootRouteChildren: RootRouteChildren = {
RunsFlowRunIdRoute: RunsFlowRunIdRoute,
RunsTaskRunIdRoute: RunsTaskRunIdRoute,
WorkPoolsWorkPoolIdRoute: WorkPoolsWorkPoolIdRoute,
WorkPoolsWorkPoolWorkPoolIdRoute:
WorkPoolsWorkPoolWorkPoolIdRouteWithChildren,
}

export const routeTree = rootRoute
Expand Down Expand Up @@ -578,7 +638,8 @@ export const routeTree = rootRoute
"/flows/flow/$id",
"/runs/flow-run/$id",
"/runs/task-run/$id",
"/work-pools/work-pool/$id"
"/work-pools/work-pool/$id",
"/work-pools/work-pool/$workPoolId"
]
},
"/": {
Expand Down Expand Up @@ -647,9 +708,19 @@ export const routeTree = rootRoute
"/work-pools/work-pool/$id": {
"filePath": "work-pools/work-pool.$id.tsx"
},
"/work-pools/work-pool/$workPoolId": {
"filePath": "work-pools/work-pool.$workPoolId.tsx",
"children": [
"/work-pools/work-pool/$workPoolId/queue/$workQueueId"
]
},
"/automations/automation/$id/edit": {
"filePath": "automations/automation.$id.edit.ts",
"parent": "/automations/automation/$id"
},
"/work-pools/work-pool/$workPoolId/queue/$workQueueId": {
"filePath": "work-pools/work-pool.$workPoolId.queue.$workQueueId.tsx",
"parent": "/work-pools/work-pool/$workPoolId"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute(
"/work-pools/work-pool/$workPoolId/queue/$workQueueId",
)({
component: RouteComponent,
});

function RouteComponent() {
return "🚧🚧 Pardon our dust! 🚧🚧";
}
9 changes: 9 additions & 0 deletions ui-v2/src/routes/work-pools/work-pool.$workPoolId.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/work-pools/work-pool/$workPoolId")({
component: RouteComponent,
});

function RouteComponent() {
return "🚧🚧 Pardon our dust! 🚧🚧";
}
8 changes: 7 additions & 1 deletion ui-v2/tests/utils/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,17 @@ const workPoolsHandlers = [
http.post(buildApiUrl("/work_pools/filter"), () => {
return HttpResponse.json([]);
}),

http.post(buildApiUrl("/work_pools/count"), () => {
return HttpResponse.json(0);
}),
];

const workeQueuesHandlers = [
http.post(buildApiUrl("/work_queues/filter"), () => {
return HttpResponse.json([]);
}),
];

export const handlers = [
...automationsHandlers,
...deploymentsHandlers,
Expand All @@ -118,4 +123,5 @@ export const handlers = [
...taskRunConcurrencyLimitsHandlers,
...variablesHandlers,
...workPoolsHandlers,
...workeQueuesHandlers,
];

0 comments on commit d7d2e9e

Please sign in to comment.