@thoughtspot/visual-embed-sdk 1.13.0-alpha.2 → 1.13.0-alpha.3

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 (103) hide show
  1. package/CHANGELOG.md +25 -4
  2. package/README.md +1 -1
  3. package/dist/src/auth.d.ts +4 -1
  4. package/dist/src/auth.spec.d.ts +1 -0
  5. package/dist/src/embed/app.d.ts +5 -0
  6. package/dist/src/embed/liveboard.d.ts +7 -1
  7. package/dist/src/embed/search.d.ts +1 -0
  8. package/dist/src/embed/searchEmbed-basic-auth.spec.d.ts +1 -0
  9. package/dist/src/embed/ts-embed.d.ts +4 -3
  10. package/dist/src/errors.d.ts +2 -0
  11. package/dist/src/test/test-utils.d.ts +6 -0
  12. package/dist/src/types.d.ts +30 -2
  13. package/dist/src/utils/processTrigger.d.ts +1 -1
  14. package/dist/src/utils.d.ts +1 -0
  15. package/dist/tsembed.es.js +105 -20
  16. package/dist/tsembed.js +105 -20
  17. package/lib/package.json +4 -2
  18. package/lib/src/auth.d.ts +4 -1
  19. package/lib/src/auth.js +11 -2
  20. package/lib/src/auth.js.map +1 -1
  21. package/lib/src/auth.spec.d.ts +1 -0
  22. package/lib/src/auth.spec.js +13 -1
  23. package/lib/src/auth.spec.js.map +1 -1
  24. package/lib/src/config.spec.js +7 -0
  25. package/lib/src/config.spec.js.map +1 -1
  26. package/lib/src/embed/app.d.ts +5 -0
  27. package/lib/src/embed/app.js +1 -1
  28. package/lib/src/embed/app.js.map +1 -1
  29. package/lib/src/embed/app.spec.js +18 -6
  30. package/lib/src/embed/app.spec.js.map +1 -1
  31. package/lib/src/embed/embed.spec.js +2 -0
  32. package/lib/src/embed/embed.spec.js.map +1 -1
  33. package/lib/src/embed/events.spec.js +3 -1
  34. package/lib/src/embed/events.spec.js.map +1 -1
  35. package/lib/src/embed/liveboard.d.ts +7 -1
  36. package/lib/src/embed/liveboard.js +16 -1
  37. package/lib/src/embed/liveboard.js.map +1 -1
  38. package/lib/src/embed/liveboard.spec.js +21 -6
  39. package/lib/src/embed/liveboard.spec.js.map +1 -1
  40. package/lib/src/embed/pinboard.spec.js +7 -5
  41. package/lib/src/embed/pinboard.spec.js.map +1 -1
  42. package/lib/src/embed/search.d.ts +1 -0
  43. package/lib/src/embed/search.js +9 -1
  44. package/lib/src/embed/search.js.map +1 -1
  45. package/lib/src/embed/search.spec.js +9 -1
  46. package/lib/src/embed/search.spec.js.map +1 -1
  47. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts +1 -0
  48. package/lib/src/embed/searchEmbed-basic-auth.spec.js +96 -0
  49. package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  50. package/lib/src/embed/ts-embed.d.ts +4 -3
  51. package/lib/src/embed/ts-embed.js +11 -9
  52. package/lib/src/embed/ts-embed.js.map +1 -1
  53. package/lib/src/embed/ts-embed.spec.js +12 -5
  54. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  55. package/lib/src/errors.d.ts +2 -0
  56. package/lib/src/errors.js +2 -0
  57. package/lib/src/errors.js.map +1 -1
  58. package/lib/src/react/index.js +3 -2
  59. package/lib/src/react/index.js.map +1 -1
  60. package/lib/src/react/index.spec.js +6 -4
  61. package/lib/src/react/index.spec.js.map +1 -1
  62. package/lib/src/test/test-utils.d.ts +6 -0
  63. package/lib/src/test/test-utils.js +15 -0
  64. package/lib/src/test/test-utils.js.map +1 -1
  65. package/lib/src/types.d.ts +30 -2
  66. package/lib/src/types.js +22 -0
  67. package/lib/src/types.js.map +1 -1
  68. package/lib/src/utils/processTrigger.d.ts +1 -1
  69. package/lib/src/utils/processTrigger.js +28 -8
  70. package/lib/src/utils/processTrigger.js.map +1 -1
  71. package/lib/src/utils/processTrigger.spec.js +11 -1
  72. package/lib/src/utils/processTrigger.spec.js.map +1 -1
  73. package/lib/src/utils.d.ts +1 -0
  74. package/lib/src/utils.js +11 -0
  75. package/lib/src/utils.js.map +1 -1
  76. package/lib/src/utils.spec.js +22 -1
  77. package/lib/src/utils.spec.js.map +1 -1
  78. package/lib/src/visual-embed-sdk.d.ts +50 -7
  79. package/package.json +4 -2
  80. package/src/auth.spec.ts +20 -1
  81. package/src/auth.ts +12 -2
  82. package/src/config.spec.ts +11 -0
  83. package/src/embed/app.spec.ts +22 -3
  84. package/src/embed/app.ts +6 -0
  85. package/src/embed/embed.spec.ts +2 -0
  86. package/src/embed/events.spec.ts +3 -0
  87. package/src/embed/liveboard.spec.ts +31 -6
  88. package/src/embed/liveboard.ts +17 -0
  89. package/src/embed/pinboard.spec.ts +8 -6
  90. package/src/embed/search.spec.ts +11 -1
  91. package/src/embed/search.ts +15 -1
  92. package/src/embed/searchEmbed-basic-auth.spec.ts +115 -0
  93. package/src/embed/ts-embed.spec.ts +19 -5
  94. package/src/embed/ts-embed.ts +19 -11
  95. package/src/errors.ts +3 -0
  96. package/src/react/index.spec.tsx +7 -2
  97. package/src/react/index.tsx +3 -2
  98. package/src/test/test-utils.ts +16 -0
  99. package/src/types.ts +28 -0
  100. package/src/utils/processTrigger.spec.ts +11 -1
  101. package/src/utils/processTrigger.ts +36 -12
  102. package/src/utils.spec.ts +29 -0
  103. package/src/utils.ts +16 -0
