Skip to content

Commit

Permalink
chat-core-zendesk: add support for session reinitialization (#53)
Browse files Browse the repository at this point in the history
Adds support for restoring a Zendesk agent conversation using the
conversation ID. Requires reinitialization of the Zendesk client, which
can only be triggered on the first message sent after the new page is
loaded. This leads to a slight bit of extra lag on that first message,
but otherwise everything seems to work as if the page was never
reloaded.

TEST=manual

Used this locally with upcoming chat-headless change to support this new
method, saw expected results across page loads and within the Zendesk platform.
  • Loading branch information
popestr authored Oct 2, 2024
1 parent 288c1b1 commit 4e23bc9
Show file tree
Hide file tree
Showing 19 changed files with 169 additions and 16 deletions.
4 changes: 2 additions & 2 deletions packages/chat-core-aws-connect/THIRD-PARTY-NOTICES
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ The following NPM packages may be included in this product:
- @types/istanbul-lib-report@3.0.3
- @types/istanbul-reports@3.0.4
- @types/jsdom@20.0.1
- @types/node@22.5.5
- @types/node@22.7.4
- @types/stack-utils@2.0.3
- @types/tough-cookie@4.0.5
- @types/yargs-parser@21.0.3
Expand Down Expand Up @@ -1863,7 +1863,7 @@ SOFTWARE.

The following NPM package may be included in this product:

- nwsapi@2.2.12
- nwsapi@2.2.13

This package contains the following license and notice below:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export interface ChatCoreAwsConnect
| [init(messageResponse)](./chat-core-aws-connect.chatcoreawsconnect.init.md) | Initialize the Amazon Connect chat session using the credentials from the Chat API. |
| [on(eventName, cb)](./chat-core-aws-connect.chatcoreawsconnect.on.md) | Register a callback for an event triggered within the Amazon Connect chat session. Supported events are: - <code>message</code>: A new message has been received. - <code>typing</code>: The agent is typing. - <code>close</code>: The chat session has been closed. |
| [processMessage(request)](./chat-core-aws-connect.chatcoreawsconnect.processmessage.md) | Process a message sent by the user. |
| [reinitializeSession(\_)](./chat-core-aws-connect.chatcoreawsconnect.reinitializesession.md) | Reinitialize the session using existing session data. |
| [resetSession()](./chat-core-aws-connect.chatcoreawsconnect.resetsession.md) | Resets the [ChatCoreAwsConnect](./chat-core-aws-connect.chatcoreawsconnect.md) instance, clearing the underlying Amazon Connect session. |

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-core-aws-connect](./chat-core-aws-connect.md) &gt; [ChatCoreAwsConnect](./chat-core-aws-connect.chatcoreawsconnect.md) &gt; [reinitializeSession](./chat-core-aws-connect.chatcoreawsconnect.reinitializesession.md)

## ChatCoreAwsConnect.reinitializeSession() method

Reinitialize the session using existing session data.

**Signature:**

