@thoughtspot/visual-embed-sdk 1.10.0 → 1.10.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.
- package/dist/src/auth.d.ts +18 -5
- package/dist/src/embed/base.d.ts +36 -6
- package/dist/src/embed/pinboard.d.ts +91 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/types.d.ts +24 -0
- package/dist/src/utils/authService.d.ts +1 -0
- package/dist/src/utils/plugin.d.ts +0 -0
- package/dist/src/utils/processData.d.ts +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/v1/api.d.ts +19 -0
- package/dist/tsembed.es.js +544 -35
- package/dist/tsembed.js +542 -34
- package/lib/package.json +2 -1
- package/lib/src/auth.d.ts +18 -5
- package/lib/src/auth.js +51 -12
- package/lib/src/auth.js.map +1 -1
- package/lib/src/auth.spec.js +69 -11
- package/lib/src/auth.spec.js.map +1 -1
- package/lib/src/embed/base.d.ts +36 -6
- package/lib/src/embed/base.js +79 -11
- package/lib/src/embed/base.js.map +1 -1
- package/lib/src/embed/base.spec.js +51 -3
- 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/pinboard.d.ts +91 -0
- package/lib/src/embed/pinboard.js +110 -0
- package/lib/src/embed/pinboard.js.map +1 -0
- package/lib/src/embed/ts-embed.js +9 -10
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +16 -6
- 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/test/test-utils.js +1 -1
- package/lib/src/test/test-utils.js.map +1 -1
- package/lib/src/types.d.ts +24 -0
- package/lib/src/types.js +10 -0
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/authService.d.ts +1 -0
- package/lib/src/utils/authService.js +23 -3
- package/lib/src/utils/authService.js.map +1 -1
- package/lib/src/utils/authService.spec.js +21 -5
- package/lib/src/utils/authService.spec.js.map +1 -1
- package/lib/src/utils/plugin.d.ts +0 -0
- package/lib/src/utils/plugin.js +1 -0
- package/lib/src/utils/plugin.js.map +1 -0
- 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 +124 -8
- package/package.json +2 -1
- package/src/auth.spec.ts +90 -11
- package/src/auth.ts +74 -16
- package/src/embed/base.spec.ts +58 -4
- package/src/embed/base.ts +98 -17
- package/src/embed/embed.spec.ts +1 -1
- package/src/embed/ts-embed.spec.ts +19 -9
- package/src/embed/ts-embed.ts +15 -12
- package/src/index.ts +5 -1
- package/src/test/test-utils.ts +1 -1
- package/src/types.ts +26 -0
- package/src/utils/authService.spec.ts +31 -5
- package/src/utils/authService.ts +29 -3
- 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/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
|
+
};
|
|
26
|
+
|
|
27
|
+
export let authPromise: Promise<boolean>;
|
|
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
|
+
}
|
|
16
42
|
|
|
17
|
-
export
|
|
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
|
+
}
|
|
18
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<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
@@ -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<void> => {
|
|
|
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,
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
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', () => {
|
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';
|
|
@@ -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
|
/**
|
|
@@ -304,7 +296,12 @@ export class TsEmbed {
|
|
|
304
296
|
if (event.source === this.iFrame.contentWindow) {
|
|
305
297
|
this.executeCallbacks(
|
|
306
298
|
eventType,
|
|
307
|
-
|
|
299
|
+
processEventData(
|
|
300
|
+
eventType,
|
|
301
|
+
eventData,
|
|
302
|
+
this.thoughtSpotHost,
|
|
303
|
+
this.el,
|
|
304
|
+
),
|
|
308
305
|
eventPort,
|
|
309
306
|
);
|
|
310
307
|
}
|
|
@@ -464,7 +461,12 @@ export class TsEmbed {
|
|
|
464
461
|
uploadMixpanelEvent(MIXPANEL_EVENT.VISUAL_SDK_RENDER_START);
|
|
465
462
|
|
|
466
463
|
getAuthPromise()
|
|
467
|
-
?.then(() => {
|
|
464
|
+
?.then((isLoggedIn: boolean) => {
|
|
465
|
+
if (!isLoggedIn) {
|
|
466
|
+
this.el.innerHTML = this.embedConfig.loginFailedMessage;
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
468
470
|
uploadMixpanelEvent(
|
|
469
471
|
MIXPANEL_EVENT.VISUAL_SDK_RENDER_COMPLETE,
|
|
470
472
|
);
|
|
@@ -492,7 +494,7 @@ export class TsEmbed {
|
|
|
492
494
|
frameWidth || DEFAULT_EMBED_WIDTH,
|
|
493
495
|
);
|
|
494
496
|
const height = getCssDimension(
|
|
495
|
-
|
|
497
|
+
frameHeight || DEFAULT_EMBED_HEIGHT,
|
|
496
498
|
);
|
|
497
499
|
setAttributes(this.iFrame, restParams);
|
|
498
500
|
|
|
@@ -536,6 +538,7 @@ export class TsEmbed {
|
|
|
536
538
|
uploadMixpanelEvent(
|
|
537
539
|
MIXPANEL_EVENT.VISUAL_SDK_RENDER_FAILED,
|
|
538
540
|
);
|
|
541
|
+
this.el.innerHTML = this.embedConfig.loginFailedMessage;
|
|
539
542
|
this.handleError(error);
|
|
540
543
|
});
|
|
541
544
|
});
|
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/test/test-utils.ts
CHANGED
|
@@ -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('
|
|
17
|
+
export const getRootEl2 = () => document.getElementById('embed-2');
|
|
18
18
|
|
|
19
19
|
export const getIFrameEl = (container: DOMElement = document) =>
|
|
20
20
|
container.querySelector('iframe');
|
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
|
|
|
@@ -131,6 +141,12 @@ export interface EmbedConfig {
|
|
|
131
141
|
*/
|
|
132
142
|
disableLoginRedirect?: boolean;
|
|
133
143
|
|
|
144
|
+
/**
|
|
145
|
+
* This message is displayed on the embed view when the login fails.
|
|
146
|
+
* @version 1.10.1 | ThoughtSpot: *
|
|
147
|
+
*/
|
|
148
|
+
loginFailedMessage?: string;
|
|
149
|
+
|
|
134
150
|
/**
|
|
135
151
|
* Calls the prefetch method internally when set to true
|
|
136
152
|
* @default false
|
|
@@ -365,6 +381,16 @@ export enum EmbedEvent {
|
|
|
365
381
|
* The ThoughtSpot auth session has expired.
|
|
366
382
|
*/
|
|
367
383
|
AuthExpire = 'ThoughtspotAuthExpired',
|
|
384
|
+
/**
|
|
385
|
+
* ThoughtSpot failed to validate the auth session.
|
|
386
|
+
* @hidden
|
|
387
|
+
*/
|
|
388
|
+
AuthFailure = 'ThoughtspotAuthFailure',
|
|
389
|
+
/**
|
|
390
|
+
* ThoughtSpot failed to validate the auth session.
|
|
391
|
+
* @hidden
|
|
392
|
+
*/
|
|
393
|
+
AuthLogout = 'ThoughtspotAuthLogout',
|
|
368
394
|
/**
|
|
369
395
|
* The height of the embedded Liveboard or visualization has been computed.
|
|
370
396
|
* @return data - The height of the embedded Liveboard or visualization
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
fetchAuthService,
|
|
5
5
|
fetchBasicAuthService,
|
|
6
6
|
} from './authService';
|
|
7
|
+
import { EndPoints } from '../auth';
|
|
7
8
|
|
|
8
9
|
const thoughtSpotHost = 'http://10.79.135.124:3000';
|
|
9
10
|
|
|
@@ -21,6 +22,7 @@ describe('Unit test for authService', () => {
|
|
|
21
22
|
Promise.resolve({
|
|
22
23
|
json: () => ({ success: true }),
|
|
23
24
|
status: 200,
|
|
25
|
+
ok: true,
|
|
24
26
|
}),
|
|
25
27
|
);
|
|
26
28
|
const response = await fetchSessionInfoService(authVerificationUrl);
|
|
@@ -32,6 +34,7 @@ describe('Unit test for authService', () => {
|
|
|
32
34
|
global.fetch = jest.fn(() =>
|
|
33
35
|
Promise.resolve({
|
|
34
36
|
text: () => ({ success: true }),
|
|
37
|
+
ok: true,
|
|
35
38
|
}),
|
|
36
39
|
);
|
|
37
40
|
const response = await fetchAuthTokenService(authEndpoint);
|
|
@@ -40,14 +43,37 @@ describe('Unit test for authService', () => {
|
|
|
40
43
|
});
|
|
41
44
|
|
|
42
45
|
test('fetchAuthService', async () => {
|
|
43
|
-
global.fetch = jest.fn(() =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
global.fetch = jest.fn(() =>
|
|
47
|
+
Promise.resolve({ success: true, ok: true }),
|
|
48
|
+
);
|
|
49
|
+
await fetchAuthService(thoughtSpotHost, username, authToken);
|
|
50
|
+
expect(fetch).toBeCalledWith(
|
|
51
|
+
`${thoughtSpotHost}${EndPoints.TOKEN_LOGIN}?username=${username}&auth_token=${authToken}`,
|
|
52
|
+
{
|
|
53
|
+
credentials: 'include',
|
|
54
|
+
redirect: 'manual',
|
|
55
|
+
},
|
|
56
|
+
);
|
|
46
57
|
});
|
|
47
58
|
|
|
48
|
-
test('fetchBasicAuthService', async () => {
|
|
49
|
-
global.fetch = jest.fn(() =>
|
|
59
|
+
test('fetchBasicAuthService called with manual redirect', async () => {
|
|
60
|
+
global.fetch = jest.fn(() =>
|
|
61
|
+
Promise.resolve({ success: true, ok: true }),
|
|
62
|
+
);
|
|
50
63
|
await fetchBasicAuthService(thoughtSpotHost, username, password);
|
|
51
64
|
expect(fetch).toBeCalled();
|
|
52
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
|
+
});
|
|
53
79
|
});
|
package/src/utils/authService.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-cycle
|
|
2
2
|
import { EndPoints } from '../auth';
|
|
3
3
|
|
|
4
|
+
function failureLoggedFetch(
|
|
5
|
+
url: string,
|
|
6
|
+
options: RequestInit = {},
|
|
7
|
+
): Promise<Response> {
|
|
8
|
+
return fetch(url, options).then(async (r) => {
|
|
9
|
+
if (!r.ok && r.type !== 'opaqueredirect' && r.type !== 'opaque') {
|
|
10
|
+
console.error('Failure', await r.text?.());
|
|
11
|
+
}
|
|
12
|
+
return r;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
4
16
|
export function fetchSessionInfoService(
|
|
5
17
|
authVerificationUrl: string,
|
|
6
18
|
): Promise<any> {
|
|
7
|
-
return
|
|
19
|
+
return failureLoggedFetch(authVerificationUrl, {
|
|
8
20
|
credentials: 'include',
|
|
9
21
|
});
|
|
10
22
|
}
|
|
@@ -20,10 +32,12 @@ export async function fetchAuthService(
|
|
|
20
32
|
username: string,
|
|
21
33
|
authToken: string,
|
|
22
34
|
): Promise<any> {
|
|
23
|
-
return
|
|
35
|
+
return failureLoggedFetch(
|
|
24
36
|
`${thoughtSpotHost}${EndPoints.TOKEN_LOGIN}?username=${username}&auth_token=${authToken}`,
|
|
25
37
|
{
|
|
26
38
|
credentials: 'include',
|
|
39
|
+
// We do not want to follow the redirect, as it starts giving a CORS error
|
|
40
|
+
redirect: 'manual',
|
|
27
41
|
},
|
|
28
42
|
);
|
|
29
43
|
}
|
|
@@ -33,7 +47,7 @@ export async function fetchBasicAuthService(
|
|
|
33
47
|
username: string,
|
|
34
48
|
password: string,
|
|
35
49
|
): Promise<any> {
|
|
36
|
-
return
|
|
50
|
+
return failureLoggedFetch(`${thoughtSpotHost}${EndPoints.BASIC_LOGIN}`, {
|
|
37
51
|
method: 'POST',
|
|
38
52
|
headers: {
|
|
39
53
|
'content-type': 'application/x-www-form-urlencoded',
|
|
@@ -45,3 +59,15 @@ export async function fetchBasicAuthService(
|
|
|
45
59
|
credentials: 'include',
|
|
46
60
|
});
|
|
47
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
|
+
}
|