mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor: Consolidate WorkflowService.getMany() (no-changelog) (#6892)
In scope: - Consolidate `WorkflowService.getMany()`. - Support non-entity field `ownedBy` for `select`. - Support `tags` for `filter`. - Move `addOwnerId` to `OwnershipService`. - Remove unneeded check for `filter.id`. - Simplify DTO validation for `filter` and `select`. - Expand tests for `GET /workflows`. Workflow list query DTOs: ``` filter → name, active, tags select → id, name, active, tags, createdAt, updatedAt, versionId, ownedBy ``` Out of scope: - Migrate `shared_workflow.roleId` and `shared_credential.roleId` to string IDs. - Refactor `WorkflowHelpers.getSharedWorkflowIds()`.
This commit is contained in:
@@ -1,52 +1,89 @@
|
||||
import { filterListQueryMiddleware } from '@/middlewares/listQuery/filter';
|
||||
import { LoggerProxy } from 'n8n-workflow';
|
||||
import { getLogger } from '@/Logger';
|
||||
import type { Request, Response, NextFunction } from 'express';
|
||||
import type { ListQueryRequest } from '@/requests';
|
||||
import { selectListQueryMiddleware } from '@/middlewares/listQuery/select';
|
||||
import { paginationListQueryMiddleware } from '@/middlewares/listQuery/pagination';
|
||||
import * as ResponseHelper from '@/ResponseHelper';
|
||||
import type { ListQuery } from '@/requests';
|
||||
import type { Response, NextFunction } from 'express';
|
||||
|
||||
describe('List query middleware', () => {
|
||||
let mockReq: Partial<ListQueryRequest>;
|
||||
let mockRes: Partial<Response>;
|
||||
let mockReq: ListQuery.Request;
|
||||
let mockRes: Response;
|
||||
let nextFn: NextFunction = jest.fn();
|
||||
let args: [Request, Response, NextFunction];
|
||||
let args: [ListQuery.Request, Response, NextFunction];
|
||||
|
||||
let sendErrorResponse: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
LoggerProxy.init(getLogger());
|
||||
mockReq = { baseUrl: '/rest/workflows' };
|
||||
args = [mockReq as Request, mockRes as Response, nextFn];
|
||||
jest.restoreAllMocks();
|
||||
|
||||
LoggerProxy.init(getLogger());
|
||||
mockReq = { baseUrl: '/rest/workflows' } as ListQuery.Request;
|
||||
mockRes = { status: () => ({ json: jest.fn() }) } as unknown as Response;
|
||||
args = [mockReq, mockRes, nextFn];
|
||||
|
||||
sendErrorResponse = jest.spyOn(ResponseHelper, 'sendErrorResponse');
|
||||
});
|
||||
|
||||
describe('Query filter', () => {
|
||||
test('should parse valid filter', () => {
|
||||
test('should not set filter on request if none sent', async () => {
|
||||
mockReq.query = {};
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toBeUndefined();
|
||||
expect(nextFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should parse valid filter', async () => {
|
||||
mockReq.query = { filter: '{ "name": "My Workflow" }' };
|
||||
filterListQueryMiddleware(...args);
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({ filter: { name: 'My Workflow' } });
|
||||
expect(nextFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should ignore invalid filter', () => {
|
||||
test('should ignore invalid filter', async () => {
|
||||
mockReq.query = { filter: '{ "name": "My Workflow", "foo": "bar" }' };
|
||||
filterListQueryMiddleware(...args);
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({ filter: { name: 'My Workflow' } });
|
||||
expect(nextFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should throw on invalid JSON', () => {
|
||||
test('should throw on invalid JSON', async () => {
|
||||
mockReq.query = { filter: '{ "name" : "My Workflow"' };
|
||||
const call = () => filterListQueryMiddleware(...args);
|
||||
|
||||
expect(call).toThrowError('Failed to parse filter JSON');
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should throw on valid filter with invalid type', async () => {
|
||||
mockReq.query = { filter: '{ "name" : 123 }' };
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Query select', () => {
|
||||
test('should not set select on request if none sent', async () => {
|
||||
mockReq.query = {};
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toBeUndefined();
|
||||
expect(nextFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should parse valid select', () => {
|
||||
mockReq.query = { select: '["name", "id"]' };
|
||||
|
||||
selectListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({ select: { name: true, id: true } });
|
||||
@@ -55,6 +92,7 @@ describe('List query middleware', () => {
|
||||
|
||||
test('ignore invalid select', () => {
|
||||
mockReq.query = { select: '["name", "foo"]' };
|
||||
|
||||
selectListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({ select: { name: true } });
|
||||
@@ -63,20 +101,31 @@ describe('List query middleware', () => {
|
||||
|
||||
test('throw on invalid JSON', () => {
|
||||
mockReq.query = { select: '["name"' };
|
||||
const call = () => selectListQueryMiddleware(...args);
|
||||
|
||||
expect(call).toThrowError('Failed to parse select JSON');
|
||||
selectListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('throw on non-string-array JSON for select', () => {
|
||||
mockReq.query = { select: '"name"' };
|
||||
const call = () => selectListQueryMiddleware(...args);
|
||||
|
||||
expect(call).toThrowError('Parsed select is not a string array');
|
||||
selectListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Query pagination', () => {
|
||||
test('should not set pagination options on request if none sent', async () => {
|
||||
mockReq.query = {};
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toBeUndefined();
|
||||
expect(nextFn).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should parse valid pagination', () => {
|
||||
mockReq.query = { skip: '1', take: '2' };
|
||||
paginationListQueryMiddleware(...args);
|
||||
@@ -103,6 +152,7 @@ describe('List query middleware', () => {
|
||||
|
||||
test('should cap take at 50', () => {
|
||||
mockReq.query = { take: '51' };
|
||||
|
||||
paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({ skip: 0, take: 50 });
|
||||
@@ -111,9 +161,64 @@ describe('List query middleware', () => {
|
||||
|
||||
test('should throw on non-numeric-integer take', () => {
|
||||
mockReq.query = { take: '3.2' };
|
||||
const call = () => paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(call).toThrowError('Parameter take or skip is not an integer string');
|
||||
paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should throw on non-numeric-integer skip', () => {
|
||||
mockReq.query = { take: '3', skip: '3.2' };
|
||||
|
||||
paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(sendErrorResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Combinations', () => {
|
||||
test('should combine filter with select', async () => {
|
||||
mockReq.query = { filter: '{ "name": "My Workflow" }', select: '["name", "id"]' };
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
selectListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({
|
||||
select: { name: true, id: true },
|
||||
filter: { name: 'My Workflow' },
|
||||
});
|
||||
|
||||
expect(nextFn).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
test('should combine filter with pagination options', async () => {
|
||||
mockReq.query = { filter: '{ "name": "My Workflow" }', skip: '1', take: '2' };
|
||||
|
||||
await filterListQueryMiddleware(...args);
|
||||
paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({
|
||||
filter: { name: 'My Workflow' },
|
||||
skip: 1,
|
||||
take: 2,
|
||||
});
|
||||
|
||||
expect(nextFn).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
test('should combine select with pagination options', async () => {
|
||||
mockReq.query = { select: '["name", "id"]', skip: '1', take: '2' };
|
||||
|
||||
selectListQueryMiddleware(...args);
|
||||
paginationListQueryMiddleware(...args);
|
||||
|
||||
expect(mockReq.listQueryOptions).toEqual({
|
||||
select: { name: true, id: true },
|
||||
skip: 1,
|
||||
take: 2,
|
||||
});
|
||||
|
||||
expect(nextFn).toBeCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user