Skip to content

Commit

Permalink
[lib] Introduce MinimallyEncodedRoleInfo and validator
Browse files Browse the repository at this point in the history
Summary:
`RoleInfo` type with `permissions` as `$ReadOnlyArray<string>` instead of `ThreadRolePermissionsBlob`.

Next diffs:
- Introduce `MinimallyEncodedThreadCurrentUserInfo`
- Introduce `MinimallyEncodedRawThreadInfo`
- Higher level utilities for translating back/forth from `RawThreadInfo` <=> `MinimallyEncodedRawThreadInfo`. Found this to be cleaner API than encoding/decoding `RawThreadInfo.currentUser.permissions` and `RawThreadInfo.members[memberID].permissions` "manually" a bunch of different places.
- Native refactoring + migrations
- Web refactoring
- Flipping the switch

Test Plan: Added some unit tests, will consume types in subsequent diffs.

Reviewers: ashoat, ginsu, tomek, rohan

Reviewed By: ashoat

Subscribers: wyilio

Differential Revision: https://phab.comm.dev/D9731
  • Loading branch information
atulsmadhugiri committed Nov 6, 2023
1 parent 2d42d3a commit 3023e16
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 2 deletions.
18 changes: 18 additions & 0 deletions lib/permissions/minimally-encoded-thread-permissions.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// @flow

import invariant from 'invariant';
import t, { type TInterface } from 'tcomb';

import { parseThreadPermissionString } from './prefixes.js';
import type {
ThreadPermission,
ThreadPermissionsInfo,
ThreadRolePermissionsBlob,
} from '../types/thread-permission-types.js';
import type { RoleInfo } from '../types/thread-types.js';
import { roleInfoValidator } from '../types/thread-types.js';
import { entries, invertObjectToMap } from '../utils/objects.js';
import { tRegex, tShape } from '../utils/validation-utils.js';
import type { TRegex } from '../utils/validation-utils.js';

// `baseRolePermissionEncoding` maps permission names to indices.
// These indices represent the 6-bit basePermission part of the 10-bit role
Expand Down Expand Up @@ -179,11 +184,24 @@ const decodeThreadRolePermissionsBitmaskArray = (
]),
);

export type MinimallyEncodedRoleInfo = {
...RoleInfo,
+permissions: $ReadOnlyArray<string>,
};

const tHexEncodedRolePermission: TRegex = tRegex(/^[0-9a-fA-F]{3,}$/);
const minimallyEncodedRoleInfoValidator: TInterface<MinimallyEncodedRoleInfo> =
tShape<MinimallyEncodedRoleInfo>({
...roleInfoValidator.meta.props,
permissions: t.list(tHexEncodedRolePermission),
});

export {
permissionsToBitmaskHex,
hasPermission,
rolePermissionToBitmaskHex,
decodeRolePermissionBitmask,
threadRolePermissionsBlobToBitmaskArray,
decodeThreadRolePermissionsBitmaskArray,
minimallyEncodedRoleInfoValidator,
};
61 changes: 61 additions & 0 deletions lib/permissions/minimally-encoded-thread-permissions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
decodeRolePermissionBitmask,
decodeThreadRolePermissionsBitmaskArray,
hasPermission,
minimallyEncodedRoleInfoValidator,
permissionsToBitmaskHex,
rolePermissionToBitmaskHex,
threadRolePermissionsBlobToBitmaskArray,
Expand Down Expand Up @@ -221,3 +222,63 @@ describe('decodeThreadRolePermissionsBitmaskArray', () => {
).toEqual(threadRolePermissionsBlob);
});
});

describe('minimallyEncodedRoleInfoValidator', () => {
it('should validate correctly formed MinimallyEncodedRoleInfo', () => {
expect(
minimallyEncodedRoleInfoValidator.is({
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);
});
});
2 changes: 1 addition & 1 deletion lib/types/thread-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export type RoleInfo = {
+permissions: ThreadRolePermissionsBlob,
+isDefault: boolean,
};
const roleInfoValidator = tShape<RoleInfo>({
export const roleInfoValidator: TInterface<RoleInfo> = tShape<RoleInfo>({
id: tID,
name: t.String,
permissions: threadRolePermissionsBlobValidator,
Expand Down
2 changes: 1 addition & 1 deletion lib/utils/validation-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function tShape<T>(spec: TStructProps<T>): TInterface<T> {
return t.interface(spec, { strict: true });
}

type TRegex = TRefinement<string>;
export type TRegex = TRefinement<string>;
function tRegex(regex: RegExp): TRegex {
return t.refinement(t.String, val => regex.test(val));
}
Expand Down

0 comments on commit 3023e16

Please sign in to comment.