diff --git a/lib/shared/redux/legacy-update-roles-and-permissions.js b/lib/shared/redux/legacy-update-roles-and-permissions.js index 497d756bf..33d36eff0 100644 --- a/lib/shared/redux/legacy-update-roles-and-permissions.js +++ b/lib/shared/redux/legacy-update-roles-and-permissions.js @@ -1,174 +1,175 @@ // @flow import { getAllThreadPermissions, getRolePermissionBlobs, makePermissionsBlob, makePermissionsForChildrenBlob, } from '../../permissions/thread-permissions.js'; import { assertAllThreadInfosAreLegacy } from '../../shared/thread-utils.js'; import type { ThreadPermissionsBlob } from '../../types/thread-permission-types.js'; import type { LegacyRawThreadInfo, + LegacyRawThreadInfos, ThreadStoreThreadInfos, LegacyMemberInfo, MixedRawThreadInfos, } from '../../types/thread-types.js'; import { values } from '../../utils/objects.js'; type ThreadTraversalNode = { +threadID: string, +children: ?$ReadOnlyArray, }; function constructThreadTraversalNodes( threadStoreInfos: ThreadStoreThreadInfos, ): $ReadOnlyArray<$ReadOnly> { const parentThreadMap: { [string]: Array } = {}; for (const threadInfo of values(threadStoreInfos)) { const parentThreadID = threadInfo.parentThreadID ?? 'root'; parentThreadMap[parentThreadID] = [ ...(parentThreadMap[parentThreadID] ?? []), threadInfo.id, ]; } const constructNodes = (nodeID: string): ThreadTraversalNode => ({ threadID: nodeID, children: parentThreadMap[nodeID]?.map(constructNodes) ?? null, }); if (!parentThreadMap['root']) { return []; } return parentThreadMap['root'].map(constructNodes); } type MemberToThreadPermissionsFromParent = { +[member: string]: ?ThreadPermissionsBlob, }; // This migration utility can only be used with LegacyRawThreadInfos function legacyUpdateRolesAndPermissions( threadStoreInfos: MixedRawThreadInfos, -): MixedRawThreadInfos { +): LegacyRawThreadInfos { const updatedThreadStoreInfos = assertAllThreadInfosAreLegacy({ ...threadStoreInfos, }); const recursivelyUpdateRoles = ( node: $ReadOnly, ): void => { const threadInfo: LegacyRawThreadInfo = updatedThreadStoreInfos[node.threadID]; const computedRolePermissionBlobs = getRolePermissionBlobs(threadInfo.type); const roles = { ...threadInfo.roles }; for (const roleID of Object.keys(roles)) { roles[roleID] = { ...roles[roleID], permissions: computedRolePermissionBlobs[roles[roleID].name], }; } updatedThreadStoreInfos[node.threadID] = { ...threadInfo, roles, }; node.children?.map(recursivelyUpdateRoles); }; const recursivelyUpdatePermissions = ( node: $ReadOnly, memberToThreadPermissionsFromParent: ?MemberToThreadPermissionsFromParent, ): void => { const threadInfo: LegacyRawThreadInfo = updatedThreadStoreInfos[node.threadID]; const updatedMembers = []; const memberToThreadPermissionsForChildren: { [string]: ?ThreadPermissionsBlob, } = {}; for (const member: LegacyMemberInfo of threadInfo.members) { const { id, role } = member; const rolePermissions = role ? threadInfo.roles[role].permissions : null; const permissionsFromParent = memberToThreadPermissionsFromParent?.[id]; const computedPermissions = makePermissionsBlob( rolePermissions, permissionsFromParent, threadInfo.id, threadInfo.type, ); updatedMembers.push({ ...member, permissions: getAllThreadPermissions( computedPermissions, threadInfo.id, ), }); memberToThreadPermissionsForChildren[member.id] = makePermissionsForChildrenBlob(computedPermissions); } updatedThreadStoreInfos[node.threadID] = { ...threadInfo, members: updatedMembers, }; node.children?.map(child => recursivelyUpdatePermissions(child, memberToThreadPermissionsForChildren), ); }; const recursivelyUpdateCurrentMemberPermissions = ( node: $ReadOnly, permissionsFromParent: ?ThreadPermissionsBlob, ): void => { const threadInfo: LegacyRawThreadInfo = updatedThreadStoreInfos[node.threadID]; const { currentUser, roles } = threadInfo; const { role } = currentUser; const rolePermissions = role ? roles[role].permissions : null; const computedPermissions = makePermissionsBlob( rolePermissions, permissionsFromParent, threadInfo.id, threadInfo.type, ); updatedThreadStoreInfos[node.threadID] = { ...threadInfo, currentUser: { ...currentUser, permissions: getAllThreadPermissions( computedPermissions, threadInfo.id, ), }, }; node.children?.map(child => recursivelyUpdateCurrentMemberPermissions( child, makePermissionsForChildrenBlob(computedPermissions), ), ); }; const rootNodes = constructThreadTraversalNodes(updatedThreadStoreInfos); rootNodes.forEach(recursivelyUpdateRoles); rootNodes.forEach(node => recursivelyUpdatePermissions(node, null)); rootNodes.forEach(node => recursivelyUpdateCurrentMemberPermissions(node, null), ); return updatedThreadStoreInfos; } export { legacyUpdateRolesAndPermissions }; diff --git a/lib/shared/redux/update-roles-and-permissions.js b/lib/shared/redux/update-roles-and-permissions.js new file mode 100644 index 000000000..a2a92365a --- /dev/null +++ b/lib/shared/redux/update-roles-and-permissions.js @@ -0,0 +1,47 @@ +// @flow + +import { legacyUpdateRolesAndPermissions } from './legacy-update-roles-and-permissions.js'; +import { + decodeMinimallyEncodedRawThreadInfo, + minimallyEncodeRawThreadInfo, + type RawThreadInfo, +} from '../../types/minimally-encoded-thread-permissions-types.js'; +import type { + RawThreadInfos, + LegacyRawThreadInfo, +} from '../../types/thread-types.js'; + +function updateRolesAndPermissions( + threadStoreInfos: RawThreadInfos, +): RawThreadInfos { + const decodedThreadStoreInfos: { [id: string]: LegacyRawThreadInfo } = {}; + + for (const threadID in threadStoreInfos) { + const rawThreadInfo = threadStoreInfos[threadID]; + + const decodedThreadInfo = + decodeMinimallyEncodedRawThreadInfo(rawThreadInfo); + + decodedThreadStoreInfos[threadID] = decodedThreadInfo; + } + + const updatedDecodedThreadStoreInfos = legacyUpdateRolesAndPermissions( + decodedThreadStoreInfos, + ); + + const updatedThreadStoreInfos: { [id: string]: RawThreadInfo } = {}; + + for (const threadID in updatedDecodedThreadStoreInfos) { + const updatedThreadInfo: LegacyRawThreadInfo = + updatedDecodedThreadStoreInfos[threadID]; + + const encodedUpdatedThreadInfo = + minimallyEncodeRawThreadInfo(updatedThreadInfo); + + updatedThreadStoreInfos[threadID] = encodedUpdatedThreadInfo; + } + + return updatedThreadStoreInfos; +} + +export { updateRolesAndPermissions };