From abce47d2e2049b76f4a30ff133069928a628dab4 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Sun, 15 Sep 2024 20:45:40 +0200 Subject: [PATCH] wip Signed-off-by: bnechyporenko --- .../tasks/DatabaseTaskStore.test.ts | 44 ++++++++++++++++++- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 6 +++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts index a58c5cb863..fbd314cbbc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts @@ -16,7 +16,7 @@ import { DatabaseManager } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; -import { DatabaseTaskStore } from './DatabaseTaskStore'; +import { DatabaseTaskStore, RawDbTaskEventRow } from './DatabaseTaskStore'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { ConflictError } from '@backstage/errors'; import { createMockDirectory } from '@backstage/backend-test-utils'; @@ -267,6 +267,48 @@ describe('DatabaseTaskStore', () => { expect(event.type).toBe('log'); }); + it('should be able to retied cancelled recoverable task', async () => { + const { store, manager } = await createStore(); + const client = await manager.getClient(); + + const { taskId } = await store.createTask({ + spec: { + EXPERIMENTAL_recovery: { EXPERIMENTAL_strategy: 'startOver' }, + } as TaskSpec, + createdBy: 'me#too', + }); + await store.completeTask({ taskId, status: 'cancelled', eventBody: {} }); + + await store.retryTask?.({ taskId }); + + const taskAfterRetry = await store.getTask(taskId); + expect(taskAfterRetry.status).toBe('open'); + + expect( + await client('task_events') + .where({ + task_id: taskId, + event_type: 'recovered', + }) + .select(['body', 'event_type', 'task_id']), + ).toEqual([ + { + body: JSON.stringify({ recoverStrategy: 'startOver' }), + event_type: 'recovered', + task_id: taskId, + }, + ]); + + expect( + await client('task_events') + .where({ + task_id: taskId, + }) + .andWhere(q => q.whereIn('event_type', ['cancelled', 'completion'])) + .select(['body', 'event_type', 'task_id']), + ).toEqual([]); + }); + it('should complete the task', async () => { const { store } = await createStore(); const { taskId } = await store.createTask({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index abf8ae7f41..65bafca619 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -618,6 +618,12 @@ export class DatabaseTaskStore implements TaskStore { for (const { id, spec } of result) { const taskSpec = JSON.parse(spec as string) as TaskSpec; + /** + * Once task is picked up, all event types are replayed. + * We have to remove cancelled or completion event_type as these are as actions for frontend to perform. + * In contrary, we send 'recovered' event_type to reset the state on the frontend side. + * + */ await tx('task_events') .where('task_id', id) .andWhere(q => q.whereIn('event_type', ['cancelled', 'completion']))