```typescript
reinitializeSession(_: unknown): Promise<void>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| \_ | unknown | |

**Returns:**

Promise&lt;void&gt;

## Remarks

This is currently not supported for Amazon Connect.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface ChatCoreAwsConnect {
init(messageResponse: MessageResponse): Promise<void>;
on<T extends keyof EventMap>(eventName: T, cb: EventCallback<T>): void;
processMessage(request: MessageRequest): Promise<void>;
reinitializeSession(_: unknown): Promise<void>;
resetSession(): void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,10 @@ export class ChatCoreAwsConnectImpl implements ChatCoreAwsConnect {
this.session.disconnectParticipant();
this.session = undefined;
}

async reinitializeSession(_: unknown): Promise<void> {
console.warn(
"Reinitializing chat session is currently not supported for AWS Connect"
);
}
}
10 changes: 10 additions & 0 deletions packages/chat-core-aws-connect/src/models/ChatCoreAwsConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,14 @@ export interface ChatCoreAwsConnect {
* Resets the {@link ChatCoreAwsConnect} instance, clearing the underlying Amazon Connect session.
*/
resetSession(): void;

/**
* Reinitialize the session using existing session data.
*
* @param credentials - The credentials to use to reinitialize the session.
*
* @remarks
* This is currently not supported for Amazon Connect.
*/
reinitializeSession(_: unknown): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Provide the current conversation ID for the chat session.
**Signature:**

```typescript
getSession(): void;
getSession(): string | undefined;
```
**Returns:**

void
string \| undefined

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Initialize the Amazon Connect chat session using the credentials from the Chat A
**Signature:**

```typescript
init(messageResponse: MessageResponse): Promise<void>;
init(messageResponse: MessageResponse): Promise<ChatCoreZendeskSessionCredentials>;
```

## Parameters
Expand All @@ -20,5 +20,5 @@ init(messageResponse: MessageResponse): Promise<void>;

**Returns:**

Promise&lt;void&gt;
Promise&lt;[ChatCoreZendeskSessionCredentials](./chat-core-zendesk.chatcorezendesksessioncredentials.md)<!-- -->&gt;

Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ export interface ChatCoreZendesk
| [init(messageResponse)](./chat-core-zendesk.chatcorezendesk.init.md) | Initialize the Amazon Connect chat session using the credentials from the Chat API. |
| [on(eventName, cb)](./chat-core-zendesk.chatcorezendesk.on.md) | Register a callback for an event triggered within the Zendesk chat session. Supported events are: - <code>message</code>: A new message has been received. - <code>typing</code>: The agent is typing. - <code>close</code>: The chat session has been closed (e.g. agent left or closed the ticket). |
| [processMessage(request)](./chat-core-zendesk.chatcorezendesk.processmessage.md) | Process a message sent by the user. |
| [reinitializeSession(credentials)](./chat-core-zendesk.chatcorezendesk.reinitializesession.md) | Reinitialize the session using existing session data. |
| [resetSession()](./chat-core-zendesk.chatcorezendesk.resetsession.md) | Reset the chat session by clearing the current conversation ID. |

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-core-zendesk](./chat-core-zendesk.md) &gt; [ChatCoreZendesk](./chat-core-zendesk.chatcorezendesk.md) &gt; [reinitializeSession](./chat-core-zendesk.chatcorezendesk.reinitializesession.md)

## ChatCoreZendesk.reinitializeSession() method

Reinitialize the session using existing session data.

**Signature:**

```typescript
reinitializeSession(credentials: ChatCoreZendeskSessionCredentials): Promise<void>;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| credentials | [ChatCoreZendeskSessionCredentials](./chat-core-zendesk.chatcorezendesksessioncredentials.md) | The credentials to use to reinitialize the session. |

**Returns:**

Promise&lt;void&gt;

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-core-zendesk](./chat-core-zendesk.md) &gt; [ChatCoreZendeskSessionCredentials](./chat-core-zendesk.chatcorezendesksessioncredentials.md) &gt; [conversationId](./chat-core-zendesk.chatcorezendesksessioncredentials.conversationid.md)

## ChatCoreZendeskSessionCredentials.conversationId property

The conversation ID for the current chat session.

**Signature:**

