Skip to content

Commit 7ba610f

Browse files
committed
spotter embed components
1 parent ba1bf4b commit 7ba610f

37 files changed

+22606
-5796
lines changed

jest-setup.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const fetchMock = require('jest-fetch-mock');
2+
const crypto = require('crypto');
23

34
fetchMock.enableMocks();
45

@@ -32,3 +33,10 @@ global.MessageChannel = jest.fn().mockImplementation(() => {
3233
},
3334
};
3435
});
36+
37+
Object.defineProperty(global, 'crypto', {
38+
value: {
39+
getRandomValues: (arr) => crypto.randomBytes(arr.length),
40+
randomUUID: () => crypto.randomBytes(16).toString('hex'),
41+
},
42+
});

package-lock.json

Lines changed: 20770 additions & 5555 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@thoughtspot/visual-embed-sdk",
3-
"version": "1.33.0",
3+
"version": "1.33.1",
44
"description": "ThoughtSpot Embed SDK",
55
"module": "lib/src/index.js",
66
"main": "dist/tsembed.js",
@@ -37,8 +37,8 @@
3737
},
3838
"size-limit": [
3939
{
40-
"path": "dist/tsembed.js",
41-
"limit": "49 kB"
40+
"path": "dist/es/index.js",
41+
"limit": "30 kB"
4242
}
4343
],
4444
"scripts": {
@@ -80,7 +80,8 @@
8080
"mixpanel-browser": "2.47.0",
8181
"ts-deepmerge": "^6.0.2",
8282
"tslib": "^2.5.3",
83-
"use-deep-compare-effect": "^1.8.1"
83+
"use-deep-compare-effect": "^1.8.1",
84+
"yaml": "^2.5.1"
8485
},
8586
"devDependencies": {
8687
"@mdx-js/mdx": "^1.6.22",
@@ -96,8 +97,8 @@
9697
"@testing-library/react": "^11.2.7",
9798
"@testing-library/user-event": "^13.1.8",
9899
"@types/jest": "^22.2.3",
99-
"@types/mixpanel-browser": "^2.35.6",
100100
"@types/lodash": "^4.17.0",
101+
"@types/mixpanel-browser": "^2.35.6",
101102
"@types/react-test-renderer": "^17.0.1",
102103
"@typescript-eslint/eslint-plugin": "^4.6.0",
103104
"@typescript-eslint/parser": "^4.6.0",
@@ -106,6 +107,7 @@
106107
"babel-preset-gatsby": "^1.10.0",
107108
"command-line-args": "^5.1.1",
108109
"coveralls": "^3.1.0",
110+
"crypto": "^1.0.1",
109111
"current-git-branch": "^1.1.0",
110112
"dts-bundle": "^0.7.3",
111113
"eslint": "^7.12.1",
@@ -132,7 +134,7 @@
132134
"react-resize-detector": "^6.6.0",
133135
"react-test-renderer": "^17.0.2",
134136
"react-use-flexsearch": "^0.1.1",
135-
"rollup": "2.30.0",
137+
"rollup": "4.24.0",
136138
"rollup-plugin-typescript2": "0.27.3",
137139
"ts-jest": "^26.5.5",
138140
"ts-loader": "8.0.4",

rollup.config.js renamed to rollup.config.mjs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* Copyright (c) 2020
33
*
44
* Rollup configuration for building ThoughtSpot Embed UI SDK module
5-
*
65
* @summary Rollup config
76
* @author Ayon Ghosh <[email protected]>
87
*/
@@ -13,31 +12,31 @@ import commonjs from '@rollup/plugin-commonjs';
1312
import json from '@rollup/plugin-json';
1413
import replace from '@rollup/plugin-replace';
1514

16-
import pkg from './package.json';
15+
import pkg from './package.json' assert {type: "json"};
16+
1717
const plugins = [
18-
typescript({
19-
typescript: require('typescript'),
20-
}),
21-
nodeResolve(),
22-
commonjs(),
23-
json({
24-
compact: true
25-
}),
26-
replace({
27-
'process.env.NODE_ENV': JSON.stringify('production'),
28-
})
29-
];
18+
typescript(),
19+
nodeResolve(),
20+
commonjs(),
21+
json({
22+
compact: true,
23+
}),
24+
replace({
25+
'process.env.NODE_ENV': JSON.stringify('production'),
26+
}),
27+
];
3028

3129
export default [{
3230
input: 'src/index.ts',
3331
output: [
3432
{
3533
file: 'dist/tsembed.js',
3634
format: 'umd',
35+
inlineDynamicImports: true,
3736
name: 'tsembed',
3837
},
3938
{
40-
file: 'dist/tsembed.es.js',
39+
dir: 'dist/es',
4140
format: 'es',
4241
},
4342
],
@@ -51,10 +50,12 @@ export default [{
5150
{
5251
file: 'dist/tsembed-react.js',
5352
format: 'umd',
53+
inlineDynamicImports: true,
5454
name: 'tsembed',
5555
},
5656
{
57-
file: 'dist/tsembed-react.es.js',
57+
dir: 'dist/es/react',
58+
inlineDynamicImports: true,
5859
format: 'es',
5960
},
6061
],

src/auth.spec.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import 'jest-fetch-mock';
12
import * as authInstance from './auth';
23
import * as authTokenService from './authToken';
34
import * as EmbedConfig from './embed/embedConfig';
45
import * as mixPanelService from './mixpanel-service';
5-
import { executeAfterWait } from './test/test-utils';
6+
import { executeAfterWait, mockSessionInfo } from './test/test-utils';
67
import { AuthType, EmbedEvent } from './types';
78
import * as checkReleaseVersionInBetaInstance from './utils';
89
import * as authService from './utils/authService/authService';
@@ -102,17 +103,6 @@ export const embedConfig: any = {
102103
};
103104

104105
const originalWindow = window;
105-
export const mockSessionInfo = {
106-
userGUID: '1234',
107-
mixpanelToken: 'abc123',
108-
isPublicUser: false,
109-
sessionId: '6588e7d9-710c-453e-a7b4-535fb3a8cbb2',
110-
genNo: 3,
111-
acSession: {
112-
sessionId: 'cb202c48-b14b-4466-8a70-899ea666d46q',
113-
genNo: 5,
114-
},
115-
};
116106

117107
export const mockSessionInfoApiResponse = {
118108
userGUID: '1234',
@@ -205,6 +195,7 @@ describe('Unit test for auth', () => {
205195
});
206196

207197
test('doTokenAuth: when user is not loggedIn & getAuthToken not present, isLoggedIn should called', async () => {
198+
fetchMock.mockResponse(JSON.stringify({ mixpanelAccessToken: '' }));
208199
jest.spyOn(tokenAuthService, 'isActiveService').mockImplementation(async () => false);
209200
jest.spyOn(authService, 'fetchAuthTokenService').mockImplementation(() => ({
210201
text: () => Promise.resolve('abc'),

src/auth.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ async function isLoggedIn(thoughtSpotHost: string): Promise<boolean> {
214214
* Services to be called after the login is successful,
215215
* This should be called after the cookie is set for cookie auth or
216216
* after the token is set for cookieless.
217-
*
218217
* @return {Promise<void>}
219218
* @example
220219
* ```js
@@ -231,7 +230,7 @@ export async function postLoginService(): Promise<void> {
231230
initMixpanel(sessionInfo);
232231
}
233232
} catch (e) {
234-
logger.error('Post login services failed.', e.message);
233+
logger.error('Post login services failed.', e.message, e);
235234
}
236235
}
237236

src/authToken.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { logger } from './utils/logger';
66
let cachedAuthToken: string | null = null;
77

88
// This method can be used to get the authToken using the embedConfig
9+
/**
10+
*
11+
* @param embedConfig
12+
*/
913
export async function getAuthenticationToken(embedConfig: EmbedConfig): Promise<string> {
1014
// Since we don't have token validation enabled , we cannot tell if the
1115
// cached token is valid or not. So we will always fetch a new token.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import 'jest-fetch-mock';
2+
import { BodylessConversation, BodylessConversationViewConfig } from './bodyless-conversation';
3+
import * as authInstance from '../auth';
4+
import { init } from '../index';
5+
import { Action, AuthType, RuntimeFilterOp } from '../types';
6+
import {
7+
executeAfterWait,
8+
getDocumentBody,
9+
getIFrameSrc,
10+
getRootEl,
11+
fixedEncodeURI,
12+
defaultParamsWithoutHiddenActions as defaultParams,
13+
expectUrlMatchesWithParams,
14+
expectUrlToHaveParamsWithValues,
15+
} from '../test/test-utils';
16+
17+
describe('BodylessConversation', () => {
18+
const thoughtSpotHost = 'tshost';
19+
20+
beforeAll(() => {
21+
init({
22+
thoughtSpotHost,
23+
authType: AuthType.None,
24+
});
25+
jest.spyOn(authInstance, 'postLoginService').mockImplementation(() => Promise.resolve({}));
26+
spyOn(window, 'alert');
27+
document.body.innerHTML = getDocumentBody();
28+
});
29+
30+
beforeEach(() => {
31+
fetchMock.resetMocks();
32+
});
33+
34+
test('should render the bodyless conversation embed', async () => {
35+
fetchMock.mockResponses(
36+
JSON.stringify({
37+
data: {
38+
ConvAssist__createConversation: {
39+
convId: 'conversationId',
40+
initialCtx: {
41+
type: 'TS_ANSWER',
42+
tsAnsCtx: {
43+
sessionId: 'sessionId',
44+
genNo: 1,
45+
stateKey: {
46+
transactionId: 'transactionId',
47+
generationNumber: 1,
48+
},
49+
worksheet: {
50+
worksheetId: 'worksheetId',
51+
worksheetName: 'GTM',
52+
},
53+
},
54+
},
55+
},
56+
},
57+
}),
58+
JSON.stringify({
59+
data: {
60+
ConvAssist__sendMessage: {
61+
responses: [
62+
{
63+
msgId: 'msgId',
64+
data: {
65+
asstRespData: {
66+
tool: 'TS_NLS',
67+
asstRespText: '',
68+
nlsAnsData: {
69+
sageQuerySuggestions: [
70+
{
71+
llmReasoning: {
72+
assumptions: 'You want the total [COL|sales] for [COL|item_type] [VAL|jackets] for [VAL|this year].',
73+
clarifications: '',
74+
interpretation: '',
75+
__typename: 'eureka_SageQuerySuggestion_LLMReasoning',
76+
},
77+
tokens: [
78+
'sum sales',
79+
"item type = 'jackets'",
80+
"date = 'this year'",
81+
],
82+
tmlTokens: [
83+
'sum [sales]',
84+
"[date] = [date].'this year'",
85+
"[item type] = [item type].'jackets'",
86+
],
87+
worksheetId: 'worksheetId',
88+
description: '',
89+
title: '',
90+
cached: false,
91+
sqlQuery: "SELECT SUM(sales) FROM __Sample_Retail_Apparel WHERE item_type = 'jackets' AND date = _this_year();",
92+
sessionId: 'sessionId',
93+
genNo: 2,
94+
formulaInfo: [],
95+
tmlPhrases: [],
96+
stateKey: {
97+
transactionId: 'transactionId',
98+
generationNumber: 1,
99+
__typename: 'sage_auto_complete_v2_ACStateKey',
100+
},
101+
__typename: 'eureka_SageQuerySuggestion',
102+
},
103+
],
104+
responseType: 'ANSWER',
105+
__typename: 'convassist_nls_tool_NLSToolAsstRespData',
106+
},
107+
__typename: 'convassist_AsstResponseData',
108+
},
109+
__typename: 'convassist_MessageData',
110+
},
111+
type: 'ASST_RESPONSE',
112+
__typename: 'convassist_MessagePayload',
113+
},
114+
],
115+
__typename: 'convassist_SendMessageResponse',
116+
},
117+
},
118+
}),
119+
);
120+
const viewConfig: BodylessConversationViewConfig = {
121+
worksheetId: 'worksheetId',
122+
};
123+
124+
const conversationEmbed = new BodylessConversation(viewConfig);
125+
const result = await conversationEmbed.sendMessage('userMessage');
126+
console.log(result.container);
127+
const iframeSrc = getIFrameSrc(result.container);
128+
expectUrlToHaveParamsWithValues(iframeSrc, {
129+
sessionId: 'sessionId',
130+
genNo: 2,
131+
acSessionId: 'transactionId',
132+
acGenNo: 1,
133+
});
134+
135+
fetchMock.mockRejectOnce(
136+
new Error('error'),
137+
);
138+
const errorResult = await conversationEmbed.sendMessage('userMessage');
139+
expect(errorResult.error instanceof Error).toBeTruthy();
140+
});
141+
});

0 commit comments

Comments
 (0)