Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3396439
D7685.id25930.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
D7685.id25930.diff
View Options
diff --git a/keyserver/src/responders/entry-responders.js b/keyserver/src/responders/entry-responders.js
--- a/keyserver/src/responders/entry-responders.js
+++ b/keyserver/src/responders/entry-responders.js
@@ -4,28 +4,33 @@
import type { TInterface } from 'tcomb';
import { filteredThreadIDs } from 'lib/selectors/calendar-filter-selectors.js';
-import type {
- CalendarQuery,
- SaveEntryRequest,
- CreateEntryRequest,
- DeleteEntryRequest,
- DeleteEntryResponse,
- RestoreEntryRequest,
- RestoreEntryResponse,
- FetchEntryInfosResponse,
- DeltaEntryInfosResult,
- SaveEntryResponse,
+import {
+ type CalendarQuery,
+ type SaveEntryRequest,
+ type CreateEntryRequest,
+ type DeleteEntryRequest,
+ type DeleteEntryResponse,
+ type RestoreEntryRequest,
+ type RestoreEntryResponse,
+ type FetchEntryInfosResponse,
+ type DeltaEntryInfosResult,
+ type SaveEntryResponse,
+ rawEntryInfoValidator,
} from 'lib/types/entry-types.js';
import {
type CalendarFilter,
calendarThreadFilterTypes,
} from 'lib/types/filter-types.js';
-import type {
- FetchEntryRevisionInfosResult,
- FetchEntryRevisionInfosRequest,
+import {
+ type FetchEntryRevisionInfosResult,
+ type FetchEntryRevisionInfosRequest,
+ historyRevisionInfoValidator,
} from 'lib/types/history-types.js';
+import { rawMessageInfoValidator } from 'lib/types/message-types.js';
+import { serverCreateUpdatesResponseValidator } from 'lib/types/update-types.js';
+import { accountUserInfoValidator } from 'lib/types/user-types.js';
import { ServerError } from 'lib/utils/errors.js';
-import { tString, tShape, tDate } from 'lib/utils/validation-utils.js';
+import { tString, tShape, tDate, tID } from 'lib/utils/validation-utils.js';
import createEntry from '../creators/entry-creator.js';
import { deleteEntry, restoreEntry } from '../deleters/entry-deleters.js';
@@ -123,6 +128,12 @@
}
}
+export const fetchEntryInfosResponseValidator: TInterface<FetchEntryInfosResponse> =
+ tShape<FetchEntryInfosResponse>({
+ rawEntryInfos: t.list(rawEntryInfoValidator),
+ userInfos: t.dict(t.String, accountUserInfoValidator),
+ });
+
async function entryFetchResponder(
viewer: Viewer,
input: any,
@@ -140,6 +151,11 @@
id: t.String,
});
+export const fetchEntryRevisionInfosResultValidator: TInterface<FetchEntryRevisionInfosResult> =
+ tShape<FetchEntryRevisionInfosResult>({
+ result: t.list(historyRevisionInfoValidator),
+ });
+
async function entryRevisionFetchResponder(
viewer: Viewer,
input: any,
@@ -160,6 +176,13 @@
calendarQuery: t.maybe(newEntryQueryInputValidator),
});
+export const saveEntryResponseValidator: TInterface<SaveEntryResponse> =
+ tShape<SaveEntryResponse>({
+ entryID: tID,
+ newMessageInfos: t.list(rawMessageInfoValidator),
+ updatesResult: serverCreateUpdatesResponseValidator,
+ });
+
async function entryCreationResponder(
viewer: Viewer,
input: any,
@@ -195,6 +218,13 @@
calendarQuery: t.maybe(newEntryQueryInputValidator),
});
+export const deleteEntryResponseValidator: TInterface<DeleteEntryResponse> =
+ tShape<DeleteEntryResponse>({
+ newMessageInfos: t.list(rawMessageInfoValidator),
+ threadID: tID,
+ updatesResult: serverCreateUpdatesResponseValidator,
+ });
+
async function entryDeletionResponder(
viewer: Viewer,
input: any,
@@ -211,6 +241,12 @@
calendarQuery: t.maybe(newEntryQueryInputValidator),
});
+export const restoreEntryResponseValidator: TInterface<RestoreEntryResponse> =
+ tShape<RestoreEntryResponse>({
+ newMessageInfos: t.list(rawMessageInfoValidator),
+ updatesResult: serverCreateUpdatesResponseValidator,
+ });
+
async function entryRestorationResponder(
viewer: Viewer,
input: any,
@@ -220,6 +256,13 @@
return await restoreEntry(viewer, request);
}
+export const deltaEntryInfosResultValidator: TInterface<DeltaEntryInfosResult> =
+ tShape<DeltaEntryInfosResult>({
+ rawEntryInfos: t.list(rawEntryInfoValidator),
+ deletedEntryIDs: t.list(tID),
+ userInfos: t.dict(t.String, accountUserInfoValidator),
+ });
+
async function calendarQueryUpdateResponder(
viewer: Viewer,
input: any,
diff --git a/keyserver/src/responders/keys-responders.js b/keyserver/src/responders/keys-responders.js
--- a/keyserver/src/responders/keys-responders.js
+++ b/keyserver/src/responders/keys-responders.js
@@ -1,10 +1,13 @@
// @flow
-import t from 'tcomb';
+import t, { type TUnion } from 'tcomb';
import type { GetSessionPublicKeysArgs } from 'lib/types/request-types.js';
-import type { SessionPublicKeys } from 'lib/types/session-types.js';
-import { tShape } from 'lib/utils/validation-utils.js';
+import {
+ type SessionPublicKeys,
+ sessionPublicKeysValidator,
+} from 'lib/types/session-types.js';
+import { tShape, tNull } from 'lib/utils/validation-utils.js';
import { fetchSessionPublicKeys } from '../fetchers/key-fetchers.js';
import type { Viewer } from '../session/viewer.js';
@@ -14,10 +17,14 @@
session: t.String,
});
+type GetSessionPublicKeysResponse = SessionPublicKeys | null;
+export const getSessionPublicKeysResponseValidator: TUnion<GetSessionPublicKeysResponse> =
+ t.union([sessionPublicKeysValidator, tNull]);
+
async function getSessionPublicKeysResponder(
viewer: Viewer,
input: any,
-): Promise<SessionPublicKeys | null> {
+): Promise<GetSessionPublicKeysResponse> {
if (!viewer.loggedIn) {
return null;
}
diff --git a/keyserver/src/responders/message-report-responder.js b/keyserver/src/responders/message-report-responder.js
--- a/keyserver/src/responders/message-report-responder.js
+++ b/keyserver/src/responders/message-report-responder.js
@@ -1,11 +1,12 @@
// @flow
-import t from 'tcomb';
+import t, { type TInterface } from 'tcomb';
import {
type MessageReportCreationRequest,
type MessageReportCreationResult,
} from 'lib/types/message-report-types.js';
+import { rawMessageInfoValidator } from 'lib/types/message-types.js';
import { tShape } from 'lib/utils/validation-utils.js';
import createMessageReport from '../creators/message-report-creator.js';
@@ -16,6 +17,9 @@
messageID: t.String,
});
+export const messageReportCreationResultValidator: TInterface<MessageReportCreationResult> =
+ tShape<MessageReportCreationResult>({ messageInfo: rawMessageInfoValidator });
+
async function messageReportCreationResponder(
viewer: Viewer,
input: any,
diff --git a/keyserver/src/responders/relationship-responders.js b/keyserver/src/responders/relationship-responders.js
--- a/keyserver/src/responders/relationship-responders.js
+++ b/keyserver/src/responders/relationship-responders.js
@@ -1,6 +1,6 @@
// @flow
-import t from 'tcomb';
+import t, { type TInterface } from 'tcomb';
import {
type RelationshipRequest,
@@ -18,6 +18,13 @@
userIDs: t.list(t.String),
});
+export const relationshipErrorsValidator: TInterface<RelationshipErrors> =
+ tShape<RelationshipErrors>({
+ invalid_user: t.maybe(t.list(t.String)),
+ already_friends: t.maybe(t.list(t.String)),
+ user_blocked: t.maybe(t.list(t.String)),
+ });
+
async function updateRelationshipsResponder(
viewer: Viewer,
input: any,
diff --git a/keyserver/src/responders/responder-validators.test.js b/keyserver/src/responders/responder-validators.test.js
--- a/keyserver/src/responders/responder-validators.test.js
+++ b/keyserver/src/responders/responder-validators.test.js
@@ -1,5 +1,23 @@
// @flow
+import {
+ setThreadUnreadStatusResult,
+ updateActivityResultValidator,
+} from 'lib/types/activity-types.js';
+
+import {
+ fetchEntryInfosResponseValidator,
+ fetchEntryRevisionInfosResultValidator,
+ saveEntryResponseValidator,
+ deleteEntryResponseValidator,
+ deltaEntryInfosResultValidator,
+ restoreEntryResponseValidator,
+} from './entry-responders.js';
+import { getSessionPublicKeysResponseValidator } from './keys-responders.js';
+import { messageReportCreationResultValidator } from './message-report-responder.js';
+import { relationshipErrorsValidator } from './relationship-responders.js';
+import { userSearchResultValidator } from './search-responders.js';
+import { siweNonceResponseValidator } from './siwe-nonce-responders.js';
import {
logInResponseValidator,
registerResponseValidator,
@@ -331,3 +349,271 @@
).toBe(false);
});
});
+
+describe('search responder', () => {
+ it('should validate search response', () => {
+ const response = {
+ userInfos: [
+ { id: '83817', username: 'temp_user0' },
+ { id: '83853', username: 'temp_user1' },
+ { id: '83890', username: 'temp_user2' },
+ { id: '83928', username: 'temp_user3' },
+ ],
+ };
+
+ expect(userSearchResultValidator.is(response)).toBe(true);
+ response.userInfos.push({ id: 123 });
+ expect(userSearchResultValidator.is(response)).toBe(false);
+ });
+});
+
+describe('message report responder', () => {
+ it('should validate message report response', () => {
+ const response = {
+ messageInfo: {
+ type: 0,
+ threadID: '101113',
+ creatorID: '5',
+ time: 1682429699746,
+ text: 'text',
+ id: '101121',
+ },
+ };
+
+ expect(messageReportCreationResultValidator.is(response)).toBe(true);
+ response.messageInfo.type = -2;
+ expect(messageReportCreationResultValidator.is(response)).toBe(false);
+ });
+});
+
+describe('relationship responder', () => {
+ it('should validate relationship response', () => {
+ const response = {
+ invalid_user: ['83817', '83890'],
+ already_friends: ['83890'],
+ };
+
+ expect(relationshipErrorsValidator.is(response)).toBe(true);
+ expect(
+ relationshipErrorsValidator.is({ ...response, user_blocked: {} }),
+ ).toBe(false);
+ });
+});
+
+describe('activity responder', () => {
+ it('should validate update activity response', () => {
+ const response = { unfocusedToUnread: ['93095'] };
+ expect(updateActivityResultValidator.is(response)).toBe(true);
+ response.unfocusedToUnread.push(123);
+ expect(updateActivityResultValidator.is(response)).toBe(false);
+ });
+
+ it('should validate set thread unread response', () => {
+ const response = { resetToUnread: false };
+ expect(setThreadUnreadStatusResult.is(response)).toBe(true);
+ expect(setThreadUnreadStatusResult.is({ ...response, unread: false })).toBe(
+ false,
+ );
+ });
+});
+
+describe('keys responder', () => {
+ it('should validate get session public keys response', () => {
+ const response = {
+ identityKey: 'key',
+ oneTimeKey: 'key',
+ };
+
+ expect(getSessionPublicKeysResponseValidator.is(response)).toBe(true);
+ expect(getSessionPublicKeysResponseValidator.is(null)).toBe(true);
+ expect(
+ getSessionPublicKeysResponseValidator.is({
+ ...response,
+ identityKey: undefined,
+ }),
+ ).toBe(false);
+ });
+});
+
+describe('siwe nonce responders', () => {
+ it('should validate siwe nonce response', () => {
+ const response = { nonce: 'nonce' };
+ expect(siweNonceResponseValidator.is(response)).toBe(true);
+ expect(siweNonceResponseValidator.is({ nonce: 123 })).toBe(false);
+ });
+});
+
+describe('entry reponders', () => {
+ it('should validate entry fetch response', () => {
+ const response = {
+ rawEntryInfos: [
+ {
+ id: '92860',
+ threadID: '85068',
+ text: 'text',
+ year: 2023,
+ month: 4,
+ day: 2,
+ creationTime: 1682082939882,
+ creatorID: '83853',
+ deleted: false,
+ },
+ ],
+ userInfos: {
+ '123': {
+ id: '123',
+ username: 'username',
+ },
+ },
+ };
+ expect(fetchEntryInfosResponseValidator.is(response)).toBe(true);
+ expect(
+ fetchEntryInfosResponseValidator.is({
+ ...response,
+ userInfos: undefined,
+ }),
+ ).toBe(false);
+ });
+
+ it('should validate entry revision fetch response', () => {
+ const response = {
+ result: [
+ {
+ id: '93297',
+ authorID: '83853',
+ text: 'text',
+ lastUpdate: 1682603494202,
+ deleted: false,
+ threadID: '83859',
+ entryID: '93270',
+ },
+ {
+ id: '93284',
+ authorID: '83853',
+ text: 'text',
+ lastUpdate: 1682603426996,
+ deleted: true,
+ threadID: '83859',
+ entryID: '93270',
+ },
+ ],
+ };
+ expect(fetchEntryRevisionInfosResultValidator.is(response)).toBe(true);
+ expect(
+ fetchEntryRevisionInfosResultValidator.is({
+ ...response,
+ result: {},
+ }),
+ ).toBe(false);
+ });
+
+ it('should validate entry save response', () => {
+ const response = {
+ entryID: '93270',
+ newMessageInfos: [
+ {
+ type: 9,
+ threadID: '83859',
+ creatorID: '83853',
+ time: 1682603362817,
+ entryID: '93270',
+ date: '2023-04-03',
+ text: 'text',
+ id: '93272',
+ },
+ ],
+ updatesResult: { viewerUpdates: [], userInfos: [] },
+ };
+
+ expect(saveEntryResponseValidator.is(response)).toBe(true);
+ expect(
+ saveEntryResponseValidator.is({
+ ...response,
+ entryID: undefined,
+ }),
+ ).toBe(false);
+ });
+
+ it('should validate entry delete response', () => {
+ const response = {
+ threadID: '83859',
+ newMessageInfos: [
+ {
+ type: 11,
+ threadID: '83859',
+ creatorID: '83853',
+ time: 1682603427038,
+ entryID: '93270',
+ date: '2023-04-03',
+ text: 'text',
+ id: '93285',
+ },
+ ],
+ updatesResult: { viewerUpdates: [], userInfos: [] },
+ };
+ expect(deleteEntryResponseValidator.is(response)).toBe(true);
+ expect(
+ deleteEntryResponseValidator.is({
+ ...response,
+ threadID: undefined,
+ }),
+ ).toBe(false);
+ });
+
+ it('should validate entry restore response', () => {
+ const response = {
+ newMessageInfos: [
+ {
+ type: 11,
+ threadID: '83859',
+ creatorID: '83853',
+ time: 1682603427038,
+ entryID: '93270',
+ date: '2023-04-03',
+ text: 'text',
+ id: '93285',
+ },
+ ],
+ updatesResult: { viewerUpdates: [], userInfos: [] },
+ };
+ expect(restoreEntryResponseValidator.is(response)).toBe(true);
+ expect(
+ restoreEntryResponseValidator.is({
+ ...response,
+ newMessageInfos: undefined,
+ }),
+ ).toBe(false);
+ });
+
+ it('should validate entry delta response', () => {
+ const response = {
+ rawEntryInfos: [
+ {
+ id: '92860',
+ threadID: '85068',
+ text: 'text',
+ year: 2023,
+ month: 4,
+ day: 2,
+ creationTime: 1682082939882,
+ creatorID: '83853',
+ deleted: false,
+ },
+ ],
+ deletedEntryIDs: ['92860'],
+ userInfos: {
+ '123': {
+ id: '123',
+ username: 'username',
+ },
+ },
+ };
+ expect(deltaEntryInfosResultValidator.is(response)).toBe(true);
+ expect(
+ deltaEntryInfosResultValidator.is({
+ ...response,
+ rawEntryInfos: undefined,
+ }),
+ ).toBe(false);
+ });
+});
diff --git a/keyserver/src/responders/search-responders.js b/keyserver/src/responders/search-responders.js
--- a/keyserver/src/responders/search-responders.js
+++ b/keyserver/src/responders/search-responders.js
@@ -1,11 +1,12 @@
// @flow
-import t from 'tcomb';
+import t, { type TInterface } from 'tcomb';
import type {
UserSearchRequest,
UserSearchResult,
} from 'lib/types/search-types.js';
+import { globalAccountUserInfoValidator } from 'lib/types/user-types.js';
import { tShape } from 'lib/utils/validation-utils.js';
import { searchForUsers } from '../search/users.js';
@@ -16,6 +17,11 @@
prefix: t.maybe(t.String),
});
+export const userSearchResultValidator: TInterface<UserSearchResult> =
+ tShape<UserSearchResult>({
+ userInfos: t.list(globalAccountUserInfoValidator),
+ });
+
async function userSearchResponder(
viewer: Viewer,
input: any,
diff --git a/keyserver/src/responders/siwe-nonce-responders.js b/keyserver/src/responders/siwe-nonce-responders.js
--- a/keyserver/src/responders/siwe-nonce-responders.js
+++ b/keyserver/src/responders/siwe-nonce-responders.js
@@ -1,11 +1,16 @@
// @flow
import { generateNonce } from 'siwe';
+import t, { type TInterface } from 'tcomb';
import type { SIWENonceResponse } from 'lib/types/siwe-types.js';
+import { tShape } from 'lib/utils/validation-utils.js';
import { createSIWENonceEntry } from '../creators/siwe-nonce-creator.js';
+export const siweNonceResponseValidator: TInterface<SIWENonceResponse> =
+ tShape<SIWENonceResponse>({ nonce: t.String });
+
async function siweNonceResponder(): Promise<SIWENonceResponse> {
const generatedNonce = generateNonce();
await createSIWENonceEntry(generatedNonce);
diff --git a/lib/types/activity-types.js b/lib/types/activity-types.js
--- a/lib/types/activity-types.js
+++ b/lib/types/activity-types.js
@@ -1,5 +1,9 @@
// @flow
+import t, { type TInterface } from 'tcomb';
+
+import { tID, tShape } from '../utils/validation-utils.js';
+
export type ActivityUpdate = {
+focus: boolean,
+threadID: string,
@@ -13,6 +17,10 @@
export type UpdateActivityResult = {
+unfocusedToUnread: $ReadOnlyArray<string>,
};
+export const updateActivityResultValidator: TInterface<UpdateActivityResult> =
+ tShape<UpdateActivityResult>({
+ unfocusedToUnread: t.list(tID),
+ });
export type ActivityUpdateSuccessPayload = {
+activityUpdates: $ReadOnlyArray<ActivityUpdate>,
@@ -32,6 +40,9 @@
export type SetThreadUnreadStatusResult = {
+resetToUnread: boolean,
};
+export const setThreadUnreadStatusResult: TInterface<SetThreadUnreadStatusResult> =
+ tShape<SetThreadUnreadStatusResult>({ resetToUnread: t.Boolean });
+
export type SetThreadUnreadStatusPayload = {
...SetThreadUnreadStatusResult,
+threadID: string,
diff --git a/lib/types/history-types.js b/lib/types/history-types.js
--- a/lib/types/history-types.js
+++ b/lib/types/history-types.js
@@ -1,5 +1,9 @@
// @flow
+import t, { type TInterface } from 'tcomb';
+
+import { tID, tShape } from '../utils/validation-utils.js';
+
export type HistoryMode = 'day' | 'entry';
export type HistoryRevisionInfo = {
@@ -11,6 +15,16 @@
+deleted: boolean,
+threadID: string,
};
+export const historyRevisionInfoValidator: TInterface<HistoryRevisionInfo> =
+ tShape<HistoryRevisionInfo>({
+ id: tID,
+ entryID: tID,
+ authorID: t.String,
+ text: t.String,
+ lastUpdate: t.Number,
+ deleted: t.Boolean,
+ threadID: tID,
+ });
export type FetchEntryRevisionInfosRequest = {
+id: string,
diff --git a/lib/types/session-types.js b/lib/types/session-types.js
--- a/lib/types/session-types.js
+++ b/lib/types/session-types.js
@@ -1,5 +1,7 @@
// @flow
+import t, { type TInterface } from 'tcomb';
+
import type { LogInActionSource } from './account-types.js';
import type { Shape } from './core.js';
import type { CalendarQuery } from './entry-types.js';
@@ -9,6 +11,7 @@
type CurrentUserInfo,
type LoggedOutUserInfo,
} from './user-types.js';
+import { tShape } from '../utils/validation-utils.js';
export const cookieLifetime = 30 * 24 * 60 * 60 * 1000; // in milliseconds
// Interval the server waits after a state check before starting a new one
@@ -107,3 +110,9 @@
+identityKey: string,
+oneTimeKey?: string,
};
+
+export const sessionPublicKeysValidator: TInterface<SessionPublicKeys> =
+ tShape<SessionPublicKeys>({
+ identityKey: t.String,
+ oneTimeKey: t.maybe(t.String),
+ });
diff --git a/lib/types/update-types.js b/lib/types/update-types.js
--- a/lib/types/update-types.js
+++ b/lib/types/update-types.js
@@ -13,6 +13,7 @@
import { type RawThreadInfo, rawThreadInfoValidator } from './thread-types.js';
import {
type UserInfo,
+ userInfoValidator,
type UserInfos,
type LoggedInUserInfo,
loggedInUserInfoValidator,
@@ -400,6 +401,12 @@
+userInfos: $ReadOnlyArray<UserInfo>,
};
+export const serverCreateUpdatesResponseValidator: TInterface<ServerCreateUpdatesResponse> =
+ tShape<ServerCreateUpdatesResponse>({
+ viewerUpdates: t.list(serverUpdateInfoValidator),
+ userInfos: t.list(userInfoValidator),
+ });
+
export type ClientCreateUpdatesResponse = {
+viewerUpdates: $ReadOnlyArray<ClientUpdateInfo>,
+userInfos: $ReadOnlyArray<UserInfo>,
diff --git a/lib/types/user-types.js b/lib/types/user-types.js
--- a/lib/types/user-types.js
+++ b/lib/types/user-types.js
@@ -25,6 +25,12 @@
+username: string,
+avatar?: ?ClientAvatar,
};
+export const globalAccountUserInfoValidator: TInterface<GlobalAccountUserInfo> =
+ tShape<GlobalAccountUserInfo>({
+ id: t.String,
+ username: t.String,
+ avatar: t.maybe(clientAvatarValidator),
+ });
export type UserInfo = {
+id: string,
@@ -46,6 +52,13 @@
+relationshipStatus?: UserRelationshipStatus,
+avatar?: ?ClientAvatar,
};
+export const accountUserInfoValidator: TInterface<AccountUserInfo> =
+ tShape<AccountUserInfo>({
+ id: t.String,
+ username: t.String,
+ relationshipStatus: t.maybe(userRelationshipStatusValidator),
+ avatar: t.maybe(clientAvatarValidator),
+ });
export type UserStore = {
+userInfos: UserInfos,
diff --git a/lib/utils/validation-utils.js b/lib/utils/validation-utils.js
--- a/lib/utils/validation-utils.js
+++ b/lib/utils/validation-utils.js
@@ -55,7 +55,7 @@
return false;
});
}
-
+const tNull: TIrreducible<null> = t.irreducible('null', x => x === null);
const tDate: TRegex = tRegex(/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/);
const tColor: TRegex = tRegex(validHexColorRegex); // we don't include # char
const tPlatform: TEnums = t.enums.of([
@@ -108,6 +108,7 @@
tShape,
tRegex,
tNumEnum,
+ tNull,
tDate,
tColor,
tPlatform,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Dec 2, 12:20 PM (19 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2607521
Default Alt Text
D7685.id25930.diff (21 KB)
Attached To
Mode
D7685: [keyserver] Introduce responder validators
Attached
Detach File
Event Timeline
Log In to Comment