@thoughtspot/visual-embed-sdk 1.10.0-alpha.3 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +22 -3
  3. package/dist/src/auth.d.ts +18 -5
  4. package/dist/src/embed/app.d.ts +4 -2
  5. package/dist/src/embed/base.d.ts +21 -5
  6. package/dist/src/embed/search.d.ts +4 -0
  7. package/dist/src/embed/ts-embed.d.ts +1 -1
  8. package/dist/src/index.d.ts +3 -2
  9. package/dist/src/types.d.ts +29 -1
  10. package/dist/src/utils/authService.d.ts +1 -0
  11. package/dist/src/utils/processData.d.ts +1 -1
  12. package/dist/tsembed.es.js +560 -43
  13. package/dist/tsembed.js +558 -42
  14. package/lib/package.json +2 -1
  15. package/lib/src/auth.d.ts +18 -5
  16. package/lib/src/auth.js +48 -9
  17. package/lib/src/auth.js.map +1 -1
  18. package/lib/src/auth.spec.js +69 -11
  19. package/lib/src/auth.spec.js.map +1 -1
  20. package/lib/src/embed/app.d.ts +4 -2
  21. package/lib/src/embed/app.js +17 -7
  22. package/lib/src/embed/app.js.map +1 -1
  23. package/lib/src/embed/app.spec.js +36 -2
  24. package/lib/src/embed/app.spec.js.map +1 -1
  25. package/lib/src/embed/base.d.ts +21 -5
  26. package/lib/src/embed/base.js +64 -10
  27. package/lib/src/embed/base.js.map +1 -1
  28. package/lib/src/embed/base.spec.js +49 -3
  29. package/lib/src/embed/base.spec.js.map +1 -1
  30. package/lib/src/embed/embed.spec.js +1 -1
  31. package/lib/src/embed/embed.spec.js.map +1 -1
  32. package/lib/src/embed/events.spec.js +30 -1
  33. package/lib/src/embed/events.spec.js.map +1 -1
  34. package/lib/src/embed/search.d.ts +4 -0
  35. package/lib/src/embed/search.js +1 -1
  36. package/lib/src/embed/search.js.map +1 -1
  37. package/lib/src/embed/ts-embed.d.ts +1 -1
  38. package/lib/src/embed/ts-embed.js +18 -14
  39. package/lib/src/embed/ts-embed.js.map +1 -1
  40. package/lib/src/embed/ts-embed.spec.js +16 -6
  41. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  42. package/lib/src/index.d.ts +3 -2
  43. package/lib/src/index.js +3 -2
  44. package/lib/src/index.js.map +1 -1
  45. package/lib/src/test/test-utils.js +1 -1
  46. package/lib/src/test/test-utils.js.map +1 -1
  47. package/lib/src/types.d.ts +29 -1
  48. package/lib/src/types.js +23 -0
  49. package/lib/src/types.js.map +1 -1
  50. package/lib/src/utils/authService.d.ts +1 -0
  51. package/lib/src/utils/authService.js +21 -3
  52. package/lib/src/utils/authService.js.map +1 -1
  53. package/lib/src/utils/authService.spec.js +21 -5
  54. package/lib/src/utils/authService.spec.js.map +1 -1
  55. package/lib/src/utils/processData.d.ts +1 -1
  56. package/lib/src/utils/processData.js +37 -3
  57. package/lib/src/utils/processData.js.map +1 -1
  58. package/lib/src/utils/processData.spec.js +106 -4
  59. package/lib/src/utils/processData.spec.js.map +1 -1
  60. package/lib/src/visual-embed-sdk.d.ts +123 -11
  61. package/package.json +2 -1
  62. package/src/auth.spec.ts +90 -11
  63. package/src/auth.ts +63 -13
  64. package/src/embed/app.spec.ts +49 -1
  65. package/src/embed/app.ts +18 -6
  66. package/src/embed/base.spec.ts +56 -4
  67. package/src/embed/base.ts +83 -16
  68. package/src/embed/embed.spec.ts +1 -1
  69. package/src/embed/events.spec.ts +32 -0
  70. package/src/embed/search.ts +5 -0
  71. package/src/embed/ts-embed.spec.ts +19 -9
  72. package/src/embed/ts-embed.ts +24 -15
  73. package/src/index.ts +5 -1
  74. package/src/test/test-utils.ts +1 -1
  75. package/src/types.ts +29 -0
  76. package/src/utils/authService.spec.ts +31 -5
  77. package/src/utils/authService.ts +27 -3
  78. package/src/utils/processData.spec.ts +139 -4
  79. package/src/utils/processData.ts +54 -4
