Use minimal replacement class for MessageModel
This commit is contained in:
parent
6b00cf756e
commit
f846678b90
95 changed files with 3919 additions and 4457 deletions
159
ts/test-node/util/messageFailures.ts
Normal file
159
ts/test-node/util/messageFailures.ts
Normal file
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { mapValues, pick } from 'lodash';
|
||||
|
||||
import type { CustomError } from '../../textsecure/Types';
|
||||
|
||||
import type { MessageAttributesType } from '../../model-types';
|
||||
import * as log from '../../logging/log';
|
||||
import * as Errors from '../../types/errors';
|
||||
import {
|
||||
getChangesForPropAtTimestamp,
|
||||
getPropForTimestamp,
|
||||
} from '../../util/editHelpers';
|
||||
import {
|
||||
isSent,
|
||||
SendActionType,
|
||||
sendStateReducer,
|
||||
someRecipientSendStatus,
|
||||
} from '../../messages/MessageSendState';
|
||||
import { isStory } from '../../messages/helpers';
|
||||
import {
|
||||
notificationService,
|
||||
NotificationType,
|
||||
} from '../../services/notifications';
|
||||
import type { MessageModel } from '../../models/messages';
|
||||
|
||||
export async function saveErrorsOnMessage(
|
||||
message: MessageModel,
|
||||
providedErrors: Error | Array<Error>,
|
||||
options: { skipSave?: boolean } = {}
|
||||
): Promise<void> {
|
||||
const { skipSave } = options;
|
||||
|
||||
let errors: Array<CustomError>;
|
||||
|
||||
if (!(providedErrors instanceof Array)) {
|
||||
errors = [providedErrors];
|
||||
} else {
|
||||
errors = providedErrors;
|
||||
}
|
||||
|
||||
errors.forEach(e => {
|
||||
log.error('Message.saveErrors:', Errors.toLogFormat(e));
|
||||
});
|
||||
errors = errors.map(e => {
|
||||
// Note: in our environment, instanceof can be scary, so we have a backup check
|
||||
// (Node.js vs Browser context).
|
||||
// We check instanceof second because typescript believes that anything that comes
|
||||
// through here must be an instance of Error, so e is 'never' after that check.
|
||||
if ((e.message && e.stack) || e instanceof Error) {
|
||||
return pick(
|
||||
e,
|
||||
'name',
|
||||
'message',
|
||||
'code',
|
||||
'number',
|
||||
'identifier',
|
||||
'retryAfter',
|
||||
'data',
|
||||
'reason'
|
||||
) as Required<Error>;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
|
||||
message.set({
|
||||
errors: errors.concat(message.get('errors') || []),
|
||||
});
|
||||
|
||||
if (!skipSave) {
|
||||
await window.MessageCache.saveMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function isReplayableError(e: Error): boolean {
|
||||
return (
|
||||
e.name === 'MessageError' ||
|
||||
e.name === 'OutgoingMessageError' ||
|
||||
e.name === 'SendMessageNetworkError' ||
|
||||
e.name === 'SendMessageChallengeError' ||
|
||||
e.name === 'OutgoingIdentityKeyError'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change any Pending send state to Failed. Note that this will not mark successful
|
||||
* sends failed.
|
||||
*/
|
||||
export function markFailed(
|
||||
message: MessageModel,
|
||||
editMessageTimestamp?: number
|
||||
): void {
|
||||
const now = Date.now();
|
||||
|
||||
const targetTimestamp = editMessageTimestamp || message.get('timestamp');
|
||||
const sendStateByConversationId = getPropForTimestamp({
|
||||
log,
|
||||
message: message.attributes,
|
||||
prop: 'sendStateByConversationId',
|
||||
targetTimestamp,
|
||||
});
|
||||
|
||||
const newSendStateByConversationId = mapValues(
|
||||
sendStateByConversationId || {},
|
||||
sendState =>
|
||||
sendStateReducer(sendState, {
|
||||
type: SendActionType.Failed,
|
||||
updatedAt: now,
|
||||
})
|
||||
);
|
||||
|
||||
const updates = getChangesForPropAtTimestamp({
|
||||
log,
|
||||
message: message.attributes,
|
||||
prop: 'sendStateByConversationId',
|
||||
targetTimestamp,
|
||||
value: newSendStateByConversationId,
|
||||
});
|
||||
if (updates) {
|
||||
message.set(updates);
|
||||
}
|
||||
|
||||
notifyStorySendFailed(message);
|
||||
}
|
||||
|
||||
export function notifyStorySendFailed(message: MessageModel): void {
|
||||
if (!isStory(message.attributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { conversationId, id, timestamp } = message.attributes;
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
|
||||
notificationService.add({
|
||||
conversationId,
|
||||
storyId: id,
|
||||
messageId: id,
|
||||
senderTitle: conversation?.getTitle() ?? window.i18n('icu:Stories__mine'),
|
||||
message: hasSuccessfulDelivery(message.attributes)
|
||||
? window.i18n('icu:Stories__failed-send--partial')
|
||||
: window.i18n('icu:Stories__failed-send--full'),
|
||||
isExpiringMessage: false,
|
||||
sentAt: timestamp,
|
||||
type: NotificationType.Message,
|
||||
});
|
||||
}
|
||||
|
||||
function hasSuccessfulDelivery(message: MessageAttributesType): boolean {
|
||||
const { sendStateByConversationId } = message;
|
||||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationIdOrThrow();
|
||||
|
||||
return someRecipientSendStatus(
|
||||
sendStateByConversationId ?? {},
|
||||
ourConversationId,
|
||||
isSent
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue