Skip to content

Spotter embed #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
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
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .sfdx/indexes/lwc/custom-components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
12 changes: 6 additions & 6 deletions src/embed/bodyless-conversation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'jest-fetch-mock';
import { BodylessConversation, BodylessConversationViewConfig } from './bodyless-conversation';
import { SpotterAgentEmbed, SpotterAgentEmbedViewConfig } from './bodyless-conversation';
import * as authInstance from '../auth';
import { init } from '../index';
import { Action, AuthType, RuntimeFilterOp } from '../types';
Expand All @@ -14,7 +14,7 @@ import {
expectUrlToHaveParamsWithValues,
} from '../test/test-utils';

describe('BodylessConversation', () => {
describe('SpotterAgentEmbed', () => {
const thoughtSpotHost = 'tshost';

beforeAll(() => {
Expand Down Expand Up @@ -117,12 +117,12 @@ describe('BodylessConversation', () => {
},
}),
);
const viewConfig: BodylessConversationViewConfig = {
const viewConfig: SpotterAgentEmbedViewConfig = {
worksheetId: 'worksheetId',
};

const conversationEmbed = new BodylessConversation(viewConfig);
const result = await conversationEmbed.sendMessage('userMessage');
const spotterEmbed = new SpotterAgentEmbed(viewConfig);
const result = await spotterEmbed.sendMessage('userMessage');
const iframeSrc = getIFrameSrc(result.container);
expectUrlToHaveParamsWithValues(iframeSrc, {
sessionId: 'sessionId',
Expand All @@ -134,7 +134,7 @@ describe('BodylessConversation', () => {
fetchMock.mockRejectOnce(
new Error('error'),
);
const errorResult = await conversationEmbed.sendMessage('userMessage');
const errorResult = await spotterEmbed.sendMessage('userMessage');
expect(errorResult.error instanceof Error).toBeTruthy();
});
});
51 changes: 43 additions & 8 deletions src/embed/bodyless-conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,30 @@ import { getQueryParamString } from '../utils';
* Configuration for bodyless conversation options.
* @group Embed components
*/
export interface BodylessConversationViewConfig extends ViewConfig {
export interface SpotterAgentEmbedViewConfig extends ViewConfig {
/**
* The ID of the worksheet to use for the conversation.
*/
worksheetId: string;
}

interface ConversationMessageViewConfig extends BodylessConversationViewConfig {
/**
* Configuration for conversation options.
* @deprecated Renamed to SpotterAgentEmbedViewConfig
* @group Embed components
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface BodylessConversationViewConfig extends SpotterAgentEmbedViewConfig {}

interface SpotterAgentMessageViewConfig extends SpotterAgentEmbedViewConfig {
sessionId: string;
genNo: number;
acSessionId: string;
acGenNo: number;
}

class ConversationMessage extends TsEmbed {
constructor(container: HTMLElement, protected viewConfig: ConversationMessageViewConfig) {
constructor(container: HTMLElement, protected viewConfig: SpotterAgentMessageViewConfig) {
viewConfig.embedComponentType = 'bodyless-conversation';
super(container, viewConfig);
}
Expand All @@ -39,6 +47,7 @@ class ConversationMessage extends TsEmbed {
const queryParams = this.getBaseQueryParams();

queryParams[Param.HideActions] = [...(queryParams[Param.HideActions] ?? [])];
queryParams[Param.isSpotterAgentEmbed] = true;
let query = '';
const queryParamsString = getQueryParamString(queryParams, true);
if (queryParamsString) {
Expand Down Expand Up @@ -68,9 +77,9 @@ class ConversationMessage extends TsEmbed {
* chatbots or other conversational interfaces.
* @example
* ```js
* import { BodylessConversation } from '@thoughtspot/visual-embed-sdk';
* import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk';
*
* const conversation = new BodylessConversation({
* const conversation = new SpotterAgentEmbed({
* worksheetId: 'worksheetId',
* });
*
Expand All @@ -80,12 +89,12 @@ class ConversationMessage extends TsEmbed {
* document.body.appendChild(container); // or to any other element
* ```
* @group Embed components
* @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl
* @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
*/
export class BodylessConversation {
export class SpotterAgentEmbed {
private conversationService: ConversationService;

constructor(private viewConfig: BodylessConversationViewConfig) {
constructor(private viewConfig: SpotterAgentEmbedViewConfig) {
const embedConfig = getEmbedConfig();

this.conversationService = new ConversationService(
Expand All @@ -112,3 +121,29 @@ export class BodylessConversation {
return { container, viz: embed };
}
}

/**
* Create a conversation embed, which can be integrated inside
* chatbots or other conversational interfaces.
* @deprecated Renamed to SpotterAgentEmbed
* @example
* ```js
* import { SpotterAgentEmbed } from '@thoughtspot/visual-embed-sdk';
*
* const conversation = new SpotterAgentEmbed({
* worksheetId: 'worksheetId',
* });
*
* const { container, error } = await conversation.sendMessage('show me sales by region');
*
* // append the container to the DOM
* document.body.appendChild(container); // or to any other element
* ```
* @group Embed components
* @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
*/
export class BodylessConversation extends SpotterAgentEmbed {
constructor(viewConfig: BodylessConversationViewConfig) {
super(viewConfig);
}
}
32 changes: 16 additions & 16 deletions src/embed/conversation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
ConversationEmbed,
ConversationViewConfig,
SpotterEmbed,
SpotterEmbedViewConfig,
} from './conversation';
import * as authInstance from '../auth';
import { init } from '../index';
Expand Down Expand Up @@ -28,14 +28,14 @@ beforeAll(() => {

describe('ConversationEmbed', () => {
it('should render the conversation embed', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand All @@ -44,15 +44,15 @@ describe('ConversationEmbed', () => {
});

it('should render the conversation embed with worksheets disabled', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
disableSourceSelection: true,
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand All @@ -61,15 +61,15 @@ describe('ConversationEmbed', () => {
});

it('should render the conversation embed with spotter limitations text if flag is set', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
showSpotterLimitations: true,
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand All @@ -78,15 +78,15 @@ describe('ConversationEmbed', () => {
});

it('should render the conversation embed with sample questions hidden', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
hideSampleQuestions: true,
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand All @@ -95,15 +95,15 @@ describe('ConversationEmbed', () => {
});

it('should render the conversation embed with worksheets hidden', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
hideSourceSelection: true,
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand All @@ -112,13 +112,13 @@ describe('ConversationEmbed', () => {
});

it('should handle error when worksheetId is not provided', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: '',
searchOptions: {
searchQuery: 'searchQuery',
},
};
const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
(conversationEmbed as any).handleError = jest.fn();
await conversationEmbed.render();
expect((conversationEmbed as any).handleError).toHaveBeenCalledWith(
Expand All @@ -127,15 +127,15 @@ describe('ConversationEmbed', () => {
});

it('should render the conversation embed if data panel v2 flag is true', async () => {
const viewConfig: ConversationViewConfig = {
const viewConfig: SpotterEmbedViewConfig = {
worksheetId: 'worksheetId',
searchOptions: {
searchQuery: 'searchQuery',
},
dataPanelV2: true,
};

const conversationEmbed = new ConversationEmbed(getRootEl(), viewConfig);
const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
await conversationEmbed.render();
expectUrlMatchesWithParams(
getIFrameSrc(),
Expand Down
53 changes: 42 additions & 11 deletions src/embed/conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export interface SearchOptions {
}

/**
* The configuration for the embedded conversationEmbed options.
* The configuration for the embedded spotterEmbed options.
* @group Embed components
*/
export interface ConversationViewConfig extends ViewConfig {
export interface SpotterEmbedViewConfig extends ViewConfig {
/**
* The ID of the worksheet to use for the conversation.
*/
Expand All @@ -32,7 +32,7 @@ export interface ConversationViewConfig extends ViewConfig {
* but still display the selected data source.
* @example
* ```js
* const embed = new ConversationEmbed('#tsEmbed', {
* const embed = new SpotterEmbed('#tsEmbed', {
* ... // other options
* disableSourceSelection : true,
* })
Expand All @@ -44,7 +44,7 @@ export interface ConversationViewConfig extends ViewConfig {
* hideSourceSelection : Hide data source selection
* @example
* ```js
* const embed = new ConversationEmbed('#tsEmbed', {
* const embed = new SpotterEmbed('#tsEmbed', {
* ... // other options
* hideSourceSelection : true,
* })
Expand All @@ -71,7 +71,7 @@ export interface ConversationViewConfig extends ViewConfig {
* default is false.
* @example
* ```js
* const embed = new ConversationEmbed('#tsEmbed', {
* const embed = new SpotterEmbed('#tsEmbed', {
* ... // other options
* showSpotterLimitations : true,
* })
Expand All @@ -84,7 +84,7 @@ export interface ConversationViewConfig extends ViewConfig {
* the initial screen of the conversation.
* @example
* ```js
* const embed = new ConversationEmbed('#tsEmbed', {
* const embed = new SpotterEmbed('#tsEmbed', {
* ... // other options
* hideSampleQuestions : true,
* })
Expand All @@ -94,23 +94,31 @@ export interface ConversationViewConfig extends ViewConfig {
hideSampleQuestions?: boolean;
}

/**
* The configuration for the embedded spotterEmbed options.
* @deprecated Renamed to SpotterEmbedViewConfig
* @group Embed components
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface ConversationViewConfig extends SpotterEmbedViewConfig {}

/**
* Embed ThoughtSpot AI Conversation.
* @group Embed components
* @example
* ```js
* const conversation = new ConversationEmbed('#tsEmbed', {
* const conversation = new SpotterEmbed('#tsEmbed', {
* worksheetId: 'worksheetId',
* searchOptions: {
* searchQuery: 'searchQuery',
* },
* });
* conversation.render();
* ```
* @version SDK: 1.33.1 | ThoughtSpot: 10.5.0.cl
* @version SDK: 1.38.0 | ThoughtSpot: 10.10.0.cl
*/
export class ConversationEmbed extends TsEmbed {
constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) {
export class SpotterEmbed extends TsEmbed {
constructor(container: HTMLElement, protected viewConfig: SpotterEmbedViewConfig) {
viewConfig.embedComponentType = 'conversation';
super(container, viewConfig);
}
Expand Down Expand Up @@ -163,11 +171,34 @@ export class ConversationEmbed extends TsEmbed {
return `${this.getEmbedBasePath(query)}/embed/${path}${tsPostHashParams}`;
}

public async render(): Promise<ConversationEmbed> {
public async render(): Promise<SpotterEmbed> {
await super.render();

const src = this.getIframeSrc();
await this.renderIFrame(src);
return this;
}
}

/**
* Embed ThoughtSpot AI Conversation.
* @deprecated Renamed to SpotterEmbed
* @group Embed components
* @example
* ```js
* const conversation = new SpotterEmbed('#tsEmbed', {
* worksheetId: 'worksheetId',
* searchOptions: {
* searchQuery: 'searchQuery',
* },
* });
* conversation.render();
* ```
* @version SDK: 1.37.0 | ThoughtSpot: 10.9.0.cl
*/
export class ConversationEmbed extends SpotterEmbed {
constructor(container: HTMLElement, protected viewConfig: ConversationViewConfig) {
viewConfig.embedComponentType = 'conversation';
super(container, viewConfig);
}
}
Loading
Loading