diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js index 5abf3a488..150c56dff 100644 --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -1,599 +1,601 @@ // @flow import t from 'tcomb'; import { baseLegalPolicies } from 'lib/facts/policies.js'; import { setThreadUnreadStatusResultValidator, updateActivityResultValidator, } from 'lib/types/activity-types.js'; import type { Endpoint } from 'lib/types/endpoints.js'; import { inviteLinkValidator } from 'lib/types/link-types.js'; import { uploadMultimediaResultValidator } from 'lib/types/media-types.js'; import { getOlmSessionInitializationDataResponseValidator } from 'lib/types/request-types.js'; +import { + saveEntryResponseValidator, + deleteEntryResponseValidator, + fetchEntryInfosResponseValidator, + fetchEntryRevisionInfosResultValidator, + deltaEntryInfosResultValidator, + restoreEntryResponseValidator, +} from 'lib/types/validators/entry-validators.js'; import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js'; import { updateActivityResponder, threadSetUnreadStatusResponder, setThreadUnreadStatusValidator, updateActivityResponderInputValidator, } from './responders/activity-responders.js'; import { deviceTokenUpdateResponder, deviceTokenUpdateRequestInputValidator, } from './responders/device-responders.js'; import { entryFetchResponder, entryRevisionFetchResponder, entryCreationResponder, entryUpdateResponder, entryDeletionResponder, entryRestorationResponder, calendarQueryUpdateResponder, createEntryRequestInputValidator, - saveEntryResponseValidator, deleteEntryRequestInputValidator, - deleteEntryResponseValidator, entryQueryInputValidator, entryRevisionHistoryFetchInputValidator, - fetchEntryInfosResponseValidator, - fetchEntryRevisionInfosResultValidator, - deltaEntryInfosResultValidator, newEntryQueryInputValidator, restoreEntryRequestInputValidator, - restoreEntryResponseValidator, saveEntryRequestInputValidator, } from './responders/entry-responders.js'; import { createOrUpdateFarcasterChannelTagResponder, deleteFarcasterChannelTagResponder, createOrUpdateFarcasterChannelTagInputValidator, deleteFarcasterChannelTagInputValidator, createOrUpdateFarcasterChannelTagResponseValidator, } from './responders/farcaster-channel-tag-responders.js'; import type { JSONResponder } from './responders/handlers.js'; import { createJSONResponder } from './responders/handlers.js'; import { getOlmSessionInitializationDataResponder } from './responders/keys-responders.js'; import { createOrUpdatePublicLinkResponder, disableInviteLinkResponder, fetchPrimaryInviteLinksResponder, inviteLinkVerificationResponder, createOrUpdatePublicLinkInputValidator, disableInviteLinkInputValidator, fetchInviteLinksResponseValidator, inviteLinkVerificationRequestInputValidator, inviteLinkVerificationResponseValidator, } from './responders/link-responders.js'; import { messageReportCreationResponder, messageReportCreationRequestInputValidator, messageReportCreationResultValidator, } from './responders/message-report-responder.js'; import { textMessageCreationResponder, messageFetchResponder, multimediaMessageCreationResponder, reactionMessageCreationResponder, editMessageCreationResponder, fetchPinnedMessagesResponder, searchMessagesResponder, sendMessageResponseValidator, sendMultimediaMessageRequestInputValidator, sendReactionMessageRequestInputValidator, editMessageRequestInputValidator, sendEditMessageResponseValidator, sendTextMessageRequestInputValidator, fetchMessageInfosRequestInputValidator, fetchMessageInfosResponseValidator, fetchPinnedMessagesResponderInputValidator, fetchPinnedMessagesResultValidator, searchMessagesResponderInputValidator, searchMessagesResponseValidator, } from './responders/message-responders.js'; import { getInitialReduxStateResponder, initialReduxStateRequestValidator, initialReduxStateValidator, } from './responders/redux-state-responders.js'; import { updateRelationshipsResponder, relationshipErrorsValidator, updateRelationshipInputValidator, } from './responders/relationship-responders.js'; import { reportCreationResponder, reportMultiCreationResponder, errorReportFetchInfosResponder, reportCreationRequestInputValidator, reportCreationResponseValidator, fetchErrorReportInfosRequestInputValidator, fetchErrorReportInfosResponseValidator, reportMultiCreationRequestInputValidator, } from './responders/report-responders.js'; import { userSearchResponder, exactUserSearchResponder, exactUserSearchRequestInputValidator, exactUserSearchResultValidator, userSearchRequestInputValidator, userSearchResultValidator, } from './responders/search-responders.js'; import { siweNonceResponder, siweNonceResponseValidator, } from './responders/siwe-nonce-responders.js'; import { threadDeletionResponder, roleUpdateResponder, memberRemovalResponder, threadLeaveResponder, threadUpdateResponder, threadCreationResponder, threadFetchMediaResponder, threadJoinResponder, toggleMessagePinResponder, roleModificationResponder, roleDeletionResponder, leaveThreadResultValidator, newThreadRequestInputValidator, newThreadResponseValidator, threadDeletionRequestInputValidator, joinThreadRequestInputValidator, leaveThreadRequestInputValidator, threadFetchMediaRequestInputValidator, threadFetchMediaResultValidator, threadJoinResultValidator, changeThreadSettingsResultValidator, removeMembersRequestInputValidator, roleChangeRequestInputValidator, toggleMessagePinRequestInputValidator, toggleMessagePinResultValidator, updateThreadRequestInputValidator, roleDeletionRequestInputValidator, roleDeletionResultValidator, roleModificationRequestInputValidator, roleModificationResultValidator, } from './responders/thread-responders.js'; import { keyserverAuthRequestInputValidator, keyserverAuthResponder, userSubscriptionUpdateResponder, passwordUpdateResponder, sendVerificationEmailResponder, sendPasswordResetEmailResponder, logOutResponder, accountDeletionResponder, accountCreationResponder, logInResponder, siweAuthResponder, oldPasswordUpdateResponder, updateUserSettingsResponder, policyAcknowledgmentResponder, updateUserAvatarResponder, registerRequestInputValidator, registerResponseValidator, logOutResponseValidator, logInRequestInputValidator, logInResponseValidator, policyAcknowledgmentRequestInputValidator, accountUpdateInputValidator, resetPasswordRequestInputValidator, siweAuthRequestInputValidator, subscriptionUpdateRequestInputValidator, subscriptionUpdateResponseValidator, updatePasswordRequestInputValidator, updateUserAvatarResponderValidator, updateUserSettingsInputValidator, claimUsernameResponder, claimUsernameResponseValidator, } from './responders/user-responders.js'; import { codeVerificationResponder, codeVerificationRequestInputValidator, } from './responders/verification-responders.js'; import { versionResponder, versionResponseValidator, } from './responders/version-responders.js'; import { uploadMediaMetadataResponder, uploadDeletionResponder, UploadDeletionRequestInputValidator, uploadMediaMetadataInputValidator, } from './uploads/uploads.js'; const ignoredArgumentValidator = t.irreducible( 'Ignored argument', () => true, ); const jsonEndpoints: { [id: Endpoint]: JSONResponder } = { create_account: createJSONResponder( accountCreationResponder, registerRequestInputValidator, registerResponseValidator, [], ), create_entry: createJSONResponder( entryCreationResponder, createEntryRequestInputValidator, saveEntryResponseValidator, baseLegalPolicies, ), create_error_report: createJSONResponder( reportCreationResponder, reportCreationRequestInputValidator, reportCreationResponseValidator, [], ), create_message_report: createJSONResponder( messageReportCreationResponder, messageReportCreationRequestInputValidator, messageReportCreationResultValidator, baseLegalPolicies, ), create_multimedia_message: createJSONResponder( multimediaMessageCreationResponder, sendMultimediaMessageRequestInputValidator, sendMessageResponseValidator, baseLegalPolicies, ), create_or_update_public_link: createJSONResponder( createOrUpdatePublicLinkResponder, createOrUpdatePublicLinkInputValidator, inviteLinkValidator, baseLegalPolicies, ), create_reaction_message: createJSONResponder( reactionMessageCreationResponder, sendReactionMessageRequestInputValidator, sendMessageResponseValidator, baseLegalPolicies, ), disable_invite_link: createJSONResponder( disableInviteLinkResponder, disableInviteLinkInputValidator, t.Nil, baseLegalPolicies, ), edit_message: createJSONResponder( editMessageCreationResponder, editMessageRequestInputValidator, sendEditMessageResponseValidator, baseLegalPolicies, ), create_report: createJSONResponder( reportCreationResponder, reportCreationRequestInputValidator, reportCreationResponseValidator, [], ), create_reports: createJSONResponder( reportMultiCreationResponder, reportMultiCreationRequestInputValidator, t.Nil, [], ), create_text_message: createJSONResponder( textMessageCreationResponder, sendTextMessageRequestInputValidator, sendMessageResponseValidator, baseLegalPolicies, ), create_thread: createJSONResponder( threadCreationResponder, newThreadRequestInputValidator, newThreadResponseValidator, baseLegalPolicies, ), delete_account: createJSONResponder( accountDeletionResponder, ignoredArgumentValidator, logOutResponseValidator, [], ), delete_entry: createJSONResponder( entryDeletionResponder, deleteEntryRequestInputValidator, deleteEntryResponseValidator, baseLegalPolicies, ), delete_community_role: createJSONResponder( roleDeletionResponder, roleDeletionRequestInputValidator, roleDeletionResultValidator, baseLegalPolicies, ), delete_thread: createJSONResponder( threadDeletionResponder, threadDeletionRequestInputValidator, leaveThreadResultValidator, baseLegalPolicies, ), delete_upload: createJSONResponder( uploadDeletionResponder, UploadDeletionRequestInputValidator, t.Nil, baseLegalPolicies, ), exact_search_user: createJSONResponder( exactUserSearchResponder, exactUserSearchRequestInputValidator, exactUserSearchResultValidator, [], ), fetch_entries: createJSONResponder( entryFetchResponder, entryQueryInputValidator, fetchEntryInfosResponseValidator, baseLegalPolicies, ), fetch_entry_revisions: createJSONResponder( entryRevisionFetchResponder, entryRevisionHistoryFetchInputValidator, fetchEntryRevisionInfosResultValidator, baseLegalPolicies, ), fetch_error_report_infos: createJSONResponder( errorReportFetchInfosResponder, fetchErrorReportInfosRequestInputValidator, fetchErrorReportInfosResponseValidator, baseLegalPolicies, ), fetch_messages: createJSONResponder( messageFetchResponder, fetchMessageInfosRequestInputValidator, fetchMessageInfosResponseValidator, baseLegalPolicies, ), fetch_pinned_messages: createJSONResponder( fetchPinnedMessagesResponder, fetchPinnedMessagesResponderInputValidator, fetchPinnedMessagesResultValidator, baseLegalPolicies, ), fetch_primary_invite_links: createJSONResponder( fetchPrimaryInviteLinksResponder, ignoredArgumentValidator, fetchInviteLinksResponseValidator, baseLegalPolicies, ), fetch_thread_media: createJSONResponder( threadFetchMediaResponder, threadFetchMediaRequestInputValidator, threadFetchMediaResultValidator, baseLegalPolicies, ), get_initial_redux_state: createJSONResponder( getInitialReduxStateResponder, initialReduxStateRequestValidator, initialReduxStateValidator, [], ), join_thread: createJSONResponder( threadJoinResponder, joinThreadRequestInputValidator, threadJoinResultValidator, baseLegalPolicies, ), keyserver_auth: createJSONResponder( keyserverAuthResponder, keyserverAuthRequestInputValidator, logInResponseValidator, [], ), leave_thread: createJSONResponder( threadLeaveResponder, leaveThreadRequestInputValidator, leaveThreadResultValidator, baseLegalPolicies, ), log_in: createJSONResponder( logInResponder, logInRequestInputValidator, logInResponseValidator, [], ), log_out: createJSONResponder( logOutResponder, ignoredArgumentValidator, logOutResponseValidator, [], ), modify_community_role: createJSONResponder( roleModificationResponder, roleModificationRequestInputValidator, roleModificationResultValidator, baseLegalPolicies, ), policy_acknowledgment: createJSONResponder( policyAcknowledgmentResponder, policyAcknowledgmentRequestInputValidator, t.Nil, [], ), remove_members: createJSONResponder( memberRemovalResponder, removeMembersRequestInputValidator, changeThreadSettingsResultValidator, baseLegalPolicies, ), restore_entry: createJSONResponder( entryRestorationResponder, restoreEntryRequestInputValidator, restoreEntryResponseValidator, baseLegalPolicies, ), search_messages: createJSONResponder( searchMessagesResponder, searchMessagesResponderInputValidator, searchMessagesResponseValidator, baseLegalPolicies, ), search_users: createJSONResponder( userSearchResponder, userSearchRequestInputValidator, userSearchResultValidator, baseLegalPolicies, ), send_password_reset_email: createJSONResponder( sendPasswordResetEmailResponder, resetPasswordRequestInputValidator, t.Nil, [], ), send_verification_email: createJSONResponder( sendVerificationEmailResponder, ignoredArgumentValidator, t.Nil, [], ), set_thread_unread_status: createJSONResponder( threadSetUnreadStatusResponder, setThreadUnreadStatusValidator, setThreadUnreadStatusResultValidator, baseLegalPolicies, ), toggle_message_pin: createJSONResponder( toggleMessagePinResponder, toggleMessagePinRequestInputValidator, toggleMessagePinResultValidator, baseLegalPolicies, ), update_account: createJSONResponder( passwordUpdateResponder, accountUpdateInputValidator, t.Nil, baseLegalPolicies, ), update_activity: createJSONResponder( updateActivityResponder, updateActivityResponderInputValidator, updateActivityResultValidator, baseLegalPolicies, ), update_calendar_query: createJSONResponder( calendarQueryUpdateResponder, newEntryQueryInputValidator, deltaEntryInfosResultValidator, baseLegalPolicies, ), update_user_settings: createJSONResponder( updateUserSettingsResponder, updateUserSettingsInputValidator, t.Nil, baseLegalPolicies, ), update_device_token: createJSONResponder( deviceTokenUpdateResponder, deviceTokenUpdateRequestInputValidator, t.Nil, [], ), update_entry: createJSONResponder( entryUpdateResponder, saveEntryRequestInputValidator, saveEntryResponseValidator, baseLegalPolicies, ), update_password: createJSONResponder( oldPasswordUpdateResponder, updatePasswordRequestInputValidator, logInResponseValidator, baseLegalPolicies, ), update_relationships: createJSONResponder( updateRelationshipsResponder, updateRelationshipInputValidator, relationshipErrorsValidator, baseLegalPolicies, ), update_role: createJSONResponder( roleUpdateResponder, roleChangeRequestInputValidator, changeThreadSettingsResultValidator, baseLegalPolicies, ), update_thread: createJSONResponder( threadUpdateResponder, updateThreadRequestInputValidator, changeThreadSettingsResultValidator, baseLegalPolicies, ), update_user_subscription: createJSONResponder( userSubscriptionUpdateResponder, subscriptionUpdateRequestInputValidator, subscriptionUpdateResponseValidator, baseLegalPolicies, ), verify_code: createJSONResponder( codeVerificationResponder, codeVerificationRequestInputValidator, t.Nil, baseLegalPolicies, ), verify_invite_link: createJSONResponder( inviteLinkVerificationResponder, inviteLinkVerificationRequestInputValidator, inviteLinkVerificationResponseValidator, baseLegalPolicies, ), siwe_nonce: createJSONResponder( siweNonceResponder, ignoredArgumentValidator, siweNonceResponseValidator, [], ), siwe_auth: createJSONResponder( siweAuthResponder, siweAuthRequestInputValidator, logInResponseValidator, [], ), claim_username: createJSONResponder( claimUsernameResponder, ignoredArgumentValidator, claimUsernameResponseValidator, [], ), update_user_avatar: createJSONResponder( updateUserAvatarResponder, updateUserAvatarRequestValidator, updateUserAvatarResponderValidator, baseLegalPolicies, ), upload_media_metadata: createJSONResponder( uploadMediaMetadataResponder, uploadMediaMetadataInputValidator, uploadMultimediaResultValidator, baseLegalPolicies, ), get_olm_session_initialization_data: createJSONResponder( getOlmSessionInitializationDataResponder, ignoredArgumentValidator, getOlmSessionInitializationDataResponseValidator, [], ), version: createJSONResponder( versionResponder, ignoredArgumentValidator, versionResponseValidator, [], ), create_or_update_farcaster_channel_tag: createJSONResponder( createOrUpdateFarcasterChannelTagResponder, createOrUpdateFarcasterChannelTagInputValidator, createOrUpdateFarcasterChannelTagResponseValidator, baseLegalPolicies, ), delete_farcaster_channel_tag: createJSONResponder( deleteFarcasterChannelTagResponder, deleteFarcasterChannelTagInputValidator, t.Nil, baseLegalPolicies, ), }; export { jsonEndpoints }; diff --git a/keyserver/src/responders/entry-responders.js b/keyserver/src/responders/entry-responders.js index a625cf37b..832e1f48a 100644 --- a/keyserver/src/responders/entry-responders.js +++ b/keyserver/src/responders/entry-responders.js @@ -1,300 +1,257 @@ // @flow import t from 'tcomb'; import type { TInterface } from 'tcomb'; import { filteredThreadIDs } from 'lib/selectors/calendar-filter-selectors.js'; 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, 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, tID } from 'lib/utils/validation-utils.js'; import createEntry from '../creators/entry-creator.js'; import { deleteEntry, restoreEntry } from '../deleters/entry-deleters.js'; import { fetchEntryInfos, fetchEntryRevisionInfo, fetchEntriesForSession, } from '../fetchers/entry-fetchers.js'; import { verifyThreadIDs } from '../fetchers/thread-fetchers.js'; import type { Viewer } from '../session/viewer.js'; import { updateEntry, compareNewCalendarQuery, } from '../updaters/entry-updaters.js'; import { commitSessionUpdate } from '../updaters/session-updaters.js'; type EntryQueryInput = { +startDate: string, +endDate: string, +navID?: ?string, +includeDeleted?: ?boolean, +filters?: ?$ReadOnlyArray, }; const entryQueryInputValidator: TInterface = tShape({ navID: t.maybe(t.String), startDate: tDate, endDate: tDate, includeDeleted: t.maybe(t.Boolean), filters: t.maybe( t.list( t.union([ tShape({ type: tString(calendarThreadFilterTypes.NOT_DELETED), }), tShape({ type: tString(calendarThreadFilterTypes.THREAD_LIST), threadIDs: t.list(tID), }), ]), ), ), }); const newEntryQueryInputValidator: TInterface = tShape({ startDate: tDate, endDate: tDate, filters: t.list( t.union([ tShape({ type: tString(calendarThreadFilterTypes.NOT_DELETED), }), tShape({ type: tString(calendarThreadFilterTypes.THREAD_LIST), threadIDs: t.list(tID), }), ]), ), }); function normalizeCalendarQuery(input: any): CalendarQuery { if (input.filters) { return { startDate: input.startDate, endDate: input.endDate, filters: input.filters, }; } const filters = []; if (!input.includeDeleted) { filters.push({ type: calendarThreadFilterTypes.NOT_DELETED }); } if (input.navID !== 'home') { filters.push({ type: calendarThreadFilterTypes.THREAD_LIST, threadIDs: [input.navID], }); } return { startDate: input.startDate, endDate: input.endDate, filters, }; } async function verifyCalendarQueryThreadIDs( request: CalendarQuery, ): Promise { const threadIDsToFilterTo = filteredThreadIDs(request.filters); if (threadIDsToFilterTo && threadIDsToFilterTo.size > 0) { const verifiedThreadIDs = await verifyThreadIDs([...threadIDsToFilterTo]); if (verifiedThreadIDs.length !== threadIDsToFilterTo.size) { throw new ServerError('invalid_parameters'); } } } -export const fetchEntryInfosResponseValidator: TInterface = - tShape({ - rawEntryInfos: t.list(rawEntryInfoValidator), - userInfos: t.dict(t.String, accountUserInfoValidator), - }); - async function entryFetchResponder( viewer: Viewer, inputQuery: EntryQueryInput, ): Promise { const request = normalizeCalendarQuery(inputQuery); await verifyCalendarQueryThreadIDs(request); const response = await fetchEntryInfos(viewer, [request]); return { ...response, userInfos: {}, }; } export const entryRevisionHistoryFetchInputValidator: TInterface = tShape({ id: tID, }); -export const fetchEntryRevisionInfosResultValidator: TInterface = - tShape({ - result: t.list(historyRevisionInfoValidator), - }); - async function entryRevisionFetchResponder( viewer: Viewer, request: FetchEntryRevisionInfosRequest, ): Promise { const entryHistory = await fetchEntryRevisionInfo(viewer, request.id); return { result: entryHistory }; } export const createEntryRequestInputValidator: TInterface = tShape({ text: t.String, sessionID: t.maybe(t.String), timestamp: t.Number, date: tDate, threadID: tID, localID: t.maybe(t.String), calendarQuery: t.maybe(newEntryQueryInputValidator), }); -export const saveEntryResponseValidator: TInterface = - tShape({ - entryID: tID, - newMessageInfos: t.list(rawMessageInfoValidator), - updatesResult: serverCreateUpdatesResponseValidator, - }); - async function entryCreationResponder( viewer: Viewer, request: CreateEntryRequest, ): Promise { return await createEntry(viewer, request); } export const saveEntryRequestInputValidator: TInterface = tShape({ entryID: tID, text: t.String, prevText: t.String, sessionID: t.maybe(t.String), timestamp: t.Number, calendarQuery: t.maybe(newEntryQueryInputValidator), }); async function entryUpdateResponder( viewer: Viewer, request: SaveEntryRequest, ): Promise { return await updateEntry(viewer, request); } export const deleteEntryRequestInputValidator: TInterface = tShape({ entryID: tID, prevText: t.String, sessionID: t.maybe(t.String), timestamp: t.Number, calendarQuery: t.maybe(newEntryQueryInputValidator), }); -export const deleteEntryResponseValidator: TInterface = - tShape({ - newMessageInfos: t.list(rawMessageInfoValidator), - threadID: tID, - updatesResult: serverCreateUpdatesResponseValidator, - }); - async function entryDeletionResponder( viewer: Viewer, request: DeleteEntryRequest, ): Promise { return await deleteEntry(viewer, request); } export const restoreEntryRequestInputValidator: TInterface = tShape({ entryID: tID, sessionID: t.maybe(t.String), timestamp: t.Number, calendarQuery: t.maybe(newEntryQueryInputValidator), }); -export const restoreEntryResponseValidator: TInterface = - tShape({ - newMessageInfos: t.list(rawMessageInfoValidator), - updatesResult: serverCreateUpdatesResponseValidator, - }); - async function entryRestorationResponder( viewer: Viewer, request: RestoreEntryRequest, ): Promise { return await restoreEntry(viewer, request); } -export const deltaEntryInfosResultValidator: TInterface = - tShape({ - rawEntryInfos: t.list(rawEntryInfoValidator), - deletedEntryIDs: t.list(tID), - userInfos: t.list(accountUserInfoValidator), - }); - async function calendarQueryUpdateResponder( viewer: Viewer, request: CalendarQuery, ): Promise { await verifyCalendarQueryThreadIDs(request); if (!viewer.loggedIn) { throw new ServerError('not_logged_in'); } const { difference, oldCalendarQuery, sessionUpdate } = compareNewCalendarQuery(viewer, request); const [response] = await Promise.all([ fetchEntriesForSession(viewer, difference, oldCalendarQuery), commitSessionUpdate(viewer, sessionUpdate), ]); return { rawEntryInfos: response.rawEntryInfos, deletedEntryIDs: response.deletedEntryIDs, // Old clients expect userInfos object userInfos: [], }; } export { entryQueryInputValidator, newEntryQueryInputValidator, normalizeCalendarQuery, verifyCalendarQueryThreadIDs, entryFetchResponder, entryRevisionFetchResponder, entryCreationResponder, entryUpdateResponder, entryDeletionResponder, entryRestorationResponder, calendarQueryUpdateResponder, }; diff --git a/keyserver/src/responders/responder-validators.test.js b/keyserver/src/responders/responder-validators.test.js index f8a8df9ad..71561b205 100644 --- a/keyserver/src/responders/responder-validators.test.js +++ b/keyserver/src/responders/responder-validators.test.js @@ -1,953 +1,953 @@ // @flow import { setThreadUnreadStatusResultValidator, updateActivityResultValidator, } from 'lib/types/activity-types.js'; - import { fetchEntryInfosResponseValidator, fetchEntryRevisionInfosResultValidator, saveEntryResponseValidator, deleteEntryResponseValidator, deltaEntryInfosResultValidator, restoreEntryResponseValidator, -} from './entry-responders.js'; +} from 'lib/types/validators/entry-validators.js'; + import { inviteLinkVerificationResponseValidator, fetchInviteLinksResponseValidator, } from './link-responders.js'; import { messageReportCreationResultValidator } from './message-report-responder.js'; import { fetchMessageInfosResponseValidator, fetchPinnedMessagesResultValidator, sendEditMessageResponseValidator, sendMessageResponseValidator, } from './message-responders.js'; import { relationshipErrorsValidator } from './relationship-responders.js'; import { reportCreationResponseValidator } from './report-responders.js'; import { userSearchResultValidator } from './search-responders.js'; import { siweNonceResponseValidator } from './siwe-nonce-responders.js'; import { changeThreadSettingsResultValidator, leaveThreadResultValidator, newThreadResponseValidator, threadFetchMediaResultValidator, threadJoinResultValidator, toggleMessagePinResultValidator, roleChangeRequestInputValidator, } from './thread-responders.js'; import { logInResponseValidator, registerResponseValidator, logOutResponseValidator, } from './user-responders.js'; describe('user responder validators', () => { it('should validate logout response', () => { const response = { currentUserInfo: { anonymous: true } }; expect(logOutResponseValidator.is(response)).toBe(true); response.currentUserInfo.anonymous = false; expect(logOutResponseValidator.is(response)).toBe(false); }); it('should validate register response', () => { const response = { id: '93079', rawMessageInfos: [ { type: 1, threadID: '93095', creatorID: '93079', time: 1682086407469, initialThreadState: { type: 6, name: null, parentThreadID: '1', color: '648caa', memberIDs: ['256', '93079'], }, id: '93110', }, { type: 0, threadID: '93095', creatorID: '256', time: 1682086407575, text: 'welcome to Comm!', id: '93113', }, ], currentUserInfo: { id: '93079', username: 'user' }, cookieChange: { threadInfos: { '1': { id: '1', type: 12, name: 'GENESIS', description: 'desc', color: 'c85000', creationTime: 1672934346213, parentThreadID: null, members: [ { id: '256', role: '83796', 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' }, }, isSender: false, }, ], roles: { '83795': { id: '83795', name: 'Members', permissions: { know_of: true, visible: true, descendant_open_know_of: true, descendant_open_visible: true, descendant_opentoplevel_join_thread: true, }, isDefault: true, }, }, currentUser: { role: '83795', permissions: { know_of: { value: true, source: '1' }, visible: { value: true, source: '1' }, voiced: { value: false, source: null }, edit_entries: { value: false, source: null }, edit_thread: { value: false, source: null }, edit_thread_description: { value: false, source: null }, edit_thread_color: { value: false, source: null }, delete_thread: { value: false, source: null }, create_subthreads: { value: false, source: null }, create_sidebars: { value: false, source: null }, 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: false, source: null }, edit_message: { value: false, source: null }, }, subscription: { home: true, pushNotifs: true }, unread: true, }, repliesCount: 0, containingThreadID: null, community: null, }, }, userInfos: [ { id: '5', username: 'commbot' }, { id: '256', username: 'ashoat' }, { id: '93079', username: 'temp_user7' }, ], }, }; expect(registerResponseValidator.is(response)).toBe(true); const cookieChange: any = response.cookieChange; cookieChange.userInfos = undefined; expect(registerResponseValidator.is(response)).toBe(false); }); it('should validate login response', () => { const response = { currentUserInfo: { id: '93079', username: 'temp_user7' }, rawMessageInfos: [ { type: 0, id: '93115', threadID: '93094', time: 1682086407577, creatorID: '5', text: 'This is your private chat, where you can set', }, { type: 1, id: '93111', threadID: '93094', time: 1682086407467, creatorID: '93079', initialThreadState: { type: 7, name: 'temp_user7', parentThreadID: '1', color: '575757', memberIDs: ['93079'], }, }, ], truncationStatuses: { '93094': 'exhaustive', '93095': 'exhaustive' }, serverTime: 1682086579416, userInfos: [ { id: '5', username: 'commbot' }, { id: '256', username: 'ashoat' }, { id: '93079', username: 'temp_user7' }, ], cookieChange: { threadInfos: { '1': { id: '1', type: 12, name: 'GENESIS', description: 'This is the first community on Comm. In the future it will', color: 'c85000', creationTime: 1672934346213, parentThreadID: null, members: [ { id: '256', role: '83796', 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' }, }, isSender: false, }, { id: '93079', role: '83795', permissions: { know_of: { value: true, source: '1' }, visible: { value: true, source: '1' }, voiced: { value: false, source: null }, edit_entries: { value: false, source: null }, edit_thread: { value: false, source: null }, edit_thread_description: { value: false, source: null }, edit_thread_color: { value: false, source: null }, delete_thread: { value: false, source: null }, create_subthreads: { value: false, source: null }, create_sidebars: { value: false, source: null }, 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: false, source: null }, edit_message: { value: false, source: null }, }, isSender: false, }, ], roles: { '83795': { id: '83795', name: 'Members', permissions: { know_of: true, visible: true, descendant_open_know_of: true, descendant_open_visible: true, descendant_opentoplevel_join_thread: true, }, isDefault: true, }, '83796': { id: '83796', name: 'Admins', 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_subthreads: true, create_sidebars: true, add_members: true, delete_thread: true, remove_members: true, change_role: true, descendant_know_of: true, descendant_visible: true, descendant_toplevel_join_thread: true, child_join_thread: true, descendant_voiced: true, descendant_edit_entries: true, descendant_edit_thread: true, descendant_edit_thread_color: true, descendant_edit_thread_description: true, descendant_toplevel_create_subthreads: true, descendant_toplevel_create_sidebars: true, descendant_add_members: true, descendant_delete_thread: true, descendant_edit_permissions: true, descendant_remove_members: true, descendant_change_role: true, }, isDefault: false, }, }, currentUser: { role: '83795', permissions: { know_of: { value: true, source: '1' }, visible: { value: true, source: '1' }, voiced: { value: false, source: null }, edit_entries: { value: false, source: null }, edit_thread: { value: false, source: null }, edit_thread_description: { value: false, source: null }, edit_thread_color: { value: false, source: null }, delete_thread: { value: false, source: null }, create_subthreads: { value: false, source: null }, create_sidebars: { value: false, source: null }, 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: false, source: null }, edit_message: { value: false, source: null }, }, subscription: { home: true, pushNotifs: true }, unread: true, }, repliesCount: 0, containingThreadID: null, community: null, }, }, userInfos: [], }, rawEntryInfos: [], }; expect(logInResponseValidator.is(response)).toBe(true); expect( logInResponseValidator.is({ ...response, currentUserInfo: undefined }), ).toBe(false); }); }); describe('search responder', () => { it('should validate search response', () => { const response: any = { 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: any = { 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(setThreadUnreadStatusResultValidator.is(response)).toBe(true); expect( setThreadUnreadStatusResultValidator.is({ ...response, unread: false }), ).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: [ { id: '123', username: 'username', }, ], }; expect(deltaEntryInfosResultValidator.is(response)).toBe(true); expect( deltaEntryInfosResultValidator.is({ ...response, rawEntryInfos: undefined, }), ).toBe(false); }); }); describe('thread responders', () => { it('should validate change thread settings response', () => { const response = { updatesResult: { newUpdates: [ { type: 1, id: '93601', time: 1682759546258, threadInfo: { id: '92796', type: 6, name: '', description: '', color: 'b8753d', creationTime: 1682076700918, parentThreadID: '1', members: [], roles: {}, currentUser: { role: '85172', permissions: {}, subscription: { home: true, pushNotifs: true, }, unread: false, }, repliesCount: 0, containingThreadID: '1', community: '1', pinnedCount: 0, }, }, ], }, newMessageInfos: [ { type: 4, threadID: '92796', creatorID: '83928', time: 1682759546275, field: 'color', value: 'b8753d', id: '93602', }, ], }; expect(changeThreadSettingsResultValidator.is(response)).toBe(true); expect( changeThreadSettingsResultValidator.is({ ...response, newMessageInfos: undefined, }), ).toBe(false); }); it('should validate leave thread response', () => { const response = { updatesResult: { newUpdates: [ { type: 3, id: '93595', time: 1682759498811, threadID: '93561' }, ], }, }; expect(leaveThreadResultValidator.is(response)).toBe(true); expect( leaveThreadResultValidator.is({ ...response, updatedResult: undefined, }), ).toBe(false); }); it('should validate new thread response', () => { const response = { newThreadID: '93619', updatesResult: { newUpdates: [ { type: 4, id: '93621', time: 1682759805331, threadInfo: { id: '93619', type: 5, name: 'a', description: '', color: 'b8753d', creationTime: 1682759805298, parentThreadID: '92796', members: [], roles: {}, currentUser: { role: '85172', permissions: {}, subscription: { home: true, pushNotifs: true, }, unread: false, }, repliesCount: 0, containingThreadID: '92796', community: '1', sourceMessageID: '93614', pinnedCount: 0, }, rawMessageInfos: [], truncationStatus: 'exhaustive', rawEntryInfos: [], }, ], }, userInfos: { '256': { id: '256', username: 'ashoat' }, '83928': { id: '83928', username: 'temp_user3' }, }, newMessageInfos: [], }; expect(newThreadResponseValidator.is(response)).toBe(true); expect( newThreadResponseValidator.is({ ...response, newMessageInfos: {}, }), ).toBe(false); }); it('should validate thread join response', () => { const response = { rawMessageInfos: [ { type: 8, threadID: '93619', creatorID: '83928', time: 1682759915935, id: '93640', }, ], truncationStatuses: {}, userInfos: { '256': { id: '256', username: 'ashoat' }, '83928': { id: '83928', username: 'temp_user3' }, }, updatesResult: { newUpdates: [], }, }; expect(threadJoinResultValidator.is(response)).toBe(true); expect( threadJoinResultValidator.is({ ...response, updatesResult: [], }), ).toBe(false); }); it('should validate thread fetch media response', () => { const response = { media: [ { type: 'photo', id: '93642', uri: 'http://0.0.0.0:3000/comm/upload/93642/1e0d7a5262952e3b', dimensions: { width: 220, height: 220 }, }, ], }; expect(threadFetchMediaResultValidator.is(response)).toBe(true); expect( threadFetchMediaResultValidator.is({ ...response, media: undefined }), ).toBe(false); }); it('should validate toggle message pin response', () => { const response = { threadID: '123', newMessageInfos: [] }; expect(toggleMessagePinResultValidator.is(response)).toBe(true); expect( toggleMessagePinResultValidator.is({ ...response, threadID: undefined }), ).toBe(false); }); it('should validate role change request input', () => { const input = { threadID: '123', memberIDs: [], role: '1', }; expect(roleChangeRequestInputValidator.is(input)).toBe(true); expect(roleChangeRequestInputValidator.is({ ...input, role: '2|1' })).toBe( true, ); expect(roleChangeRequestInputValidator.is({ ...input, role: '-1' })).toBe( false, ); expect(roleChangeRequestInputValidator.is({ ...input, role: '2|-1' })).toBe( false, ); }); }); describe('message responders', () => { it('should validate send message response', () => { const response = { newMessageInfo: { type: 0, threadID: '93619', creatorID: '83928', time: 1682761023640, text: 'a', localID: 'local3', id: '93649', }, }; expect(sendMessageResponseValidator.is(response)).toBe(true); expect( sendMessageResponseValidator.is({ ...response, newMEssageInfos: undefined, }), ).toBe(false); }); it('should validate fetch message infos response', () => { const response = { rawMessageInfos: [ { type: 0, id: '83954', threadID: '83938', time: 1673561155110, creatorID: '256', text: 'welcome to Comm!', }, ], truncationStatuses: { '83938': 'exhaustive' }, userInfos: { '256': { id: '256', username: 'ashoat' }, '83928': { id: '83928', username: 'temp_user3' }, }, }; expect(fetchMessageInfosResponseValidator.is(response)).toBe(true); expect( fetchMessageInfosResponseValidator.is({ ...response, userInfos: undefined, }), ).toBe(false); }); it('should validate send edit message response', () => { const response = { newMessageInfos: [ { type: 0, id: '83954', threadID: '83938', time: 1673561155110, creatorID: '256', text: 'welcome to Comm!', }, ], }; expect(sendEditMessageResponseValidator.is(response)).toBe(true); expect( sendEditMessageResponseValidator.is({ ...response, newMessageInfos: undefined, }), ).toBe(false); }); it('should validate fetch pinned message response', () => { const response = { pinnedMessages: [ { type: 0, id: '83954', threadID: '83938', time: 1673561155110, creatorID: '256', text: 'welcome to Comm!', }, ], }; expect(fetchPinnedMessagesResultValidator.is(response)).toBe(true); expect( fetchPinnedMessagesResultValidator.is({ ...response, pinnedMessages: undefined, }), ).toBe(false); }); }); describe('report responders', () => { it('should validate report creation response', () => { const response = { id: '123' }; expect(reportCreationResponseValidator.is(response)).toBe(true); expect(reportCreationResponseValidator.is({})).toBe(false); }); }); describe('link responders', () => { it('should validate invite link verification response', () => { const response = { status: 'already_joined', community: { name: 'name', id: '123', }, }; expect(inviteLinkVerificationResponseValidator.is(response)).toBe(true); expect(inviteLinkVerificationResponseValidator.is({})).toBe(false); }); it('should validate invite link verification response', () => { const response = { links: [ { name: 'name', primary: true, role: '123', communityID: '123', expirationTime: 123, limitOfUses: 123, numberOfUses: 123, }, ], }; expect(fetchInviteLinksResponseValidator.is(response)).toBe(true); expect(fetchInviteLinksResponseValidator.is({ links: {} })).toBe(false); }); }); diff --git a/lib/types/validators/entry-validators.js b/lib/types/validators/entry-validators.js new file mode 100644 index 000000000..3fa9adf0f --- /dev/null +++ b/lib/types/validators/entry-validators.js @@ -0,0 +1,59 @@ +// @flow + +import t from 'tcomb'; +import type { TInterface } from 'tcomb'; + +import { tShape, tID } from '../../utils/validation-utils.js'; +import { + type DeleteEntryResponse, + type RestoreEntryResponse, + type FetchEntryInfosResponse, + type DeltaEntryInfosResult, + type SaveEntryResponse, + rawEntryInfoValidator, +} from '../entry-types.js'; +import { + type FetchEntryRevisionInfosResult, + historyRevisionInfoValidator, +} from '../history-types.js'; +import { rawMessageInfoValidator } from '../message-types.js'; +import { serverCreateUpdatesResponseValidator } from '../update-types.js'; +import { accountUserInfoValidator } from '../user-types.js'; + +export const fetchEntryInfosResponseValidator: TInterface = + tShape({ + rawEntryInfos: t.list(rawEntryInfoValidator), + userInfos: t.dict(t.String, accountUserInfoValidator), + }); + +export const fetchEntryRevisionInfosResultValidator: TInterface = + tShape({ + result: t.list(historyRevisionInfoValidator), + }); + +export const saveEntryResponseValidator: TInterface = + tShape({ + entryID: tID, + newMessageInfos: t.list(rawMessageInfoValidator), + updatesResult: serverCreateUpdatesResponseValidator, + }); + +export const deleteEntryResponseValidator: TInterface = + tShape({ + newMessageInfos: t.list(rawMessageInfoValidator), + threadID: tID, + updatesResult: serverCreateUpdatesResponseValidator, + }); + +export const restoreEntryResponseValidator: TInterface = + tShape({ + newMessageInfos: t.list(rawMessageInfoValidator), + updatesResult: serverCreateUpdatesResponseValidator, + }); + +export const deltaEntryInfosResultValidator: TInterface = + tShape({ + rawEntryInfos: t.list(rawEntryInfoValidator), + deletedEntryIDs: t.list(tID), + userInfos: t.list(accountUserInfoValidator), + });