diff --git a/lib/permissions/minimally-encoded-raw-thread-info-validators.js b/lib/permissions/minimally-encoded-raw-thread-info-validators.js --- a/lib/permissions/minimally-encoded-raw-thread-info-validators.js +++ b/lib/permissions/minimally-encoded-raw-thread-info-validators.js @@ -36,6 +36,26 @@ specialRole: t.maybe(specialRoleValidator), }); +type RoleInfoPossiblyWithIsDefaultField = $ReadOnly<{ + ...RoleInfo, + +isDefault?: boolean, +}>; +// This validator is to be used in `convertClientDBThreadInfoToRawThreadInfo` +// which validates the persisted JSON blob BEFORE any migrations are run. +// `roleInfoValidator` will fail for persisted `RoleInfo`s that include +// the `isDefault` field. Figured it made sense to create a separate validator +// instead of adding complexity to `roleInfoValidator` which should maintain +// 1:1 correspondance with the `RoleInfo` type. +const persistedRoleInfoValidator: TInterface = + tShape({ + id: tID, + name: t.String, + minimallyEncoded: tBool(true), + permissions: t.list(tHexEncodedRolePermission), + specialRole: t.maybe(specialRoleValidator), + isDefault: t.maybe(t.Boolean), + }); + const memberInfoValidator: TInterface = tShape({ ...legacyMemberInfoValidator.meta.props, minimallyEncoded: tBool(true), @@ -58,6 +78,7 @@ export { memberInfoValidator, roleInfoValidator, + persistedRoleInfoValidator, threadCurrentUserInfoValidator, rawThreadInfoValidator, mixedRawThreadInfoValidator, diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -2,6 +2,7 @@ import { memberInfoValidator, + persistedRoleInfoValidator, rawThreadInfoValidator, roleInfoValidator, threadCurrentUserInfoValidator, @@ -405,6 +406,21 @@ }); }); +describe('persistedRoleInfoValidator', () => { + it('should validate persisted RoleInfo with isDefault field', () => { + expect( + persistedRoleInfoValidator.is({ + minimallyEncoded: true, + id: 'roleID', + name: 'roleName', + permissions: ['abc', 'def'], + specialRole: specialRoles.DEFAULT_ROLE, + isDefault: true, + }), + ).toBe(true); + }); +}); + describe('minimallyEncodedThreadCurrentUserInfoValidator', () => { it('should validate correctly formed MinimallyEncodedThreadCurrentUserInfo', () => { expect( diff --git a/lib/utils/thread-ops-utils.js b/lib/utils/thread-ops-utils.js --- a/lib/utils/thread-ops-utils.js +++ b/lib/utils/thread-ops-utils.js @@ -4,7 +4,7 @@ import { memberInfoValidator, - roleInfoValidator, + persistedRoleInfoValidator, threadCurrentUserInfoValidator, } from '../permissions/minimally-encoded-raw-thread-info-validators.js'; import type { @@ -66,7 +66,7 @@ ).reduce((acc: { [string]: RoleInfo }, roleID: string) => { const roleInfo = rawRoles[roleID]; invariant( - roleInfoValidator.is(roleInfo) || + persistedRoleInfoValidator.is(roleInfo) || clientLegacyRoleInfoValidator.is(roleInfo), 'rawRole must be valid [MinimallyEncoded/Legacy]RoleInfo', );