```typescript
conversationId: string;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-core-zendesk](./chat-core-zendesk.md) &gt; [ChatCoreZendeskSessionCredentials](./chat-core-zendesk.chatcorezendesksessioncredentials.md)

## ChatCoreZendeskSessionCredentials interface

Credentials for the Zendesk session created by the [ChatCoreZendesk](./chat-core-zendesk.chatcorezendesk.md)<!-- -->. Used for reinitializing the session across page reloads.

**Signature:**

```typescript
export interface ChatCoreZendeskSessionCredentials
```

## Properties

| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [conversationId](./chat-core-zendesk.chatcorezendesksessioncredentials.conversationid.md) | | string | The conversation ID for the current chat session. |

1 change: 1 addition & 0 deletions packages/chat-core-zendesk/docs/chat-core-zendesk.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
| --- | --- |
| [ChatCoreZendesk](./chat-core-zendesk.chatcorezendesk.md) | Provides methods for interacting with Chat's Zendesk integration. |
| [ChatCoreZendeskConfig](./chat-core-zendesk.chatcorezendeskconfig.md) | Configuration for this instance of the [ChatCoreZendesk](./chat-core-zendesk.chatcorezendesk.md)<!-- -->. |
| [ChatCoreZendeskSessionCredentials](./chat-core-zendesk.chatcorezendesksessioncredentials.md) | Credentials for the Zendesk session created by the [ChatCoreZendesk](./chat-core-zendesk.chatcorezendesk.md)<!-- -->. Used for reinitializing the session across page reloads. |

10 changes: 8 additions & 2 deletions packages/chat-core-zendesk/etc/chat-core-zendesk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { MessageResponse } from '@yext/chat-core';
// @public
export interface ChatCoreZendesk {
emit<T extends keyof EventMap>(eventName: T, data: EventMap[T]): void;
getSession(): void;
init(messageResponse: MessageResponse): Promise<void>;
getSession(): string | undefined;
init(messageResponse: MessageResponse): Promise<ChatCoreZendeskSessionCredentials>;
// Warning: (ae-forgotten-export) The symbol "EventMap" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "EventCallback" needs to be exported by the entry point index.d.ts
on<T extends keyof EventMap>(eventName: T, cb: EventCallback<T>): void;
processMessage(request: MessageRequest): Promise<void>;
reinitializeSession(credentials: ChatCoreZendeskSessionCredentials): Promise<void>;
resetSession(): void;
}

Expand All @@ -27,6 +28,11 @@ export interface ChatCoreZendeskConfig {
ticketTags?: string[];
}

// @public
export interface ChatCoreZendeskSessionCredentials {
conversationId: string;
}

// Warning: (ae-forgotten-export) The symbol "ChatCoreZendeskImpl" needs to be exported by the entry point index.d.ts
//
// @public
Expand Down
28 changes: 24 additions & 4 deletions packages/chat-core-zendesk/src/infra/ChatCoreZendeskImpl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { MessageRequest, MessageResponse } from "@yext/chat-core";
import { ChatCoreZendesk } from "../models";

/**
* Issue 1: Smooch Version
Expand Down Expand Up @@ -32,6 +33,7 @@ const Smooch = (SmoochLib.default || SmoochLib) as typeof SmoochLib;

import { ChatCoreZendeskConfig } from "../models/ChatCoreZendeskConfig";
import { EventCallback, EventMap } from "../models/EventCallback";
import { ChatCoreZendeskSessionCredentials } from "../models/ChatCoreZendeskSessionCredentials";

const MetadataChatSDKKey = "YEXT_CHAT_SDK";

Expand All @@ -43,7 +45,7 @@ const MetadataChatSDKKey = "YEXT_CHAT_SDK";
*
* @internal
*/
export class ChatCoreZendeskImpl {
export class ChatCoreZendeskImpl implements ChatCoreZendesk {
private eventListeners: { [T in keyof EventMap]?: EventCallback<T>[] } = {};
private conversationId: string | undefined;
private integrationId: string;
Expand All @@ -63,7 +65,12 @@ export class ChatCoreZendeskImpl {
* mode on the first invocation. Subsequent calls to this method will create a
* new conversation session.
*/
async init(messageRsp: MessageResponse): Promise<void> {
async init(messageRsp: MessageResponse): Promise<ChatCoreZendeskSessionCredentials> {
await this.initializeZendeskSdk();
return this.createZendeskConversation(messageRsp);
}

private async initializeZendeskSdk(): Promise<void> {
const divId = "yext-chat-core-zendesk-container";
if (!window.document.getElementById(divId)) {
const div = window.document.createElement("div");
Expand All @@ -83,15 +90,16 @@ export class ChatCoreZendeskImpl {
}
this.setupEventListeners();
}
await this.setupSession(messageRsp);
}

/**
* Set up a new session by creating a new conversation with the Smooch SDK.
* On ticket creation, the metadata is set to include the tag "yext-chat"
* with the conversation summary as the initial message.
*/
private async setupSession(messageRsp: MessageResponse) {
private async createZendeskConversation(
messageRsp: MessageResponse
): Promise<ChatCoreZendeskSessionCredentials> {
const ticketFields: Record<string, unknown> = {};
try {
if (messageRsp.integrationDetails?.zendeskHandoff?.ticketFields) {
Expand Down Expand Up @@ -133,6 +141,10 @@ export class ChatCoreZendeskImpl {
}`,
this.conversationId
);

