feat(scaffolder): Allow sorting by status in scaffolderService.listTasks.

Added optional `status` filter to `ScaffolderService.listTasks`, by exposing the `status` query parameter,  allowing callers to retrieve tasks of a specific status. Also updated the `list-scaffolder-tasks` action to support this parameter.

Signed-off-by: John Collier <jcollier@redhat.com>
This commit is contained in:
John Collier
2026-03-04 16:25:29 -05:00
parent 545557a928
commit 77bee9f013
5 changed files with 80 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Updated the `list-scaffolder-tasks` action to support the new "status" filter paramter, allowing the action to return tasks matching a specific status.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-node': minor
---
Added optional `status` filter to `ScaffolderService.listTasks`, allowing callers to retrieve tasks matching a specific status.
@@ -63,7 +63,12 @@ describe('createListScaffolderTasksAction', () => {
totalTasks: mockTasks.totalTasks ?? 0,
});
expect(mockScaffolderService.listTasks).toHaveBeenCalledWith(
{ createdBy: undefined, limit: undefined, offset: undefined },
{
createdBy: undefined,
limit: undefined,
offset: undefined,
status: undefined,
},
expect.objectContaining({ credentials: expect.anything() }),
);
});
@@ -106,7 +111,7 @@ describe('createListScaffolderTasksAction', () => {
});
expect(mockScaffolderService.listTasks).toHaveBeenCalledWith(
{ createdBy: undefined, limit: 2, offset: 1 },
{ createdBy: undefined, limit: 2, offset: 1, status: undefined },
expect.objectContaining({ credentials: expect.anything() }),
);
@@ -188,11 +193,57 @@ describe('createListScaffolderTasksAction', () => {
createdBy: 'user:default/alice',
limit: undefined,
offset: undefined,
status: undefined,
},
expect.objectContaining({ credentials: expect.anything() }),
);
});
it('should filter tasks by status when status is provided', async () => {
const mockActionsRegistry = actionsRegistryServiceMock();
const mockAuth = mockServices.auth.mock();
const mockScaffolderService = scaffolderServiceMock.mock();
const completedTasks = generateMockTasks().tasks.filter(
t => t.status === 'completed',
);
mockScaffolderService.listTasks.mockResolvedValue({
items: completedTasks as ScaffolderTask[],
totalItems: completedTasks.length,
});
createListScaffolderTasksAction({
actionsRegistry: mockActionsRegistry,
auth: mockAuth,
scaffolderService: mockScaffolderService,
});
const result = await mockActionsRegistry.invoke({
id: 'test:list-scaffolder-tasks',
input: { status: 'completed' },
});
expect(mockScaffolderService.listTasks).toHaveBeenCalledWith(
{
createdBy: undefined,
limit: undefined,
offset: undefined,
status: 'completed',
},
expect.objectContaining({ credentials: expect.anything() }),
);
expect(result.output).toEqual({
tasks: completedTasks.map(task => ({
id: task.id,
spec: task.spec,
status: task.status,
createdAt: task.createdAt,
lastHeartbeatAt: task.lastHeartbeatAt,
})),
totalTasks: completedTasks.length,
});
});
it('should throw NotAllowedError when owned is true without user identity', async () => {
const mockActionsRegistry = actionsRegistryServiceMock();
const mockAuth = mockServices.auth.mock();
@@ -65,6 +65,17 @@ Pagination is supported via limit and offset.
.min(0)
.describe('The offset to start from for pagination')
.optional(),
status: z
.enum([
'open',
'processing',
'completed',
'failed',
'cancelled',
'skipped',
])
.optional()
.describe('Filter tasks by status'),
}),
output: z =>
z
@@ -112,6 +123,7 @@ Pagination is supported via limit and offset.
createdBy,
limit: input.limit,
offset: input.offset,
status: input.status,
},
{ credentials },
);
@@ -83,6 +83,7 @@ export interface ScaffolderService {
createdBy?: string;
limit?: number;
offset?: number;
status?: ScaffolderTaskStatus;
},
options: ScaffolderServiceRequestOptions,
): Promise<{ items: ScaffolderTask[]; totalItems: number }>;
@@ -185,6 +186,7 @@ class DefaultScaffolderService implements ScaffolderService {
createdBy?: string;
limit?: number;
offset?: number;
status?: ScaffolderTaskStatus;
},
options: ScaffolderServiceRequestOptions,
): Promise<{ items: ScaffolderTask[]; totalItems: number }> {
@@ -201,6 +203,9 @@ class DefaultScaffolderService implements ScaffolderService {
if (request.offset !== undefined) {
params.set('offset', String(request.offset));
}
if (request.status !== undefined) {
params.set('status', request.status);
}
const query = params.toString();
const url = `${baseUrl}/v2/tasks${query ? `?${query}` : ''}`;