1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/src/lib/services/group-service.ts

254 lines
7.7 KiB
TypeScript
Raw Normal View History

import {
2023-07-24 11:05:55 +02:00
ICreateGroupModel,
IGroup,
IGroupModel,
IGroupModelWithProjectRole,
IGroupProject,
IGroupRole,
IGroupUser,
} from '../types/group';
import { IUnleashConfig, IUnleashStores } from '../types';
import { IGroupStore } from '../types/stores/group-store';
import { Logger } from '../logger';
import BadDataError from '../error/bad-data-error';
import { GROUP_CREATED, GROUP_DELETED, GROUP_UPDATED } from '../types/events';
import { IEventStore } from '../types/stores/event-store';
import NameExistsError from '../error/name-exists-error';
import { IAccountStore } from '../types/stores/account-store';
import { IUser } from '../types/user';
export class GroupService {
private groupStore: IGroupStore;
private eventStore: IEventStore;
private accountStore: IAccountStore;
private logger: Logger;
constructor(
stores: Pick<
IUnleashStores,
'groupStore' | 'eventStore' | 'accountStore'
>,
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
) {
this.logger = getLogger('service/group-service.js');
this.groupStore = stores.groupStore;
this.eventStore = stores.eventStore;
this.accountStore = stores.accountStore;
}
async getAll(): Promise<IGroupModel[]> {
const groups = await this.groupStore.getAll();
const allGroupUsers = await this.groupStore.getAllUsersByGroups(
groups.map((g) => g.id),
);
const users = await this.accountStore.getAllWithId(
allGroupUsers.map((u) => u.userId),
);
const groupProjects = await this.groupStore.getGroupProjects(
groups.map((g) => g.id),
);
return groups.map((group) => {
const mappedGroup = this.mapGroupWithUsers(
group,
allGroupUsers,
users,
);
return this.mapGroupWithProjects(groupProjects, mappedGroup);
});
}
mapGroupWithProjects(
groupProjects: IGroupProject[],
group: IGroupModel,
): IGroupModel {
return {
...group,
projects: groupProjects
.filter((project) => project.groupId === group.id)
.map((project) => project.project),
};
}
async getGroup(id: number): Promise<IGroupModel> {
const group = await this.groupStore.get(id);
const groupUsers = await this.groupStore.getAllUsersByGroups([id]);
const users = await this.accountStore.getAllWithId(
groupUsers.map((u) => u.userId),
);
return this.mapGroupWithUsers(group, groupUsers, users);
}
2023-07-24 11:05:55 +02:00
async createGroup(
group: ICreateGroupModel,
userName: string,
): Promise<IGroup> {
await this.validateGroup(group);
const newGroup = await this.groupStore.create(group);
await this.groupStore.addUsersToGroup(
newGroup.id,
group.users,
userName,
);
await this.eventStore.store({
type: GROUP_CREATED,
createdBy: userName,
data: group,
});
return newGroup;
}
async updateGroup(group: IGroupModel, userName: string): Promise<IGroup> {
const preData = await this.groupStore.get(group.id);
await this.validateGroup(group, preData);
const newGroup = await this.groupStore.update(group);
const existingUsers = await this.groupStore.getAllUsersByGroups([
group.id,
]);
const existingUserIds = existingUsers.map((g) => g.userId);
const deletableUsers = existingUsers.filter(
(existingUser) =>
!group.users.some(
(groupUser) => groupUser.user.id == existingUser.userId,
),
);
await this.groupStore.updateGroupUsers(
newGroup.id,
group.users.filter(
(user) => !existingUserIds.includes(user.user.id),
),
deletableUsers,
userName,
);
await this.eventStore.store({
type: GROUP_UPDATED,
createdBy: userName,
data: newGroup,
preData,
});
return newGroup;
}
async getProjectGroups(
feat: multiple project roles (#4512) https://linear.app/unleash/issue/2-1128/change-the-api-to-support-adding-multiple-roles-to-a-usergroup-on-a https://linear.app/unleash/issue/2-1125/be-able-to-fetch-all-roles-for-a-user-in-a-project https://linear.app/unleash/issue/2-1127/adapt-the-ui-to-be-able-to-do-a-multi-select-on-role-permissions-for - Allows assigning project roles to groups with root roles - Implements new methods that support assigning, editing, removing and retrieving multiple project roles in project access, along with other auxiliary methods - Adds new events for updating and removing assigned roles - Adapts `useProjectApi` to new methods that use new endpoints that support multiple roles - Adds the `multipleRoles` feature flag that controls the possibility of selecting multiple roles on the UI - Adapts `ProjectAccessAssign` to support multiple role, using the new methods - Adds a new `MultipleRoleSelect` component that allows you to select multiple roles based on the `RoleSelect` component - Adapts the `RoleCell` component to support either a single role or multiple roles - Updates the `access.spec.ts` Cypress e2e test to reflect our new logic - Updates `access-service.e2e.test.ts` with tests covering the multiple roles logic and covering some corner cases - Updates `project-service.e2e.test.ts` to adapt to the new logic, adding a test that covers adding access with `[roles], [groups], [users]` - Misc refactors and boy scouting ![image](https://github.com/Unleash/unleash/assets/14320932/d1cc7626-9387-4ab8-9860-cd293a0d4f62) --------- Co-authored-by: David Leek <david@getunleash.io> Co-authored-by: Mateusz Kwasniewski <kwasniewski.mateusz@gmail.com> Co-authored-by: Nuno Góis <github@nunogois.com>
2023-08-25 10:31:37 +02:00
projectId: string,
): Promise<IGroupModelWithProjectRole[]> {
feat: multiple project roles (#4512) https://linear.app/unleash/issue/2-1128/change-the-api-to-support-adding-multiple-roles-to-a-usergroup-on-a https://linear.app/unleash/issue/2-1125/be-able-to-fetch-all-roles-for-a-user-in-a-project https://linear.app/unleash/issue/2-1127/adapt-the-ui-to-be-able-to-do-a-multi-select-on-role-permissions-for - Allows assigning project roles to groups with root roles - Implements new methods that support assigning, editing, removing and retrieving multiple project roles in project access, along with other auxiliary methods - Adds new events for updating and removing assigned roles - Adapts `useProjectApi` to new methods that use new endpoints that support multiple roles - Adds the `multipleRoles` feature flag that controls the possibility of selecting multiple roles on the UI - Adapts `ProjectAccessAssign` to support multiple role, using the new methods - Adds a new `MultipleRoleSelect` component that allows you to select multiple roles based on the `RoleSelect` component - Adapts the `RoleCell` component to support either a single role or multiple roles - Updates the `access.spec.ts` Cypress e2e test to reflect our new logic - Updates `access-service.e2e.test.ts` with tests covering the multiple roles logic and covering some corner cases - Updates `project-service.e2e.test.ts` to adapt to the new logic, adding a test that covers adding access with `[roles], [groups], [users]` - Misc refactors and boy scouting ![image](https://github.com/Unleash/unleash/assets/14320932/d1cc7626-9387-4ab8-9860-cd293a0d4f62) --------- Co-authored-by: David Leek <david@getunleash.io> Co-authored-by: Mateusz Kwasniewski <kwasniewski.mateusz@gmail.com> Co-authored-by: Nuno Góis <github@nunogois.com>
2023-08-25 10:31:37 +02:00
const projectGroups = await this.groupStore.getProjectGroups(projectId);
if (projectGroups.length > 0) {
const groups = await this.groupStore.getAllWithId(
projectGroups.map((g) => g.id),
);
const groupUsers = await this.groupStore.getAllUsersByGroups(
groups.map((g) => g.id),
);
const users = await this.accountStore.getAllWithId(
groupUsers.map((u) => u.userId),
);
feat: multiple project roles (#4512) https://linear.app/unleash/issue/2-1128/change-the-api-to-support-adding-multiple-roles-to-a-usergroup-on-a https://linear.app/unleash/issue/2-1125/be-able-to-fetch-all-roles-for-a-user-in-a-project https://linear.app/unleash/issue/2-1127/adapt-the-ui-to-be-able-to-do-a-multi-select-on-role-permissions-for - Allows assigning project roles to groups with root roles - Implements new methods that support assigning, editing, removing and retrieving multiple project roles in project access, along with other auxiliary methods - Adds new events for updating and removing assigned roles - Adapts `useProjectApi` to new methods that use new endpoints that support multiple roles - Adds the `multipleRoles` feature flag that controls the possibility of selecting multiple roles on the UI - Adapts `ProjectAccessAssign` to support multiple role, using the new methods - Adds a new `MultipleRoleSelect` component that allows you to select multiple roles based on the `RoleSelect` component - Adapts the `RoleCell` component to support either a single role or multiple roles - Updates the `access.spec.ts` Cypress e2e test to reflect our new logic - Updates `access-service.e2e.test.ts` with tests covering the multiple roles logic and covering some corner cases - Updates `project-service.e2e.test.ts` to adapt to the new logic, adding a test that covers adding access with `[roles], [groups], [users]` - Misc refactors and boy scouting ![image](https://github.com/Unleash/unleash/assets/14320932/d1cc7626-9387-4ab8-9860-cd293a0d4f62) --------- Co-authored-by: David Leek <david@getunleash.io> Co-authored-by: Mateusz Kwasniewski <kwasniewski.mateusz@gmail.com> Co-authored-by: Nuno Góis <github@nunogois.com>
2023-08-25 10:31:37 +02:00
return groups.flatMap((group) => {
return projectGroups
.filter((gr) => gr.id === group.id)
.map((groupRole) => ({
...this.mapGroupWithUsers(group, groupUsers, users),
...groupRole,
}));
});
}
return [];
}
async deleteGroup(id: number, userName: string): Promise<void> {
const group = await this.groupStore.get(id);
await this.groupStore.delete(id);
await this.eventStore.store({
type: GROUP_DELETED,
createdBy: userName,
data: group,
});
}
async validateGroup(
group: IGroupModel | ICreateGroupModel,
existingGroup?: IGroup,
): Promise<void> {
if (!group.name) {
throw new BadDataError('Group name cannot be empty');
}
if (!existingGroup || existingGroup.name != group.name) {
if (await this.groupStore.existsWithName(group.name)) {
throw new NameExistsError('Group name already exists');
}
}
}
async getRolesForProject(projectId: string): Promise<IGroupRole[]> {
return this.groupStore.getProjectGroupRoles(projectId);
}
private mapGroupWithUsers(
group: IGroup,
allGroupUsers: IGroupUser[],
allUsers: IUser[],
): IGroupModel {
const groupUsers = allGroupUsers.filter(
(user) => user.groupId == group.id,
);
const groupUsersId = groupUsers.map((user) => user.userId);
const selectedUsers = allUsers.filter((user) =>
groupUsersId.includes(user.id),
);
const finalUsers = selectedUsers.map((user) => {
const roleUser = groupUsers.find((gu) => gu.userId == user.id);
return {
user: user,
joinedAt: roleUser.joinedAt,
createdBy: roleUser.createdBy,
};
});
return { ...group, users: finalUsers };
}
async syncExternalGroups(
userId: number,
externalGroups: string[],
createdBy?: string,
): Promise<void> {
if (Array.isArray(externalGroups)) {
let newGroups = await this.groupStore.getNewGroupsForExternalUser(
userId,
externalGroups,
);
await this.groupStore.addUserToGroups(
userId,
newGroups.map((g) => g.id),
createdBy,
);
let oldGroups = await this.groupStore.getOldGroupsForExternalUser(
userId,
externalGroups,
);
await this.groupStore.deleteUsersFromGroup(oldGroups);
}
}
async getGroupsForUser(userId: number): Promise<IGroup[]> {
return this.groupStore.getGroupsForUser(userId);
}
}