From 31db95c5f2afcd0f9340aeb7c0badb456b9290cc Mon Sep 17 00:00:00 2001 From: Ryan Pope Date: Tue, 1 Oct 2024 14:05:06 -0400 Subject: [PATCH] catch and handle credential parse error --- .../chat-headless-react/THIRD-PARTY-NOTICES | 2 +- .../chat-headless/src/ChatHeadlessImpl.ts | 23 +++++++++-- .../tests/chatheadless.clients.test.ts | 38 +++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/packages/chat-headless-react/THIRD-PARTY-NOTICES b/packages/chat-headless-react/THIRD-PARTY-NOTICES index e6343ec..aa7fee6 100644 --- a/packages/chat-headless-react/THIRD-PARTY-NOTICES +++ b/packages/chat-headless-react/THIRD-PARTY-NOTICES @@ -67,7 +67,7 @@ The following npm packages may be included in this product: - @types/hoist-non-react-statics@3.3.5 - @types/prop-types@15.7.13 - @types/react-dom@18.3.0 - - @types/react@18.3.9 + - @types/react@18.3.10 - @types/use-sync-external-store@0.0.3 These packages each contain the following license and notice below: diff --git a/packages/chat-headless/src/ChatHeadlessImpl.ts b/packages/chat-headless/src/ChatHeadlessImpl.ts index 88eacea..ad2c494 100644 --- a/packages/chat-headless/src/ChatHeadlessImpl.ts +++ b/packages/chat-headless/src/ChatHeadlessImpl.ts @@ -104,7 +104,14 @@ export class ChatHeadlessImpl implements ChatHeadless { this.credentialsSessionStorageKey ); if (credentials) { - return JSON.parse(credentials); + try { + return JSON.parse(credentials); + } catch (e) { + this.removeSessionAgentCredentials(); + throw new Error( + `Error occurred while parsing credentials from session storage: ${e}` + ); + } } } } @@ -265,9 +272,17 @@ export class ChatHeadlessImpl implements ChatHeadless { } this.credentialsSessionStorageKey = `${BASE_HANDOFF_CREDENTIALS_SESSION_STORAGE_KEY}__${hostname}__${this.config.botId}`; - - if (this.sessionAgentCredentials) { - this.handoff(); + + try { + const credentials = this.sessionAgentCredentials; + if (credentials) { + this.handoff(); + } + } catch (e) { + console.error( + "Error occurred while initializing agent session using stored credentials:", + e + ); } } diff --git a/packages/chat-headless/tests/chatheadless.clients.test.ts b/packages/chat-headless/tests/chatheadless.clients.test.ts index 8fdd465..aba5612 100644 --- a/packages/chat-headless/tests/chatheadless.clients.test.ts +++ b/packages/chat-headless/tests/chatheadless.clients.test.ts @@ -272,6 +272,44 @@ it("does not reinitialize session if saveToLocalStorage is false", async () => { expect(botClient.getNextMessage).toHaveBeenCalledTimes(2); }); +it("defaults to bot client if session storage contains invalid data", async () => { + const botClient = createMockHttpClient([ + { message: createMessage("message 1"), notes: {}, integrationDetails: {} }, //trigger handoff + { message: createMessage("message 2"), notes: {} }, + ]); + const callbacks: Record = {}; + let agentClient = createMockEventClient(callbacks); + const newConfig = { ...config, saveToLocalStorage: true }; + let headless = provideChatHeadless(newConfig, { + bot: botClient, + agent: agentClient, + }); + + // start with bot client, immediately trigger handoff + await headless.getNextMessage(); + expect(botClient.getNextMessage).toHaveBeenCalledTimes(1); + expect(agentClient.init).toHaveBeenCalledTimes(1); + + // save invalid data to session storage + sessionStorage.setItem( + "yext_chat_handoff_credentials__localhost__botId", + "invalid" + ); + + agentClient = createMockEventClient(callbacks); + headless = provideChatHeadless(newConfig, { + bot: botClient, + agent: agentClient, + }); + + // agent does not reinitialize, nothing saved to session + expect(agentClient.reinitializeSession).toHaveBeenCalledTimes(0); + + // bot client is the active client + await headless.getNextMessage(); + expect(botClient.getNextMessage).toHaveBeenCalledTimes(2); +}); + function createMessage(text: string): Message { return { text,