Add more timestamp fallbacks for call migration
This commit is contained in:
parent
bc74a696f4
commit
eae9e570fc
2 changed files with 104 additions and 10 deletions
|
@ -37,7 +37,7 @@ type GroupCallHistoryDetailsType = {
|
||||||
callMode: CallMode.Group;
|
callMode: CallMode.Group;
|
||||||
creatorUuid: string;
|
creatorUuid: string;
|
||||||
eraId: string;
|
eraId: string;
|
||||||
startedTime: number;
|
startedTime?: number; // Treat this as optional, some calls may be missing it
|
||||||
};
|
};
|
||||||
export type CallHistoryDetailsType =
|
export type CallHistoryDetailsType =
|
||||||
| DirectCallHistoryDetailsType
|
| DirectCallHistoryDetailsType
|
||||||
|
@ -113,7 +113,8 @@ function convertLegacyCallDetails(
|
||||||
ourUuid: string | undefined,
|
ourUuid: string | undefined,
|
||||||
peerId: string,
|
peerId: string,
|
||||||
message: MessageType,
|
message: MessageType,
|
||||||
partialDetails: CallHistoryDetailsFromDiskType
|
partialDetails: CallHistoryDetailsFromDiskType,
|
||||||
|
logger: LoggerType
|
||||||
): CallHistoryDetails {
|
): CallHistoryDetails {
|
||||||
const details = upcastCallHistoryDetailsFromDiskType(partialDetails);
|
const details = upcastCallHistoryDetailsFromDiskType(partialDetails);
|
||||||
const { callMode: mode } = details;
|
const { callMode: mode } = details;
|
||||||
|
@ -127,6 +128,10 @@ function convertLegacyCallDetails(
|
||||||
|
|
||||||
strictAssert(mode != null, 'mode must exist');
|
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) {
|
if (mode === CallMode.Direct) {
|
||||||
// We don't have a callId for older calls, generating a uuid instead
|
// We don't have a callId for older calls, generating a uuid instead
|
||||||
callId = details.callId ?? generateUuid();
|
callId = details.callId ?? generateUuid();
|
||||||
|
@ -141,7 +146,7 @@ function convertLegacyCallDetails(
|
||||||
? DirectCallStatus.Declined
|
? DirectCallStatus.Declined
|
||||||
: DirectCallStatus.Missed;
|
: DirectCallStatus.Missed;
|
||||||
}
|
}
|
||||||
timestamp = details.acceptedTime ?? details.endedTime ?? message.timestamp;
|
timestamp = details.acceptedTime ?? details.endedTime ?? fallbackTimestamp;
|
||||||
} else if (mode === CallMode.Group) {
|
} else if (mode === CallMode.Group) {
|
||||||
callId = Long.fromValue(callIdFromEra(details.eraId)).toString();
|
callId = Long.fromValue(callIdFromEra(details.eraId)).toString();
|
||||||
type = CallType.Group;
|
type = CallType.Group;
|
||||||
|
@ -150,7 +155,7 @@ function convertLegacyCallDetails(
|
||||||
? CallDirection.Outgoing
|
? CallDirection.Outgoing
|
||||||
: CallDirection.Incoming;
|
: CallDirection.Incoming;
|
||||||
status = GroupCallStatus.GenericGroupCall;
|
status = GroupCallStatus.GenericGroupCall;
|
||||||
timestamp = details.startedTime;
|
timestamp = details.startedTime ?? fallbackTimestamp;
|
||||||
ringerId = details.creatorUuid;
|
ringerId = details.creatorUuid;
|
||||||
} else {
|
} else {
|
||||||
throw missingCaseError(mode);
|
throw missingCaseError(mode);
|
||||||
|
@ -167,7 +172,16 @@ function convertLegacyCallDetails(
|
||||||
timestamp,
|
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(
|
export default function updateToSchemaVersion89(
|
||||||
|
@ -265,7 +279,8 @@ export default function updateToSchemaVersion89(
|
||||||
ourUuid,
|
ourUuid,
|
||||||
peerId,
|
peerId,
|
||||||
message,
|
message,
|
||||||
details
|
details,
|
||||||
|
logger
|
||||||
);
|
);
|
||||||
|
|
||||||
const [insertQuery, insertParams] = sql`
|
const [insertQuery, insertParams] = sql`
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('SQL/updateToSchemaVersion89', () => {
|
||||||
callId: string | null;
|
callId: string | null;
|
||||||
noCallMode?: boolean;
|
noCallMode?: boolean;
|
||||||
wasDeclined?: boolean;
|
wasDeclined?: boolean;
|
||||||
|
noTimestamps?: boolean;
|
||||||
}): CallHistoryDetailsFromDiskType {
|
}): CallHistoryDetailsFromDiskType {
|
||||||
return {
|
return {
|
||||||
callId: options.callId ?? undefined,
|
callId: options.callId ?? undefined,
|
||||||
|
@ -56,27 +57,42 @@ describe('SQL/updateToSchemaVersion89', () => {
|
||||||
function getGroupCallHistoryDetails(options: {
|
function getGroupCallHistoryDetails(options: {
|
||||||
eraId: string;
|
eraId: string;
|
||||||
noCallMode?: boolean;
|
noCallMode?: boolean;
|
||||||
|
noTimestamps?: boolean;
|
||||||
}): CallHistoryDetailsFromDiskType {
|
}): CallHistoryDetailsFromDiskType {
|
||||||
return {
|
return {
|
||||||
eraId: options.eraId,
|
eraId: options.eraId,
|
||||||
callMode: options.noCallMode ? undefined : CallMode.Group,
|
callMode: options.noCallMode ? undefined : CallMode.Group,
|
||||||
creatorUuid: generateGuid(),
|
creatorUuid: generateGuid(),
|
||||||
startedTime: Date.now(),
|
startedTime: options.noTimestamps ? undefined : Date.now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Timestamps = Pick<
|
||||||
|
MessageWithCallHistoryDetails,
|
||||||
|
'sent_at' | 'received_at_ms' | 'timestamp'
|
||||||
|
>;
|
||||||
|
|
||||||
function createCallHistoryMessage(options: {
|
function createCallHistoryMessage(options: {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
callHistoryDetails: CallHistoryDetailsFromDiskType;
|
callHistoryDetails: CallHistoryDetailsFromDiskType;
|
||||||
|
timestamps?: Partial<Timestamps>;
|
||||||
}): MessageWithCallHistoryDetails {
|
}): 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 = {
|
const message: MessageWithCallHistoryDetails = {
|
||||||
id: options.messageId,
|
id: options.messageId,
|
||||||
type: 'call-history',
|
type: 'call-history',
|
||||||
conversationId: options.conversationId,
|
conversationId: options.conversationId,
|
||||||
sent_at: Date.now() - 10,
|
received_at: Date.now(),
|
||||||
received_at: Date.now() - 10,
|
...timestamps,
|
||||||
timestamp: Date.now() - 10,
|
|
||||||
callHistoryDetails: options.callHistoryDetails,
|
callHistoryDetails: options.callHistoryDetails,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -300,6 +316,69 @@ describe('SQL/updateToSchemaVersion89', () => {
|
||||||
assert.strictEqual(callHistory[0].peerId, conversation.id);
|
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', () => {
|
describe('clients with schema version 87', () => {
|
||||||
function createCallHistoryTable() {
|
function createCallHistoryTable() {
|
||||||
const [query] = sql`
|
const [query] = sql`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue