diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js index 20ea48042..bffe201fd 100644 --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -1,603 +1,605 @@ // @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 { createOrUpdateFarcasterChannelTagResponseValidator } from 'lib/types/validators/farcaster-channel-tag-validators.js'; import { fetchInviteLinksResponseValidator, inviteLinkVerificationResponseValidator, } from 'lib/types/validators/link-validators.js'; +import { messageReportCreationResultValidator } from 'lib/types/validators/message-report-validators.js'; +import { + fetchMessageInfosResponseValidator, + fetchPinnedMessagesResultValidator, + searchMessagesResponseValidator, + sendEditMessageResponseValidator, + sendMessageResponseValidator, +} from 'lib/types/validators/message-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, deleteEntryRequestInputValidator, entryQueryInputValidator, entryRevisionHistoryFetchInputValidator, newEntryQueryInputValidator, restoreEntryRequestInputValidator, saveEntryRequestInputValidator, } from './responders/entry-responders.js'; import { createOrUpdateFarcasterChannelTagResponder, deleteFarcasterChannelTagResponder, createOrUpdateFarcasterChannelTagInputValidator, deleteFarcasterChannelTagInputValidator, } 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, inviteLinkVerificationRequestInputValidator, } 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/message-report-responder.js b/keyserver/src/responders/message-report-responder.js index f739c1ff4..58ba28daa 100644 --- a/keyserver/src/responders/message-report-responder.js +++ b/keyserver/src/responders/message-report-responder.js @@ -1,31 +1,27 @@ // @flow import 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, tID } from 'lib/utils/validation-utils.js'; import createMessageReport from '../creators/message-report-creator.js'; import type { Viewer } from '../session/viewer.js'; export const messageReportCreationRequestInputValidator: TInterface = tShape({ messageID: tID, }); -export const messageReportCreationResultValidator: TInterface = - tShape({ messageInfo: rawMessageInfoValidator }); - async function messageReportCreationResponder( viewer: Viewer, request: MessageReportCreationRequest, ): Promise { const rawMessageInfos = await createMessageReport(viewer, request); return { messageInfo: rawMessageInfos[0] }; } export { messageReportCreationResponder }; diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js index 00f6c20bd..bafa54cb1 100644 --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -1,452 +1,423 @@ // @flow import invariant from 'invariant'; import t, { type TInterface, type TUnion } from 'tcomb'; import { onlyOneEmojiRegex } from 'lib/shared/emojis.js'; import { createMediaMessageData, trimMessage, } from 'lib/shared/message-utils.js'; import { relationshipBlockedInEitherDirection } from 'lib/shared/relationship-utils.js'; import type { Media } from 'lib/types/media-types.js'; import { messageTypes } from 'lib/types/message-types-enum.js'; import { type SendTextMessageRequest, type SendMultimediaMessageRequest, type SendReactionMessageRequest, type SendEditMessageRequest, type FetchMessageInfosResponse, type FetchMessageInfosRequest, defaultNumberPerThread, type SendMessageResponse, type SendEditMessageResponse, type FetchPinnedMessagesRequest, type FetchPinnedMessagesResult, - messageTruncationStatusesValidator, - rawMessageInfoValidator, type SearchMessagesResponse, type SearchMessagesRequest, } from 'lib/types/message-types.js'; import type { EditMessageData } from 'lib/types/messages/edit.js'; import type { ReactionMessageData } from 'lib/types/messages/reaction.js'; import type { TextMessageData } from 'lib/types/messages/text.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; -import { userInfosValidator } from 'lib/types/user-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { values } from 'lib/utils/objects.js'; import { tRegex, tShape, tMediaMessageMedia, tID, } from 'lib/utils/validation-utils.js'; import createMessages from '../creators/message-creator.js'; import { fetchMessageInfos, fetchMessageInfoForLocalID, fetchMessageInfoByID, fetchThreadMessagesCount, fetchPinnedMessageInfos, searchMessagesInSingleChat, } from '../fetchers/message-fetchers.js'; import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js'; import { fetchImages, fetchMediaFromMediaMessageContent, } from '../fetchers/upload-fetchers.js'; import { fetchKnownUserInfos } from '../fetchers/user-fetchers.js'; import type { Viewer } from '../session/viewer.js'; import { assignImages, assignMessageContainerToMedia, } from '../updaters/upload-updaters.js'; export const sendTextMessageRequestInputValidator: TInterface = tShape({ threadID: tID, localID: t.maybe(t.String), text: t.String, sidebarCreation: t.maybe(t.Boolean), }); -export const sendMessageResponseValidator: TInterface = - tShape({ newMessageInfo: rawMessageInfoValidator }); - async function textMessageCreationResponder( viewer: Viewer, request: SendTextMessageRequest, ): Promise { const { threadID, localID, text: rawText, sidebarCreation } = request; const text = trimMessage(rawText); if (!text) { throw new ServerError('invalid_parameters'); } const hasPermission = await checkThreadPermission( viewer, threadID, threadPermissions.VOICED, ); if (!hasPermission) { throw new ServerError('invalid_parameters'); } let messageData: TextMessageData = { type: messageTypes.TEXT, threadID, creatorID: viewer.id, time: Date.now(), text, }; if (localID) { messageData = { ...messageData, localID }; } if (sidebarCreation) { const numMessages = await fetchThreadMessagesCount(threadID); if (numMessages === 2) { // sidebarCreation is set below to prevent double notifs from a sidebar // creation. We expect precisely two messages to appear before a // sidebarCreation: a SIDEBAR_SOURCE and a CREATE_SIDEBAR. If two users // attempt to create a sidebar at the same time, then both clients will // attempt to set sidebarCreation here, but we only want to suppress // notifs for the client that won the race. messageData = { ...messageData, sidebarCreation }; } } const rawMessageInfos = await createMessages(viewer, [messageData]); return { newMessageInfo: rawMessageInfos[0] }; } export const fetchMessageInfosRequestInputValidator: TInterface = tShape({ cursors: t.dict(tID, t.maybe(tID)), numberPerThread: t.maybe(t.Number), }); -export const fetchMessageInfosResponseValidator: TInterface = - tShape({ - rawMessageInfos: t.list(rawMessageInfoValidator), - truncationStatuses: messageTruncationStatusesValidator, - userInfos: userInfosValidator, - }); - async function messageFetchResponder( viewer: Viewer, request: FetchMessageInfosRequest, ): Promise { const response = await fetchMessageInfos( viewer, { threadCursors: request.cursors }, request.numberPerThread ? request.numberPerThread : defaultNumberPerThread, ); return { ...response, userInfos: {}, }; } export const sendMultimediaMessageRequestInputValidator: TUnion = t.union([ // This option is only used for messageTypes.IMAGES tShape({ threadID: tID, localID: t.String, sidebarCreation: t.maybe(t.Boolean), mediaIDs: t.list(tID), }), tShape({ threadID: tID, localID: t.String, sidebarCreation: t.maybe(t.Boolean), mediaMessageContents: t.list(tMediaMessageMedia), }), ]); async function multimediaMessageCreationResponder( viewer: Viewer, request: SendMultimediaMessageRequest, ): Promise { if ( (request.mediaIDs && request.mediaIDs.length === 0) || (request.mediaMessageContents && request.mediaMessageContents.length === 0) ) { throw new ServerError('invalid_parameters'); } const { threadID, localID, sidebarCreation } = request; const hasPermission = await checkThreadPermission( viewer, threadID, threadPermissions.VOICED, ); if (!hasPermission) { throw new ServerError('invalid_parameters'); } const existingMessageInfoPromise = fetchMessageInfoForLocalID( viewer, localID, ); const mediaPromise: Promise<$ReadOnlyArray> = request.mediaIDs ? fetchImages(viewer, request.mediaIDs) : fetchMediaFromMediaMessageContent(viewer, request.mediaMessageContents); const [existingMessageInfo, media] = await Promise.all([ existingMessageInfoPromise, mediaPromise, ]); if (media.length === 0 && !existingMessageInfo) { throw new ServerError('invalid_parameters'); } // We use the MULTIMEDIA type for encrypted photos const containsEncryptedMedia = media.some( m => m.type === 'encrypted_photo' || m.type === 'encrypted_video', ); const messageData = createMediaMessageData( { localID, threadID, creatorID: viewer.id, media, sidebarCreation, }, { forceMultimediaMessageType: containsEncryptedMedia }, ); const [newMessageInfo] = await createMessages(viewer, [messageData]); const { id } = newMessageInfo; invariant( id !== null && id !== undefined, 'serverID should be set in createMessages result', ); if (request.mediaIDs) { await assignImages(viewer, request.mediaIDs, id, threadID); } else { await assignMessageContainerToMedia( viewer, request.mediaMessageContents, id, threadID, ); } return { newMessageInfo }; } export const sendReactionMessageRequestInputValidator: TInterface = tShape({ threadID: tID, localID: t.maybe(t.String), targetMessageID: tID, reaction: tRegex(onlyOneEmojiRegex), action: t.enums.of(['add_reaction', 'remove_reaction']), }); async function reactionMessageCreationResponder( viewer: Viewer, request: SendReactionMessageRequest, ): Promise { const { threadID, localID, targetMessageID, reaction, action } = request; if (!targetMessageID || !reaction) { throw new ServerError('invalid_parameters'); } const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); if (!targetMessageInfo || !targetMessageInfo.id) { throw new ServerError('invalid_parameters'); } const [serverThreadInfos, hasPermission, targetMessageUserInfos] = await Promise.all([ fetchServerThreadInfos({ threadID }), checkThreadPermission( viewer, threadID, threadPermissions.REACT_TO_MESSAGE, ), fetchKnownUserInfos(viewer, [targetMessageInfo.creatorID]), ]); const targetMessageThreadInfo = serverThreadInfos.threadInfos[threadID]; if (targetMessageThreadInfo.sourceMessageID === targetMessageID) { throw new ServerError('invalid_parameters'); } const targetMessageCreator = targetMessageUserInfos[targetMessageInfo.creatorID]; const targetMessageCreatorRelationship = targetMessageCreator?.relationshipStatus; const creatorRelationshipHasBlock = targetMessageCreatorRelationship && relationshipBlockedInEitherDirection(targetMessageCreatorRelationship); if (!hasPermission || creatorRelationshipHasBlock) { throw new ServerError('invalid_parameters'); } let messageData: ReactionMessageData = { type: messageTypes.REACTION, threadID, creatorID: viewer.id, time: Date.now(), targetMessageID, reaction, action, }; if (localID) { messageData = { ...messageData, localID }; } const rawMessageInfos = await createMessages(viewer, [messageData]); return { newMessageInfo: rawMessageInfos[0] }; } export const editMessageRequestInputValidator: TInterface = tShape({ targetMessageID: tID, text: t.String, }); -export const sendEditMessageResponseValidator: TInterface = - tShape({ - newMessageInfos: t.list(rawMessageInfoValidator), - }); - async function editMessageCreationResponder( viewer: Viewer, request: SendEditMessageRequest, ): Promise { const { targetMessageID, text: rawText } = request; const text = trimMessage(rawText); if (!targetMessageID || !text) { throw new ServerError('invalid_parameters'); } const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); if (!targetMessageInfo || !targetMessageInfo.id) { throw new ServerError('invalid_parameters'); } if (targetMessageInfo.type !== messageTypes.TEXT) { throw new ServerError('invalid_parameters'); } const { threadID } = targetMessageInfo; const [serverThreadInfos, hasPermission, rawSidebarThreadInfos] = await Promise.all([ fetchServerThreadInfos({ threadID }), checkThreadPermission(viewer, threadID, threadPermissions.EDIT_MESSAGE), fetchServerThreadInfos({ parentThreadID: threadID, sourceMessageID: targetMessageID, }), ]); const targetMessageThreadInfo = serverThreadInfos.threadInfos[threadID]; if (targetMessageThreadInfo.sourceMessageID === targetMessageID) { // We are editing first message of the sidebar // If client wants to do that it sends id of the sourceMessage instead throw new ServerError('invalid_parameters'); } if (!hasPermission) { throw new ServerError('invalid_parameters'); } if (targetMessageInfo.creatorID !== viewer.id) { throw new ServerError('invalid_parameters'); } const time = Date.now(); const messagesData = []; let messageData: EditMessageData = { type: messageTypes.EDIT_MESSAGE, threadID, creatorID: viewer.id, time, targetMessageID, text, }; messagesData.push(messageData); const sidebarThreadValues = values(rawSidebarThreadInfos.threadInfos); for (const sidebarThreadValue of sidebarThreadValues) { if (sidebarThreadValue && sidebarThreadValue.id) { messageData = { type: messageTypes.EDIT_MESSAGE, threadID: sidebarThreadValue.id, creatorID: viewer.id, time, targetMessageID, text: text, }; messagesData.push(messageData); } } const newMessageInfos = await createMessages(viewer, messagesData); return { newMessageInfos }; } export const fetchPinnedMessagesResponderInputValidator: TInterface = tShape({ threadID: tID, }); -export const fetchPinnedMessagesResultValidator: TInterface = - tShape({ - pinnedMessages: t.list(rawMessageInfoValidator), - }); - async function fetchPinnedMessagesResponder( viewer: Viewer, request: FetchPinnedMessagesRequest, ): Promise { return await fetchPinnedMessageInfos(viewer, request); } export const searchMessagesResponderInputValidator: TInterface = tShape({ query: t.String, threadID: tID, cursor: t.maybe(tID), }); -export const searchMessagesResponseValidator: TInterface = - tShape({ - messages: t.list(rawMessageInfoValidator), - endReached: t.Boolean, - }); - async function searchMessagesResponder( viewer: Viewer, request: SearchMessagesRequest, ): Promise { return await searchMessagesInSingleChat( request.query, request.threadID, viewer, request.cursor, ); } export { textMessageCreationResponder, messageFetchResponder, multimediaMessageCreationResponder, reactionMessageCreationResponder, editMessageCreationResponder, fetchPinnedMessagesResponder, searchMessagesResponder, }; diff --git a/keyserver/src/responders/responder-validators.test.js b/keyserver/src/responders/responder-validators.test.js index ccd20e330..5f8c896bc 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 'lib/types/validators/entry-validators.js'; import { inviteLinkVerificationResponseValidator, fetchInviteLinksResponseValidator, } from 'lib/types/validators/link-validators.js'; - -import { messageReportCreationResultValidator } from './message-report-responder.js'; +import { messageReportCreationResultValidator } from 'lib/types/validators/message-report-validators.js'; import { fetchMessageInfosResponseValidator, fetchPinnedMessagesResultValidator, sendEditMessageResponseValidator, sendMessageResponseValidator, -} from './message-responders.js'; +} from 'lib/types/validators/message-validators.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/message-report-validators.js b/lib/types/validators/message-report-validators.js new file mode 100644 index 000000000..3bed10463 --- /dev/null +++ b/lib/types/validators/message-report-validators.js @@ -0,0 +1,10 @@ +// @flow + +import type { TInterface } from 'tcomb'; + +import { tShape } from '../../utils/validation-utils.js'; +import { type MessageReportCreationResult } from '../message-report-types.js'; +import { rawMessageInfoValidator } from '../message-types.js'; + +export const messageReportCreationResultValidator: TInterface = + tShape({ messageInfo: rawMessageInfoValidator }); diff --git a/lib/types/validators/message-validators.js b/lib/types/validators/message-validators.js new file mode 100644 index 000000000..484c6fd7b --- /dev/null +++ b/lib/types/validators/message-validators.js @@ -0,0 +1,43 @@ +// @flow + +import t, { type TInterface } from 'tcomb'; + +import { tShape } from '../../utils/validation-utils.js'; +import { + type FetchMessageInfosResponse, + type SendMessageResponse, + type SendEditMessageResponse, + type FetchPinnedMessagesResult, + type SearchMessagesResponse, +} from '../message-types.js'; +import { + rawMessageInfoValidator, + messageTruncationStatusesValidator, +} from '../message-types.js'; +import { userInfosValidator } from '../user-types.js'; + +export const sendMessageResponseValidator: TInterface = + tShape({ newMessageInfo: rawMessageInfoValidator }); + +export const sendEditMessageResponseValidator: TInterface = + tShape({ + newMessageInfos: t.list(rawMessageInfoValidator), + }); + +export const fetchMessageInfosResponseValidator: TInterface = + tShape({ + rawMessageInfos: t.list(rawMessageInfoValidator), + truncationStatuses: messageTruncationStatusesValidator, + userInfos: userInfosValidator, + }); + +export const fetchPinnedMessagesResultValidator: TInterface = + tShape({ + pinnedMessages: t.list(rawMessageInfoValidator), + }); + +export const searchMessagesResponseValidator: TInterface = + tShape({ + messages: t.list(rawMessageInfoValidator), + endReached: t.Boolean, + });