@@ -1,11 +1,12 @@
1
1
  import { AppEmbed, AppViewConfig, Page } from './app';
2
2
  import { init } from '../index';
3
- import { Action, AuthType, RuntimeFilterOp } from '../types';
3
+ import { Action, AuthType, HostEvent, RuntimeFilterOp } from '../types';
4
4
  import {
5
5
  executeAfterWait,
6
6
  getDocumentBody,
7
7
  getIFrameSrc,
8
8
  getRootEl,
9
+ getIFrameEl,
9
10
  } from '../test/test-utils';
10
11
  import { version } from '../../package.json';
11
12
  import * as config from '../config';
@@ -82,6 +83,7 @@ describe('App embed tests', () => {
82
83
  [Page.Liveboards]: 'pinboards',
83
84
  [Page.Data]: 'data/tables',
84
85
  [Page.Home]: 'home',
86
+ [Page.SpotIQ]: 'insights/results',
85
87
  };
86
88
 
87
89
  const pageIds = Object.keys(pageRouteMap);
@@ -194,6 +196,52 @@ describe('App embed tests', () => {
194
196
  );
195
197
  });
196
198
 
199
+ test('navigateToPage with noReload should trigger the appropriate event', async () => {
200
+ const appEmbed = new AppEmbed(getRootEl(), {
201
+ frameParams: {
202
+ width: '100%',
203
+ height: '100%',
204
+ },
205
+ });
206
+ await appEmbed.render();
207
+
208
+ const iframe = getIFrameEl();
209
+ iframe.contentWindow.postMessage = jest.fn();
210
+ appEmbed.navigateToPage(path, true);
211
+
212
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
213
+ expect.objectContaining({
214
+ type: HostEvent.Navigate,
215
+ data: path,
216
+ }),
217
+ `http://${thoughtSpotHost}`,
218
+ );
219
+
220
+ appEmbed.navigateToPage(-1, true);
221
+ expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(
222
+ expect.objectContaining({
223
+ type: HostEvent.Navigate,
224
+ data: -1,
225
+ }),
226
+ `http://${thoughtSpotHost}`,
227
+ );
228
+ });
229
+
230
+ test('Do not allow number path without noReload in navigateToPage', async () => {
231
+ const appEmbed = new AppEmbed(getRootEl(), {
232
+ frameParams: {
233
+ width: '100%',
234
+ height: '100%',
235
+ },
236
+ });
237
+ await appEmbed.render();
238
+ spyOn(console, 'warn');
239
+ appEmbed.navigateToPage(-1);
240
+ expect(console.warn).toHaveBeenCalledWith(
241
+ 'Path can only by a string when triggered without noReload',
242
+ );
243
+ });
244
+
197
245
  test('navigateToPage function use before render', async () => {
198
246
  spyOn(console, 'log');
199
247
  const appEmbed = new AppEmbed(getRootEl(), {
package/src/embed/app.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { getFilterQuery, getQueryParamString } from '../utils';
13
- import { Param, RuntimeFilter, DOMSelector } from '../types';
13
+ import { Param, RuntimeFilter, DOMSelector, HostEvent } from '../types';
14
14
  import { V1Embed, ViewConfig } from './ts-embed';
15
15
 
16
16
  /**
@@ -194,19 +194,31 @@ export class AppEmbed extends V1Embed {
194
194
  /**
195
195
  * Navigate to particular page for app embed. eg:answers/pinboards/home
196
196
  * This is used for embedding answers, pinboards, visualizations and full application only.
197
- * @param path The string, set to iframe src and navigate to new page
197
+ * @param path string | number The string, set to iframe src and navigate to new page
198
198
  * eg: appEmbed.navigateToPage('pinboards')
199
+ * When used with `noReload` this can also be a number like 1/-1 to go forward/back.
200
+ * @param noReload boolean Trigger the navigation without reloading the page (version: 1.12.0 | 8.4.0.cl)
199
201
  */
200
- public navigateToPage(path: string): void {
201
- if (this.iFrame) {
202
+ public navigateToPage(path: string | number, noReload = false): void {
203
+ if (!this.iFrame) {
204
+ console.log('Please call render before invoking this method');
205
+ return;
206
+ }
207
+ if (noReload) {
208
+ this.trigger(HostEvent.Navigate, path);
209
+ } else {
210
+ if (typeof path !== 'string') {
211
+ console.warn(
212
+ 'Path can only by a string when triggered without noReload',
213
+ );
214
+ return;
215
+ }
202
216
  const iframeSrc = this.iFrame.src;
203
217
  const embedPath = '#/embed';
204
218
  const currentPath = iframeSrc.includes(embedPath) ? embedPath : '#';
205
219
  this.iFrame.src = `${
206
220
  iframeSrc.split(currentPath)[0]
207
221
  }${currentPath}/${path.replace(/^\/?#?\//, '')}`;
208
- } else {
209
- console.log('Please call render before invoking this method');
210
222
  }
211
223
  }
212
224
 
@@ -1,4 +1,7 @@
1
+ import EventEmitter from 'eventemitter3';
2
+ import * as auth from '../auth';
1
3
  import * as index from '../index';
4
+ import * as base from './base';
2
5
  import {
3
6
  executeAfterWait,
4
7
  getAllIframeEl,
@@ -9,10 +12,11 @@ import {
9
12
  } from '../test/test-utils';
10
13
 
11
14
  const thoughtSpotHost = 'tshost';
15
+ let authEE: EventEmitter;
12
16
 
13
17
  describe('Base TS Embed', () => {
14
18
  beforeAll(() => {
15
- index.init({
19
+ authEE = index.init({
16
20
  thoughtSpotHost,
17
21
  authType: index.AuthType.None,
18
22
  });
@@ -38,10 +42,12 @@ describe('Base TS Embed', () => {
38
42
  },
39
43
  '*',
40
44
  );
41
-
42
- jest.spyOn(window, 'alert').mockImplementation(() => {
45
+ jest.spyOn(window, 'alert').mockReset();
46
+ jest.spyOn(window, 'alert').mockImplementation(() => undefined);
47
+ authEE.on(auth.AuthStatus.FAILURE, (reason) => {
48
+ expect(reason).toEqual(auth.AuthFailureType.NO_COOKIE_ACCESS);
43
49
  expect(window.alert).toBeCalledWith(
44
- 'Third party cookie access is blocked on this browser, please allow third party cookies for ThoughtSpot to work properly',
50
+ 'Third party cookie access is blocked on this browser, please allow third party cookies for this to work properly. \nYou can use `suppressNoCookieAccessAlert` to suppress this message.',
45
51
  );
46
52
  done();
47
53
  });
@@ -92,4 +98,50 @@ describe('Base TS Embed', () => {
92
98
  expect(getIFrameSrc()).toContain('disableLoginRedirect=true');
93
99
  });
94
100
  });
101
+
102
+ test('handleAuth notifies for SDK auth failure', (done) => {
103
+ jest.spyOn(auth, 'authenticate').mockResolvedValue(false);
104
+ const authEmitter = index.init({
105
+ thoughtSpotHost,
106
+ authType: index.AuthType.Basic,
107
+ username: 'test',
108
+ password: 'test',
109
+ });
110
+ authEmitter.on(auth.AuthStatus.FAILURE, (reason) => {
111
+ expect(reason).toBe(auth.AuthFailureType.SDK);
112
+ done();
113
+ });
114
+ });
115
+
116
+ test('Logout method should disable autoLogin', () => {
117
+ jest.spyOn(window, 'fetch').mockResolvedValue({
118
+ type: 'opaque',
119
+ });
120
+ index.init({
121
+ thoughtSpotHost,
122
+ authType: index.AuthType.None,
123
+ autoLogin: true,
124
+ });
125
+ index.logout();
126
+ expect(window.fetch).toHaveBeenCalledWith(
127
+ `http://${thoughtSpotHost}${auth.EndPoints.LOGOUT}`,
128
+ {
129
+ credentials: 'include',
130
+ mode: 'no-cors',
131
+ method: 'POST',
132
+ },
133
+ );
134
+ expect(base.getEmbedConfig().autoLogin).toBe(false);
135
+ });
136
+ });
137
+
138
+ describe('Base without init', () => {
139
+ test('notify should error when called without init', () => {
140
+ base.reset();
141
+ jest.spyOn(global.console, 'error').mockImplementation(() => undefined);
142
+ base.notifyAuthSuccess();
143
+ base.notifyAuthFailure(auth.AuthFailureType.SDK);
144
+ base.notifyLogout();
145
+ expect(global.console.error).toHaveBeenCalledTimes(3);
146
+ });
95
147
  });
package/src/embed/base.ts CHANGED
@@ -7,31 +7,72 @@
7
7
  * @summary Base classes
8
8
  * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
9
9
  */
10
+ import EventEmitter from 'eventemitter3';
10
11
  import { getThoughtSpotHost } from '../config';
11
- import { EmbedConfig } from '../types';
12
- import { authenticate } from '../auth';
12
+ import { AuthType, EmbedConfig } from '../types';
13
+ import {
14
+ authenticate,
15
+ logout as _logout,
16
+ AuthFailureType,
17
+ AuthStatus,
18
+ } from '../auth';
13
19
  import { uploadMixpanelEvent, MIXPANEL_EVENT } from '../mixpanel-service';
14
20
 
15
21
  let config = {} as EmbedConfig;
22
+ const CONFIG_DEFAULTS: Partial<EmbedConfig> = {
23
+ loginFailedMessage: 'Not logged in',
24
+ authType: AuthType.None,
25
+ };
26
+
27
+ export let authPromise: Promise<boolean>;
28
+
29
+ export const getEmbedConfig = (): EmbedConfig => config;
30
+
31
+ export const getAuthPromise = (): Promise<boolean> => authPromise;
16
32
 
17
- export let authPromise: Promise<void>;
33
+ let authEE: EventEmitter;
18
34
 
35
+ export function notifyAuthSuccess(): void {
36
+ if (!authEE) {
37
+ console.error('SDK not initialized');
38
+ return;
39
+ }
40
+ authEE.emit(AuthStatus.SUCCESS);
41
+ }
42
+
43
+ export function notifyAuthFailure(failureType: AuthFailureType): void {
44
+ if (!authEE) {
45
+ console.error('SDK not initialized');
46
+ return;
47
+ }
48
+ authEE.emit(AuthStatus.FAILURE, failureType);
49
+ }
50
+
51
+ export function notifyLogout(): void {
52
+ if (!authEE) {
53
+ console.error('SDK not initialized');
54
+ return;
55
+ }
56
+ authEE.emit(AuthStatus.LOGOUT);
57
+ }
19
58
  /**
20
59
  * Perform authentication on the ThoughtSpot app as applicable.
21
60
  */
22
- export const handleAuth = (): Promise<void> => {
23
- const authConfig = {
24
- ...config,
25
- thoughtSpotHost: getThoughtSpotHost(config),
26
- };
27
- authPromise = authenticate(authConfig);
61
+ export const handleAuth = (): Promise<boolean> => {
62
+ authPromise = authenticate(config);
63
+ authPromise.then(
64
+ (isLoggedIn) => {
65
+ if (!isLoggedIn) {
66
+ notifyAuthFailure(AuthFailureType.SDK);
67
+ }
68
+ },
69
+ () => {
70
+ notifyAuthFailure(AuthFailureType.SDK);
71
+ },
72
+ );
28
73
  return authPromise;
29
74
  };
30
75
 
31
- export const getEmbedConfig = (): EmbedConfig => config;
32
-
33
- export const getAuthPromise = (): Promise<void> => authPromise;
34
-
35
76
  /**
36
77
  * Prefetches static resources from the specified URL. Web browsers can then cache the prefetched resources and serve them from the user's local disk to provide faster access to your app.
37
78
  * @param url The URL provided for prefetch
@@ -59,8 +100,13 @@ export const prefetch = (url?: string): void => {
59
100
  *
60
101
  * @returns authPromise Promise which resolves when authentication is complete.
61
102
  */
62
- export const init = (embedConfig: EmbedConfig): Promise<void> => {
63
- config = embedConfig;
103
+ export const init = (embedConfig: EmbedConfig): EventEmitter => {
104
+ config = {
105
+ ...CONFIG_DEFAULTS,
106
+ ...embedConfig,
107
+ thoughtSpotHost: getThoughtSpotHost(embedConfig),
108
+ };
109
+ authEE = new EventEmitter();
64
110
  handleAuth();
65
111
 
66
112
  uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_CALLED_INIT, {
@@ -71,7 +117,21 @@ export const init = (embedConfig: EmbedConfig): Promise<void> => {
71
117
  if (config.callPrefetch) {
72
118
  prefetch(config.thoughtSpotHost);
73
119
  }
74
- return authPromise;
120
+ return authEE;
121
+ };
122
+
123
+ export function disableAutoLogin(): void {
124
+ config.autoLogin = false;
125
+ }
126
+
127
+ export const logout = (doNotDisableAutoLogin = false): Promise<boolean> => {
128
+ if (!doNotDisableAutoLogin) {
129
+ disableAutoLogin();
130
+ }
131
+ return _logout(config).then((isLoggedIn) => {
132
+ notifyLogout();
133
+ return isLoggedIn;
134
+ });
75
135
  };
76
136
 
77
137
  let renderQueue: Promise<any> = Promise.resolve();
@@ -89,3 +149,10 @@ export const renderInQueue = (fn: (next?: (val?: any) => void) => void) => {
89
149
  fn(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
90
150
  }
91
151
  };
152
+
153
+ // For testing purposes only
154
+ export function reset(): void {
155
+ config = {} as any;
156
+ authEE = null;
157
+ authPromise = null;
158
+ }
@@ -71,7 +71,7 @@ describe('Custom CSS Url', () => {
71
71
  document.body.innerHTML = getDocumentBody();
72
72
  });
73
73
 
74
- test.only('passing customCssUrl should set the correct query params on the iframe', async (done) => {
74
+ test('passing customCssUrl should set the correct query params on the iframe', async (done) => {
75
75
  init({
76
76
  thoughtSpotHost,
77
77
  authType: AuthType.None,
@@ -5,6 +5,7 @@ import {
5
5
  SearchEmbed,
6
6
  PinboardEmbed,
7
7
  LiveboardEmbed,
8
+ AppEmbed,
8
9
  HostEvent,
9
10
  } from '../index';
10
11
  import {
@@ -247,4 +248,35 @@ describe('test communication between host app and ThoughtSpot', () => {
247
248
  };
248
249
  expect(mockPort.postMessage).toHaveBeenCalledWith(heightObj);
249
250
  });
251
+ test('ALL event listener should fire for all events with the event type set correctly', async () => {
252
+ const embed = new AppEmbed(getRootEl(), defaultViewConfig);
253
+ const spy = jest.fn();
254
+ embed.on(EmbedEvent.ALL, spy);
255
+ embed.render();
256
+
257
+ await executeAfterWait(() => {
258
+ const iframe = getIFrameEl();
259
+ postMessageToParent(iframe.contentWindow, {
260
+ type: EmbedEvent.CustomAction,
261
+ data: PAYLOAD,
262
+ });
263
+ postMessageToParent(iframe.contentWindow, {
264
+ type: EmbedEvent.DialogOpen,
265
+ });
266
+ });
267
+
268
+ await executeAfterWait(() => {
269
+ expect(spy).toHaveBeenCalledTimes(3);
270
+ expect(spy.mock.calls[0][0]).toMatchObject({
271
+ type: EmbedEvent.Init,
272
+ });
273
+ expect(spy.mock.calls[1][0]).toMatchObject({
274
+ type: EmbedEvent.CustomAction,
275
+ data: PAYLOAD,
276
+ });
277
+ expect(spy.mock.calls[2][0]).toMatchObject({
278
+ type: EmbedEvent.DialogOpen,
279
+ });
280
+ }, EVENT_WAIT_TIME);
281
+ });
250
282
  });
@@ -49,6 +49,10 @@ export interface SearchViewConfig extends ViewConfig {
49
49
  * using raw answer data.
50
50
  */
51
51
  hideResults?: boolean;
52
+ /**
53
+ * If set to true, expands all the data sources panel.
54
+ */
55
+ expandAllDataSource?: boolean;
52
56
  /**
53
57
  * If set to true, the Search Assist feature is enabled.
54
58
  */
@@ -126,6 +130,7 @@ export class SearchEmbed extends TsEmbed {
126
130
  private getIFrameSrc(answerId: string, dataSources?: string[]) {
127
131
  const {
128
132
  hideResults,
133
+ expandAllDataSource,
129
134
  enableSearchAssist,
130
135
  forceTable,
131
136
  searchOptions,
@@ -165,15 +165,16 @@ describe('Unit test case for ts embed', () => {
165
165
  });
166
166
  });
167
167
 
168
- describe('when thoughtSpotHost have value and authPromise return success response', () => {
168
+ describe('when thoughtSpotHost have value and authPromise return response true/false', () => {
169
169
  beforeAll(() => {
170
170
  init({
171
171
  thoughtSpotHost,
172
172
  authType: AuthType.None,
173
+ loginFailedMessage: 'Failed to Login',
173
174
  });
174
175
  });
175
176
 
176
- beforeEach(() => {
177
+ const setup = async (isLoggedIn = false) => {
177
178
  jest.spyOn(window, 'addEventListener').mockImplementationOnce(
178
179
  (event, handler, options) => {
179
180
  handler({
@@ -186,10 +187,9 @@ describe('Unit test case for ts embed', () => {
186
187
  },
187
188
  );
188
189
  const iFrame: any = document.createElement('div');
189
- jest.spyOn(
190
- baseInstance,
191
- 'getAuthPromise',
192
- ).mockResolvedValueOnce(() => Promise.resolve());
190
+ jest.spyOn(baseInstance, 'getAuthPromise').mockResolvedValueOnce(
191
+ isLoggedIn,
192
+ );
193
193
  const tsEmbed = new SearchEmbed(getRootEl(), {});
194
194
  iFrame.contentWindow = null;
195
195
  tsEmbed.on(EmbedEvent.CustomAction, jest.fn());
@@ -199,10 +199,11 @@ describe('Unit test case for ts embed', () => {
199
199
  },
200
200
  );
201
201
  jest.spyOn(document, 'createElement').mockReturnValueOnce(iFrame);
202
- tsEmbed.render();
203
- });
202
+ await tsEmbed.render();
203
+ };
204
204
 
205
- test('mixpanel should call with VISUAL_SDK_RENDER_COMPLETE', () => {
205
+ test('mixpanel should call with VISUAL_SDK_RENDER_COMPLETE', async () => {
206
+ await setup(true);
206
207
  expect(mockMixPanelEvent).toBeCalledWith(
207
208
  MIXPANEL_EVENT.VISUAL_SDK_RENDER_START,
208
209
  );
@@ -212,11 +213,20 @@ describe('Unit test case for ts embed', () => {
212
213
  });
213
214
 
214
215
  test('Should remove prefetch iframe', async () => {
216
+ await setup(true);
215
217
  const prefetchIframe = document.querySelectorAll<HTMLIFrameElement>(
216
218
  '.prefetchIframe',
217
219
  );
218
220
  expect(prefetchIframe.length).toBe(0);
219
221
  });
222
+
223
+ test('Should render failure when login fails', async (done) => {
224
+ setup(false);
225
+ executeAfterWait(() => {
226
+ expect(getRootEl().innerHTML).toContain('Failed to Login');
227
+ done();
228
+ });
229
+ });
220
230
  });
221
231
 
222
232
  describe('when thoughtSpotHost have value and authPromise return error', () => {
@@ -34,7 +34,7 @@ import {
34
34
  MessageCallbackObj,
35
35
  } from '../types';
36
36
  import { uploadMixpanelEvent, MIXPANEL_EVENT } from '../mixpanel-service';
37
- import { getProcessData } from '../utils/processData';
37
+ import { processEventData } from '../utils/processData';
38
38
  import { processTrigger } from '../utils/processTrigger';
39
39
  import pkgInfo from '../../package.json';
40
40
  import { getAuthPromise, getEmbedConfig, renderInQueue } from './base';
@@ -212,14 +212,6 @@ export class TsEmbed {
212
212
  this.isError = false;
213
213
  this.viewConfig = viewConfig;
214
214
  this.shouldEncodeUrlQueryParams = this.embedConfig.shouldEncodeUrlQueryParams;
215
- if (!this.embedConfig.suppressNoCookieAccessAlert) {
216
- this.on(EmbedEvent.NoCookieAccess, () => {
217
- // eslint-disable-next-line no-alert
218
- alert(
219
- 'Third party cookie access is blocked on this browser, please allow third party cookies for ThoughtSpot to work properly',
220
- );
221
- });
222
- }
223
215
  }
224
216
 
225
217
  /**
@@ -279,9 +271,10 @@ export class TsEmbed {
279
271
  * will be removed for ts7.oct.cl
280
272
  * @hidden
281
273
  */
282
- private formatEventData(event: MessageEvent) {
274
+ private formatEventData(event: MessageEvent, eventType: string) {
283
275
  const eventData = {
284
276
  ...event.data,
277
+ type: eventType,
285
278
  };
286
279
  if (!eventData.data) {
287
280
  eventData.data = event.data.payload;
@@ -299,11 +292,16 @@ export class TsEmbed {
299
292
  window.addEventListener('message', (event) => {
300
293
  const eventType = this.getEventType(event);
301
294
  const eventPort = this.getEventPort(event);
302
- const eventData = this.formatEventData(event);
295
+ const eventData = this.formatEventData(event, eventType);
303
296
  if (event.source === this.iFrame.contentWindow) {
304
297
  this.executeCallbacks(
305
298
  eventType,
306
- getProcessData(eventType, eventData, this.thoughtSpotHost),
299
+ processEventData(
300
+ eventType,
301
+ eventData,
302
+ this.thoughtSpotHost,
303
+ this.el,
304
+ ),
307
305
  eventPort,
308
306
  );
309
307
  }
@@ -457,12 +455,18 @@ export class TsEmbed {
457
455
  data: {
458
456
  timestamp: initTimestamp,
459
457
  },
458
+ type: EmbedEvent.Init,
460
459
  });
461
460
 
462
461
  uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
463
462
 
464
463
  getAuthPromise()
465
- ?.then(() => {
464
+ ?.then((isLoggedIn: boolean) => {
465
+ if (!isLoggedIn) {
466
+ this.el.innerHTML = this.embedConfig.loginFailedMessage;
467
+ return;
468
+ }
469
+
466
470
  uploadMixpanelEvent(
467
471
  MIXPANEL_EVENT.VISUAL_SDK_RENDER_COMPLETE,
468
472
  );
@@ -490,7 +494,7 @@ export class TsEmbed {
490
494
  frameWidth || DEFAULT_EMBED_WIDTH,
491
495
  );
492
496
  const height = getCssDimension(
493
- frameWidth || DEFAULT_EMBED_HEIGHT,
497
+ frameHeight || DEFAULT_EMBED_HEIGHT,
494
498
  );
495
499
  setAttributes(this.iFrame, restParams);
496
500
 
@@ -505,6 +509,7 @@ export class TsEmbed {
505
509
  data: {
506
510
  timestamp: loadTimestamp,
507
511
  },
512
+ type: EmbedEvent.Load,
508
513
  });
509
514
  uploadMixpanelEvent(
510
515
  MIXPANEL_EVENT.VISUAL_SDK_IFRAME_LOAD_PERFORMANCE,
@@ -533,6 +538,7 @@ export class TsEmbed {
533
538
  uploadMixpanelEvent(
534
539
  MIXPANEL_EVENT.VISUAL_SDK_RENDER_FAILED,
535
540
  );
541
+ this.el.innerHTML = this.embedConfig.loginFailedMessage;
536
542
  this.handleError(error);
537
543
  });
538
544
  });
@@ -558,6 +564,8 @@ export class TsEmbed {
558
564
  eventPort?: MessagePort | void,
559
565
  ): void {
560
566
  const callbacks = this.eventHandlerMap.get(eventType) || [];
567
+ const allHandlers = this.eventHandlerMap.get(EmbedEvent.ALL) || [];
568
+ callbacks.push(...allHandlers);
561
569
  const dataStatus = data?.status || embedEventStatus.END;
562
570
  callbacks.forEach((callbackObj) => {
563
571
  if (
@@ -760,9 +768,10 @@ export class V1Embed extends TsEmbed {
760
768
  public on(
761
769
  messageType: EmbedEvent,
762
770
  callback: MessageCallback,
771
+ options: MessageOptions = { start: false },
763
772
  ): typeof TsEmbed.prototype {
764
773
  const eventType = this.getCompatibleEventType(messageType);
765
774
 
766
- return super.on(eventType, callback);
775
+ return super.on(eventType, callback, options);
767
776
  }
768
777
  }
package/src/index.ts CHANGED
@@ -9,13 +9,14 @@
9
9
  */
10
10
 
11
11
  import { AppEmbed, Page, AppViewConfig } from './embed/app';
12
- import { init, prefetch } from './embed/base';
12
+ import { init, prefetch, logout } from './embed/base';
13
13
  import {
14
14
  PinboardEmbed,
15
15
  LiveboardViewConfig,
16
16
  LiveboardEmbed,
17
17
  } from './embed/liveboard';
18
18
  import { SearchEmbed, SearchViewConfig } from './embed/search';
19
+ import { AuthFailureType, AuthStatus } from './auth';
19
20
  import {
20
21
  AuthType,
21
22
  RuntimeFilter,
@@ -29,11 +30,14 @@ import {
29
30
 
30
31
  export {
31
32
  init,
33
+ logout,
32
34
  prefetch,
33
35
  SearchEmbed,
34
36
  PinboardEmbed,
35
37
  LiveboardEmbed,
36
38
  AppEmbed,
39
+ AuthFailureType,
40
+ AuthStatus,
37
41
  // types
38
42
  Page,
39
43
  AuthType,
@@ -14,7 +14,7 @@ type DOMElement = HTMLElement | Document;
14
14
 
15
15
  export const getRootEl = () => document.getElementById('embed');
16
16
 
17
- export const getRootEl2 = () => document.getElementById('emebed-2');
17
+ export const getRootEl2 = () => document.getElementById('embed-2');
18
18
 
19
19
  export const getIFrameEl = (container: DOMElement = document) =>
20
20
  container.querySelector('iframe');