diff --git a/lib/permissions/minimally-encoded-thread-permissions-validators.js b/lib/permissions/minimally-encoded-thread-permissions-validators.js new file mode 100644 index 000000000..1a75e7b25 --- /dev/null +++ b/lib/permissions/minimally-encoded-thread-permissions-validators.js @@ -0,0 +1,78 @@ +// @flow +import t, { type TInterface } from 'tcomb'; + +import { + tHexEncodedPermissionsBitmask, + tHexEncodedRolePermission, +} from './minimally-encoded-thread-permissions.js'; +import type { + MinimallyEncodedMemberInfo, + MinimallyEncodedRawThreadInfo, + MinimallyEncodedRelativeMemberInfo, + MinimallyEncodedRoleInfo, + MinimallyEncodedThreadCurrentUserInfo, + MinimallyEncodedThreadInfo, +} from '../types/minimally-encoded-thread-permissions-types.js'; +import { + memberInfoValidator, + rawThreadInfoValidator, + roleInfoValidator, + threadCurrentUserInfoValidator, + threadInfoValidator, +} from '../types/thread-types.js'; +import { tBool, tID, tShape } from '../utils/validation-utils.js'; + +const minimallyEncodedRoleInfoValidator: TInterface = + tShape({ + ...roleInfoValidator.meta.props, + minimallyEncoded: tBool(true), + permissions: t.list(tHexEncodedRolePermission), + }); + +const minimallyEncodedThreadCurrentUserInfoValidator: TInterface = + tShape({ + ...threadCurrentUserInfoValidator.meta.props, + minimallyEncoded: tBool(true), + permissions: tHexEncodedPermissionsBitmask, + }); + +const minimallyEncodedMemberInfoValidator: TInterface = + tShape({ + ...memberInfoValidator.meta.props, + minimallyEncoded: tBool(true), + permissions: tHexEncodedPermissionsBitmask, + }); + +const minimallyEncodedRelativeMemberInfoValidator: TInterface = + tShape({ + ...minimallyEncodedMemberInfoValidator.meta.props, + username: t.maybe(t.String), + isViewer: t.Boolean, + }); + +const minimallyEncodedThreadInfoValidator: TInterface = + tShape({ + ...threadInfoValidator.meta.props, + minimallyEncoded: tBool(true), + members: t.list(minimallyEncodedRelativeMemberInfoValidator), + roles: t.dict(tID, minimallyEncodedRoleInfoValidator), + currentUser: minimallyEncodedThreadCurrentUserInfoValidator, + }); + +const minimallyEncodedRawThreadInfoValidator: TInterface = + tShape({ + ...rawThreadInfoValidator.meta.props, + minimallyEncoded: tBool(true), + members: t.list(minimallyEncodedMemberInfoValidator), + roles: t.dict(tID, minimallyEncodedRoleInfoValidator), + currentUser: minimallyEncodedThreadCurrentUserInfoValidator, + }); + +export { + minimallyEncodedRoleInfoValidator, + minimallyEncodedThreadCurrentUserInfoValidator, + minimallyEncodedMemberInfoValidator, + minimallyEncodedRelativeMemberInfoValidator, + minimallyEncodedThreadInfoValidator, + minimallyEncodedRawThreadInfoValidator, +}; diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js index 02fb47442..6a8af44bf 100644 --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -1,478 +1,480 @@ // @flow import { exampleMinimallyEncodedRawThreadInfoA, exampleRawThreadInfoA, expectedDecodedExampleRawThreadInfoA, } from './minimally-encoded-thread-permissions-test-data.js'; +import { + minimallyEncodedMemberInfoValidator, + minimallyEncodedRawThreadInfoValidator, + minimallyEncodedRoleInfoValidator, + minimallyEncodedThreadCurrentUserInfoValidator, +} from './minimally-encoded-thread-permissions-validators.js'; import { decodeRolePermissionBitmask, decodeThreadRolePermissionsBitmaskArray, hasPermission, permissionsToBitmaskHex, rolePermissionToBitmaskHex, threadPermissionsFromBitmaskHex, threadRolePermissionsBlobToBitmaskArray, } from './minimally-encoded-thread-permissions.js'; import { - minimallyEncodedMemberInfoValidator, - minimallyEncodedThreadCurrentUserInfoValidator, - minimallyEncodedRawThreadInfoValidator, - minimallyEncodedRoleInfoValidator, minimallyEncodeRawThreadInfo, decodeMinimallyEncodedRawThreadInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; const permissions = { know_of: { value: true, source: '1' }, visible: { value: true, source: '1' }, voiced: { value: true, source: '1' }, edit_entries: { value: true, source: '1' }, edit_thread: { value: true, source: '1' }, edit_thread_description: { value: true, source: '1' }, edit_thread_color: { value: true, source: '1' }, delete_thread: { value: true, source: '1' }, create_subthreads: { value: true, source: '1' }, create_sidebars: { value: true, source: '1' }, join_thread: { value: false, source: null }, edit_permissions: { value: false, source: null }, add_members: { value: true, source: '1' }, remove_members: { value: true, source: '1' }, change_role: { value: true, source: '1' }, leave_thread: { value: false, source: null }, react_to_message: { value: true, source: '1' }, edit_message: { value: true, source: '1' }, edit_thread_avatar: { value: false, source: null }, manage_pins: { value: false, source: null }, manage_invite_links: { value: false, source: null }, }; describe('minimallyEncodedThreadPermissions', () => { it('should encode ThreadPermissionsInfo as bitmask', () => { const permissionsBitmask = permissionsToBitmaskHex(permissions); expect(permissionsBitmask).toBe('373ff'); expect(hasPermission(permissionsBitmask, 'know_of')).toBe(true); expect(hasPermission(permissionsBitmask, 'visible')).toBe(true); expect(hasPermission(permissionsBitmask, 'voiced')).toBe(true); expect(hasPermission(permissionsBitmask, 'edit_entries')).toBe(true); expect(hasPermission(permissionsBitmask, 'edit_thread')).toBe(true); expect(hasPermission(permissionsBitmask, 'edit_thread_description')).toBe( true, ); expect(hasPermission(permissionsBitmask, 'edit_thread_color')).toBe(true); expect(hasPermission(permissionsBitmask, 'delete_thread')).toBe(true); expect(hasPermission(permissionsBitmask, 'create_subthreads')).toBe(true); expect(hasPermission(permissionsBitmask, 'create_sidebars')).toBe(true); expect(hasPermission(permissionsBitmask, 'join_thread')).toBe(false); expect(hasPermission(permissionsBitmask, 'edit_permissions')).toBe(false); expect(hasPermission(permissionsBitmask, 'remove_members')).toBe(true); expect(hasPermission(permissionsBitmask, 'change_role')).toBe(true); expect(hasPermission(permissionsBitmask, 'leave_thread')).toBe(false); expect(hasPermission(permissionsBitmask, 'react_to_message')).toBe(true); expect(hasPermission(permissionsBitmask, 'edit_message')).toBe(true); }); }); describe('hasPermission', () => { const permissionsSansKnowOf = { know_of: { value: false, source: null }, visible: { value: true, source: '1' }, }; const permissionsSansKnowOfBitmask = permissionsToBitmaskHex( permissionsSansKnowOf, ); it('should fail check if know_of is false even if permission specified in request is true', () => { expect(hasPermission(permissionsSansKnowOfBitmask, 'visible')).toBe(false); }); const permissionsWithKnowOf = { know_of: { value: true, source: '1' }, visible: { value: true, source: '1' }, }; const permissionsWithKnowOfBitmask = permissionsToBitmaskHex( permissionsWithKnowOf, ); it('should succeed permission check if know_of is true', () => { expect(hasPermission(permissionsWithKnowOfBitmask, 'visible')).toBe(true); }); }); describe('threadPermissionsFromBitmaskHex', () => { const expectedDecodedThreadPermissions = { know_of: { value: true, source: 'null' }, visible: { value: true, source: 'null' }, voiced: { value: true, source: 'null' }, edit_entries: { value: true, source: 'null' }, edit_thread: { value: true, source: 'null' }, edit_thread_description: { value: true, source: 'null' }, edit_thread_color: { value: true, source: 'null' }, delete_thread: { value: true, source: 'null' }, create_subthreads: { value: true, source: 'null' }, create_sidebars: { value: true, source: 'null' }, join_thread: { value: false, source: null }, edit_permissions: { value: false, source: null }, add_members: { value: true, source: 'null' }, remove_members: { value: true, source: 'null' }, change_role: { value: true, source: 'null' }, leave_thread: { value: false, source: null }, react_to_message: { value: true, source: 'null' }, edit_message: { value: true, source: 'null' }, edit_thread_avatar: { value: false, source: null }, manage_pins: { value: false, source: null }, manage_invite_links: { value: false, source: null }, }; it('should decode ThreadPermissionsInfo from bitmask', () => { const permissionsBitmask = permissionsToBitmaskHex(permissions); const decodedThreadPermissions = threadPermissionsFromBitmaskHex(permissionsBitmask); expect(decodedThreadPermissions).toStrictEqual( expectedDecodedThreadPermissions, ); }); }); describe('rolePermissionToBitmaskHex', () => { it('should encode `child_opentoplevel_visible` successfully', () => { expect(rolePermissionToBitmaskHex('child_opentoplevel_visible')).toBe( '01b', ); }); it('should encode `child_opentoplevel_know_of` successfully', () => { expect(rolePermissionToBitmaskHex('child_opentoplevel_know_of')).toBe( '00b', ); }); it('should encode `child_toplevel_visible` successfully', () => { expect(rolePermissionToBitmaskHex('child_toplevel_visible')).toBe('01a'); }); it('should encode `child_toplevel_know_of` successfully', () => { expect(rolePermissionToBitmaskHex('child_toplevel_know_of')).toBe('00a'); }); it('should encode `child_opentoplevel_join_thread` successfully', () => { expect(rolePermissionToBitmaskHex('child_opentoplevel_join_thread')).toBe( '0ab', ); }); it('should encode `child_visible` successfully', () => { expect(rolePermissionToBitmaskHex('child_visible')).toBe('018'); }); it('should encode `child_know_of` successfully', () => { expect(rolePermissionToBitmaskHex('child_know_of')).toBe('008'); }); }); describe('decodeRolePermissionBitmask', () => { it('should decode `01b` to `child_opentoplevel_visible` successfully', () => { expect(decodeRolePermissionBitmask('01b')).toBe( 'child_opentoplevel_visible', ); }); it('should decode `00b` to `child_opentoplevel_know_of` successfully', () => { expect(decodeRolePermissionBitmask('00b')).toBe( 'child_opentoplevel_know_of', ); }); it('should decode `01a` to `child_toplevel_visible` successfully', () => { expect(decodeRolePermissionBitmask('01a')).toBe('child_toplevel_visible'); }); it('should decode `00a` to `child_toplevel_know_of` successfully', () => { expect(decodeRolePermissionBitmask('00a')).toBe('child_toplevel_know_of'); }); it('should decode `0ab` to `child_opentoplevel_join_thread` successfully', () => { expect(decodeRolePermissionBitmask('0ab')).toBe( 'child_opentoplevel_join_thread', ); }); it('should decode `018` to `child_visible` successfully', () => { expect(decodeRolePermissionBitmask('018')).toBe('child_visible'); }); it('should decode `008` to `child_know_of` successfully', () => { expect(decodeRolePermissionBitmask('008')).toBe('child_know_of'); }); }); const threadRolePermissionsBlob: ThreadRolePermissionsBlob = { add_members: true, child_open_join_thread: true, create_sidebars: true, create_subthreads: true, descendant_open_know_of: true, descendant_open_visible: true, descendant_opentoplevel_join_thread: true, edit_entries: true, edit_message: true, edit_permissions: true, edit_thread: true, edit_thread_avatar: true, edit_thread_color: true, edit_thread_description: true, know_of: true, leave_thread: true, react_to_message: true, remove_members: true, visible: true, voiced: true, open_know_of: true, open_visible: true, opentoplevel_join_thread: true, toplevel_know_of: true, toplevel_visible: true, opentoplevel_know_of: true, opentoplevel_visible: true, child_know_of: true, child_visible: true, child_opentoplevel_join_thread: true, child_toplevel_know_of: true, child_toplevel_visible: true, child_opentoplevel_know_of: true, child_opentoplevel_visible: true, }; const threadRolePermissionsBitmaskArray = [ '0c0', '0a9', '090', '080', '005', '015', '0a7', '030', '110', '0b0', '040', '120', '060', '050', '000', '0f0', '100', '0d0', '010', '020', '001', '011', '0a3', '002', '012', '003', '013', '008', '018', '0ab', '00a', '01a', '00b', '01b', ]; describe('threadRolePermissionsBlobToBitmaskArray', () => { it('should encode threadRolePermissionsBlob as bitmask array', () => { const arr = threadRolePermissionsBlobToBitmaskArray( threadRolePermissionsBlob, ); expect(arr).toEqual(threadRolePermissionsBitmaskArray); }); }); describe('decodeThreadRolePermissionsBitmaskArray', () => { it('should decode threadRolePermissionsBitmaskArray', () => { expect( decodeThreadRolePermissionsBitmaskArray( threadRolePermissionsBitmaskArray, ), ).toEqual(threadRolePermissionsBlob); }); }); describe('minimallyEncodedRoleInfoValidator', () => { it('should validate correctly formed MinimallyEncodedRoleInfo', () => { expect( minimallyEncodedRoleInfoValidator.is({ minimallyEncoded: true, id: 'roleID', name: 'roleName', permissions: ['abc', 'def'], isDefault: true, }), ).toBe(true); }); it('should NOT validate malformed MinimallyEncodedRoleInfo', () => { expect( minimallyEncodedRoleInfoValidator.is({ id: 1234, name: 'roleName', permissions: ['abc', 'def'], isDefault: true, }), ).toBe(false); expect( minimallyEncodedRoleInfoValidator.is({ id: 'roleID', name: 'roleName', permissions: ['hello a02 test', 'def'], isDefault: true, }), ).toBe(false); expect( minimallyEncodedRoleInfoValidator.is({ id: 'roleID', name: 'roleName', permissions: [123, 456], isDefault: true, }), ).toBe(false); expect( minimallyEncodedRoleInfoValidator.is({ id: 'roleID', name: 'roleName', permissions: ['ZZZ', 'YYY'], isDefault: true, }), ).toBe(false); expect( minimallyEncodedRoleInfoValidator.is({ id: 'roleID', name: 'roleName', permissions: ['AAAAA', 'YYY'], isDefault: true, }), ).toBe(false); }); }); describe('minimallyEncodedThreadCurrentUserInfoValidator', () => { it('should validate correctly formed MinimallyEncodedThreadCurrentUserInfo', () => { expect( minimallyEncodedThreadCurrentUserInfoValidator.is({ minimallyEncoded: true, permissions: '100', subscription: { home: true, pushNotifs: true }, }), ).toBe(true); expect( minimallyEncodedThreadCurrentUserInfoValidator.is({ minimallyEncoded: true, permissions: 'ABCDEFABCDEFABCD', subscription: { home: true, pushNotifs: true }, }), ).toBe(true); }); it('should NOT validate malformed MinimallyEncodedThreadCurrentUserInfo', () => { expect( minimallyEncodedThreadCurrentUserInfoValidator.is({ minimallyEncoded: true, permissions: 'INVALID', subscription: { home: true, pushNotifs: true }, }), ).toBe(false); expect( minimallyEncodedThreadCurrentUserInfoValidator.is({ minimallyEncoded: true, permissions: 'ABCDEF hello ABCDEFABCD', subscription: { home: true, pushNotifs: true }, }), ).toBe(false); expect( minimallyEncodedThreadCurrentUserInfoValidator.is({ minimallyEncoded: true, permissions: 100, subscription: { home: true, pushNotifs: true }, }), ).toBe(false); }); }); describe('minimallyEncodedMemberInfoValidator', () => { it('should validate correctly formed MinimallyEncodedMemberInfo', () => { expect( minimallyEncodedMemberInfoValidator.is({ minimallyEncoded: true, id: 'memberID', permissions: 'ABCDEF', isSender: true, }), ).toBe(true); expect( minimallyEncodedMemberInfoValidator.is({ minimallyEncoded: true, id: 'memberID', permissions: '01b', isSender: false, }), ).toBe(true); }); it('should NOT validate malformed MinimallyEncodedMemberInfo', () => { expect( minimallyEncodedMemberInfoValidator.is({ minimallyEncoded: true, id: 'memberID', permissions: 'INVALID', isSender: false, }), ).toBe(false); expect( minimallyEncodedMemberInfoValidator.is({ minimallyEncoded: true, id: 'memberID', permissions: 100, isSender: false, }), ).toBe(false); }); }); describe('minimallyEncodedRawThreadInfoValidator', () => { it('should validate correctly formed MinimallyEncodedRawThreadInfo', () => { expect( minimallyEncodedRawThreadInfoValidator.is( exampleMinimallyEncodedRawThreadInfoA, ), ).toBe(true); }); }); describe('minimallyEncodeRawThreadInfo', () => { it('should correctly encode RawThreadInfo', () => { expect( minimallyEncodedRawThreadInfoValidator.is( minimallyEncodeRawThreadInfo(exampleRawThreadInfoA), ), ).toBe(true); }); }); describe('decodeMinimallyEncodedRawThreadInfo', () => { it('should correctly decode minimallyEncodedRawThreadInfo', () => { expect( decodeMinimallyEncodedRawThreadInfo( minimallyEncodeRawThreadInfo(exampleRawThreadInfoA), ), ).toStrictEqual(expectedDecodedExampleRawThreadInfoA); }); }); diff --git a/lib/types/minimally-encoded-thread-permissions-types.js b/lib/types/minimally-encoded-thread-permissions-types.js index 68df0685a..f826df4ec 100644 --- a/lib/types/minimally-encoded-thread-permissions-types.js +++ b/lib/types/minimally-encoded-thread-permissions-types.js @@ -1,269 +1,205 @@ // @flow import _mapValues from 'lodash/fp/mapValues.js'; -import type { TInterface } from 'tcomb'; -import t from 'tcomb'; -import { - memberInfoValidator, - rawThreadInfoValidator, - roleInfoValidator, - threadCurrentUserInfoValidator, - threadInfoValidator, -} from './thread-types.js'; import type { MemberInfo, RawThreadInfo, RelativeMemberInfo, RoleInfo, ThreadCurrentUserInfo, ThreadInfo, } from './thread-types.js'; import { decodeThreadRolePermissionsBitmaskArray, permissionsToBitmaskHex, threadPermissionsFromBitmaskHex, threadRolePermissionsBlobToBitmaskArray, - tHexEncodedRolePermission, - tHexEncodedPermissionsBitmask, } from '../permissions/minimally-encoded-thread-permissions.js'; -import { tBool, tID, tShape } from '../utils/validation-utils.js'; export type MinimallyEncodedRoleInfo = $ReadOnly<{ ...RoleInfo, +minimallyEncoded: true, +permissions: $ReadOnlyArray, }>; -const minimallyEncodedRoleInfoValidator: TInterface = - tShape({ - ...roleInfoValidator.meta.props, - minimallyEncoded: tBool(true), - permissions: t.list(tHexEncodedRolePermission), - }); - const minimallyEncodeRoleInfo = ( roleInfo: RoleInfo, ): MinimallyEncodedRoleInfo => ({ ...roleInfo, minimallyEncoded: true, permissions: threadRolePermissionsBlobToBitmaskArray(roleInfo.permissions), }); const decodeMinimallyEncodedRoleInfo = ( minimallyEncodedRoleInfo: MinimallyEncodedRoleInfo, ): RoleInfo => { const { minimallyEncoded, ...rest } = minimallyEncodedRoleInfo; return { ...rest, permissions: decodeThreadRolePermissionsBitmaskArray( minimallyEncodedRoleInfo.permissions, ), }; }; export type MinimallyEncodedThreadCurrentUserInfo = $ReadOnly<{ ...ThreadCurrentUserInfo, +minimallyEncoded: true, +permissions: string, }>; -const minimallyEncodedThreadCurrentUserInfoValidator: TInterface = - tShape({ - ...threadCurrentUserInfoValidator.meta.props, - minimallyEncoded: tBool(true), - permissions: tHexEncodedPermissionsBitmask, - }); - const minimallyEncodeThreadCurrentUserInfo = ( threadCurrentUserInfo: ThreadCurrentUserInfo, ): MinimallyEncodedThreadCurrentUserInfo => ({ ...threadCurrentUserInfo, minimallyEncoded: true, permissions: permissionsToBitmaskHex(threadCurrentUserInfo.permissions), }); const decodeMinimallyEncodedThreadCurrentUserInfo = ( minimallyEncodedThreadCurrentUserInfo: MinimallyEncodedThreadCurrentUserInfo, ): ThreadCurrentUserInfo => { const { minimallyEncoded, ...rest } = minimallyEncodedThreadCurrentUserInfo; return { ...rest, permissions: threadPermissionsFromBitmaskHex( minimallyEncodedThreadCurrentUserInfo.permissions, ), }; }; export type MinimallyEncodedMemberInfo = $ReadOnly<{ ...MemberInfo, +minimallyEncoded: true, +permissions: string, }>; -const minimallyEncodedMemberInfoValidator: TInterface = - tShape({ - ...memberInfoValidator.meta.props, - minimallyEncoded: tBool(true), - permissions: tHexEncodedPermissionsBitmask, - }); - const minimallyEncodeMemberInfo = ( memberInfo: MemberInfo, ): MinimallyEncodedMemberInfo => ({ ...memberInfo, minimallyEncoded: true, permissions: permissionsToBitmaskHex(memberInfo.permissions), }); const decodeMinimallyEncodedMemberInfo = ( minimallyEncodedMemberInfo: MinimallyEncodedMemberInfo, ): MemberInfo => { const { minimallyEncoded, ...rest } = minimallyEncodedMemberInfo; return { ...rest, permissions: threadPermissionsFromBitmaskHex( minimallyEncodedMemberInfo.permissions, ), }; }; export type MinimallyEncodedRelativeMemberInfo = $ReadOnly<{ ...MinimallyEncodedMemberInfo, +username: ?string, +isViewer: boolean, }>; -const minimallyEncodedRelativeMemberInfoValidator: TInterface = - tShape({ - ...minimallyEncodedMemberInfoValidator.meta.props, - username: t.maybe(t.String), - isViewer: t.Boolean, - }); - const minimallyEncodeRelativeMemberInfo = ( relativeMemberInfo: RelativeMemberInfo, ): MinimallyEncodedRelativeMemberInfo => ({ ...relativeMemberInfo, minimallyEncoded: true, permissions: permissionsToBitmaskHex(relativeMemberInfo.permissions), }); const decodeMinimallyEncodedRelativeMemberInfo = ( minimallyEncodedRelativeMemberInfo: MinimallyEncodedRelativeMemberInfo, ): RelativeMemberInfo => { const { minimallyEncoded, ...rest } = minimallyEncodedRelativeMemberInfo; return { ...rest, permissions: threadPermissionsFromBitmaskHex( minimallyEncodedRelativeMemberInfo.permissions, ), }; }; export type MinimallyEncodedRawThreadInfo = $ReadOnly<{ ...RawThreadInfo, +minimallyEncoded: true, +members: $ReadOnlyArray, +roles: { +[id: string]: MinimallyEncodedRoleInfo }, +currentUser: MinimallyEncodedThreadCurrentUserInfo, }>; -const minimallyEncodedRawThreadInfoValidator: TInterface = - tShape({ - ...rawThreadInfoValidator.meta.props, - minimallyEncoded: tBool(true), - members: t.list(minimallyEncodedMemberInfoValidator), - roles: t.dict(tID, minimallyEncodedRoleInfoValidator), - currentUser: minimallyEncodedThreadCurrentUserInfoValidator, - }); - const minimallyEncodeRawThreadInfo = ( rawThreadInfo: RawThreadInfo, ): MinimallyEncodedRawThreadInfo => { const { members, roles, currentUser, ...rest } = rawThreadInfo; return { ...rest, minimallyEncoded: true, members: members.map(minimallyEncodeMemberInfo), roles: _mapValues(minimallyEncodeRoleInfo)(roles), currentUser: minimallyEncodeThreadCurrentUserInfo(currentUser), }; }; const decodeMinimallyEncodedRawThreadInfo = ( minimallyEncodedRawThreadInfo: MinimallyEncodedRawThreadInfo, ): RawThreadInfo => { const { minimallyEncoded, members, roles, currentUser, ...rest } = minimallyEncodedRawThreadInfo; return { ...rest, members: members.map(decodeMinimallyEncodedMemberInfo), roles: _mapValues(decodeMinimallyEncodedRoleInfo)(roles), currentUser: decodeMinimallyEncodedThreadCurrentUserInfo(currentUser), }; }; export type MinimallyEncodedThreadInfo = $ReadOnly<{ ...ThreadInfo, +minimallyEncoded: true, +members: $ReadOnlyArray, +roles: { +[id: string]: MinimallyEncodedRoleInfo }, +currentUser: MinimallyEncodedThreadCurrentUserInfo, }>; -const minimallyEncodedThreadInfoValidator: TInterface = - tShape({ - ...threadInfoValidator.meta.props, - minimallyEncoded: tBool(true), - members: t.list(minimallyEncodedRelativeMemberInfoValidator), - roles: t.dict(tID, minimallyEncodedRoleInfoValidator), - currentUser: minimallyEncodedThreadCurrentUserInfoValidator, - }); - const minimallyEncodeThreadInfo = ( threadInfo: ThreadInfo, ): MinimallyEncodedThreadInfo => { const { members, roles, currentUser, ...rest } = threadInfo; return { ...rest, minimallyEncoded: true, members: members.map(minimallyEncodeRelativeMemberInfo), roles: _mapValues(minimallyEncodeRoleInfo)(roles), currentUser: minimallyEncodeThreadCurrentUserInfo(currentUser), }; }; const decodeMinimallyEncodedThreadInfo = ( minimallyEncodedThreadInfo: MinimallyEncodedThreadInfo, ): ThreadInfo => { const { minimallyEncoded, members, roles, currentUser, ...rest } = minimallyEncodedThreadInfo; return { ...rest, members: members.map(decodeMinimallyEncodedRelativeMemberInfo), roles: _mapValues(decodeMinimallyEncodedRoleInfo)(roles), currentUser: decodeMinimallyEncodedThreadCurrentUserInfo(currentUser), }; }; export { - minimallyEncodedRoleInfoValidator, minimallyEncodeRoleInfo, decodeMinimallyEncodedRoleInfo, - minimallyEncodedThreadCurrentUserInfoValidator, minimallyEncodeThreadCurrentUserInfo, decodeMinimallyEncodedThreadCurrentUserInfo, - minimallyEncodedMemberInfoValidator, minimallyEncodeMemberInfo, decodeMinimallyEncodedMemberInfo, - minimallyEncodedRawThreadInfoValidator, minimallyEncodeRawThreadInfo, decodeMinimallyEncodedRawThreadInfo, - minimallyEncodedRelativeMemberInfoValidator, minimallyEncodeRelativeMemberInfo, decodeMinimallyEncodedRelativeMemberInfo, - minimallyEncodedThreadInfoValidator, minimallyEncodeThreadInfo, decodeMinimallyEncodedThreadInfo, };