@@ -75,7 +75,7 @@ export interface FrameParams {
75
75
  * This parameters will be passed on the iframe
76
76
  * as is.
77
77
  */
78
- [key: string]: string | number | boolean;
78
+ [key: string]: string | number | boolean | undefined;
79
79
  }
80
80
 
81
81
  /**
@@ -202,6 +202,8 @@ export class TsEmbed {
202
202
  */
203
203
  private shouldEncodeUrlQueryParams = false;
204
204
 
205
+ private defaultHiddenActions = [Action.ReportError];
206
+
205
207
  constructor(domSelector: DOMSelector, viewConfig?: ViewConfig) {
206
208
  this.el = this.getDOMNode(domSelector);
207
209
  // TODO: handle error
@@ -242,7 +244,7 @@ export class TsEmbed {
242
244
  error,
243
245
  });
244
246
  // Log error
245
- console.log(error);
247
+ console.error(error);
246
248
  }
247
249
 
248
250
  /**
@@ -382,9 +384,10 @@ export class TsEmbed {
382
384
  if (disabledActionReason) {
383
385
  queryParams[Param.DisableActionReason] = disabledActionReason;
384
386
  }
385
- if (hiddenActions?.length) {
386
- queryParams[Param.HideActions] = hiddenActions;
387
- }
387
+ queryParams[Param.HideActions] = [
388
+ ...this.defaultHiddenActions,
389
+ ...(hiddenActions ?? []),
390
+ ];
388
391
  if (Array.isArray(visibleActions)) {
389
392
  queryParams[Param.VisibleActions] = visibleActions;
390
393
  }
@@ -412,12 +415,16 @@ export class TsEmbed {
412
415
  showPrimaryNavbar = false,
413
416
  disableProfileAndHelp = false,
414
417
  isAppEmbed = false,
418
+ enableSearchAssist = false,
415
419
  ): string {
416
420
  const queryStringFrag = queryString ? `&${queryString}` : '';
417
421
  const primaryNavParam = `&primaryNavHidden=${!showPrimaryNavbar}`;
418
422
  const disableProfileAndHelpParam = `&profileAndHelpInNavBarHidden=${disableProfileAndHelp}`;
423
+ const enableSearchAssistParam = `&${Param.EnableSearchAssist}=${enableSearchAssist}`;
419
424
  let queryParams = `?embedApp=true${isAppEmbed ? primaryNavParam : ''}${
420
425
  isAppEmbed ? disableProfileAndHelpParam : ''
426
+ }${
427
+ enableSearchAssist ? enableSearchAssistParam : ''
421
428
  }${queryStringFrag}`;
422
429
  if (this.shouldEncodeUrlQueryParams) {
423
430
  queryParams = `?base64UrlEncodedFlags=${getEncodedQueryParamsString(
@@ -691,15 +698,16 @@ export class TsEmbed {
691
698
  * @param messageType The event type
692
699
  * @param data The payload to send with the message
693
700
  */
