Add more timestamp fallbacks for call migration

This commit is contained in:
Jamie Kyle 2023-08-29 16:31:45 -07:00 committed by GitHub
parent bc74a696f4
commit eae9e570fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 104 additions and 10 deletions

View file

@ -37,7 +37,7 @@ type GroupCallHistoryDetailsType = {
callMode: CallMode.Group;
creatorUuid: string;
eraId: string;
startedTime: number;
startedTime?: number; // Treat this as optional, some calls may be missing it
};
export type CallHistoryDetailsType =
| DirectCallHistoryDetailsType
@ -113,7 +113,8 @@ function convertLegacyCallDetails(
ourUuid: string | undefined,
peerId: string,
message: MessageType,
partialDetails: CallHistoryDetailsFromDiskType
partialDetails: CallHistoryDetailsFromDiskType,
logger: LoggerType
): CallHistoryDetails {
const details = upcastCallHistoryDetailsFromDiskType(partialDetails);
const { callMode: mode } = details;
@ -127,6 +128,10 @@ function convertLegacyCallDetails(
strictAssert(mode != null, 'mode must exist');
// If we cannot find any timestamp on the message, we'll use 0
const fallbackTimestamp =
message.timestamp ?? message.sent_at ?? message.received_at_ms ?? 0;
if (mode === CallMode.Direct) {
// We don't have a callId for older calls, generating a uuid instead
callId = details.callId ?? generateUuid();
@ -141,7 +146,7 @@ function convertLegacyCallDetails(
? DirectCallStatus.Declined
: DirectCallStatus.Missed;
}
timestamp = details.acceptedTime ?? details.endedTime ?? message.timestamp;
timestamp = details.acceptedTime ?? details.endedTime ?? fallbackTimestamp;
} else if (mode === CallMode.Group) {
callId = Long.fromValue(callIdFromEra(details.eraId)).toString();
type = CallType.Group;
@ -150,7 +155,7 @@ function convertLegacyCallDetails(
? CallDirection.Outgoing
: CallDirection.Incoming;
status = GroupCallStatus.GenericGroupCall;
timestamp = details.startedTime;
timestamp = details.startedTime ?? fallbackTimestamp;
ringerId = details.creatorUuid;
} else {
throw missingCaseError(mode);
@ -167,7 +172,16 @@ function convertLegacyCallDetails(
timestamp,
};
return callHistoryDetailsSchema.parse(callHistory);
const result = callHistoryDetailsSchema.safeParse(callHistory);
if (result.success) {
return result.data;
}
logger.error(
`convertLegacyCallDetails: Could not convert ${mode} call`,
result.error.toString()
);
throw new Error(`Failed to convert legacy ${mode} call details`);
}
export default function updateToSchemaVersion89(
@ -265,7 +279,8 @@ export default function updateToSchemaVersion89(
ourUuid,
peerId,
message,
details
details,
logger
);
const [insertQuery, insertParams] = sql`

View file

@ -41,6 +41,7 @@ describe('SQL/updateToSchemaVersion89', () => {
callId: string | null;
noCallMode?: boolean;
wasDeclined?: boolean;
noTimestamps?: boolean;
}): CallHistoryDetailsFromDiskType {
return {
callId: options.callId ?? undefined,
@ -56,27 +57,42 @@ describe('SQL/updateToSchemaVersion89', () => {
function getGroupCallHistoryDetails(options: {
eraId: string;
noCallMode?: boolean;
noTimestamps?: boolean;
}): CallHistoryDetailsFromDiskType {
return {
eraId: options.eraId,
callMode: options.noCallMode ? undefined : CallMode.Group,
creatorUuid: generateGuid(),
startedTime: Date.now(),
startedTime: options.noTimestamps ? undefined : Date.now(),
};
}
type Timestamps = Pick<
MessageWithCallHistoryDetails,
'sent_at' | 'received_at_ms' | 'timestamp'
>;
function createCallHistoryMessage(options: {
messageId: string;
conversationId: string;
callHistoryDetails: CallHistoryDetailsFromDiskType;
timestamps?: Partial<Timestamps>;
}): MessageWithCallHistoryDetails {
// @ts-expect-error Purposefully violating the type to test the migration
const timestamps: Timestamps = options.timestamps
? options.timestamps
: {
sent_at: Date.now(),
received_at_ms: Date.now(),
timestamp: Date.now(),
};
const message: MessageWithCallHistoryDetails = {
id: options.messageId,
type: 'call-history',
conversationId: options.conversationId,
sent_at: Date.now() - 10,
received_at: Date.now() - 10,
timestamp: Date.now() - 10,
received_at: Date.now(),
...timestamps,
callHistoryDetails: options.callHistoryDetails,
};
@ -300,6 +316,69 @@ describe('SQL/updateToSchemaVersion89', () => {
assert.strictEqual(callHistory[0].peerId, conversation.id);
});
it('migrates call-history messages with no timestamp', () => {
updateToVersion(db, 88);
const conversation = createConversation('private', Date.now());
const timestampCases = {
noTimestamps: {
sent_at: undefined,
received_at_ms: undefined,
timestamp: undefined,
},
onlyTimestamp: {
sent_at: undefined,
received_at_ms: undefined,
timestamp: 1,
},
onlySentAt: {
sent_at: 2,
received_at_ms: undefined,
timestamp: undefined,
},
onlyReceivedAt: {
sent_at: undefined,
received_at_ms: 3,
timestamp: undefined,
},
} satisfies Record<string, Partial<Timestamps>>;
for (const [id, timestamps] of Object.entries(timestampCases)) {
createCallHistoryMessage({
messageId: generateGuid(),
conversationId: conversation.id,
callHistoryDetails: getDirectCallHistoryDetails({
callId: id,
noTimestamps: true,
}),
timestamps,
});
createCallHistoryMessage({
messageId: generateGuid(),
conversationId: conversation.id,
callHistoryDetails: getGroupCallHistoryDetails({
eraId: id,
noTimestamps: true,
}),
timestamps,
});
}
updateToVersion(db, 89);
const callHistory = getAllCallHistory();
assert.strictEqual(callHistory.length, 8);
assert.strictEqual(callHistory[0].timestamp, 0);
assert.strictEqual(callHistory[1].timestamp, 0);
assert.strictEqual(callHistory[2].timestamp, 1);
assert.strictEqual(callHistory[3].timestamp, 1);
assert.strictEqual(callHistory[4].timestamp, 2);
assert.strictEqual(callHistory[5].timestamp, 2);
assert.strictEqual(callHistory[6].timestamp, 3);
assert.strictEqual(callHistory[7].timestamp, 3);
});
describe('clients with schema version 87', () => {
function createCallHistoryTable() {
const [query] = sql`