From 33ed72716f57df9503f632d5b3a1317d2bb4a93b Mon Sep 17 00:00:00 2001 From: sjaanus Date: Mon, 25 Jul 2022 10:11:16 +0000 Subject: [PATCH] Implement group access edit for project (#1854) --- src/lib/db/group-store.ts | 9 +++-- src/lib/services/group-service.ts | 5 +++ src/lib/services/project-service.ts | 61 ++++++++++++++++++++++++++++- src/lib/types/group.ts | 1 + 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/lib/db/group-store.ts b/src/lib/db/group-store.ts index 943b22014c..5a1d39f688 100644 --- a/src/lib/db/group-store.ts +++ b/src/lib/db/group-store.ts @@ -17,10 +17,10 @@ const T = { GROUP_ROLE: 'group_role', USERS: 'users', PROJECTS: 'projects', + ROLES: 'roles', }; const GROUP_COLUMNS = ['id', 'name', 'description', 'created_at', 'created_by']; -const GROUP_ROLE_COLUMNS = ['group_id', 'role_id', 'created_at']; const rowToGroup = (row) => { if (!row) { @@ -81,14 +81,17 @@ export default class GroupStore implements IGroupStore { async getProjectGroupRoles(projectId: string): Promise { const rows = await this.db - .select(GROUP_ROLE_COLUMNS) - .from(`${T.GROUP_ROLE}`) + .select('gr.group_id', 'gr.role_id', 'gr.created_at', 'r.name') + .from(`${T.GROUP_ROLE} as gr`) + .innerJoin(`${T.ROLES} as r`, 'gr.role_id', 'r.id') .where('project', projectId); + return rows.map((r) => { return { groupId: r.group_id, roleId: r.role_id, createdAt: r.created_at, + name: r.name, }; }); } diff --git a/src/lib/services/group-service.ts b/src/lib/services/group-service.ts index ccaf75ebb2..7cfb81d221 100644 --- a/src/lib/services/group-service.ts +++ b/src/lib/services/group-service.ts @@ -3,6 +3,7 @@ import { IGroupModel, IGroupModelWithProjectRole, IGroupProject, + IGroupRole, IGroupUser, } from '../types/group'; import { IUnleashConfig, IUnleashStores } from '../types'; @@ -193,6 +194,10 @@ export class GroupService { } } + async getRolesForProject(projectId: string): Promise { + return this.groupStore.getProjectGroupRoles(projectId); + } + private mapGroupWithUsers( group: IGroup, allGroupUsers: IGroupUser[], diff --git a/src/lib/services/project-service.ts b/src/lib/services/project-service.ts index 9a73468ec5..8da1bd49ac 100644 --- a/src/lib/services/project-service.ts +++ b/src/lib/services/project-service.ts @@ -14,6 +14,7 @@ import { ProjectUserUpdateRoleEvent, ProjectGroupAddedEvent, ProjectGroupRemovedEvent, + ProjectGroupUpdateRoleEvent, } from '../types/events'; import { IUnleashStores } from '../types'; import { IUnleashConfig } from '../types/option'; @@ -45,7 +46,7 @@ import ProjectWithoutOwnerError from '../error/project-without-owner-error'; import { IUserStore } from 'lib/types/stores/user-store'; import { arraysHaveSameItems } from '../util/arraysHaveSameItems'; import { GroupService } from './group-service'; -import { IGroupModelWithProjectRole } from 'lib/types/group'; +import { IGroupModelWithProjectRole, IGroupRole } from 'lib/types/group'; const getCreatedBy = (user: User) => user.email || user.username; @@ -436,6 +437,20 @@ export default class ProjectService { ); } + async findProjectGroupRole( + projectId: string, + roleId: number, + ): Promise { + const roles = await this.groupService.getRolesForProject(projectId); + const role = roles.find((r) => r.roleId === roleId); + if (!role) { + throw new NotFoundError( + `Couldn't find roleId=${roleId} on project=${projectId}`, + ); + } + return role; + } + async findProjectRole( projectId: string, roleId: number, @@ -513,6 +528,50 @@ export default class ProjectService { ); } + async changeGroupRole( + projectId: string, + roleId: number, + userId: number, + createdBy: string, + ): Promise { + const usersWithRoles = await this.getAccessToProject(projectId); + const user = usersWithRoles.groups.find((u) => u.id === userId); + const currentRole = usersWithRoles.roles.find( + (r) => r.id === user.roleId, + ); + + if (currentRole.id === roleId) { + // Nothing to do.... + return; + } + + await this.validateAtLeastOneOwner(projectId, currentRole); + + await this.accessService.updateGroupProjectRole( + userId, + roleId, + projectId, + ); + const role = await this.findProjectGroupRole(projectId, roleId); + + await this.eventStore.store( + new ProjectGroupUpdateRoleEvent({ + project: projectId, + createdBy, + preData: { + userId, + roleId: currentRole.id, + roleName: currentRole.name, + }, + data: { + userId, + roleId, + roleName: role.name, + }, + }), + ); + } + async getMembers(projectId: string): Promise { return this.store.getMembers(projectId); } diff --git a/src/lib/types/group.ts b/src/lib/types/group.ts index fd65e09550..fd2503a094 100644 --- a/src/lib/types/group.ts +++ b/src/lib/types/group.ts @@ -17,6 +17,7 @@ export interface IGroupUser { } export interface IGroupRole { + name: string; groupId: number; roleId: number; createdAt: Date;