694
- public trigger(
695
- messageType: HostEvent,
696
- data: any,
697
- ): typeof TsEmbed.prototype {
698
- processTrigger(this.iFrame, messageType, this.thoughtSpotHost, data);
701
+ public trigger(messageType: HostEvent, data: any): Promise<any> {
699
702
  uploadMixpanelEvent(
700
703
  `${MIXPANEL_EVENT.VISUAL_SDK_TRIGGER}-${messageType}`,
701
704
  );
702
- return this;
705
+ return processTrigger(
706
+ this.iFrame,
707
+ messageType,
708
+ this.thoughtSpotHost,
709
+ data,
710
+ );
703
711
  }
704
712
 
705
713
  /**
package/src/errors.ts CHANGED
@@ -3,4 +3,7 @@ export const ERROR_MESSAGE = {
3
3
  'Error parsing ThoughtSpot host. Please provide a valid URL.',
4
4
  LIVEBOARD_VIZ_ID_VALIDATION:
5
5
  'Please provide either liveboardId or pinboardId',
6
+ TRIGGER_TIMED_OUT: 'Trigger timedout in getting response',
7
+ SEARCHEMBED_BETA_WRANING_MESSAGE:
8
+ 'Search Embed is in Beta in this release.',
6
9
  };
@@ -2,15 +2,17 @@ import React from 'react';
2
2
  import '@testing-library/jest-dom';
3
3
  import '@testing-library/jest-dom/extend-expect';
4
4
  import { cleanup, fireEvent, render, waitFor } from '@testing-library/react';
5
+ import { Action, EmbedEvent, HostEvent } from '../types';
5
6
  import {
6
7
  executeAfterWait,
7
8
  getIFrameEl,
8
9
  getIFrameSrc,
9
10
  postMessageToParent,
11
+ mockMessageChannel,
10
12
  } from '../test/test-utils';
11
13
  import { SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef } from './index';
12
14
  import { AuthType, init } from '../index';
13
- import { EmbedEvent, HostEvent } from '../types';
15
+
14
16
  import { version } from '../../package.json';
15
17
 
16
18
  const thoughtSpotHost = 'localhost';
@@ -20,6 +22,7 @@ beforeAll(() => {
20
22
  thoughtSpotHost,
21
23
  authType: AuthType.None,
22
24
  });
25
+ spyOn(window, 'alert');
23
26
  });
24
27
 
25
28
  describe('React Components', () => {
@@ -37,7 +40,7 @@ describe('React Components', () => {
37
40
  ),
38
41
  ).toBe(true);
39
42
  expect(getIFrameSrc(container)).toBe(
40
- `http://${thoughtSpotHost}/?hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}&hideAction=[%22editACopy%22,%22saveAsView%22,%22updateTSL%22,%22editTSL%22,%22onDeleteAnswer%22]&dataSourceMode=hide&useLastSelectedSources=false&isSearchEmbed=true#/embed/answer`,
43
+ `http://${thoughtSpotHost}/?hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}&hideAction=[%22${Action.ReportError}%22,%22editACopy%22,%22saveAsView%22,%22updateTSL%22,%22editTSL%22,%22onDeleteAnswer%22]&dataSourceMode=hide&useLastSelectedSources=false&isSearchEmbed=true#/embed/answer`,
41
44
  );
42
45
  });
43
46
 
@@ -73,6 +76,7 @@ describe('React Components', () => {
73
76
  describe('LiveboardEmbed', () => {
74
77
  //
75
78
  it('Should be able to trigger events on the embed using refs', async () => {
79
+ mockMessageChannel();
76
80
  const TestComponent = () => {
77
81
  const embedRef = useEmbedRef();
78
82
  const onLiveboardRendered = () => {
@@ -109,6 +113,7 @@ describe('React Components', () => {
109
113
  data: ['viz1', 'viz2'],
110
114
  },
111
115
  `http://${thoughtSpotHost}`,
116
+ expect.anything(),
112
117
  );
113
118
  });
114
119
  });
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
2
3
  import { SearchEmbed as _SearchEmbed, SearchViewConfig } from '../embed/search';
3
4
  import { AppEmbed as _AppEmbed, AppViewConfig } from '../embed/app';
4
5
  import {
@@ -25,7 +26,7 @@ const componentFactory = <
25
26
  Omit<U, 'className'>,
26
27
  V
27
28
  >(embedProps);
28
- React.useEffect(() => {
29
+ useDeepCompareEffect(() => {
29
30
  const tsEmbed = new EmbedConstructor(ref!.current, {
30
31
  ...viewConfig,
31
32
  });
@@ -40,7 +41,7 @@ const componentFactory = <
40
41
  // eslint-disable-next-line no-param-reassign
41
42
  forwardedRef.current = tsEmbed;
42
43
  }
43
- }, [embedProps]);
44
+ }, [viewConfig, listeners]);
44
45
 
45
46
  return (
46
47
  <div
@@ -70,3 +70,19 @@ export const EVENT_WAIT_TIME = 1000;
70
70
  export function fixedEncodeURI(str: string) {
71
71
  return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']');
72
72
  }
73
+
74
+ /**
75
+ * MessageChannel is available in Node > 15.0.0. Since the current node environment's
76
+ * used for github actions is not above 14, we are mocking this for the current unit tests.
77
+ */
78
+ export const messageChannelMock: any = {
79
+ port1: {},
80
+ port2: {},
81
+ };
82
+ export const mockMessageChannel = () => {
83
+ messageChannelMock.port1.close = jest.fn();
84
+ messageChannelMock.port2.onmessage = jest.fn();
85
+ window.MessageChannel = function MessageChannelMock() {
86
+ return messageChannelMock;
87
+ } as any;
88
+ };
package/src/types.ts CHANGED
@@ -19,8 +19,13 @@ export enum AuthType {
19
19
  None = 'None',
20
20
  /**
21
21
  * SSO using SAML
22
+ * @deprecated Use {@link SAML} instead
22
23
  */
23
24
  SSO = 'SSO_SAML',
25
+ /**
26
+ * SSO using SAML
27
+ */
28
+ SAML = 'SSO_SAML',
24
29
  /**
25
30
  * SSO using OIDC
26
31
  */
@@ -177,6 +182,12 @@ export interface EmbedConfig {
177
182
  * @version SDK: 1.10.4 | ThoughtSpot: *
178
183
  */
179
184
  detectCookieAccessSlow?: boolean;
185
+ /**
186
+ * Hide beta alert warning message for SearchEmbed.
187
+ *
188
+ * @version SDK: 1.12.0 | ThoughtSpot: *
189
+ */
190
+ suppressSearchEmbedBetaWarning?: boolean;
180
191
  }
181
192
 
182
193
  /**
@@ -608,6 +619,18 @@ export enum HostEvent {
608
619
  * @version SDK: 1.12.0 | ThoughtSpot: 8.4.0.cl
609
620
  */
610
621
  Navigate = 'Navigate',
622
+ /**
623
+ * Gets the current pinboard content.
624
+ * @version SDK: 1.13.0 | ThoughtSpot: 8.5.0.cl
625
+ */
626
+ getExportRequestForCurrentPinboard = 'getExportRequestForCurrentPinboard',
627
+ /**
628
+ * Fires the pin action on an embedded object
629
+ * @param - incase of liveboard embed, takes in an object with vizId as a key
630
+ * can be left empty for search and viz embeds
631
+ * @version SDK: 1.15.0 | ThoughtSpot: 8.7.0.cl
632
+ */
633
+ Pin = 'pin',
611
634
  }
612
635
 
613
636
  /**
@@ -656,6 +679,7 @@ export enum Param {
656
679
  fullHeight = 'isFullHeightPinboard',
657
680
  livedBoardEmbed = 'isLiveboardEmbed',
658
681
  searchEmbed = 'isSearchEmbed',
682
+ vizEmbed = 'isVizEmbed',
659
683
  Version = 'sdkVersion',
660
684
  ViewPortHeight = 'viewPortHeight',
661
685
  ViewPortWidth = 'viewPortWidth',
@@ -808,6 +832,10 @@ export enum Action {
808
832
  * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl
809
833
  */
810
834
  CreateMonitor = 'createMonitor',
835
+ /**
836
+ * @version SDK: 1.11.1 | ThoughtSpot: 8.3.0.cl
837
+ */
838
+ ReportError = 'reportError',
811
839
  }
