mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(API): Exclude pinned data from workflows (#12261)
This commit is contained in:
@@ -80,7 +80,7 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
transformer: sqlite.jsonColumn,
|
transformer: sqlite.jsonColumn,
|
||||||
})
|
})
|
||||||
pinData: ISimplifiedPinData;
|
pinData?: ISimplifiedPinData;
|
||||||
|
|
||||||
@Column({ length: 36 })
|
@Column({ length: 36 })
|
||||||
versionId: string;
|
versionId: string;
|
||||||
|
|||||||
@@ -74,11 +74,12 @@ export declare namespace WorkflowRequest {
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
excludePinnedData?: boolean;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type Create = AuthenticatedRequest<{}, {}, WorkflowEntity, {}>;
|
type Create = AuthenticatedRequest<{}, {}, WorkflowEntity, {}>;
|
||||||
type Get = AuthenticatedRequest<{ id: string }, {}, {}, {}>;
|
type Get = AuthenticatedRequest<{ id: string }, {}, {}, { excludePinnedData?: boolean }>;
|
||||||
type Delete = Get;
|
type Delete = Get;
|
||||||
type Update = AuthenticatedRequest<{ id: string }, {}, WorkflowEntity, {}>;
|
type Update = AuthenticatedRequest<{ id: string }, {}, WorkflowEntity, {}>;
|
||||||
type Activate = Get;
|
type Activate = Get;
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ get:
|
|||||||
summary: Retrieves a workflow
|
summary: Retrieves a workflow
|
||||||
description: Retrieves a workflow.
|
description: Retrieves a workflow.
|
||||||
parameters:
|
parameters:
|
||||||
|
- name: excludePinnedData
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Set this to avoid retrieving pinned data
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
- $ref: '../schemas/parameters/workflowId.yml'
|
- $ref: '../schemas/parameters/workflowId.yml'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
|
|||||||
@@ -60,6 +60,13 @@ get:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: VmwOO9HeTEj20kxM
|
example: VmwOO9HeTEj20kxM
|
||||||
|
- name: excludePinnedData
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: Set this to avoid retrieving pinned data
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
- $ref: '../../../../shared/spec/parameters/limit.yml'
|
- $ref: '../../../../shared/spec/parameters/limit.yml'
|
||||||
- $ref: '../../../../shared/spec/parameters/cursor.yml'
|
- $ref: '../../../../shared/spec/parameters/cursor.yml'
|
||||||
responses:
|
responses:
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export = {
|
|||||||
projectScope('workflow:read', 'workflow'),
|
projectScope('workflow:read', 'workflow'),
|
||||||
async (req: WorkflowRequest.Get, res: express.Response): Promise<express.Response> => {
|
async (req: WorkflowRequest.Get, res: express.Response): Promise<express.Response> => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
const { excludePinnedData = false } = req.query;
|
||||||
|
|
||||||
const workflow = await Container.get(SharedWorkflowRepository).findWorkflowForUser(
|
const workflow = await Container.get(SharedWorkflowRepository).findWorkflowForUser(
|
||||||
id,
|
id,
|
||||||
@@ -120,6 +121,10 @@ export = {
|
|||||||
return res.status(404).json({ message: 'Not Found' });
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (excludePinnedData) {
|
||||||
|
delete workflow.pinData;
|
||||||
|
}
|
||||||
|
|
||||||
Container.get(EventService).emit('user-retrieved-workflow', {
|
Container.get(EventService).emit('user-retrieved-workflow', {
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
publicApi: true,
|
publicApi: true,
|
||||||
@@ -131,7 +136,15 @@ export = {
|
|||||||
getWorkflows: [
|
getWorkflows: [
|
||||||
validCursor,
|
validCursor,
|
||||||
async (req: WorkflowRequest.GetAll, res: express.Response): Promise<express.Response> => {
|
async (req: WorkflowRequest.GetAll, res: express.Response): Promise<express.Response> => {
|
||||||
const { offset = 0, limit = 100, active, tags, name, projectId } = req.query;
|
const {
|
||||||
|
offset = 0,
|
||||||
|
limit = 100,
|
||||||
|
excludePinnedData = false,
|
||||||
|
active,
|
||||||
|
tags,
|
||||||
|
name,
|
||||||
|
projectId,
|
||||||
|
} = req.query;
|
||||||
|
|
||||||
const where: FindOptionsWhere<WorkflowEntity> = {
|
const where: FindOptionsWhere<WorkflowEntity> = {
|
||||||
...(active !== undefined && { active }),
|
...(active !== undefined && { active }),
|
||||||
@@ -199,6 +212,12 @@ export = {
|
|||||||
...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }),
|
...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (excludePinnedData) {
|
||||||
|
workflows.forEach((workflow) => {
|
||||||
|
delete workflow.pinData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Container.get(EventService).emit('user-retrieved-all-workflows', {
|
Container.get(EventService).emit('user-retrieved-all-workflows', {
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
publicApi: true,
|
publicApi: true,
|
||||||
|
|||||||
@@ -378,6 +378,47 @@ describe('GET /workflows', () => {
|
|||||||
expect(updatedAt).toBeDefined();
|
expect(updatedAt).toBeDefined();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return all owned workflows without pinned data', async () => {
|
||||||
|
await Promise.all([
|
||||||
|
createWorkflow(
|
||||||
|
{
|
||||||
|
pinData: {
|
||||||
|
Webhook1: [{ json: { first: 'first' } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
member,
|
||||||
|
),
|
||||||
|
createWorkflow(
|
||||||
|
{
|
||||||
|
pinData: {
|
||||||
|
Webhook2: [{ json: { second: 'second' } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
member,
|
||||||
|
),
|
||||||
|
createWorkflow(
|
||||||
|
{
|
||||||
|
pinData: {
|
||||||
|
Webhook3: [{ json: { third: 'third' } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
member,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const response = await authMemberAgent.get('/workflows?excludePinnedData=true');
|
||||||
|
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
expect(response.body.data.length).toBe(3);
|
||||||
|
expect(response.body.nextCursor).toBeNull();
|
||||||
|
|
||||||
|
for (const workflow of response.body.data) {
|
||||||
|
const { pinData } = workflow;
|
||||||
|
|
||||||
|
expect(pinData).not.toBeDefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /workflows/:id', () => {
|
describe('GET /workflows/:id', () => {
|
||||||
@@ -444,6 +485,26 @@ describe('GET /workflows/:id', () => {
|
|||||||
expect(createdAt).toEqual(workflow.createdAt.toISOString());
|
expect(createdAt).toEqual(workflow.createdAt.toISOString());
|
||||||
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should retrieve workflow without pinned data', async () => {
|
||||||
|
// create and assign workflow to owner
|
||||||
|
const workflow = await createWorkflow(
|
||||||
|
{
|
||||||
|
pinData: {
|
||||||
|
Webhook1: [{ json: { first: 'first' } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
member,
|
||||||
|
);
|
||||||
|
|
||||||
|
const response = await authMemberAgent.get(`/workflows/${workflow.id}?excludePinnedData=true`);
|
||||||
|
|
||||||
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
|
const { pinData } = response.body;
|
||||||
|
|
||||||
|
expect(pinData).not.toBeDefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /workflows/:id', () => {
|
describe('DELETE /workflows/:id', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user