Changeset View
Changeset View
Standalone View
Standalone View
keyserver/src/responders/message-responders.js
Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
import { fetchKnownUserInfos } from '../fetchers/user-fetchers.js'; | import { fetchKnownUserInfos } from '../fetchers/user-fetchers.js'; | ||||
import type { Viewer } from '../session/viewer.js'; | import type { Viewer } from '../session/viewer.js'; | ||||
import { | import { | ||||
assignImages, | assignImages, | ||||
assignMessageContainerToMedia, | assignMessageContainerToMedia, | ||||
} from '../updaters/upload-updaters.js'; | } from '../updaters/upload-updaters.js'; | ||||
import { validateInput, validateOutput } from '../utils/validation-utils.js'; | import { validateInput, validateOutput } from '../utils/validation-utils.js'; | ||||
const sendTextMessageRequestInputValidator = tShape({ | const sendTextMessageRequestInputValidator = tShape<SendTextMessageRequest>({ | ||||
threadID: tID, | threadID: tID, | ||||
localID: t.maybe(t.String), | localID: t.maybe(t.String), | ||||
text: t.String, | text: t.String, | ||||
sidebarCreation: t.maybe(t.Boolean), | sidebarCreation: t.maybe(t.Boolean), | ||||
}); | }); | ||||
export const sendMessageResponseValidator: TInterface<SendMessageResponse> = | export const sendMessageResponseValidator: TInterface<SendMessageResponse> = | ||||
tShape<SendMessageResponse>({ newMessageInfo: rawMessageInfoValidator }); | tShape<SendMessageResponse>({ newMessageInfo: rawMessageInfoValidator }); | ||||
async function textMessageCreationResponder( | async function textMessageCreationResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<SendMessageResponse> { | ): Promise<SendMessageResponse> { | ||||
const request: SendTextMessageRequest = input; | const request = await validateInput( | ||||
await validateInput(viewer, sendTextMessageRequestInputValidator, request); | viewer, | ||||
sendTextMessageRequestInputValidator, | |||||
input, | |||||
); | |||||
const { threadID, localID, text: rawText, sidebarCreation } = request; | const { threadID, localID, text: rawText, sidebarCreation } = request; | ||||
const text = trimMessage(rawText); | const text = trimMessage(rawText); | ||||
if (!text) { | if (!text) { | ||||
throw new ServerError('invalid_parameters'); | throw new ServerError('invalid_parameters'); | ||||
} | } | ||||
const hasPermission = await checkThreadPermission( | const hasPermission = await checkThreadPermission( | ||||
Show All 28 Lines | if (sidebarCreation) { | ||||
} | } | ||||
} | } | ||||
const rawMessageInfos = await createMessages(viewer, [messageData]); | const rawMessageInfos = await createMessages(viewer, [messageData]); | ||||
const response = { newMessageInfo: rawMessageInfos[0] }; | const response = { newMessageInfo: rawMessageInfos[0] }; | ||||
return validateOutput(viewer, sendMessageResponseValidator, response); | return validateOutput(viewer, sendMessageResponseValidator, response); | ||||
} | } | ||||
const fetchMessageInfosRequestInputValidator = tShape({ | const fetchMessageInfosRequestInputValidator = tShape<FetchMessageInfosRequest>( | ||||
{ | |||||
cursors: t.dict(tID, t.maybe(tID)), | cursors: t.dict(tID, t.maybe(tID)), | ||||
numberPerThread: t.maybe(t.Number), | numberPerThread: t.maybe(t.Number), | ||||
}); | }, | ||||
); | |||||
export const fetchMessageInfosResponseValidator: TInterface<FetchMessageInfosResponse> = | export const fetchMessageInfosResponseValidator: TInterface<FetchMessageInfosResponse> = | ||||
tShape<FetchMessageInfosResponse>({ | tShape<FetchMessageInfosResponse>({ | ||||
rawMessageInfos: t.list(rawMessageInfoValidator), | rawMessageInfos: t.list(rawMessageInfoValidator), | ||||
truncationStatuses: messageTruncationStatusesValidator, | truncationStatuses: messageTruncationStatusesValidator, | ||||
userInfos: userInfosValidator, | userInfos: userInfosValidator, | ||||
}); | }); | ||||
async function messageFetchResponder( | async function messageFetchResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<FetchMessageInfosResponse> { | ): Promise<FetchMessageInfosResponse> { | ||||
const request: FetchMessageInfosRequest = input; | const request = await validateInput( | ||||
await validateInput(viewer, fetchMessageInfosRequestInputValidator, request); | viewer, | ||||
fetchMessageInfosRequestInputValidator, | |||||
input, | |||||
); | |||||
const response = await fetchMessageInfos( | const response = await fetchMessageInfos( | ||||
viewer, | viewer, | ||||
{ threadCursors: request.cursors }, | { threadCursors: request.cursors }, | ||||
request.numberPerThread ? request.numberPerThread : defaultNumberPerThread, | request.numberPerThread ? request.numberPerThread : defaultNumberPerThread, | ||||
); | ); | ||||
return validateOutput(viewer, fetchMessageInfosResponseValidator, { | return validateOutput(viewer, fetchMessageInfosResponseValidator, { | ||||
...response, | ...response, | ||||
userInfos: {}, | userInfos: {}, | ||||
}); | }); | ||||
} | } | ||||
const sendMultimediaMessageRequestInputValidator = t.union([ | const sendMultimediaMessageRequestInputValidator = | ||||
t.union<SendMultimediaMessageRequest>([ | |||||
// This option is only used for messageTypes.IMAGES | // This option is only used for messageTypes.IMAGES | ||||
tShape({ | tShape({ | ||||
threadID: tID, | threadID: tID, | ||||
localID: t.String, | localID: t.String, | ||||
sidebarCreation: t.maybe(t.Boolean), | sidebarCreation: t.maybe(t.Boolean), | ||||
mediaIDs: t.list(tID), | mediaIDs: t.list(tID), | ||||
}), | }), | ||||
tShape({ | tShape({ | ||||
threadID: tID, | threadID: tID, | ||||
localID: t.String, | localID: t.String, | ||||
sidebarCreation: t.maybe(t.Boolean), | sidebarCreation: t.maybe(t.Boolean), | ||||
mediaMessageContents: t.list(tMediaMessageMedia), | mediaMessageContents: t.list(tMediaMessageMedia), | ||||
}), | }), | ||||
]); | ]); | ||||
async function multimediaMessageCreationResponder( | async function multimediaMessageCreationResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<SendMessageResponse> { | ): Promise<SendMessageResponse> { | ||||
const request: SendMultimediaMessageRequest = input; | const request = await validateInput( | ||||
await validateInput( | |||||
viewer, | viewer, | ||||
sendMultimediaMessageRequestInputValidator, | sendMultimediaMessageRequestInputValidator, | ||||
request, | input, | ||||
); | ); | ||||
if ( | if ( | ||||
(request.mediaIDs && request.mediaIDs.length === 0) || | (request.mediaIDs && request.mediaIDs.length === 0) || | ||||
(request.mediaMessageContents && request.mediaMessageContents.length === 0) | (request.mediaMessageContents && request.mediaMessageContents.length === 0) | ||||
) { | ) { | ||||
throw new ServerError('invalid_parameters'); | throw new ServerError('invalid_parameters'); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | await assignMessageContainerToMedia( | ||||
threadID, | threadID, | ||||
); | ); | ||||
} | } | ||||
const response = { newMessageInfo }; | const response = { newMessageInfo }; | ||||
return validateOutput(viewer, sendMessageResponseValidator, response); | return validateOutput(viewer, sendMessageResponseValidator, response); | ||||
} | } | ||||
const sendReactionMessageRequestInputValidator = tShape({ | const sendReactionMessageRequestInputValidator = | ||||
tShape<SendReactionMessageRequest>({ | |||||
threadID: tID, | threadID: tID, | ||||
localID: t.maybe(t.String), | localID: t.maybe(t.String), | ||||
targetMessageID: tID, | targetMessageID: tID, | ||||
reaction: tRegex(onlyOneEmojiRegex), | reaction: tRegex(onlyOneEmojiRegex), | ||||
action: t.enums.of(['add_reaction', 'remove_reaction']), | action: t.enums.of(['add_reaction', 'remove_reaction']), | ||||
}); | }); | ||||
async function reactionMessageCreationResponder( | async function reactionMessageCreationResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<SendMessageResponse> { | ): Promise<SendMessageResponse> { | ||||
const request: SendReactionMessageRequest = input; | const request = await validateInput( | ||||
await validateInput(viewer, sendReactionMessageRequestInputValidator, input); | viewer, | ||||
sendReactionMessageRequestInputValidator, | |||||
input, | |||||
); | |||||
const { threadID, localID, targetMessageID, reaction, action } = request; | const { threadID, localID, targetMessageID, reaction, action } = request; | ||||
if (!targetMessageID || !reaction) { | if (!targetMessageID || !reaction) { | ||||
throw new ServerError('invalid_parameters'); | throw new ServerError('invalid_parameters'); | ||||
} | } | ||||
const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); | const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | ): Promise<SendMessageResponse> { | ||||
} | } | ||||
const rawMessageInfos = await createMessages(viewer, [messageData]); | const rawMessageInfos = await createMessages(viewer, [messageData]); | ||||
const response = { newMessageInfo: rawMessageInfos[0] }; | const response = { newMessageInfo: rawMessageInfos[0] }; | ||||
return validateOutput(viewer, sendMessageResponseValidator, response); | return validateOutput(viewer, sendMessageResponseValidator, response); | ||||
} | } | ||||
const editMessageRequestInputValidator = tShape({ | const editMessageRequestInputValidator = tShape<SendEditMessageRequest>({ | ||||
targetMessageID: tID, | targetMessageID: tID, | ||||
text: t.String, | text: t.String, | ||||
}); | }); | ||||
export const sendEditMessageResponseValidator: TInterface<SendEditMessageResponse> = | export const sendEditMessageResponseValidator: TInterface<SendEditMessageResponse> = | ||||
tShape<SendEditMessageResponse>({ | tShape<SendEditMessageResponse>({ | ||||
newMessageInfos: t.list(rawMessageInfoValidator), | newMessageInfos: t.list(rawMessageInfoValidator), | ||||
}); | }); | ||||
async function editMessageCreationResponder( | async function editMessageCreationResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<SendEditMessageResponse> { | ): Promise<SendEditMessageResponse> { | ||||
const request: SendEditMessageRequest = input; | const request = await validateInput( | ||||
await validateInput(viewer, editMessageRequestInputValidator, input); | viewer, | ||||
editMessageRequestInputValidator, | |||||
input, | |||||
); | |||||
const { targetMessageID, text: rawText } = request; | const { targetMessageID, text: rawText } = request; | ||||
const text = trimMessage(rawText); | const text = trimMessage(rawText); | ||||
if (!targetMessageID || !text) { | if (!targetMessageID || !text) { | ||||
throw new ServerError('invalid_parameters'); | throw new ServerError('invalid_parameters'); | ||||
} | } | ||||
const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); | const targetMessageInfo = await fetchMessageInfoByID(viewer, targetMessageID); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | ): Promise<SendEditMessageResponse> { | ||||
} | } | ||||
const newMessageInfos = await createMessages(viewer, messagesData); | const newMessageInfos = await createMessages(viewer, messagesData); | ||||
const response = { newMessageInfos }; | const response = { newMessageInfos }; | ||||
return validateOutput(viewer, sendEditMessageResponseValidator, response); | return validateOutput(viewer, sendEditMessageResponseValidator, response); | ||||
} | } | ||||
const fetchPinnedMessagesResponderInputValidator = tShape({ | const fetchPinnedMessagesResponderInputValidator = | ||||
tShape<FetchPinnedMessagesRequest>({ | |||||
threadID: tID, | threadID: tID, | ||||
}); | }); | ||||
export const fetchPinnedMessagesResultValidator: TInterface<FetchPinnedMessagesResult> = | export const fetchPinnedMessagesResultValidator: TInterface<FetchPinnedMessagesResult> = | ||||
tShape<FetchPinnedMessagesResult>({ | tShape<FetchPinnedMessagesResult>({ | ||||
pinnedMessages: t.list(rawMessageInfoValidator), | pinnedMessages: t.list(rawMessageInfoValidator), | ||||
}); | }); | ||||
async function fetchPinnedMessagesResponder( | async function fetchPinnedMessagesResponder( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
input: any, | input: mixed, | ||||
): Promise<FetchPinnedMessagesResult> { | ): Promise<FetchPinnedMessagesResult> { | ||||
const request: FetchPinnedMessagesRequest = input; | const request = await validateInput( | ||||
await validateInput( | |||||
viewer, | viewer, | ||||
fetchPinnedMessagesResponderInputValidator, | fetchPinnedMessagesResponderInputValidator, | ||||
input, | input, | ||||
); | ); | ||||
const response = await fetchPinnedMessageInfos(viewer, request); | const response = await fetchPinnedMessageInfos(viewer, request); | ||||
return validateOutput(viewer, fetchPinnedMessagesResultValidator, response); | return validateOutput(viewer, fetchPinnedMessagesResultValidator, response); | ||||
} | } | ||||
export { | export { | ||||
textMessageCreationResponder, | textMessageCreationResponder, | ||||
messageFetchResponder, | messageFetchResponder, | ||||
multimediaMessageCreationResponder, | multimediaMessageCreationResponder, | ||||
reactionMessageCreationResponder, | reactionMessageCreationResponder, | ||||
editMessageCreationResponder, | editMessageCreationResponder, | ||||
fetchPinnedMessagesResponder, | fetchPinnedMessagesResponder, | ||||
}; | }; |