812
840
 
813
841
  export interface SessionInterface {
@@ -1,5 +1,6 @@
1
1
  import * as _processTriggerInstance from './processTrigger';
2
2
  import { HostEvent } from '../types';
3
+ import { messageChannelMock, mockMessageChannel } from '../test/test-utils';
3
4
 
4
5
  describe('Unit test for processTrigger', () => {
5
6
  const iFrame: any = {
@@ -29,12 +30,21 @@ describe('Unit test for processTrigger', () => {
29
30
  const messageType = HostEvent.Search;
30
31
  const thoughtSpotHost = 'http://localhost:3000';
31
32
  const data = {};
32
- _processTriggerInstance.processTrigger(
33
+ mockMessageChannel();
34
+ const triggerPromise = _processTriggerInstance.processTrigger(
33
35
  iFrame,
34
36
  messageType,
35
37
  thoughtSpotHost,
36
38
  data,
37
39
  );
38
40
  expect(iFrame.contentWindow.postMessage).toBeCalled();
41
+ const res = {
42
+ data: {
43
+ test: '123',
44
+ },
45
+ };
46
+ messageChannelMock.port1.onmessage(res);
47
+ expect(messageChannelMock.port1.close).toBeCalled();
48
+ expect(triggerPromise).resolves.toEqual(res.data);
39
49
  });
40
50
  });
@@ -1,3 +1,4 @@
1
+ import { ERROR_MESSAGE } from '../errors';
1
2
  import { HostEvent } from '../types';
2
3
 
3
4
  /**
@@ -17,24 +18,47 @@ function postIframeMessage(
17
18
  iFrame: HTMLIFrameElement,
18
19
  message: { type: HostEvent; data: any },
19
20
  thoughtSpotHost: string,
21
+ channel?: MessageChannel,
20
22
  ) {
21
- return iFrame.contentWindow.postMessage(message, thoughtSpotHost);
23
+ return iFrame.contentWindow.postMessage(message, thoughtSpotHost, [
24
+ channel?.port2,
25
+ ]);
22
26
  }
23
27
 
28
+ const TRIGGER_TIMEOUT = 30000;
29
+
24
30
  export function processTrigger(
25
31
  iFrame: HTMLIFrameElement,
26
32
  messageType: HostEvent,
27
33
  thoughtSpotHost: string,
28
34
  data: any,
29
- ) {
30
- switch (messageType) {
31
- case HostEvent.Reload:
32
- return reload(iFrame);
33
- default:
34
- return postIframeMessage(
35
- iFrame,
36
- { type: messageType, data },
37
- thoughtSpotHost,
38
- );
39
- }
35
+ ): Promise<any> {
36
+ return new Promise<any>((res, rej) => {
37
+ if (messageType === HostEvent.Reload) {
38
+ reload(iFrame);
39
+ return res(null);
40
+ }
41
+ const channel = new MessageChannel();
42
+ channel.port1.onmessage = ({ data: responseData }) => {
43
+ channel.port1.close();
44
+ if (responseData.error) {
45
+ rej(responseData.error);
46
+ } else {
47
+ res(responseData);
48
+ }
49
+ };
50
+
51
+ // Close the messageChannel and resolve the promise if timeout.
52
+ setTimeout(() => {
53
+ channel.port1.close();
54
+ res(new Error(ERROR_MESSAGE.TRIGGER_TIMED_OUT));
55
+ }, TRIGGER_TIMEOUT);
56
+
57
+ return postIframeMessage(
58
+ iFrame,
59
+ { type: messageType, data },
60
+ thoughtSpotHost,
61
+ channel,
62
+ );
63
+ });
40
64
  }
package/src/utils.spec.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  getEncodedQueryParamsString,
10
10
  appendToUrlHash,
11
11
  getRedirectUrl,
12
+ checkReleaseVersionInBeta,
12
13
  } from './utils';
13
14
  import { RuntimeFilterOp } from './types';
14
15
 
@@ -110,4 +111,32 @@ describe('unit test for utils', () => {
110
111
  expect(getEncodedQueryParamsString('')).toBe('');
111
112
  expect(getEncodedQueryParamsString('test')).toBe('dGVzdA');
112
113
  });
114
+
115
+ test('when ReleaseVersion is empty ', () => {
116
+ expect(checkReleaseVersionInBeta('', false)).toBe(false);
117
+ });
118
+
119
+ test('when ReleaseVersion is 7.0.1.cl ', () => {
120
+ expect(checkReleaseVersionInBeta('7.0.1.cl', false)).toBe(false);
121
+ });
122
+
123
+ test('when cluster has dev version', () => {
124
+ expect(checkReleaseVersionInBeta('dev', false)).toBe(false);
125
+ });
126
+
127
+ test('when cluster is above 8.4.0.cl-11 software version', () => {
128
+ expect(checkReleaseVersionInBeta('8.4.0.cl-117', false)).toBe(false);
129
+ });
130
+
131
+ test('when cluster is bellow 8.0.0.sw software version', () => {
132
+ expect(checkReleaseVersionInBeta('7.2.1.sw', false)).toBe(true);
133
+ });
134
+
135
+ test('when suppressBetaWarning is true and ReleaseVersion is 7.0.1', () => {
136
+ expect(checkReleaseVersionInBeta('7.0.1', true)).toBe(false);
137
+ });
138
+
139
+ test('when suppressBetaWarning is false ReleaseVersion is 7.0.1', () => {
140
+ expect(checkReleaseVersionInBeta('7.0.1', false)).toBe(true);
141
+ });
113
142
  });
package/src/utils.ts CHANGED
@@ -154,3 +154,19 @@ export const setAttributes = (
154
154
  element.setAttribute(key, attributes[key].toString());
155
155
  });
156
156
  };
157
+
158
+ const isCloudRelease = (version: string) => version.endsWith('.cl');
159
+
160
+ /* For Search Embed: ReleaseVersionInBeta */
161
+ export const checkReleaseVersionInBeta = (
162
+ releaseVersion: string,
163
+ suppressBetaWarning: boolean,
164
+ ): boolean => {
165
+ if (releaseVersion !== '' && !isCloudRelease(releaseVersion)) {
166
+ const splittedReleaseVersion = releaseVersion.split('.');
167
+ const majorVersion = Number(splittedReleaseVersion[0]);
168
+ const isBetaVersion = majorVersion < 8;
169
+ return !suppressBetaWarning && isBetaVersion;
170
+ }
171
+ return false;
172
+ };