Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3368768
D7569.id26059.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
D7569.id26059.diff
View Options
diff --git a/lib/types/avatar-types.js b/lib/types/avatar-types.js
--- a/lib/types/avatar-types.js
+++ b/lib/types/avatar-types.js
@@ -1,12 +1,20 @@
// @flow
+import t, { type TUnion } from 'tcomb';
+
import type { CreateUpdatesResult } from './update-types.js';
+import { tShape, tString } from '../utils/validation-utils.js';
export type EmojiAvatarDBContent = {
+type: 'emoji',
+emoji: string,
+color: string, // hex, without "#" or "0x"
};
+const emojiAvatarDBContentValidator = tShape<EmojiAvatarDBContent>({
+ type: tString('emoji'),
+ emoji: t.String,
+ color: t.String,
+});
export type ImageAvatarDBContent = {
+type: 'image',
@@ -16,6 +24,7 @@
export type ENSAvatarDBContent = {
+type: 'ens',
};
+const ensAvatarDBContentValidator = tShape({ type: tString('ens') });
export type AvatarDBContent =
| EmojiAvatarDBContent
@@ -29,16 +38,29 @@
| UpdateUserAvatarRemoveRequest;
export type ClientEmojiAvatar = EmojiAvatarDBContent;
+const clientEmojiAvatarValidator = emojiAvatarDBContentValidator;
+
export type ClientImageAvatar = {
+type: 'image',
+uri: string,
};
+const clientImageAvatarValidator = tShape<ClientImageAvatar>({
+ type: tString('image'),
+ uri: t.String,
+});
+
export type ClientENSAvatar = ENSAvatarDBContent;
+const clientENSAvatarValidator = ensAvatarDBContentValidator;
export type ClientAvatar =
| ClientEmojiAvatar
| ClientImageAvatar
| ClientENSAvatar;
+export const clientAvatarValidator: TUnion<ClientAvatar> = t.union([
+ clientEmojiAvatarValidator,
+ clientImageAvatarValidator,
+ clientENSAvatarValidator,
+]);
export type ResolvedClientAvatar = ClientEmojiAvatar | ClientImageAvatar;
diff --git a/lib/types/subscription-types.js b/lib/types/subscription-types.js
--- a/lib/types/subscription-types.js
+++ b/lib/types/subscription-types.js
@@ -1,6 +1,10 @@
// @flow
+import _mapValues from 'lodash/fp/mapValues.js';
+import t, { type TInterface } from 'tcomb';
+
import type { Shape } from './core.js';
+import { tShape } from '../utils/validation-utils.js';
export const threadSubscriptions = Object.freeze({
home: 'home',
@@ -12,6 +16,9 @@
() => boolean,
>;
+export const threadSubscriptionValidator: TInterface<ThreadSubscription> =
+ tShape<ThreadSubscription>(_mapValues(() => t.Boolean)(threadSubscriptions));
+
export type SubscriptionUpdateRequest = {
threadID: string,
updatedFields: Shape<ThreadSubscription>,
diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js
--- a/lib/types/thread-types.js
+++ b/lib/types/thread-types.js
@@ -1,11 +1,13 @@
// @flow
import invariant from 'invariant';
+import t, { type TInterface } from 'tcomb';
-import type {
- AvatarDBContent,
- ClientAvatar,
- UpdateUserAvatarRequest,
+import {
+ type AvatarDBContent,
+ type ClientAvatar,
+ type UpdateUserAvatarRequest,
+ clientAvatarValidator,
} from './avatar-types.js';
import type { Shape } from './core.js';
import type { CalendarQuery, RawEntryInfo } from './entry-types.js';
@@ -14,10 +16,15 @@
RawMessageInfo,
MessageTruncationStatuses,
} from './message-types.js';
-import type { ThreadSubscription } from './subscription-types.js';
+import {
+ type ThreadSubscription,
+ threadSubscriptionValidator,
+} from './subscription-types.js';
import type { ServerUpdateInfo, ClientUpdateInfo } from './update-types.js';
import type { UserInfo, UserInfos } from './user-types.js';
import type { ThreadEntity } from '../utils/entity-text.js';
+import { values } from '../utils/objects.js';
+import { tNumEnum, tBool, tID, tShape } from '../utils/validation-utils.js';
export const threadTypes = Object.freeze({
//OPEN: 0, (DEPRECATED)
@@ -68,6 +75,8 @@
);
return threadType;
}
+const threadTypeValidator = tNumEnum(values(threadTypes));
+
export const communityThreadTypes: $ReadOnlyArray<number> = Object.freeze([
threadTypes.COMMUNITY_ROOT,
threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT,
@@ -138,6 +147,7 @@
);
return ourThreadPermissions;
}
+const threadPermissionValidator = t.enums.of(values(threadPermissions));
export const threadPermissionPropagationPrefixes = Object.freeze({
DESCENDANT: 'descendant_',
@@ -165,14 +175,24 @@
| { +value: true, +source: string }
| { +value: false, +source: null };
+const threadPermissionInfoValidator = t.union([
+ tShape({ value: tBool(true), source: t.String }),
+ tShape({ value: tBool(false), source: t.Nil }),
+]);
+
export type ThreadPermissionsBlob = {
+[permission: string]: ThreadPermissionInfo,
};
export type ThreadRolePermissionsBlob = { +[permission: string]: boolean };
+const threadRolePermissionsBlobValidator = t.dict(t.String, t.Boolean);
export type ThreadPermissionsInfo = {
+[permission: ThreadPermission]: ThreadPermissionInfo,
};
+const threadPermissionsInfoValidator = t.dict(
+ threadPermissionValidator,
+ threadPermissionInfoValidator,
+);
export type MemberInfo = {
+id: string,
@@ -180,6 +200,12 @@
+permissions: ThreadPermissionsInfo,
+isSender: boolean,
};
+const memberInfoValidator = tShape<MemberInfo>({
+ id: t.String,
+ role: t.maybe(t.String),
+ permissions: threadPermissionsInfoValidator,
+ isSender: t.Boolean,
+});
export type RelativeMemberInfo = {
...MemberInfo,
@@ -193,6 +219,12 @@
+permissions: ThreadRolePermissionsBlob,
+isDefault: boolean,
};
+const roleInfoValidator = tShape<RoleInfo>({
+ id: tID,
+ name: t.String,
+ permissions: threadRolePermissionsBlobValidator,
+ isDefault: t.Boolean,
+});
export type ThreadCurrentUserInfo = {
+role: ?string,
@@ -200,6 +232,12 @@
+subscription: ThreadSubscription,
+unread: ?boolean,
};
+const threadCurrentUserInfoValidator = tShape<ThreadCurrentUserInfo>({
+ role: t.maybe(t.String),
+ permissions: threadPermissionsInfoValidator,
+ subscription: threadSubscriptionValidator,
+ unread: t.maybe(t.Boolean),
+});
export type RawThreadInfo = {
+id: string,
@@ -219,6 +257,25 @@
+repliesCount: number,
+pinnedCount?: number,
};
+export const rawThreadInfoValidator: TInterface<RawThreadInfo> =
+ tShape<RawThreadInfo>({
+ id: tID,
+ type: threadTypeValidator,
+ name: t.maybe(t.String),
+ avatar: t.maybe(clientAvatarValidator),
+ description: t.maybe(t.String),
+ color: t.String,
+ creationTime: t.Number,
+ parentThreadID: t.maybe(tID),
+ containingThreadID: t.maybe(tID),
+ community: t.maybe(tID),
+ members: t.list(memberInfoValidator),
+ roles: t.dict(t.String, roleInfoValidator),
+ currentUser: threadCurrentUserInfoValidator,
+ sourceMessageID: t.maybe(tID),
+ repliesCount: t.Number,
+ pinnedCount: t.maybe(t.Number),
+ });
export type ThreadInfo = {
+id: string,
diff --git a/lib/types/validation.test.js b/lib/types/validation.test.js
--- a/lib/types/validation.test.js
+++ b/lib/types/validation.test.js
@@ -8,6 +8,7 @@
mediaValidator,
} from './media-types.js';
import { messageTypes } from './message-types-enum.js';
+import { threadTypes, rawThreadInfoValidator } from './thread-types.js';
import { messageSpecs } from '../shared/messages/message-specs.js';
describe('media validation', () => {
@@ -319,3 +320,316 @@
}
}
});
+
+describe('thread validation', () => {
+ const thread = {
+ id: '85171',
+ type: threadTypes.PERSONAL,
+ name: '',
+ description: '',
+ color: '6d49ab',
+ creationTime: 1675887298557,
+ parentThreadID: '1',
+ members: [
+ {
+ id: '256',
+ role: null,
+ permissions: {
+ know_of: {
+ value: true,
+ source: '1',
+ },
+ membership: {
+ value: false,
+ source: null,
+ },
+ 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: true,
+ source: '1',
+ },
+ edit_permissions: {
+ value: true,
+ source: '1',
+ },
+ 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: false,
+ source: null,
+ },
+ edit_message: {
+ value: false,
+ source: null,
+ },
+ manage_pins: {
+ value: true,
+ source: '1',
+ },
+ },
+ isSender: false,
+ },
+ {
+ id: '83853',
+ role: '85172',
+ permissions: {
+ know_of: {
+ value: true,
+ source: '85171',
+ },
+ membership: {
+ value: false,
+ source: null,
+ },
+ visible: {
+ value: true,
+ source: '85171',
+ },
+ voiced: {
+ value: true,
+ source: '85171',
+ },
+ edit_entries: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread_description: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread_color: {
+ value: true,
+ source: '85171',
+ },
+ delete_thread: {
+ value: false,
+ source: null,
+ },
+ create_subthreads: {
+ value: false,
+ source: null,
+ },
+ create_sidebars: {
+ value: true,
+ source: '85171',
+ },
+ join_thread: {
+ value: false,
+ source: null,
+ },
+ edit_permissions: {
+ value: false,
+ source: null,
+ },
+ add_members: {
+ value: false,
+ source: null,
+ },
+ remove_members: {
+ value: false,
+ source: null,
+ },
+ change_role: {
+ value: false,
+ source: null,
+ },
+ leave_thread: {
+ value: false,
+ source: null,
+ },
+ react_to_message: {
+ value: true,
+ source: '85171',
+ },
+ edit_message: {
+ value: true,
+ source: '85171',
+ },
+ manage_pins: {
+ value: false,
+ source: null,
+ },
+ },
+ isSender: true,
+ },
+ ],
+ roles: {
+ '85172': {
+ id: '85172',
+ name: 'Members',
+ permissions: {
+ know_of: true,
+ visible: true,
+ voiced: true,
+ react_to_message: true,
+ edit_message: true,
+ edit_entries: true,
+ edit_thread: true,
+ edit_thread_color: true,
+ edit_thread_description: true,
+ create_sidebars: true,
+ descendant_open_know_of: true,
+ descendant_open_visible: true,
+ child_open_join_thread: true,
+ },
+ isDefault: true,
+ },
+ },
+ currentUser: {
+ role: '85172',
+ permissions: {
+ know_of: {
+ value: true,
+ source: '85171',
+ },
+ membership: {
+ value: false,
+ source: null,
+ },
+ visible: {
+ value: true,
+ source: '85171',
+ },
+ voiced: {
+ value: true,
+ source: '85171',
+ },
+ edit_entries: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread_description: {
+ value: true,
+ source: '85171',
+ },
+ edit_thread_color: {
+ value: true,
+ source: '85171',
+ },
+ delete_thread: {
+ value: false,
+ source: null,
+ },
+ create_subthreads: {
+ value: false,
+ source: null,
+ },
+ create_sidebars: {
+ value: true,
+ source: '85171',
+ },
+ join_thread: {
+ value: false,
+ source: null,
+ },
+ edit_permissions: {
+ value: false,
+ source: null,
+ },
+ add_members: {
+ value: false,
+ source: null,
+ },
+ remove_members: {
+ value: false,
+ source: null,
+ },
+ change_role: {
+ value: false,
+ source: null,
+ },
+ leave_thread: {
+ value: false,
+ source: null,
+ },
+ react_to_message: {
+ value: true,
+ source: '85171',
+ },
+ edit_message: {
+ value: true,
+ source: '85171',
+ },
+ manage_pins: {
+ value: false,
+ source: null,
+ },
+ },
+ subscription: {
+ home: true,
+ pushNotifs: true,
+ },
+ unread: false,
+ },
+ repliesCount: 0,
+ containingThreadID: '1',
+ community: '1',
+ pinnedCount: 0,
+ };
+
+ it('should validate correct thread', () => {
+ expect(rawThreadInfoValidator.is(thread)).toBe(true);
+ });
+ it('should not validate incorrect thread', () => {
+ expect(
+ rawThreadInfoValidator.is({ ...thread, creationTime: undefined }),
+ ).toBe(false);
+ });
+});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 26, 8:48 PM (20 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2586196
Default Alt Text
D7569.id26059.diff (14 KB)
Attached To
Mode
D7569: [lib] Introduce raw thread info validators
Attached
Detach File
Event Timeline
Log In to Comment