| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import { groupBy, isEqual } from 'lodash'
- import {
- OrganizationRole,
- OrganizationRolesResponse,
- } from '@/data/organization-members/organization-roles-query'
- import { OrganizationMember } from '@/data/organizations/organization-members-query'
- export interface ProjectRoleConfiguration {
- ref?: string
- name?: string
- roleId: number
- baseRoleId?: number
- }
- export const formatMemberRoleToProjectRoleConfiguration = (
- member: OrganizationMember,
- allRoles: OrganizationRolesResponse
- ) => {
- const { org_scoped_roles, project_scoped_roles } = allRoles
- const roleConfiguration = member.role_ids
- .map((id) => {
- const orgRole = org_scoped_roles.find((role) => role.id === id)
- if (orgRole !== undefined) {
- return { ref: undefined, name: undefined, roleId: orgRole.id }
- }
- const projectRole = project_scoped_roles.find((role) => role.id === id)
- if (projectRole !== undefined) {
- return projectRole.projects.map((x) => ({
- ref: x.ref,
- name: x.name,
- roleId: projectRole.id,
- baseRoleId: projectRole.base_role_id,
- }))
- }
- return undefined
- })
- .filter(Boolean)
- .flat()
- .filter((p) => {
- // [Joshen] Validate only for project scoped roles
- // This filters out project scoped roles for projects that the user doesn't have access to
- // (e.g if the project was deleted)
- if ('baseRoleId' in p!) {
- return p.ref !== undefined
- } else {
- return p
- }
- })
- .sort((a, b) => (a?.name ?? '').localeCompare(b?.name ?? '')) as ProjectRoleConfiguration[]
- return roleConfiguration
- }
- export const deriveChanges = (
- original: ProjectRoleConfiguration[] = [],
- final: ProjectRoleConfiguration[] = []
- ) => {
- const removed: ProjectRoleConfiguration[] = []
- const added: ProjectRoleConfiguration[] = []
- const updated: {
- ref?: string
- name?: string
- originalRole: number
- originalBaseRole?: number
- updatedRole: number
- }[] = []
- original.forEach((x) => {
- const updatedRoleForProject = final.find((y) => x.ref === y.ref)
- if (updatedRoleForProject === undefined) {
- removed.push(x)
- } else if (updatedRoleForProject.roleId !== x.roleId) {
- updated.push({
- ref: updatedRoleForProject.ref,
- name: updatedRoleForProject.name,
- originalRole: x.roleId,
- originalBaseRole: x.baseRoleId,
- updatedRole: updatedRoleForProject.roleId,
- })
- }
- })
- final.forEach((x) => {
- const newRoleForProject = original.find((y) => x.ref === y.ref)
- if (newRoleForProject === undefined) {
- added.push(x)
- }
- })
- return { removed, added, updated }
- }
- // [Joshen] Essentially spit out 3 arrays
- // What to (1) Remove (2) Assign (3) Update
- // And so that we can unit test this
- export const deriveRoleChangeActions = (
- existingRoles: OrganizationRole[],
- changesToRoles: {
- removed: ProjectRoleConfiguration[]
- added: ProjectRoleConfiguration[]
- updated: {
- ref?: string
- name?: string
- originalRole: number
- originalBaseRole?: number
- updatedRole: number
- }[]
- }
- ) => {
- const { removed, added, updated } = changesToRoles
- const toRemove: number[] = []
- const toAssign: { roleId: number; refs: string[] }[] = []
- const toUpdate: { roleId: number; refs: string[] }[] = []
- const groupByAddedRoles = groupBy(added, 'roleId')
- const groupByRemovedRoles = groupBy(removed, 'roleId')
- const groupByUpdatingFromRoles = groupBy(updated, 'originalRole')
- const groupByUpdatingToRoles = groupBy(updated, 'updatedRole')
- const existingProjectRolesByBaseIds = existingRoles
- .filter((r) => (r?.projects ?? []).length > 0)
- .map((r) => r.base_role_id)
- existingRoles.forEach((role) => {
- const projectRefsApplied = role.projects.map((x) => x.ref)
- if (projectRefsApplied.length === 0) {
- // [Joshen] In this case we're removing an org scope role, skip all the chekcs
- return toRemove.push(role.id)
- }
- const toRemoveRole = projectRefsApplied.every((ref) => {
- const isRemoved = removed.map((r) => r.ref).some((x) => x === ref)
- const isUpdated = updated.map((r) => r.ref).some((x) => x === ref)
- return isRemoved || isUpdated
- })
- const isRoleGettingAdded = added.some((r) => r.roleId === role.base_role_id)
- const isRoleGettingUpdatedTo = updated.some((r) => r.updatedRole === role.base_role_id)
- if (toRemoveRole && !isRoleGettingAdded && !isRoleGettingUpdatedTo) {
- return toRemove.push(role.id)
- }
- const projectsToAddToRole = (groupByAddedRoles[role.base_role_id]?.map((r) => r.ref) ??
- []) as string[]
- const projectsToRemoveFromRole = (groupByRemovedRoles[role.id]?.map((r) => r.ref) ??
- []) as string[]
- const projectsUpdatingFromRole = (groupByUpdatingFromRoles[role.id]?.map((r) => r.ref) ??
- []) as string[]
- const projectsUpdatingToRole = (groupByUpdatingToRoles[role.base_role_id]?.map((r) => r.ref) ??
- []) as string[]
- const projectRefsAppliedUpdated = projectRefsApplied
- .filter((x) => !projectsToRemoveFromRole.includes(x))
- .filter((x) => !projectsUpdatingFromRole.includes(x))
- .concat(projectsToAddToRole)
- .concat(projectsUpdatingToRole)
- if (!isEqual(projectRefsApplied, projectRefsAppliedUpdated)) {
- toUpdate.push({ roleId: role.id, refs: projectRefsAppliedUpdated })
- }
- })
- Object.keys(groupByAddedRoles).forEach((roleId) => {
- if (!existingProjectRolesByBaseIds.includes(Number(roleId))) {
- toAssign.push({
- roleId: Number(roleId),
- refs: groupByAddedRoles[roleId].map((x) => x.ref).filter((x) => x !== undefined),
- })
- }
- })
- Object.keys(groupByUpdatingToRoles).forEach((roleId) => {
- if (!existingProjectRolesByBaseIds.includes(Number(roleId))) {
- toAssign.push({
- roleId: Number(roleId),
- refs: groupByUpdatingToRoles[roleId].map((x) => x.ref).filter((x) => x !== undefined),
- })
- }
- })
- // [Joshen] Am sorting the results just so its more deterministic when writing tests
- return {
- toRemove: toRemove.sort((a, b) => a - b),
- toAssign: toAssign.map((x) => ({ ...x, refs: x.refs.sort((a, b) => a.localeCompare(b)) })),
- toUpdate: toUpdate.map((x) => ({ ...x, refs: x.refs.sort((a, b) => a.localeCompare(b)) })),
- }
- }
|