mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(API): Add cancel status filters to the public api executions endpoint (#19136)
This commit is contained in:
@@ -32,7 +32,7 @@ export class ExecutionEntity {
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Whether the execution finished sucessfully.
|
||||
* Whether the execution finished successfully.
|
||||
*
|
||||
* @deprecated Use `status` instead
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
import { Container, type Constructable } from '@n8n/di';
|
||||
import { DataSource, EntityManager, In, LessThan, type EntityMetadata } from '@n8n/typeorm';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { Class } from 'n8n-core';
|
||||
import type { DeepPartial } from 'ts-essentials';
|
||||
|
||||
import { ExecutionEntity } from '../../entities';
|
||||
import { ExecutionRepository } from '../execution.repository';
|
||||
|
||||
const mockInstance = <T>(
|
||||
serviceClass: Constructable<T>,
|
||||
data: DeepPartial<T> | undefined = undefined,
|
||||
) => {
|
||||
const instance = mock<T>(data);
|
||||
Container.set(serviceClass, instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
const mockEntityManager = (entityClass: Class) => {
|
||||
const entityManager = mockInstance(EntityManager);
|
||||
const dataSource = mockInstance(DataSource, {
|
||||
manager: entityManager,
|
||||
getMetadata: () => mock<EntityMetadata>({ target: entityClass }),
|
||||
});
|
||||
Object.assign(entityManager, { connection: dataSource });
|
||||
return entityManager;
|
||||
};
|
||||
|
||||
describe('ExecutionRepository', () => {
|
||||
const entityManager = mockEntityManager(ExecutionEntity);
|
||||
const executionRepository = Container.get(ExecutionRepository);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('getExecutionsForPublicApi', () => {
|
||||
test('should get executions matching the filter parameters', async () => {
|
||||
const limit = 10;
|
||||
const params = {
|
||||
limit: 10,
|
||||
lastId: '3',
|
||||
workflowIds: ['3', '4'],
|
||||
};
|
||||
const mockEntities = [{ id: '1' }, { id: '2' }];
|
||||
|
||||
entityManager.find.mockResolvedValueOnce(mockEntities);
|
||||
const result = await executionRepository.getExecutionsForPublicApi(params);
|
||||
|
||||
expect(entityManager.find).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
select: [
|
||||
'id',
|
||||
'mode',
|
||||
'retryOf',
|
||||
'retrySuccessId',
|
||||
'startedAt',
|
||||
'stoppedAt',
|
||||
'workflowId',
|
||||
'waitTill',
|
||||
'finished',
|
||||
'status',
|
||||
],
|
||||
where: {
|
||||
id: LessThan(params.lastId),
|
||||
workflowId: In(params.workflowIds),
|
||||
},
|
||||
order: { id: 'DESC' },
|
||||
take: limit,
|
||||
relations: ['executionData'],
|
||||
});
|
||||
expect(result.length).toBe(mockEntities.length);
|
||||
expect(result[0].id).toEqual(mockEntities[0].id);
|
||||
});
|
||||
|
||||
describe('with status filter', () => {
|
||||
test.each`
|
||||
filterStatus | entityStatus
|
||||
${'canceled'} | ${'canceled'}
|
||||
${'error'} | ${In(['error', 'crashed'])}
|
||||
${'success'} | ${'success'}
|
||||
${'waiting'} | ${'waiting'}
|
||||
`('should find all "$filterStatus" executions', async ({ filterStatus, entityStatus }) => {
|
||||
const limit = 10;
|
||||
const mockEntities = [{ id: '1' }, { id: '2' }];
|
||||
|
||||
entityManager.find.mockResolvedValueOnce(mockEntities);
|
||||
const result = await executionRepository.getExecutionsForPublicApi({
|
||||
limit,
|
||||
status: filterStatus,
|
||||
});
|
||||
|
||||
expect(entityManager.find).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
select: [
|
||||
'id',
|
||||
'mode',
|
||||
'retryOf',
|
||||
'retrySuccessId',
|
||||
'startedAt',
|
||||
'stoppedAt',
|
||||
'workflowId',
|
||||
'waitTill',
|
||||
'finished',
|
||||
'status',
|
||||
],
|
||||
where: { status: entityStatus },
|
||||
order: { id: 'DESC' },
|
||||
take: limit,
|
||||
relations: ['executionData'],
|
||||
});
|
||||
expect(result.length).toBe(mockEntities.length);
|
||||
expect(result[0].id).toEqual(mockEntities[0].id);
|
||||
});
|
||||
|
||||
test.each`
|
||||
filterStatus
|
||||
${'crashed'}
|
||||
${'new'}
|
||||
${'running'}
|
||||
${'unknown'}
|
||||
`(
|
||||
'should find all executions and ignore status filter "$filterStatus"',
|
||||
async ({ filterStatus }) => {
|
||||
const limit = 10;
|
||||
const mockEntities = [{ id: '1' }, { id: '2' }];
|
||||
|
||||
entityManager.find.mockResolvedValueOnce(mockEntities);
|
||||
const result = await executionRepository.getExecutionsForPublicApi({
|
||||
limit,
|
||||
status: filterStatus,
|
||||
});
|
||||
|
||||
expect(entityManager.find).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
select: [
|
||||
'id',
|
||||
'mode',
|
||||
'retryOf',
|
||||
'retrySuccessId',
|
||||
'startedAt',
|
||||
'stoppedAt',
|
||||
'workflowId',
|
||||
'waitTill',
|
||||
'finished',
|
||||
'status',
|
||||
],
|
||||
where: {},
|
||||
order: { id: 'DESC' },
|
||||
take: limit,
|
||||
relations: ['executionData'],
|
||||
});
|
||||
expect(result.length).toBe(mockEntities.length);
|
||||
expect(result[0].id).toEqual(mockEntities[0].id);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getExecutionsCountForPublicApi', () => {
|
||||
test('should get executions matching the filter parameters', async () => {
|
||||
const limit = 10;
|
||||
const mockCount = 20;
|
||||
const params = {
|
||||
limit: 10,
|
||||
lastId: '3',
|
||||
workflowIds: ['3', '4'],
|
||||
};
|
||||
|
||||
entityManager.count.mockResolvedValueOnce(mockCount);
|
||||
const result = await executionRepository.getExecutionsCountForPublicApi(params);
|
||||
|
||||
expect(entityManager.count).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
where: {
|
||||
id: LessThan(params.lastId),
|
||||
workflowId: In(params.workflowIds),
|
||||
},
|
||||
take: limit,
|
||||
});
|
||||
expect(result).toBe(mockCount);
|
||||
});
|
||||
|
||||
describe('with status filter', () => {
|
||||
test.each`
|
||||
filterStatus | entityStatus
|
||||
${'canceled'} | ${'canceled'}
|
||||
${'error'} | ${In(['error', 'crashed'])}
|
||||
${'success'} | ${'success'}
|
||||
${'waiting'} | ${'waiting'}
|
||||
`('should retrieve all $filterStatus executions', async ({ filterStatus, entityStatus }) => {
|
||||
const limit = 10;
|
||||
const mockCount = 20;
|
||||
|
||||
entityManager.count.mockResolvedValueOnce(mockCount);
|
||||
const result = await executionRepository.getExecutionsCountForPublicApi({
|
||||
limit,
|
||||
status: filterStatus,
|
||||
});
|
||||
|
||||
expect(entityManager.count).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
where: { status: entityStatus },
|
||||
take: limit,
|
||||
});
|
||||
|
||||
expect(result).toBe(mockCount);
|
||||
});
|
||||
|
||||
test.each`
|
||||
filterStatus
|
||||
${'crashed'}
|
||||
${'new'}
|
||||
${'running'}
|
||||
${'unknown'}
|
||||
`(
|
||||
'should find all executions and ignore status filter "$filterStatus"',
|
||||
async ({ filterStatus }) => {
|
||||
const limit = 10;
|
||||
const mockCount = 20;
|
||||
|
||||
entityManager.count.mockResolvedValueOnce(mockCount);
|
||||
const result = await executionRepository.getExecutionsCountForPublicApi({
|
||||
limit,
|
||||
status: filterStatus,
|
||||
});
|
||||
|
||||
expect(entityManager.count).toHaveBeenCalledWith(ExecutionEntity, {
|
||||
where: {},
|
||||
take: limit,
|
||||
});
|
||||
|
||||
expect(result).toBe(mockCount);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -638,7 +638,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
|
||||
status?: ExecutionStatus;
|
||||
excludedWorkflowIds?: string[];
|
||||
}): Promise<number> {
|
||||
const executions = await this.count({
|
||||
const executionsCount = await this.count({
|
||||
where: {
|
||||
...(data.lastId && { id: LessThan(data.lastId) }),
|
||||
...(data.status && { ...this.getStatusCondition(data.status) }),
|
||||
@@ -648,7 +648,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
|
||||
take: data.limit,
|
||||
});
|
||||
|
||||
return executions;
|
||||
return executionsCount;
|
||||
}
|
||||
|
||||
private getStatusCondition(status: ExecutionStatus) {
|
||||
@@ -660,6 +660,8 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
|
||||
condition.status = 'waiting';
|
||||
} else if (status === 'error') {
|
||||
condition.status = In(['error', 'crashed']);
|
||||
} else if (status === 'canceled') {
|
||||
condition.status = 'canceled';
|
||||
}
|
||||
|
||||
return condition;
|
||||
|
||||
Reference in New Issue
Block a user