From 289324fd020b8c2f6b9c3bf540f41ebf7e1fc6f9 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Wed, 25 Sep 2024 10:29:05 +0200 Subject: [PATCH] feat: add group project roles to project roles (#8245) This PR adds project roles you get from groups to the list of roles listed for each project. --- .../personal-dashboard-controller.e2e.test.ts | 67 ++++++++++++++++++- .../personal-dashboard-read-model.ts | 41 ++++++++++-- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts b/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts index 2512d1db8e2a..23701039caab 100644 --- a/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts +++ b/src/lib/features/personal-dashboard/personal-dashboard-controller.e2e.test.ts @@ -5,6 +5,7 @@ import { } from '../../../test/e2e/helpers/test-helper'; import getLogger from '../../../test/fixtures/no-logger'; import type { IUser } from '../../types'; +import { randomId } from '../../util'; let app: IUnleashTest; let db: ITestDb; @@ -38,6 +39,7 @@ afterAll(async () => { beforeEach(async () => { await db.stores.featureToggleStore.deleteAll(); + await db.stores.userStore.deleteAll(); }); test('should return personal dashboard with own flags and favorited flags', async () => { @@ -135,6 +137,67 @@ test('should return personal dashboard with membered projects', async () => { }); }); -test('should return projects where users are part of a group', () => { - // TODO +test('should return projects where users are part of a group', async () => { + const { body: user1 } = await loginUser('user1@test.com'); + const projectA = await createProject(`x${randomId()}`, user1); + + const { body: user2 } = await loginUser('user2@test.com'); + + const group = await app.services.groupService.createGroup( + { + name: 'groupA', + users: [{ user: user2 }], + }, + user1, + ); + + await app.services.projectService.addAccess( + projectA.id, + [5], // member role + [], + [user2.id], + user1, + ); + + await app.services.projectService.addAccess( + projectA.id, + [4], // owner role + [group.id], + [], + user1, + ); + + const { body } = await app.request.get(`/api/admin/personal-dashboard`); + + expect(body).toMatchObject({ + projects: [ + { + name: 'Default', + id: 'default', + roles: [ + { + name: 'Editor', + id: 2, + type: 'root', + }, + ], + }, + { + name: projectA.name, + id: projectA.id, + roles: [ + { + name: 'Owner', + id: 4, + type: 'project', + }, + { + name: 'Member', + id: 5, + type: 'project', + }, + ], + }, + ], + }); }); diff --git a/src/lib/features/personal-dashboard/personal-dashboard-read-model.ts b/src/lib/features/personal-dashboard/personal-dashboard-read-model.ts index dd9a856355e8..9225a96f5433 100644 --- a/src/lib/features/personal-dashboard/personal-dashboard-read-model.ts +++ b/src/lib/features/personal-dashboard/personal-dashboard-read-model.ts @@ -5,6 +5,12 @@ import type { PersonalProject, } from './personal-dashboard-read-model-type'; +type IntermediateProjectResult = Omit & { + roles: { + [id: number]: { id: number; name: string; type: string }; + }; +}; + export class PersonalDashboardReadModel implements IPersonalDashboardReadModel { private db: Db; @@ -22,7 +28,21 @@ export class PersonalDashboardReadModel implements IPersonalDashboardReadModel { }>('projects') .join('role_user', 'projects.id', 'role_user.project') .join('roles', 'role_user.role_id', 'roles.id') + .leftJoin('group_user', (join) => { + join.on('group_user.user_id', '=', this.db.raw('?', [userId])); + }) + .leftJoin( + 'group_role', + 'group_role.group_id', + 'group_user.group_id', + ) + .leftJoin( + 'roles as group_roles', + 'group_role.role_id', + 'group_roles.id', + ) .where('role_user.user_id', userId) + .orWhere('group_user.user_id', userId) .whereNull('projects.archived_at') .select( 'projects.name', @@ -35,28 +55,37 @@ export class PersonalDashboardReadModel implements IPersonalDashboardReadModel { const dict = result.reduce((acc, row) => { if (acc[row.id]) { - acc[row.id].roles.push({ + acc[row.id].roles[row.roleId] = { id: row.roleId, name: row.roleName, type: row.roleType, - }); + }; } else { acc[row.id] = { id: row.id, name: row.name, - roles: [ - { + roles: { + [row.roleId]: { id: row.roleId, name: row.roleName, type: row.roleType, }, - ], + }, }; } return acc; }, {}); - const projectList: PersonalProject[] = Object.values(dict); + const projectList: PersonalProject[] = Object.values(dict).map( + (project: IntermediateProjectResult) => { + const roles = Object.values(project.roles); + roles.sort((a, b) => a.id - b.id); + return { + ...project, + roles, + } as PersonalProject; + }, + ); projectList.sort((a, b) => a.name.localeCompare(b.name)); return projectList; }