From 4ab6e5d7c34997512793dd58ed8516ce1d72e5a4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 28 Aug 2024 19:15:04 -0700 Subject: [PATCH] Properly normalize very old chat session data (#227030) Fix microsoft/vscode-copilot#7729 --- .../contrib/chat/common/chatModel.ts | 26 ++++++++++ .../chat/test/common/chatModel.test.ts | 51 ++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 14a94c046d7..b9578baed08 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -616,6 +616,8 @@ export type ISerializableChatDataIn = ISerializableChatData1 | ISerializableChat * TODO- ChatModel#_deserialize and reviveSerializedAgent also still do some normalization and maybe that should be done in here too. */ export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISerializableChatData { + normalizeOldFields(raw); + if (!('version' in raw)) { return { version: 3, @@ -636,6 +638,30 @@ export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISe return raw; } +function normalizeOldFields(raw: ISerializableChatDataIn): void { + // Fill in fields that very old chat data may be missing + if (!raw.sessionId) { + raw.sessionId = generateUuid(); + } + + if (!raw.creationDate) { + raw.creationDate = getLastYearDate(); + } + + if ('version' in raw && (raw.version === 2 || raw.version === 3)) { + if (!raw.lastMessageDate) { + // A bug led to not porting creationDate properly, and that was copied to lastMessageDate, so fix that up if missing. + raw.lastMessageDate = getLastYearDate(); + } + } +} + +function getLastYearDate(): number { + const lastYearDate = new Date(); + lastYearDate.setFullYear(lastYearDate.getFullYear() - 1); + return lastYearDate.getTime(); +} + export function isExportableSessionData(obj: unknown): obj is IExportableChatData { const data = obj as IExportableChatData; return typeof data === 'object' && diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 4be9380fe47..c37b05e8a89 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -17,7 +17,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { ChatModel, ISerializableChatData1, ISerializableChatData2, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ChatModel, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, normalizeSerializableChatData, Response } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -230,4 +230,53 @@ suite('normalizeSerializableChatData', () => { assert.strictEqual(newData.lastMessageDate, v2Data.lastMessageDate); assert.strictEqual(newData.customTitle, v2Data.computedTitle); }); + + test('old bad data', () => { + const v1Data: ISerializableChatData1 = { + // Testing the scenario where these are missing + sessionId: undefined!, + creationDate: undefined!, + + initialLocation: undefined, + isImported: false, + requesterAvatarIconUri: undefined, + requesterUsername: 'me', + requests: [], + responderAvatarIconUri: undefined, + responderUsername: 'bot', + welcomeMessage: [] + }; + + const newData = normalizeSerializableChatData(v1Data); + assert.strictEqual(newData.version, 3); + assert.ok(newData.creationDate > 0); + assert.ok(newData.lastMessageDate > 0); + assert.ok(newData.sessionId); + }); + + test('v3 with bug', () => { + const v3Data: ISerializableChatData3 = { + // Test case where old data was wrongly normalized and these fields were missing + creationDate: undefined!, + lastMessageDate: undefined!, + + version: 3, + initialLocation: undefined, + isImported: false, + requesterAvatarIconUri: undefined, + requesterUsername: 'me', + requests: [], + responderAvatarIconUri: undefined, + responderUsername: 'bot', + sessionId: 'session1', + welcomeMessage: [], + customTitle: 'computed title' + }; + + const newData = normalizeSerializableChatData(v3Data); + assert.strictEqual(newData.version, 3); + assert.ok(newData.creationDate > 0); + assert.ok(newData.lastMessageDate > 0); + assert.ok(newData.sessionId); + }); });