return {
conversationId: convo.id,
};
}

private setupEventListeners() {
Expand Down Expand Up @@ -208,6 +220,14 @@ export class ChatCoreZendeskImpl {
}

resetSession(): void {
// @ts-ignore - off() is not in the Smooch types, but does exist
Smooch.off();
this.conversationId = undefined;
}

async reinitializeSession(credentials: ChatCoreZendeskSessionCredentials): Promise<void> {
this.conversationId = credentials.conversationId;
await this.initializeZendeskSdk();
await Smooch.loadConversation(credentials.conversationId);
}
}
12 changes: 10 additions & 2 deletions packages/chat-core-zendesk/src/models/ChatCoreZendesk.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MessageRequest, MessageResponse } from "@yext/chat-core";
import { EventCallback, EventMap } from "./EventCallback";
import { ChatCoreZendeskSessionCredentials } from "./ChatCoreZendeskSessionCredentials";

/**
* Provides methods for interacting with Chat's Zendesk integration.
Expand All @@ -12,7 +13,7 @@ export interface ChatCoreZendesk {
*
* @param messageResponse - The response returned from a successful call to the Chat API.
*/
init(messageResponse: MessageResponse): Promise<void>;
init(messageResponse: MessageResponse): Promise<ChatCoreZendeskSessionCredentials>;

/**
* Register a callback for an event triggered within the Zendesk chat session.
Expand Down Expand Up @@ -46,10 +47,17 @@ export interface ChatCoreZendesk {
/**
* Provide the current conversation ID for the chat session.
*/
getSession(): void;
getSession(): string | undefined;

/**
* Reset the chat session by clearing the current conversation ID.
*/
resetSession(): void;

/**
* Reinitialize the session using existing session data.
*
* @param credentials - The credentials to use to reinitialize the session.
*/
reinitializeSession(credentials: ChatCoreZendeskSessionCredentials): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Credentials for the Zendesk session created by the {@link ChatCoreZendesk}.
* Used for reinitializing the session across page reloads.
*
* @public
*/
export interface ChatCoreZendeskSessionCredentials {
/**
* The conversation ID for the current chat session.
*/
conversationId: string;
}
1 change: 1 addition & 0 deletions packages/chat-core-zendesk/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { ChatCoreZendeskConfig } from "./ChatCoreZendeskConfig";
export { ChatCoreZendesk } from "./ChatCoreZendesk";
export { ChatCoreZendeskSessionCredentials } from "./ChatCoreZendeskSessionCredentials";
5 changes: 3 additions & 2 deletions packages/chat-core-zendesk/tests/ChatCoreZendesk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jest.mock("smooch", () => ({
on: jest.fn(),
startTyping: jest.fn(),
stopTyping: jest.fn(),
off: jest.fn(),
}));

beforeEach(() => {
Expand All @@ -58,11 +59,11 @@ describe("chat session initialization", () => {
);
});

it("returns no error when successfully connecting to chat session", async () => {
it("returns convo id and no error when successfully connecting to chat session", async () => {
const chatCoreZendesk = provideChatCoreZendesk(mockConfig);
await expect(
chatCoreZendesk.init(mockMessageResponse())
).resolves.toBeUndefined();
).resolves.toStrictEqual({conversationId: "mock-conversation-id"});
});

it("avoid rendering smooch web widget on subsequent initialization", async () => {
Expand Down

0 comments on commit 4e23bc9

Please sign in to comment.