Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions clients/chat-client/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @epilot/chat-client

Generated TypeScript client for epilot Chat API widget management and public chat.
Generated TypeScript client for epilot Chat API Website Chat management and public chat.

## Usage

Expand All @@ -9,13 +9,13 @@ import { createClient } from '@epilot/chat-client';

const chat = createClient();
chat.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
const { data } = await chat.listChatWidgets();
const { data: widget } = await chat.getChatWidget({ widget_id });
const { data } = await chat.listWebsiteChats();
const { data: websiteChat } = await chat.getWebsiteChat({ website_chat_id });

await chat.updateChatWidget({ widget_id }, {
version: widget.version,
website_chat: {
...widget.website_chat,
await chat.updateWebsiteChat({ website_chat_id }, {
version: websiteChat.version,
settings: {
...websiteChat.settings,
design_id,
authentication: { email_code: { email_template_id } },
},
Expand All @@ -27,10 +27,12 @@ The same API is available as `epilot.chat` from `@epilot/sdk`, or through the
for callers that want a shared singleton.

Use a separate `createClient()` instance for anonymous chat. Bootstrap and session
creation use no epilot access token; `sendAnonymousChatMessage` uses the anonymous
session token. Its response is SSE: request `responseType: 'stream'` in Node and
consume the stream, or use a streaming fetch transport in the browser. The client
does not parse SSE events or implement the host/iframe handoff protocol.
creation use no epilot access token; `sendAnonymousChatMessage` and the email
verification operations (`getChatVerification`, `startChatEmailVerification`,
`verifyChatEmailCode`, `cancelChatVerification`) use the anonymous session token.
`sendAnonymousChatMessage` responds with SSE: request `responseType: 'stream'` in
Node and consume the stream, or use a streaming fetch transport in the browser. The
client does not parse SSE events or implement the host/iframe handoff protocol.

## Regeneration

Expand Down
4 changes: 2 additions & 2 deletions clients/chat-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@epilot/chat-client",
"version": "0.1.1",
"version": "0.2.0",
"description": "Client for the epilot Chat API",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -19,7 +19,7 @@
"keywords": [
"epilot",
"chat",
"widgets"
"website-chat"
],
"scripts": {
"test": "vitest run",
Expand Down
41 changes: 26 additions & 15 deletions clients/chat-client/src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,38 @@ describe('Chat client', () => {
expect(client.defaults.baseURL).toBe('https://chat.sls.epilot.io');
expect(client.api.getOperations().map((operation) => operation.operationId)).toEqual(
expect.arrayContaining([
'listChatWidgets',
'createChatWidget',
'getChatWidget',
'updateChatWidget',
'deleteChatWidget',
'getPublicChatWidget',
'listWebsiteChats',
'createWebsiteChat',
'getWebsiteChat',
'updateWebsiteChat',
'deleteWebsiteChat',
'getPublicWebsiteChat',
'createPublicChatGrant',
'createAnonymousChatSession',
'sendAnonymousChatMessage',
'getChatVerification',
'startChatEmailVerification',
'verifyChatEmailCode',
'cancelChatVerification',
]),
);
expect(definition.paths['/v1/widgets'].get.security).toEqual([{ EpilotAuth: [] }]);
expect(definition.paths['/v1/website-chats'].get.security).toEqual([{ EpilotAuth: [] }]);
expect(definition.paths['/v1/bootstrap'].post.security).toEqual([]);
expect(definition.paths['/v1/sessions'].post.security).toEqual([]);
expect(definition.paths['/v1/widgets/{widget_id}/configuration'].get.security).toEqual([]);
expect(definition.paths['/v1/website-chats/{website_chat_id}/configuration'].get.security).toEqual([]);
expect(definition.paths['/v1/messages'].post.security).toEqual([{ AnonymousSession: [] }]);
expect(definition.paths['/v1/verification/code'].post.security).toEqual([{ AnonymousSession: [] }]);
expect(definition.components.responses.Error.content['application/json'].schema.properties.code.enum).toContain(
'SESSION_EXPIRED',
);
expect(definition.components.schemas.Error.properties).not.toHaveProperty('code');
});

it('encodes a widget update and preserves design and email verification settings', async () => {
it('encodes a Website Chat update and preserves design and email verification settings', async () => {
const client = createClient();
const payload: Components.Schemas.UpdateChatWidgetRequest = {
const payload: Components.Schemas.UpdateWebsiteChatRequest = {
version: 1,
website_chat: {
settings: {
allowed_origins: ['https://example.com'],
organisation_name: 'Example Energy',
default_locale: 'en',
Expand All @@ -45,12 +50,18 @@ describe('Chat client', () => {
};
client.defaults.adapter = async (config) => {
expect(config.method).toBe('put');
expect(config.url).toBe('/v1/widgets/widget-1');
expect(config.url).toBe('/v1/website-chats/website-chat-1');
expect(JSON.parse(config.data)).toEqual(payload);
return { config, data: { widget_id: 'widget-1', ...payload }, status: 200, statusText: 'OK', headers: {} };
return {
config,
data: { website_chat_id: 'website-chat-1', ...payload },
status: 200,
statusText: 'OK',
headers: {},
};
};
const response = await client.updateChatWidget({ widget_id: 'widget-1' }, payload);
expect(response.data.website_chat.design_id).toBe(payload.website_chat?.design_id);
const response = await client.updateWebsiteChat({ website_chat_id: 'website-chat-1' }, payload);
expect(response.data.settings.design_id).toBe(payload.settings?.design_id);
});

it('can isolate an anonymous session from the authenticated management singleton', () => {
Expand Down
58 changes: 47 additions & 11 deletions clients/chat-client/src/openapi-runtime.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
}
],
"paths": {
"/v1/widgets": {
"/v1/website-chats": {
"get": {
"operationId": "listChatWidgets",
"operationId": "listWebsiteChats",
"responses": {},
"parameters": [
{
Expand All @@ -22,7 +22,7 @@
]
},
"post": {
"operationId": "createChatWidget",
"operationId": "createWebsiteChat",
"responses": {},
"requestBody": {
"required": true,
Expand All @@ -32,20 +32,20 @@
}
}
},
"/v1/widgets/{widget_id}": {
"/v1/website-chats/{website_chat_id}": {
"parameters": [
{
"in": "path",
"name": "widget_id",
"name": "website_chat_id",
"required": true
}
],
"get": {
"operationId": "getChatWidget",
"operationId": "getWebsiteChat",
"responses": {}
},
"put": {
"operationId": "updateChatWidget",
"operationId": "updateWebsiteChat",
"responses": {},
"requestBody": {
"required": true,
Expand All @@ -55,7 +55,7 @@
}
},
"delete": {
"operationId": "deleteChatWidget",
"operationId": "deleteWebsiteChat",
"responses": {},
"parameters": [
{
Expand All @@ -66,13 +66,13 @@
]
}
},
"/v1/widgets/{widget_id}/configuration": {
"/v1/website-chats/{website_chat_id}/configuration": {
"get": {
"operationId": "getPublicChatWidget",
"operationId": "getPublicWebsiteChat",
"parameters": [
{
"in": "path",
"name": "widget_id",
"name": "website_chat_id",
"required": true
}
],
Expand Down Expand Up @@ -114,6 +114,42 @@
},
"responses": {}
}
},
"/v1/verification": {
"get": {
"operationId": "getChatVerification",
"responses": {}
}
},
"/v1/verification/email": {
"post": {
"operationId": "startChatEmailVerification",
"requestBody": {
"required": true,
"content": {
"application/json": {}
}
},
"responses": {}
}
},
"/v1/verification/code": {
"post": {
"operationId": "verifyChatEmailCode",
"requestBody": {
"required": true,
"content": {
"application/json": {}
}
},
"responses": {}
}
},
"/v1/verification/cancel": {
"post": {
"operationId": "cancelChatVerification",
"responses": {}
}
}
},
"components": {
Expand Down
Loading
Loading