From b9fe707cbd9df4a33b6040215826375bef238b65 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 8 Aug 2022 16:15:56 -0400 Subject: [PATCH] fix(public-api): fix issue paginating executions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: Fix pagination issue in /executions * :zap: Enable all executions tests Co-authored-by: Iván Ovejero --- .../handlers/executions/executions.service.ts | 23 +++++-- .../integration/publicApi/executions.test.ts | 65 +++++++++++++++++++ 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts index 5f05a8510bc9f..0986a59227cce 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.service.ts @@ -1,5 +1,5 @@ import { parse } from 'flatted'; -import { In, Not, ObjectLiteral, LessThan, IsNull } from 'typeorm'; +import { In, Not, Raw, ObjectLiteral, LessThan, IsNull } from 'typeorm'; import { Db, IExecutionFlattedDb, IExecutionResponseApi } from '../../../..'; import { ExecutionStatus } from '../../../types'; @@ -59,14 +59,23 @@ export async function getExecutions(data: { status?: ExecutionStatus; excludedExecutionsIds?: number[]; }): Promise { + const where = { + ...(data.lastId && { id: LessThan(data.lastId) }), + ...(data.status && { ...getStatusCondition(data.status) }), + ...(data.workflowIds && { workflowId: In(data.workflowIds.map(String)) }), + ...(data.excludedExecutionsIds && { id: Not(In(data.excludedExecutionsIds)) }), + }; + + if (data.lastId && data.excludedExecutionsIds) { + where.id = Raw((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, { + lastId: data.lastId, + excludedExecutionsIds: data.excludedExecutionsIds, + }); + } + const executions = await Db.collections.Execution.find({ select: getExecutionSelectableProperties(data.includeData), - where: { - ...(data.lastId && { id: LessThan(data.lastId) }), - ...(data.status && { ...getStatusCondition(data.status) }), - ...(data.workflowIds && { workflowId: In(data.workflowIds.map(String)) }), - ...(data.excludedExecutionsIds && { id: Not(In(data.excludedExecutionsIds)) }), - }, + where, order: { id: 'DESC' }, take: data.limit, }); diff --git a/packages/cli/test/integration/publicApi/executions.test.ts b/packages/cli/test/integration/publicApi/executions.test.ts index a3978de190c7b..a1f8dacb11603 100644 --- a/packages/cli/test/integration/publicApi/executions.test.ts +++ b/packages/cli/test/integration/publicApi/executions.test.ts @@ -279,6 +279,71 @@ test('GET /executions should retrieve all successfull executions', async () => { expect(waitTill).toBeNull(); }); +test('GET /executions should paginate two executions', async () => { + const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() }); + + const authOwnerAgent = utils.createAgent(app, { + apiPath: 'public', + auth: true, + user: owner, + version: 1, + }); + + const workflow = await testDb.createWorkflow({}, owner); + + const fistSuccessfullExecution = await testDb.createSuccessfulExecution(workflow); + + const secondSuccessfullExecution = await testDb.createSuccessfulExecution(workflow); + + await testDb.createErrorExecution(workflow); + + const firstExecutionResponse = await authOwnerAgent.get(`/executions`).query({ + status: 'success', + limit: 1, + }); + + expect(firstExecutionResponse.statusCode).toBe(200); + expect(firstExecutionResponse.body.data.length).toBe(1); + expect(firstExecutionResponse.body.nextCursor).toBeDefined(); + + const secondExecutionResponse = await authOwnerAgent.get(`/executions`).query({ + status: 'success', + limit: 1, + cursor: firstExecutionResponse.body.nextCursor, + }); + + expect(secondExecutionResponse.statusCode).toBe(200); + expect(secondExecutionResponse.body.data.length).toBe(1); + expect(secondExecutionResponse.body.nextCursor).toBeNull(); + + const successfullExecutions = [fistSuccessfullExecution, secondSuccessfullExecution]; + const executions = [...firstExecutionResponse.body.data, ...secondExecutionResponse.body.data]; + + for (let i = 0; i < executions.length; i++) { + const { + id, + finished, + mode, + retryOf, + retrySuccessId, + startedAt, + stoppedAt, + workflowId, + waitTill, + } = executions[i] + + expect(id).toBeDefined(); + expect(finished).toBe(true); + expect(mode).toEqual(successfullExecutions[i].mode); + expect(retrySuccessId).toBeNull(); + expect(retryOf).toBeNull(); + expect(startedAt).not.toBeNull(); + expect(stoppedAt).not.toBeNull(); + expect(workflowId).toBe(successfullExecutions[i].workflowId); + expect(waitTill).toBeNull(); + } +}); + test('GET /executions should retrieve all error executions', async () => { const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });