@thoughtspot/visual-embed-sdk 1.11.0-auth.9 → 1.11.2
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.
- package/CHANGELOG.md +24 -2
- package/README.md +15 -2
- package/dist/src/auth.d.ts +13 -0
- package/dist/src/embed/base.d.ts +34 -4
- package/dist/src/embed/search.d.ts +4 -0
- package/dist/src/embed/ts-embed.d.ts +2 -2
- package/dist/src/index.d.ts +3 -2
- package/dist/src/types.d.ts +32 -1
- package/dist/src/utils/authService.d.ts +1 -0
- package/dist/src/utils/processData.d.ts +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/dist/tsembed.es.js +1483 -2869
- package/dist/tsembed.js +1481 -2868
- package/lib/package.json +3 -2
- package/lib/src/auth.d.ts +13 -0
- package/lib/src/auth.js +35 -9
- package/lib/src/auth.js.map +1 -1
- package/lib/src/auth.spec.js +76 -1
- package/lib/src/auth.spec.js.map +1 -1
- package/lib/src/embed/app.spec.js +4 -3
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/base.d.ts +34 -4
- package/lib/src/embed/base.js +79 -11
- package/lib/src/embed/base.js.map +1 -1
- package/lib/src/embed/base.spec.js +50 -2
- package/lib/src/embed/base.spec.js.map +1 -1
- package/lib/src/embed/embed.spec.js +1 -1
- package/lib/src/embed/embed.spec.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +4 -3
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/pinboard.spec.js +4 -3
- package/lib/src/embed/pinboard.spec.js.map +1 -1
- package/lib/src/embed/search.d.ts +4 -0
- package/lib/src/embed/search.js +1 -1
- package/lib/src/embed/search.js.map +1 -1
- package/lib/src/embed/search.spec.js +6 -1
- package/lib/src/embed/search.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +2 -2
- package/lib/src/embed/ts-embed.js +11 -20
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +1 -1
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/index.d.ts +3 -2
- package/lib/src/index.js +3 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/react/index.spec.js +2 -2
- package/lib/src/react/index.spec.js.map +1 -1
- package/lib/src/types.d.ts +32 -1
- package/lib/src/types.js +14 -0
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/authService.d.ts +1 -0
- package/lib/src/utils/authService.js +12 -2
- package/lib/src/utils/authService.js.map +1 -1
- package/lib/src/utils/authService.spec.js +10 -0
- package/lib/src/utils/authService.spec.js.map +1 -1
- package/lib/src/utils/processData.d.ts +1 -1
- package/lib/src/utils/processData.js +37 -3
- package/lib/src/utils/processData.js.map +1 -1
- package/lib/src/utils/processData.spec.js +106 -4
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils.d.ts +1 -0
- package/lib/src/utils.js +4 -0
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js +14 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +135 -8
- package/package.json +3 -2
- package/src/auth.spec.ts +98 -2
- package/src/auth.ts +44 -7
- package/src/embed/app.spec.ts +4 -3
- package/src/embed/base.spec.ts +57 -3
- package/src/embed/base.ts +96 -15
- package/src/embed/embed.spec.ts +1 -1
- package/src/embed/liveboard.spec.ts +4 -3
- package/src/embed/pinboard.spec.ts +4 -3
- package/src/embed/search.spec.ts +6 -1
- package/src/embed/search.ts +5 -0
- package/src/embed/ts-embed.spec.ts +1 -1
- package/src/embed/ts-embed.ts +17 -23
- package/src/index.ts +5 -1
- package/src/react/index.spec.tsx +3 -2
- package/src/types.ts +32 -0
- package/src/utils/authService.spec.ts +13 -0
- package/src/utils/authService.ts +14 -2
- package/src/utils/processData.spec.ts +139 -4
- package/src/utils/processData.ts +54 -4
- package/src/utils.spec.ts +26 -0
- package/src/utils.ts +5 -0
- package/dist/src/embed/pinboard.d.ts +0 -91
- package/dist/src/utils/plugin.d.ts +0 -0
- package/dist/src/v1/api.d.ts +0 -19
- package/lib/src/embed/pinboard.d.ts +0 -91
- package/lib/src/embed/pinboard.js +0 -110
- package/lib/src/embed/pinboard.js.map +0 -1
- package/lib/src/utils/plugin.d.ts +0 -0
- package/lib/src/utils/plugin.js +0 -1
- package/lib/src/utils/plugin.js.map +0 -1
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 {
|
|
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
|
+
};
|
|
16
26
|
|
|
17
27
|
export let authPromise: Promise<boolean>;
|
|
18
28
|
|
|
29
|
+
export const getEmbedConfig = (): EmbedConfig => config;
|
|
30
|
+
|
|
31
|
+
export const getAuthPromise = (): Promise<boolean> => authPromise;
|
|
32
|
+
|
|
33
|
+
let authEE: EventEmitter;
|
|
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
61
|
export const handleAuth = (): Promise<boolean> => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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<boolean> => 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
|
|
@@ -57,10 +98,18 @@ export const prefetch = (url?: string): void => {
|
|
|
57
98
|
* @param embedConfig The configuration object containing ThoughtSpot host,
|
|
58
99
|
* authentication mechanism and so on.
|
|
59
100
|
*
|
|
60
|
-
*
|
|
101
|
+
* eg: authStatus = init(config);
|
|
102
|
+
* authStatus.on(AuthStatus.FAILURE, (reason) => { // do something here });
|
|
103
|
+
*
|
|
104
|
+
* @returns event emitter which emits events on authentication success, failure and logout. {@link AuthStatus}
|
|
61
105
|
*/
|
|
62
|
-
export const init = (embedConfig: EmbedConfig):
|
|
63
|
-
config =
|
|
106
|
+
export const init = (embedConfig: EmbedConfig): EventEmitter => {
|
|
107
|
+
config = {
|
|
108
|
+
...CONFIG_DEFAULTS,
|
|
109
|
+
...embedConfig,
|
|
110
|
+
thoughtSpotHost: getThoughtSpotHost(embedConfig),
|
|
111
|
+
};
|
|
112
|
+
authEE = new EventEmitter();
|
|
64
113
|
handleAuth();
|
|
65
114
|
|
|
66
115
|
uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_CALLED_INIT, {
|
|
@@ -71,7 +120,32 @@ export const init = (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
|
71
120
|
if (config.callPrefetch) {
|
|
72
121
|
prefetch(config.thoughtSpotHost);
|
|
73
122
|
}
|
|
74
|
-
return
|
|
123
|
+
return authEE;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export function disableAutoLogin(): void {
|
|
127
|
+
config.autoLogin = false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Logout from ThoughtSpot. This also sets the autoLogin flag to false, to prevent
|
|
132
|
+
* the SDK from automatically logging in again.
|
|
133
|
+
*
|
|
134
|
+
* You can call the `init` method again to re login, if autoLogin is set to true in this
|
|
135
|
+
* second call it will be honored.
|
|
136
|
+
*
|
|
137
|
+
* @param doNotDisableAutoLogin This flag when passed will not disable autoLogin
|
|
138
|
+
* @returns Promise which resolves when logout completes.
|
|
139
|
+
* @version SDK: 1.10.1 | ThoughtSpot: *
|
|
140
|
+
*/
|
|
141
|
+
export const logout = (doNotDisableAutoLogin = false): Promise<boolean> => {
|
|
142
|
+
if (!doNotDisableAutoLogin) {
|
|
143
|
+
disableAutoLogin();
|
|
144
|
+
}
|
|
145
|
+
return _logout(config).then((isLoggedIn) => {
|
|
146
|
+
notifyLogout();
|
|
147
|
+
return isLoggedIn;
|
|
148
|
+
});
|
|
75
149
|
};
|
|
76
150
|
|
|
77
151
|
let renderQueue: Promise<any> = Promise.resolve();
|
|
@@ -89,3 +163,10 @@ export const renderInQueue = (fn: (next?: (val?: any) => void) => void) => {
|
|
|
89
163
|
fn(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
|
90
164
|
}
|
|
91
165
|
};
|
|
166
|
+
|
|
167
|
+
// For testing purposes only
|
|
168
|
+
export function reset(): void {
|
|
169
|
+
config = {} as any;
|
|
170
|
+
authEE = null;
|
|
171
|
+
authPromise = null;
|
|
172
|
+
}
|
package/src/embed/embed.spec.ts
CHANGED
|
@@ -71,7 +71,7 @@ describe('Custom CSS Url', () => {
|
|
|
71
71
|
document.body.innerHTML = getDocumentBody();
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
test
|
|
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,
|
|
@@ -18,7 +18,8 @@ const defaultViewConfig = {
|
|
|
18
18
|
const liveboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
|
|
19
19
|
const vizId = '6e73f724-660e-11eb-ae93-0242ac130002';
|
|
20
20
|
const thoughtSpotHost = 'tshost';
|
|
21
|
-
const
|
|
21
|
+
const defaultParamsSansHideAction = `&hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}`;
|
|
22
|
+
const defaultParams = `${defaultParamsSansHideAction}&hideAction=[%22${Action.ReportError}%22]`;
|
|
22
23
|
const prefixParams = '&isLiveboardEmbed=true&isPinboardV2Enabled=false';
|
|
23
24
|
|
|
24
25
|
beforeAll(() => {
|
|
@@ -60,7 +61,7 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
60
61
|
liveboardEmbed.render();
|
|
61
62
|
await executeAfterWait(() => {
|
|
62
63
|
expect(getIFrameSrc()).toBe(
|
|
63
|
-
`http://${thoughtSpotHost}/?embedApp=true${
|
|
64
|
+
`http://${thoughtSpotHost}/?embedApp=true${defaultParamsSansHideAction}&disableAction=[%22${Action.DownloadAsCsv}%22,%22${Action.DownloadAsPdf}%22,%22${Action.DownloadAsXlsx}%22]&disableHint=Action%20denied&hideAction=[%22${Action.ReportError}%22]${prefixParams}#/embed/viz/${liveboardId}`,
|
|
64
65
|
);
|
|
65
66
|
});
|
|
66
67
|
});
|
|
@@ -78,7 +79,7 @@ describe('Liveboard/viz embed tests', () => {
|
|
|
78
79
|
liveboardEmbed.render();
|
|
79
80
|
await executeAfterWait(() => {
|
|
80
81
|
expect(getIFrameSrc()).toBe(
|
|
81
|
-
`http://${thoughtSpotHost}/?embedApp=true${
|
|
82
|
+
`http://${thoughtSpotHost}/?embedApp=true${defaultParamsSansHideAction}&hideAction=[%22${Action.ReportError}%22,%22${Action.DownloadAsCsv}%22,%22${Action.DownloadAsPdf}%22,%22${Action.DownloadAsXlsx}%22]${prefixParams}#/embed/viz/${liveboardId}`,
|
|
82
83
|
);
|
|
83
84
|
});
|
|
84
85
|
});
|
|
@@ -18,7 +18,8 @@ const defaultViewConfig = {
|
|
|
18
18
|
const pinboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
|
|
19
19
|
const vizId = '6e73f724-660e-11eb-ae93-0242ac130002';
|
|
20
20
|
const thoughtSpotHost = 'tshost';
|
|
21
|
-
const
|
|
21
|
+
const defaultParamsWithoutHideActions = `&hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}`;
|
|
22
|
+
const defaultParams = `${defaultParamsWithoutHideActions}&hideAction=[%22${Action.ReportError}%22]`;
|
|
22
23
|
const prefixParams = '&isLiveboardEmbed=true&isPinboardV2Enabled=false';
|
|
23
24
|
|
|
24
25
|
beforeAll(() => {
|
|
@@ -60,7 +61,7 @@ describe('Pinboard/viz embed tests', () => {
|
|
|
60
61
|
pinboardEmbed.render();
|
|
61
62
|
await executeAfterWait(() => {
|
|
62
63
|
expect(getIFrameSrc()).toBe(
|
|
63
|
-
`http://${thoughtSpotHost}/?embedApp=true${
|
|
64
|
+
`http://${thoughtSpotHost}/?embedApp=true${defaultParamsWithoutHideActions}&disableAction=[%22${Action.DownloadAsCsv}%22,%22${Action.DownloadAsPdf}%22,%22${Action.DownloadAsXlsx}%22]&disableHint=Action%20denied&hideAction=[%22${Action.ReportError}%22]${prefixParams}#/embed/viz/${pinboardId}`,
|
|
64
65
|
);
|
|
65
66
|
});
|
|
66
67
|
});
|
|
@@ -78,7 +79,7 @@ describe('Pinboard/viz embed tests', () => {
|
|
|
78
79
|
pinboardEmbed.render();
|
|
79
80
|
await executeAfterWait(() => {
|
|
80
81
|
expect(getIFrameSrc()).toBe(
|
|
81
|
-
`http://${thoughtSpotHost}/?embedApp=true${
|
|
82
|
+
`http://${thoughtSpotHost}/?embedApp=true${defaultParamsWithoutHideActions}&hideAction=[%22${Action.ReportError}%22,%22${Action.DownloadAsCsv}%22,%22${Action.DownloadAsPdf}%22,%22${Action.DownloadAsXlsx}%22]${prefixParams}#/embed/viz/${pinboardId}`,
|
|
82
83
|
);
|
|
83
84
|
});
|
|
84
85
|
});
|
package/src/embed/search.spec.ts
CHANGED
|
@@ -20,7 +20,10 @@ const answerId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
|
|
|
20
20
|
const thoughtSpotHost = 'tshost';
|
|
21
21
|
const defaultParams = `hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}`;
|
|
22
22
|
const hideBydefault = `&hideAction=${fixedEncodeURI(
|
|
23
|
-
JSON.stringify(
|
|
23
|
+
JSON.stringify([
|
|
24
|
+
Action.ReportError,
|
|
25
|
+
...HiddenActionItemByDefaultForSearchEmbed,
|
|
26
|
+
]),
|
|
24
27
|
)}`;
|
|
25
28
|
const defaultParamsWithHiddenActions = defaultParams + hideBydefault;
|
|
26
29
|
const prefixParams = '&isSearchEmbed=true';
|
|
@@ -197,6 +200,7 @@ describe('Search embed tests', () => {
|
|
|
197
200
|
searchEmbed.render();
|
|
198
201
|
const hideActionUrl = fixedEncodeURI(
|
|
199
202
|
JSON.stringify([
|
|
203
|
+
Action.ReportError,
|
|
200
204
|
...hiddenActionsForSearch,
|
|
201
205
|
...HiddenActionItemByDefaultForSearchEmbed,
|
|
202
206
|
]),
|
|
@@ -220,6 +224,7 @@ describe('Search embed tests', () => {
|
|
|
220
224
|
searchEmbed.render();
|
|
221
225
|
const hideActionUrl = fixedEncodeURI(
|
|
222
226
|
JSON.stringify([
|
|
227
|
+
Action.ReportError,
|
|
223
228
|
...hiddenActionsForSearch,
|
|
224
229
|
...HiddenActionItemByDefaultForSearchEmbed,
|
|
225
230
|
]),
|
package/src/embed/search.ts
CHANGED
|
@@ -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,
|
|
@@ -34,7 +34,7 @@ const defaultViewConfig = {
|
|
|
34
34
|
const pinboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
|
|
35
35
|
const liveboardId = 'eca215d4-0d2c-4a55-90e3-d81ef6848ae0';
|
|
36
36
|
const thoughtSpotHost = 'tshost';
|
|
37
|
-
const defaultParamsForPinboardEmbed = `hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}`;
|
|
37
|
+
const defaultParamsForPinboardEmbed = `hostAppUrl=local-host&viewPortHeight=768&viewPortWidth=1024&sdkVersion=${version}&hideAction=[%22${Action.ReportError}%22]`;
|
|
38
38
|
const defaultParamsPost = '&isPinboardV2Enabled=false';
|
|
39
39
|
|
|
40
40
|
describe('Unit test case for ts embed', () => {
|
package/src/embed/ts-embed.ts
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
MessageCallbackObj,
|
|
35
35
|
} from '../types';
|
|
36
36
|
import { uploadMixpanelEvent, MIXPANEL_EVENT } from '../mixpanel-service';
|
|
37
|
-
import {
|
|
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';
|
|
@@ -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
|
|
@@ -212,20 +214,6 @@ export class TsEmbed {
|
|
|
212
214
|
this.isError = false;
|
|
213
215
|
this.viewConfig = viewConfig;
|
|
214
216
|
this.shouldEncodeUrlQueryParams = this.embedConfig.shouldEncodeUrlQueryParams;
|
|
215
|
-
this.on(EmbedEvent.NoCookieAccess, () => {
|
|
216
|
-
if (!this.embedConfig.suppressNoCookieAccessAlert) {
|
|
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 this to work properly. \nYou can use `suppressNoCookieAccessAlert` to suppress this message.',
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
this.el.innerHTML = this.getLoginFiledMessage();
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private getLoginFiledMessage(): string {
|
|
227
|
-
const { loginFailedMessage } = this.embedConfig;
|
|
228
|
-
return loginFailedMessage || 'Login failed';
|
|
229
217
|
}
|
|
230
218
|
|
|
231
219
|
/**
|
|
@@ -310,7 +298,12 @@ export class TsEmbed {
|
|
|
310
298
|
if (event.source === this.iFrame.contentWindow) {
|
|
311
299
|
this.executeCallbacks(
|
|
312
300
|
eventType,
|
|
313
|
-
|
|
301
|
+
processEventData(
|
|
302
|
+
eventType,
|
|
303
|
+
eventData,
|
|
304
|
+
this.thoughtSpotHost,
|
|
305
|
+
this.el,
|
|
306
|
+
),
|
|
314
307
|
eventPort,
|
|
315
308
|
);
|
|
316
309
|
}
|
|
@@ -391,9 +384,10 @@ export class TsEmbed {
|
|
|
391
384
|
if (disabledActionReason) {
|
|
392
385
|
queryParams[Param.DisableActionReason] = disabledActionReason;
|
|
393
386
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
387
|
+
queryParams[Param.HideActions] = [
|
|
388
|
+
...this.defaultHiddenActions,
|
|
389
|
+
...(hiddenActions ?? []),
|
|
390
|
+
];
|
|
397
391
|
if (Array.isArray(visibleActions)) {
|
|
398
392
|
queryParams[Param.VisibleActions] = visibleActions;
|
|
399
393
|
}
|
|
@@ -472,7 +466,7 @@ export class TsEmbed {
|
|
|
472
466
|
getAuthPromise()
|
|
473
467
|
?.then((isLoggedIn: boolean) => {
|
|
474
468
|
if (!isLoggedIn) {
|
|
475
|
-
this.el.innerHTML = this.
|
|
469
|
+
this.el.innerHTML = this.embedConfig.loginFailedMessage;
|
|
476
470
|
return;
|
|
477
471
|
}
|
|
478
472
|
|
|
@@ -503,7 +497,7 @@ export class TsEmbed {
|
|
|
503
497
|
frameWidth || DEFAULT_EMBED_WIDTH,
|
|
504
498
|
);
|
|
505
499
|
const height = getCssDimension(
|
|
506
|
-
|
|
500
|
+
frameHeight || DEFAULT_EMBED_HEIGHT,
|
|
507
501
|
);
|
|
508
502
|
setAttributes(this.iFrame, restParams);
|
|
509
503
|
|
|
@@ -547,7 +541,7 @@ export class TsEmbed {
|
|
|
547
541
|
uploadMixpanelEvent(
|
|
548
542
|
MIXPANEL_EVENT.VISUAL_SDK_RENDER_FAILED,
|
|
549
543
|
);
|
|
550
|
-
this.el.innerHTML = this.
|
|
544
|
+
this.el.innerHTML = this.embedConfig.loginFailedMessage;
|
|
551
545
|
this.handleError(error);
|
|
552
546
|
});
|
|
553
547
|
});
|
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,
|
package/src/react/index.spec.tsx
CHANGED
|
@@ -2,6 +2,7 @@ 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,
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
} from '../test/test-utils';
|
|
11
12
|
import { SearchEmbed, AppEmbed, LiveboardEmbed, useEmbedRef } from './index';
|
|
12
13
|
import { AuthType, init } from '../index';
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
import { version } from '../../package.json';
|
|
15
16
|
|
|
16
17
|
const thoughtSpotHost = 'localhost';
|
|
@@ -37,7 +38,7 @@ describe('React Components', () => {
|
|
|
37
38
|
),
|
|
38
39
|
).toBe(true);
|
|
39
40
|
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`,
|
|
41
|
+
`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
42
|
);
|
|
42
43
|
});
|
|
43
44
|
|
package/src/types.ts
CHANGED
|
@@ -97,6 +97,16 @@ export interface EmbedConfig {
|
|
|
97
97
|
*/
|
|
98
98
|
noRedirect?: boolean;
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* [SSO] For SSO Authentication, one can supply an optional path param,
|
|
102
|
+
* this will be the path on the host origin where the SAML flow will be
|
|
103
|
+
* terminated.
|
|
104
|
+
*
|
|
105
|
+
* Eg: "/dashboard", "#/foo" [Do not include the host]
|
|
106
|
+
* @version SDK: 1.10.2 | ThoughtSpot: *
|
|
107
|
+
*/
|
|
108
|
+
redirectPath?: string;
|
|
109
|
+
|
|
100
110
|
/** @internal */
|
|
101
111
|
basepath?: string;
|
|
102
112
|
|
|
@@ -159,6 +169,14 @@ export interface EmbedConfig {
|
|
|
159
169
|
* @default ''
|
|
160
170
|
*/
|
|
161
171
|
customCssUrl?: string;
|
|
172
|
+
/**
|
|
173
|
+
* [AuthServer|Basic] Detect if 3rd party cookies are enabled by doing an additional call. This is slower
|
|
174
|
+
* and should be avoided. Listen to the NO_COOKIE_ACCESS event to handle the situation.
|
|
175
|
+
*
|
|
176
|
+
* This is slightly slower than letting the browser handle the cookie check, as it involves an extra network call.
|
|
177
|
+
* @version SDK: 1.10.4 | ThoughtSpot: *
|
|
178
|
+
*/
|
|
179
|
+
detectCookieAccessSlow?: boolean;
|
|
162
180
|
}
|
|
163
181
|
|
|
164
182
|
/**
|
|
@@ -371,6 +389,16 @@ export enum EmbedEvent {
|
|
|
371
389
|
* The ThoughtSpot auth session has expired.
|
|
372
390
|
*/
|
|
373
391
|
AuthExpire = 'ThoughtspotAuthExpired',
|
|
392
|
+
/**
|
|
393
|
+
* ThoughtSpot failed to validate the auth session.
|
|
394
|
+
* @hidden
|
|
395
|
+
*/
|
|
396
|
+
AuthFailure = 'ThoughtspotAuthFailure',
|
|
397
|
+
/**
|
|
398
|
+
* ThoughtSpot failed to validate the auth session.
|
|
399
|
+
* @hidden
|
|
400
|
+
*/
|
|
401
|
+
AuthLogout = 'ThoughtspotAuthLogout',
|
|
374
402
|
/**
|
|
375
403
|
* The height of the embedded Liveboard or visualization has been computed.
|
|
376
404
|
* @return data - The height of the embedded Liveboard or visualization
|
|
@@ -780,6 +808,10 @@ export enum Action {
|
|
|
780
808
|
* @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl
|
|
781
809
|
*/
|
|
782
810
|
CreateMonitor = 'createMonitor',
|
|
811
|
+
/**
|
|
812
|
+
* @version SDK: 1.11.1 | ThoughtSpot: 8.3.0.cl
|
|
813
|
+
*/
|
|
814
|
+
ReportError = 'reportError',
|
|
783
815
|
}
|
|
784
816
|
|
|
785
817
|
export interface SessionInterface {
|
|
@@ -63,4 +63,17 @@ describe('Unit test for authService', () => {
|
|
|
63
63
|
await fetchBasicAuthService(thoughtSpotHost, username, password);
|
|
64
64
|
expect(fetch).toBeCalled();
|
|
65
65
|
});
|
|
66
|
+
|
|
67
|
+
test('log error on API failures', async () => {
|
|
68
|
+
jest.spyOn(global.console, 'error').mockImplementation(() => undefined);
|
|
69
|
+
global.fetch = jest.fn(() =>
|
|
70
|
+
Promise.resolve({
|
|
71
|
+
text: () => Promise.resolve('error'),
|
|
72
|
+
status: 500,
|
|
73
|
+
ok: false,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
await fetchSessionInfoService(authVerificationUrl);
|
|
77
|
+
expect(global.console.error).toHaveBeenCalledWith('Failure', 'error');
|
|
78
|
+
});
|
|
66
79
|
});
|
package/src/utils/authService.ts
CHANGED
|
@@ -6,8 +6,8 @@ function failureLoggedFetch(
|
|
|
6
6
|
options: RequestInit = {},
|
|
7
7
|
): Promise<Response> {
|
|
8
8
|
return fetch(url, options).then(async (r) => {
|
|
9
|
-
if (!r.ok && r.type !== 'opaqueredirect') {
|
|
10
|
-
console.error('Failure', await r.text());
|
|
9
|
+
if (!r.ok && r.type !== 'opaqueredirect' && r.type !== 'opaque') {
|
|
10
|
+
console.error('Failure', await r.text?.());
|
|
11
11
|
}
|
|
12
12
|
return r;
|
|
13
13
|
});
|
|
@@ -59,3 +59,15 @@ export async function fetchBasicAuthService(
|
|
|
59
59
|
credentials: 'include',
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
export async function fetchLogoutService(
|
|
64
|
+
thoughtSpotHost: string,
|
|
65
|
+
): Promise<any> {
|
|
66
|
+
return failureLoggedFetch(`${thoughtSpotHost}${EndPoints.LOGOUT}`, {
|
|
67
|
+
credentials: 'include',
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'x-requested-by': 'ThoughtSpot',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|