feat: add support for status filtering in scaffolder endpoint
Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
---
|
||||
|
||||
Add support for status filtering in scaffolder tasks endpoint
|
||||
@@ -427,7 +427,7 @@ export class DatabaseTaskStore implements TaskStore {
|
||||
// (undocumented)
|
||||
heartbeatTask(taskId: string): Promise<void>;
|
||||
// (undocumented)
|
||||
list(options: { createdBy?: string }): Promise<{
|
||||
list(options: { createdBy?: string; status?: TaskStatus_2 }): Promise<{
|
||||
tasks: SerializedTask_2[];
|
||||
}>;
|
||||
// (undocumented)
|
||||
@@ -646,7 +646,7 @@ export interface TaskStore {
|
||||
// (undocumented)
|
||||
heartbeatTask(taskId: string): Promise<void>;
|
||||
// (undocumented)
|
||||
list?(options: { createdBy?: string }): Promise<{
|
||||
list?(options: { createdBy?: string; status?: TaskStatus }): Promise<{
|
||||
tasks: SerializedTask[];
|
||||
}>;
|
||||
// (undocumented)
|
||||
|
||||
@@ -95,6 +95,33 @@ describe('DatabaseTaskStore', () => {
|
||||
expect(tasks[0].id).toBeDefined();
|
||||
});
|
||||
|
||||
it('should list filtered created tasks by status', async () => {
|
||||
const { store } = await createStore();
|
||||
|
||||
const { taskId } = await store.createTask({
|
||||
spec: {} as TaskSpec,
|
||||
createdBy: 'me',
|
||||
});
|
||||
|
||||
await store.createTask({
|
||||
spec: {} as TaskSpec,
|
||||
createdBy: 'him',
|
||||
});
|
||||
|
||||
const message = `This task was marked as stale as it exceeded its timeout`;
|
||||
await store.completeTask({
|
||||
taskId,
|
||||
status: 'cancelled',
|
||||
eventBody: { message },
|
||||
});
|
||||
|
||||
const { tasks } = await store.list({ status: 'open' });
|
||||
expect(tasks.length).toBe(1);
|
||||
expect(tasks[0].createdBy).toBe('him');
|
||||
expect(tasks[0].status).toBe('open');
|
||||
expect(tasks[0].id).toBeDefined();
|
||||
});
|
||||
|
||||
it('should sent an event to start cancelling the task', async () => {
|
||||
const { store } = await createStore();
|
||||
|
||||
|
||||
@@ -22,19 +22,19 @@ import { Knex } from 'knex';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import {
|
||||
TaskStore,
|
||||
TaskStoreEmitOptions,
|
||||
TaskStoreListEventsOptions,
|
||||
TaskStoreCreateTaskOptions,
|
||||
TaskStoreCreateTaskResult,
|
||||
TaskStoreShutDownTaskOptions,
|
||||
TaskStoreEmitOptions,
|
||||
TaskStoreListEventsOptions,
|
||||
TaskStoreRecoverTaskOptions,
|
||||
TaskStoreShutDownTaskOptions,
|
||||
} from './types';
|
||||
import {
|
||||
SerializedTaskEvent,
|
||||
SerializedTask,
|
||||
TaskStatus,
|
||||
SerializedTaskEvent,
|
||||
TaskEventType,
|
||||
TaskSecrets,
|
||||
TaskStatus,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import { TaskRecovery, TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
@@ -182,6 +182,7 @@ export class DatabaseTaskStore implements TaskStore {
|
||||
|
||||
async list(options: {
|
||||
createdBy?: string;
|
||||
status?: TaskStatus;
|
||||
}): Promise<{ tasks: SerializedTask[] }> {
|
||||
const queryBuilder = this.db<RawDbTaskRow>('tasks');
|
||||
|
||||
@@ -191,6 +192,10 @@ export class DatabaseTaskStore implements TaskStore {
|
||||
});
|
||||
}
|
||||
|
||||
if (options.status) {
|
||||
queryBuilder.where({ status: options.status });
|
||||
}
|
||||
|
||||
const results = await queryBuilder.orderBy('created_at', 'desc').select();
|
||||
|
||||
const tasks = results.map(result => ({
|
||||
|
||||
@@ -20,13 +20,14 @@ import { JsonObject, JsonValue, Observable } from '@backstage/types';
|
||||
import { Logger } from 'winston';
|
||||
import ObservableImpl from 'zen-observable';
|
||||
import {
|
||||
TaskSecrets,
|
||||
SerializedTask,
|
||||
SerializedTaskEvent,
|
||||
TaskBroker,
|
||||
TaskBrokerDispatchOptions,
|
||||
TaskCompletionState,
|
||||
TaskContext,
|
||||
TaskSecrets,
|
||||
TaskStatus,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { InternalTaskSecrets, TaskStore } from './types';
|
||||
import { readDuration } from './helper';
|
||||
@@ -279,13 +280,17 @@ export class StorageTaskBroker implements TaskBroker {
|
||||
|
||||
async list(options?: {
|
||||
createdBy?: string;
|
||||
status?: TaskStatus;
|
||||
}): Promise<{ tasks: SerializedTask[] }> {
|
||||
if (!this.storage.list) {
|
||||
throw new Error(
|
||||
'TaskStore does not implement the list method. Please implement the list method to be able to list tasks',
|
||||
);
|
||||
}
|
||||
return await this.storage.list({ createdBy: options?.createdBy });
|
||||
return await this.storage.list({
|
||||
createdBy: options?.createdBy,
|
||||
status: options?.status,
|
||||
});
|
||||
}
|
||||
|
||||
private deferredDispatch = defer();
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JsonValue, JsonObject, HumanDuration } from '@backstage/types';
|
||||
import { HumanDuration, JsonObject, JsonValue } from '@backstage/types';
|
||||
import { TaskSpec, TaskStep } from '@backstage/plugin-scaffolder-common';
|
||||
import { TaskSecrets } from '@backstage/plugin-scaffolder-node';
|
||||
import {
|
||||
TemplateAction,
|
||||
TaskStatus as _TaskStatus,
|
||||
TaskCompletionState as _TaskCompletionState,
|
||||
SerializedTask as _SerializedTask,
|
||||
TaskEventType as _TaskEventType,
|
||||
SerializedTaskEvent as _SerializedTaskEvent,
|
||||
TaskBrokerDispatchResult as _TaskBrokerDispatchResult,
|
||||
TaskBrokerDispatchOptions as _TaskBrokerDispatchOptions,
|
||||
TaskContext as _TaskContext,
|
||||
TaskBroker as _TaskBroker,
|
||||
TaskBrokerDispatchOptions as _TaskBrokerDispatchOptions,
|
||||
TaskBrokerDispatchResult as _TaskBrokerDispatchResult,
|
||||
TaskCompletionState as _TaskCompletionState,
|
||||
TaskContext as _TaskContext,
|
||||
TaskEventType as _TaskEventType,
|
||||
TaskSecrets,
|
||||
TaskStatus as _TaskStatus,
|
||||
TemplateAction,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,10 @@ export interface TaskStore {
|
||||
tasks: { taskId: string }[];
|
||||
}>;
|
||||
|
||||
list?(options: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>;
|
||||
list?(options: {
|
||||
createdBy?: string;
|
||||
status?: TaskStatus;
|
||||
}): Promise<{ tasks: SerializedTask[] }>;
|
||||
|
||||
emitLogEvent(options: TaskStoreEmitOptions): Promise<void>;
|
||||
|
||||
|
||||
@@ -432,10 +432,11 @@ describe('createRouter', () => {
|
||||
});
|
||||
|
||||
const response = await request(app).get(
|
||||
`/v2/tasks?createdBy=user:default/foo`,
|
||||
`/v2/tasks?createdBy=user:default/foo&status=completed`,
|
||||
);
|
||||
expect(taskBroker.list).toHaveBeenCalledWith({
|
||||
createdBy: 'user:default/foo',
|
||||
status: 'completed',
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
createLegacyAuthAdapters,
|
||||
HostDiscovery,
|
||||
PluginDatabaseManager,
|
||||
UrlReader,
|
||||
createLegacyAuthAdapters,
|
||||
} from '@backstage/backend-common';
|
||||
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
@@ -35,22 +35,22 @@ import { ScmIntegrations } from '@backstage/integration';
|
||||
import { HumanDuration, JsonObject, JsonValue } from '@backstage/types';
|
||||
import {
|
||||
TaskSpec,
|
||||
TemplateEntityStepV1beta3,
|
||||
TemplateEntityV1beta3,
|
||||
templateEntityV1beta3Validator,
|
||||
TemplateParametersV1beta3,
|
||||
TemplateEntityStepV1beta3,
|
||||
} from '@backstage/plugin-scaffolder-common';
|
||||
import {
|
||||
RESOURCE_TYPE_SCAFFOLDER_ACTION,
|
||||
RESOURCE_TYPE_SCAFFOLDER_TEMPLATE,
|
||||
scaffolderActionPermissions,
|
||||
scaffolderTaskPermissions,
|
||||
scaffolderTemplatePermissions,
|
||||
taskCancelPermission,
|
||||
taskCreatePermission,
|
||||
taskReadPermission,
|
||||
templateParameterReadPermission,
|
||||
templateStepReadPermission,
|
||||
scaffolderTaskPermissions,
|
||||
} from '@backstage/plugin-scaffolder-common/alpha';
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
@@ -58,8 +58,9 @@ import { validate } from 'jsonschema';
|
||||
import { Logger } from 'winston';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
TemplateAction,
|
||||
TaskBroker,
|
||||
TaskStatus,
|
||||
TemplateAction,
|
||||
TemplateFilter,
|
||||
TemplateGlobal,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
@@ -587,8 +588,17 @@ export async function createRouter(
|
||||
);
|
||||
}
|
||||
|
||||
const [statusQuery] = [req.query.status].flat();
|
||||
if (
|
||||
typeof statusQuery !== 'string' &&
|
||||
typeof statusQuery !== 'undefined'
|
||||
) {
|
||||
throw new InputError('status query parameter must be a string');
|
||||
}
|
||||
|
||||
const tasks = await taskBroker.list({
|
||||
createdBy: userEntityRef,
|
||||
status: statusQuery ? (statusQuery as TaskStatus) : undefined,
|
||||
});
|
||||
|
||||
res.status(200).json(tasks);
|
||||
|
||||
@@ -315,7 +315,7 @@ export interface TaskBroker {
|
||||
// (undocumented)
|
||||
get(taskId: string): Promise<SerializedTask>;
|
||||
// (undocumented)
|
||||
list?(options?: { createdBy?: string }): Promise<{
|
||||
list?(options?: { createdBy?: string; status?: TaskStatus }): Promise<{
|
||||
tasks: SerializedTask[];
|
||||
}>;
|
||||
// (undocumented)
|
||||
|
||||
@@ -180,5 +180,8 @@ export interface TaskBroker {
|
||||
|
||||
get(taskId: string): Promise<SerializedTask>;
|
||||
|
||||
list?(options?: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>;
|
||||
list?(options?: {
|
||||
createdBy?: string;
|
||||
status?: TaskStatus;
|
||||
}): Promise<{ tasks: SerializedTask[] }>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user