Skip to content

Commit

Permalink
make sure every shared workflow is linked to a project
Browse files Browse the repository at this point in the history
  • Loading branch information
despairblue committed Feb 8, 2024
1 parent aca375f commit d10b08c
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHist
import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository';
import { TagRepository } from '@/databases/repositories/tag.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { ProjectRepository } from '@/databases/repositories/project.repository';

export = {
createWorkflow: [
Expand All @@ -41,7 +42,10 @@ export = {

addNodeIds(workflow);

const createdWorkflow = await createWorkflow(workflow, req.user, 'workflow:owner');
const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(
req.user.id,
);
const createdWorkflow = await createWorkflow(workflow, req.user, project, 'workflow:owner');

await Container.get(WorkflowHistoryService).saveVersion(
req.user,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SharedWorkflow, type WorkflowSharingRole } from '@db/entities/SharedWor
import config from '@/config';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import type { Project } from '@/databases/entities/Project';

function insertIf(condition: boolean, elements: string[]): string[] {
return condition ? elements : [];
Expand Down Expand Up @@ -42,6 +43,7 @@ export async function getWorkflowById(id: string): Promise<WorkflowEntity | null
export async function createWorkflow(
workflow: WorkflowEntity,
user: User,
personalProject: Project,
role: WorkflowSharingRole,
): Promise<WorkflowEntity> {
return await Db.transaction(async (transactionManager) => {
Expand All @@ -53,6 +55,7 @@ export async function createWorkflow(
Object.assign(newSharedWorkflow, {
role,
user,
project: personalProject,
workflow: savedWorkflow,
});
await transactionManager.save<SharedWorkflow>(newSharedWorkflow);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { SharedWorkflow, type WorkflowSharingRole } from '../entities/SharedWork
import { type User } from '../entities/User';
import type { Scope } from '@n8n/permissions';
import type { WorkflowEntity } from '../entities/WorkflowEntity';
import { ProjectRepository } from './project.repository';

@Service()
export class SharedWorkflowRepository extends Repository<SharedWorkflow> {
constructor(dataSource: DataSource) {
constructor(
dataSource: DataSource,
private readonly projectRepository: ProjectRepository,
) {
super(SharedWorkflow, dataSource.manager);
}

Expand Down Expand Up @@ -125,18 +129,22 @@ export class SharedWorkflowRepository extends Repository<SharedWorkflow> {
}

async share(transaction: EntityManager, workflow: WorkflowEntity, users: User[]) {
const newSharedWorkflows = users.reduce<SharedWorkflow[]>((acc, user) => {
const newSharedWorkflows = [];

for (const user of users) {
if (user.isPending) {
return acc;
continue;
}

const project = await this.projectRepository.getPersonalProjectForUserOrFail(user.id);
const entity: Partial<SharedWorkflow> = {
workflowId: workflow.id,
userId: user.id,
projectId: project.id,
role: 'workflow:editor',
};
acc.push(this.create(entity));
return acc;
}, []);
newSharedWorkflows.push(this.create(entity));
}

return await transaction.save(newSharedWorkflows);
}
Expand Down
15 changes: 10 additions & 5 deletions packages/cli/src/services/import.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Service } from 'typedi';
import Container, { Service } from 'typedi';
import { v4 as uuid } from 'uuid';
import { type INode, type INodeCredentialsDetails } from 'n8n-workflow';

Expand All @@ -12,6 +12,7 @@ import { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { WorkflowTagMapping } from '@db/entities/WorkflowTagMapping';
import type { TagEntity } from '@db/entities/TagEntity';
import type { ICredentialsDb } from '@/Interfaces';
import { ProjectRepository } from '@/databases/repositories/project.repository';

@Service()
export class ImportService {
Expand Down Expand Up @@ -57,10 +58,14 @@ export class ImportService {

const workflowId = upsertResult.identifiers.at(0)?.id as string;

await tx.upsert(SharedWorkflow, { workflowId, userId, role: 'workflow:owner' }, [
'workflowId',
'userId',
]);
const personalProject =
await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(userId);

await tx.upsert(
SharedWorkflow,
{ workflowId, userId, projectId: personalProject.id, role: 'workflow:owner' },
['workflowId', 'userId'],
);

if (!workflow.tags?.length) continue;

Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/workflows/workflows.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { EnterpriseWorkflowService } from './workflow.service.ee';
import { WorkflowExecutionService } from './workflowExecution.service';
import { WorkflowSharingService } from './workflowSharing.service';
import { UserManagementMailer } from '@/UserManagement/email';
import { ProjectRepository } from '@/databases/repositories/project.repository';

@Authorized()
@RestController('/workflows')
Expand All @@ -61,6 +62,7 @@ export class WorkflowsController {
private readonly license: License,
private readonly mailer: UserManagementMailer,
private readonly credentialsService: CredentialsService,
private readonly projectRepository: ProjectRepository,
) {}

@Post('/')
Expand Down Expand Up @@ -110,11 +112,14 @@ export class WorkflowsController {
await Db.transaction(async (transactionManager) => {
savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);

const project = await this.projectRepository.getPersonalProjectForUserOrFail(req.user.id);

const newSharedWorkflow = new SharedWorkflow();

Object.assign(newSharedWorkflow, {
role: 'workflow:owner',
user: req.user,
project,
workflow: savedWorkflow,
});

Expand Down
5 changes: 5 additions & 0 deletions packages/cli/test/integration/PermissionChecker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type { SaveCredentialFunction } from '../integration/shared/types';
import { mockNodeTypesData } from '../unit/Helpers';
import { affixRoleToSaveCredential } from '../integration/shared/db/credentials';
import { createOwner, createUser } from '../integration/shared/db/users';
import { ProjectRepository } from '@/databases/repositories/project.repository';

export const toTargetCallErrorMsg = (subworkflowId: string) =>
`Target workflow ID ${subworkflowId} may not be called`;
Expand Down Expand Up @@ -240,10 +241,14 @@ describe('check()', () => {
};

const workflowEntity = await Container.get(WorkflowRepository).save(workflowDetails);
const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(
member.id,
);

await Container.get(SharedWorkflowRepository).save({
workflow: workflowEntity,
user: member,
project,
role: 'workflow:owner',
});

Expand Down
21 changes: 16 additions & 5 deletions packages/cli/test/integration/shared/db/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import type { SharedWorkflow } from '@db/entities/SharedWorkflow';
import { ProjectRepository } from '@/databases/repositories/project.repository';

export async function createManyWorkflows(
amount: number,
Expand Down Expand Up @@ -48,8 +49,10 @@ export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, u
const workflow = await Container.get(WorkflowRepository).save(workflowEntity);

if (user) {
const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(user.id);
await Container.get(SharedWorkflowRepository).save({
user,
project,
workflow,
role: 'workflow:owner',
});
Expand All @@ -58,11 +61,19 @@ export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, u
}

export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: User[]) {
const sharedWorkflows: Array<DeepPartial<SharedWorkflow>> = users.map((user) => ({
userId: user.id,
workflowId: workflow.id,
role: 'workflow:editor',
}));
const sharedWorkflows: Array<DeepPartial<SharedWorkflow>> = await Promise.all(
users.map(async (user) => {
const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail(
user.id,
);
return {
userId: user.id,
projectId: project.id,
workflowId: workflow.id,
role: 'workflow:editor',
};
}),
);
return await Container.get(SharedWorkflowRepository).save(sharedWorkflows);
}

Expand Down

0 comments on commit d10b08c

Please sign in to comment.