diff --git a/packages/cli/src/databases/repositories/shared-workflow.repository.ts b/packages/cli/src/databases/repositories/shared-workflow.repository.ts index 31eef22e2adc7..8f4bedcb15f37 100644 --- a/packages/cli/src/databases/repositories/shared-workflow.repository.ts +++ b/packages/cli/src/databases/repositories/shared-workflow.repository.ts @@ -211,4 +211,13 @@ export class SharedWorkflowRepository extends Repository { }, }); } + + async getAllRelationsForWorkflows(workflowIds: string[]) { + return await this.find({ + where: { + workflowId: In(workflowIds), + }, + relations: ['project'], + }); + } } diff --git a/packages/cli/src/databases/repositories/workflow.repository.ts b/packages/cli/src/databases/repositories/workflow.repository.ts index 0317124472720..5dcd369def9c6 100644 --- a/packages/cli/src/databases/repositories/workflow.repository.ts +++ b/packages/cli/src/databases/repositories/workflow.repository.ts @@ -95,7 +95,8 @@ export class WorkflowRepository extends Repository { .execute(); } - async getMany(sharedWorkflowIds: string[], options?: ListQuery.Options) { + async getMany(sharedWorkflowIds: string[], originalOptions: ListQuery.Options = {}) { + const options = structuredClone(originalOptions); if (sharedWorkflowIds.length === 0) return { workflows: [], count: 0 }; if (typeof options?.filter?.projectId === 'string' && options.filter.projectId !== '') { diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index bce8770303622..7220e1a640cad 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -66,6 +66,20 @@ export class WorkflowService { let { workflows, count } = await this.workflowRepository.getMany(sharedWorkflowIds, options); if (hasSharing(workflows)) { + // Since we're filtering using project ID as part of the relation, + // we end up filtering out all the other relations, meaning that if + // it's shared to a project, it won't be able to find the home project. + // To solve this, we have to get all the relation now, even though + // we're deleting them later. + if (typeof options?.filter?.projectId === 'string' && options.filter.projectId !== '') { + const relations = await this.sharedWorkflowRepository.getAllRelationsForWorkflows( + workflows.map((c) => c.id), + ); + workflows.forEach((c) => { + c.shared = relations.filter((r) => r.workflowId === c.id); + }); + } + workflows = workflows.map((w) => this.ownershipService.addOwnedByAndSharedWith(w)); } @@ -75,8 +89,8 @@ export class WorkflowService { } workflows.forEach((w) => { - // @ts-expect-error: This is to emulate the old behaviour of removing the shared - // field as part of `addOwnedByAndSharedWith`. We need this field in `addScopes` + // This is to emulate the old behaviour of removing the shared field as + // part of `addOwnedByAndSharedWith`. We need this field in `addScopes` // though. So to avoid leaking the information we just delete it. delete w.shared; }); diff --git a/packages/cli/test/integration/workflows/workflows.controller.test.ts b/packages/cli/test/integration/workflows/workflows.controller.test.ts index 84c1505887684..fb28918509e57 100644 --- a/packages/cli/test/integration/workflows/workflows.controller.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller.test.ts @@ -17,10 +17,14 @@ import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; import { mockInstance } from '../../shared/mocking'; import { saveCredential } from '../shared/db/credentials'; -import { createTeamProject, linkUserToProject } from '../shared/db/projects'; +import { createTeamProject, getPersonalProject, linkUserToProject } from '../shared/db/projects'; import { createTag } from '../shared/db/tags'; import { createManyUsers, createMember, createOwner } from '../shared/db/users'; -import { createWorkflow, shareWorkflowWithProjects } from '../shared/db/workflows'; +import { + createWorkflow, + shareWorkflowWithProjects, + shareWorkflowWithUsers, +} from '../shared/db/workflows'; import { randomCredentialPayload } from '../shared/random'; import * as testDb from '../shared/test-db'; import type { SuperAgentTest } from '../shared/types'; @@ -676,6 +680,21 @@ describe('GET /workflows', () => { expect(response2.body.data).toHaveLength(0); }); + + test('should return homeProject when filtering workflows by projectId', async () => { + const workflow = await createWorkflow({ name: 'First' }, owner); + await shareWorkflowWithUsers(workflow, [member]); + const pp = await getPersonalProject(member); + + const response = await authMemberAgent + .get('/workflows') + .query(`filter={ "projectId": "${pp.id}" }`) + .expect(200); + + expect(response.body.data).toHaveLength(1); + expect(response.body.data[0].id).toBe(workflow.id); + expect(response.body.data[0].homeProject).not.toBeNull(); + }); }); describe('select', () => {