mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Update endpoint to update a workflow, to support updating the workflow parent folder (no-chagelog) (#13906)
This commit is contained in:
@@ -5,6 +5,7 @@ import type { Scope } from '@n8n/permissions';
|
||||
import type { EntityManager } from '@n8n/typeorm';
|
||||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In } from '@n8n/typeorm';
|
||||
import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity';
|
||||
import omit from 'lodash/omit';
|
||||
import pick from 'lodash/pick';
|
||||
import { BinaryDataService, Logger } from 'n8n-core';
|
||||
@@ -27,6 +28,7 @@ import { EventService } from '@/events/event.service';
|
||||
import { ExternalHooks } from '@/external-hooks';
|
||||
import { validateEntity } from '@/generic-helpers';
|
||||
import { hasSharing, type ListQuery } from '@/requests';
|
||||
import { FolderService } from '@/services/folder.service';
|
||||
import { OrchestrationService } from '@/services/orchestration.service';
|
||||
import { OwnershipService } from '@/services/ownership.service';
|
||||
import { ProjectService } from '@/services/project.service.ee';
|
||||
@@ -57,6 +59,7 @@ export class WorkflowService {
|
||||
private readonly executionRepository: ExecutionRepository,
|
||||
private readonly eventService: EventService,
|
||||
private readonly globalConfig: GlobalConfig,
|
||||
private readonly folderService: FolderService,
|
||||
) {}
|
||||
|
||||
async getMany(
|
||||
@@ -179,6 +182,7 @@ export class WorkflowService {
|
||||
workflowUpdateData: WorkflowEntity,
|
||||
workflowId: string,
|
||||
tagIds?: string[],
|
||||
parentFolderId?: string,
|
||||
forceSave?: boolean,
|
||||
): Promise<WorkflowEntity> {
|
||||
const workflow = await this.sharedWorkflowRepository.findWorkflowForUser(workflowId, user, [
|
||||
@@ -263,20 +267,25 @@ export class WorkflowService {
|
||||
await validateEntity(workflowUpdateData);
|
||||
}
|
||||
|
||||
await this.workflowRepository.update(
|
||||
workflowId,
|
||||
pick(workflowUpdateData, [
|
||||
'name',
|
||||
'active',
|
||||
'nodes',
|
||||
'connections',
|
||||
'meta',
|
||||
'settings',
|
||||
'staticData',
|
||||
'pinData',
|
||||
'versionId',
|
||||
]),
|
||||
);
|
||||
const updatePayload: QueryDeepPartialEntity<WorkflowEntity> = pick(workflowUpdateData, [
|
||||
'name',
|
||||
'active',
|
||||
'nodes',
|
||||
'connections',
|
||||
'meta',
|
||||
'settings',
|
||||
'staticData',
|
||||
'pinData',
|
||||
'versionId',
|
||||
]);
|
||||
|
||||
if (parentFolderId) {
|
||||
const project = await this.sharedWorkflowRepository.getWorkflowOwningProject(workflow.id);
|
||||
await this.folderService.findFolderInProjectOrFail(parentFolderId, project?.id ?? '');
|
||||
updatePayload.parentFolder = { id: parentFolderId };
|
||||
}
|
||||
|
||||
await this.workflowRepository.update(workflowId, updatePayload);
|
||||
|
||||
const tagsDisabled = this.globalConfig.tags.disabled;
|
||||
|
||||
|
||||
@@ -349,7 +349,7 @@ export class WorkflowsController {
|
||||
const forceSave = req.query.forceSave === 'true';
|
||||
|
||||
let updateData = new WorkflowEntity();
|
||||
const { tags, ...rest } = req.body;
|
||||
const { tags, parentFolderId, ...rest } = req.body;
|
||||
Object.assign(updateData, rest);
|
||||
|
||||
const isSharingEnabled = this.license.isSharingEnabled();
|
||||
@@ -366,6 +366,7 @@ export class WorkflowsController {
|
||||
updateData,
|
||||
workflowId,
|
||||
tags,
|
||||
parentFolderId,
|
||||
isSharingEnabled ? forceSave : true,
|
||||
);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ beforeAll(async () => {
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2044,6 +2044,48 @@ describe('PATCH /workflows/:workflowId', () => {
|
||||
expect(updatedWorkflow.id).toBe(workflow.id);
|
||||
expect(updatedWorkflow.meta).toEqual(payload.meta);
|
||||
});
|
||||
|
||||
test('should update workflow parent folder', async () => {
|
||||
const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
|
||||
const folder1 = await createFolder(ownerPersonalProject, { name: 'folder1' });
|
||||
|
||||
const workflow = await createWorkflow({}, owner);
|
||||
const payload = {
|
||||
versionId: workflow.versionId,
|
||||
parentFolderId: folder1.id,
|
||||
};
|
||||
|
||||
const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload);
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
|
||||
const updatedWorkflow = await Container.get(WorkflowRepository).findOneOrFail({
|
||||
where: { id: workflow.id },
|
||||
relations: ['parentFolder'],
|
||||
});
|
||||
|
||||
expect(updatedWorkflow.parentFolder?.id).toBe(folder1.id);
|
||||
});
|
||||
|
||||
test('should fail if trying update workflow parent folder with a folder that does not belong to project', async () => {
|
||||
const ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
|
||||
const memberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
|
||||
member.id,
|
||||
);
|
||||
|
||||
await createFolder(ownerPersonalProject, { name: 'folder1' });
|
||||
const folder2 = await createFolder(memberPersonalProject, { name: 'folder2' });
|
||||
|
||||
const workflow = await createWorkflow({}, owner);
|
||||
const payload = {
|
||||
versionId: workflow.versionId,
|
||||
parentFolderId: folder2.id,
|
||||
};
|
||||
|
||||
const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload);
|
||||
|
||||
expect(response.statusCode).toBe(500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /workflows/:workflowId/run', () => {
|
||||
|
||||
Reference in New Issue
Block a user