@thoughtspot/visual-embed-sdk 1.29.0-alpha.2 → 1.29.0-alpha.authRefactor
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/cjs/package.json +1 -1
- package/cjs/src/embed/liveboard.js +1 -1
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/utils/sessionInfoService.d.ts +66 -0
- package/cjs/src/utils/sessionInfoService.d.ts.map +1 -0
- package/cjs/src/utils/sessionInfoService.js +92 -0
- package/cjs/src/utils/sessionInfoService.js.map +1 -0
- package/dist/tsembed-react.es.js +2 -2
- package/dist/tsembed-react.js +2 -2
- package/dist/tsembed.es.js +2 -2
- package/dist/tsembed.js +2 -2
- package/lib/package.json +1 -1
- package/lib/src/embed/liveboard.js +1 -1
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/utils/sessionInfoService.d.ts +66 -0
- package/lib/src/utils/sessionInfoService.d.ts.map +1 -0
- package/lib/src/utils/sessionInfoService.js +85 -0
- package/lib/src/utils/sessionInfoService.js.map +1 -0
- package/package.json +1 -2
- package/src/auth.spec.ts +66 -72
- package/src/auth.ts +43 -59
- package/src/authToken.ts +10 -4
- package/src/embed/app.spec.ts +4 -2
- package/src/embed/base.spec.ts +1 -0
- package/src/embed/base.ts +3 -0
- package/src/embed/embed.spec.ts +2 -0
- package/src/embed/events.spec.ts +2 -0
- package/src/embed/liveboard.spec.ts +2 -0
- package/src/embed/pinboard.spec.ts +2 -0
- package/src/embed/sage.spec.ts +3 -0
- package/src/embed/search.spec.ts +1 -0
- package/src/embed/ts-embed-trigger.spec.ts +3 -0
- package/src/embed/ts-embed.spec.ts +8 -0
- package/src/embed/ts-embed.ts +1 -0
- package/src/index.ts +2 -1
- package/src/mixpanel-service.spec.ts +4 -3
- package/src/mixpanel-service.ts +3 -1
- package/src/react/index.spec.tsx +7 -0
- package/src/react/index.tsx +1 -0
- package/src/utils/authService/authService.spec.ts +9 -4
- package/src/utils/authService/authService.ts +1 -0
- package/src/utils/authService/tokenizedAuthService.ts +38 -8
- package/src/utils/processData.spec.ts +3 -2
- package/src/utils/processData.ts +1 -3
- package/src/utils/sessionInfoService.ts +101 -0
package/src/embed/app.spec.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { version } from '../../package.json';
|
|
|
19
19
|
import * as config from '../config';
|
|
20
20
|
import { TsEmbed, V1Embed } from './ts-embed';
|
|
21
21
|
import { logger } from '../utils/logger';
|
|
22
|
+
import * as auth from '../auth';
|
|
22
23
|
|
|
23
24
|
const defaultViewConfig = {
|
|
24
25
|
frameParams: {
|
|
@@ -35,6 +36,7 @@ beforeAll(() => {
|
|
|
35
36
|
thoughtSpotHost,
|
|
36
37
|
authType: AuthType.None,
|
|
37
38
|
});
|
|
39
|
+
jest.spyOn(auth, 'postLoginService').mockImplementation(() => Promise.resolve({}));
|
|
38
40
|
(window as any).ResizeObserver = window.ResizeObserver
|
|
39
41
|
|| jest.fn().mockImplementation(() => ({
|
|
40
42
|
disconnect: jest.fn(),
|
|
@@ -67,13 +69,13 @@ describe('App embed tests', () => {
|
|
|
67
69
|
test('should hide the primary nav bar', async () => {
|
|
68
70
|
const appEmbed = new AppEmbed(getRootEl(), {
|
|
69
71
|
...defaultViewConfig,
|
|
70
|
-
showPrimaryNavbar:
|
|
72
|
+
showPrimaryNavbar: false,
|
|
71
73
|
} as AppViewConfig);
|
|
72
74
|
appEmbed.render();
|
|
73
75
|
await executeAfterWait(() => {
|
|
74
76
|
expectUrlMatchesWithParams(
|
|
75
77
|
getIFrameSrc(),
|
|
76
|
-
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=
|
|
78
|
+
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/home`,
|
|
77
79
|
);
|
|
78
80
|
});
|
|
79
81
|
});
|
package/src/embed/base.spec.ts
CHANGED
package/src/embed/base.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
notifyLogout,
|
|
31
31
|
setAuthEE,
|
|
32
32
|
AuthEventEmitter,
|
|
33
|
+
postLoginService,
|
|
33
34
|
} from '../auth';
|
|
34
35
|
import { uploadMixpanelEvent, MIXPANEL_EVENT } from '../mixpanel-service';
|
|
35
36
|
import { getEmbedConfig, setEmbedConfig } from './embedConfig';
|
|
@@ -75,6 +76,8 @@ export const handleAuth = (): Promise<boolean> => {
|
|
|
75
76
|
if (!isLoggedIn) {
|
|
76
77
|
notifyAuthFailure(AuthFailureType.SDK);
|
|
77
78
|
} else {
|
|
79
|
+
// Post login service is called after successful login.
|
|
80
|
+
postLoginService();
|
|
78
81
|
notifyAuthSDKSuccess();
|
|
79
82
|
}
|
|
80
83
|
},
|
package/src/embed/embed.spec.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getIFrameEl,
|
|
10
10
|
getRootEl,
|
|
11
11
|
} from '../test/test-utils';
|
|
12
|
+
import * as authInstance from '../auth';
|
|
12
13
|
|
|
13
14
|
const thoughtSpotHost = 'tshost';
|
|
14
15
|
const defaultViewConfig = {
|
|
@@ -24,6 +25,7 @@ beforeAll(() => {
|
|
|
24
25
|
authType: AuthType.None,
|
|
25
26
|
});
|
|
26
27
|
spyOn(window, 'alert');
|
|
28
|
+
jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(true);
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
describe('test view config', () => {
|
package/src/embed/events.spec.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
postMessageToParent,
|
|
21
21
|
} from '../test/test-utils';
|
|
22
22
|
import { LiveboardViewConfig } from './liveboard';
|
|
23
|
+
import * as authInstance from '../auth';
|
|
23
24
|
|
|
24
25
|
const thoughtSpotHost = 'tshost';
|
|
25
26
|
const defaultViewConfig = {
|
|
@@ -36,6 +37,7 @@ beforeAll(() => {
|
|
|
36
37
|
authType: AuthType.None,
|
|
37
38
|
});
|
|
38
39
|
spyOn(window, 'alert');
|
|
40
|
+
jest.spyOn(authInstance, 'postLoginService').mockReturnValue(true);
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
describe('test communication between host app and ThoughtSpot', () => {
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from '../test/test-utils';
|
|
24
24
|
import * as tsEmbed from './ts-embed';
|
|
25
25
|
import * as processTriggerInstance from '../utils/processTrigger';
|
|
26
|
+
import * as auth from '../auth';
|
|
26
27
|
|
|
27
28
|
const defaultViewConfig = {
|
|
28
29
|
frameParams: {
|
|
@@ -43,6 +44,7 @@ beforeAll(() => {
|
|
|
43
44
|
thoughtSpotHost,
|
|
44
45
|
authType: AuthType.None,
|
|
45
46
|
});
|
|
47
|
+
jest.spyOn(auth, 'postLoginService').mockImplementation(() => Promise.resolve({}));
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
describe('Liveboard/viz embed tests', () => {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
expectUrlMatchesWithParams,
|
|
14
14
|
} from '../test/test-utils';
|
|
15
15
|
import { version } from '../../package.json';
|
|
16
|
+
import * as auth from '../auth';
|
|
16
17
|
|
|
17
18
|
const defaultViewConfig = {
|
|
18
19
|
frameParams: {
|
|
@@ -30,6 +31,7 @@ beforeAll(() => {
|
|
|
30
31
|
thoughtSpotHost,
|
|
31
32
|
authType: AuthType.None,
|
|
32
33
|
});
|
|
34
|
+
jest.spyOn(auth, 'postLoginService').mockReturnValue(true);
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
describe('Pinboard/viz embed tests', () => {
|
package/src/embed/sage.spec.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
getRootEl,
|
|
10
10
|
} from '../test/test-utils';
|
|
11
11
|
|
|
12
|
+
import * as authInstance from '../auth';
|
|
13
|
+
|
|
12
14
|
const defaultConfig: SageViewConfig = {
|
|
13
15
|
disableWorksheetChange: false,
|
|
14
16
|
hideWorksheetSelector: false,
|
|
@@ -27,6 +29,7 @@ beforeAll(() => {
|
|
|
27
29
|
authType: AuthType.None,
|
|
28
30
|
});
|
|
29
31
|
spyOn(window, 'alert');
|
|
32
|
+
jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(true);
|
|
30
33
|
});
|
|
31
34
|
|
|
32
35
|
describe('Sage embed tests', () => {
|
package/src/embed/search.spec.ts
CHANGED
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
getRootEl,
|
|
9
9
|
} from '../test/test-utils';
|
|
10
10
|
|
|
11
|
+
import * as authInstance from '../auth';
|
|
12
|
+
|
|
11
13
|
describe('Trigger', () => {
|
|
12
14
|
beforeEach(() => {
|
|
13
15
|
document.body.innerHTML = getDocumentBody();
|
|
16
|
+
jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(true);
|
|
14
17
|
});
|
|
15
18
|
test('should trigger the event', async (done) => {
|
|
16
19
|
init({
|
|
@@ -83,6 +83,10 @@ describe('Unit test case for ts embed', () => {
|
|
|
83
83
|
resetCachedAuthToken();
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
beforeAll(() => {
|
|
87
|
+
jest.spyOn(authInstance, 'postLoginService').mockResolvedValue(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
86
90
|
describe('AuthExpire embedEvent in cookieless authentication authType', () => {
|
|
87
91
|
beforeAll(() => {
|
|
88
92
|
jest.spyOn(authInstance, 'doCookielessTokenAuth').mockResolvedValueOnce(true);
|
|
@@ -680,6 +684,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
680
684
|
const mockPort: any = {
|
|
681
685
|
postMessage: jest.fn(),
|
|
682
686
|
};
|
|
687
|
+
const loggerSpy = jest.spyOn(logger, 'error').mockResolvedValueOnce(true);
|
|
683
688
|
await executeAfterWait(() => {
|
|
684
689
|
const iframe = getIFrameEl();
|
|
685
690
|
postMessageToParent(iframe.contentWindow, mockEmbedEventPayload, mockPort);
|
|
@@ -689,6 +694,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
689
694
|
expect(baseInstance.notifyAuthFailure).toBeCalledWith(
|
|
690
695
|
authInstance.AuthFailureType.EXPIRY,
|
|
691
696
|
);
|
|
697
|
+
expect(loggerSpy).toHaveBeenCalledTimes(1);
|
|
692
698
|
});
|
|
693
699
|
|
|
694
700
|
jest.spyOn(authService, 'verifyTokenService').mockClear();
|
|
@@ -707,6 +713,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
707
713
|
const searchEmbed = new SearchEmbed(getRootEl(), { ...defaultViewConfig, preRenderId: 'test' });
|
|
708
714
|
jest.spyOn(baseInstance, 'notifyAuthFailure');
|
|
709
715
|
searchEmbed.preRender();
|
|
716
|
+
const loggerSpy = jest.spyOn(logger, 'error').mockResolvedValueOnce(true);
|
|
710
717
|
const mockPort: any = {
|
|
711
718
|
postMessage: jest.fn(),
|
|
712
719
|
};
|
|
@@ -720,6 +727,7 @@ describe('Unit test case for ts embed', () => {
|
|
|
720
727
|
expect(baseInstance.notifyAuthFailure).toBeCalledWith(
|
|
721
728
|
authInstance.AuthFailureType.EXPIRY,
|
|
722
729
|
);
|
|
730
|
+
expect(loggerSpy).toHaveBeenCalledTimes(1);
|
|
723
731
|
});
|
|
724
732
|
|
|
725
733
|
jest.spyOn(authService, 'verifyTokenService').mockClear();
|
package/src/embed/ts-embed.ts
CHANGED
|
@@ -349,6 +349,7 @@ export class TsEmbed {
|
|
|
349
349
|
data: { authToken },
|
|
350
350
|
});
|
|
351
351
|
} catch (e) {
|
|
352
|
+
logger.error(`Received invalid token. Error : ${e?.message}`);
|
|
352
353
|
processAuthFailure(e, this.isPreRendered ? this.preRenderWrapper : this.el);
|
|
353
354
|
}
|
|
354
355
|
} else if (autoLogin) {
|
package/src/index.ts
CHANGED
|
@@ -21,8 +21,9 @@ import { PinboardEmbed, LiveboardViewConfig, LiveboardEmbed } from './embed/live
|
|
|
21
21
|
import { SearchEmbed, SearchViewConfig } from './embed/search';
|
|
22
22
|
import { SearchBarEmbed, SearchBarViewConfig } from './embed/search-bar';
|
|
23
23
|
import {
|
|
24
|
-
AuthFailureType, AuthStatus, AuthEvent, AuthEventEmitter,
|
|
24
|
+
AuthFailureType, AuthStatus, AuthEvent, AuthEventEmitter,
|
|
25
25
|
} from './auth';
|
|
26
|
+
import { getSessionInfo } from './utils/sessionInfoService';
|
|
26
27
|
import {
|
|
27
28
|
AuthType,
|
|
28
29
|
RuntimeFilter,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
testResetMixpanel,
|
|
7
7
|
} from './mixpanel-service';
|
|
8
8
|
import { AuthType } from './types';
|
|
9
|
+
import { SessionInfo } from './utils/sessionInfoService';
|
|
9
10
|
|
|
10
11
|
const config = {
|
|
11
12
|
thoughtSpotHost: 'https://10.87.89.232',
|
|
@@ -29,7 +30,7 @@ describe('Unit test for mixpanel', () => {
|
|
|
29
30
|
mixpanelToken: 'abc123',
|
|
30
31
|
userGUID: '12345',
|
|
31
32
|
isPublicUser: false,
|
|
32
|
-
};
|
|
33
|
+
} as SessionInfo;
|
|
33
34
|
initMixpanel(sessionInfo);
|
|
34
35
|
expect(mixpanel.init).toHaveBeenCalledWith(sessionInfo.mixpanelToken, undefined, 'tsEmbed');
|
|
35
36
|
expect(mixpanel.identify).toHaveBeenCalledWith(sessionInfo.userGUID);
|
|
@@ -49,7 +50,7 @@ describe('Unit test for mixpanel', () => {
|
|
|
49
50
|
clusterId: 'newClusterId',
|
|
50
51
|
clusterName: 'newClusterName',
|
|
51
52
|
releaseVersion: 'newReleaseVersion',
|
|
52
|
-
};
|
|
53
|
+
} as SessionInfo;
|
|
53
54
|
initMixpanel(sessionInfo);
|
|
54
55
|
|
|
55
56
|
expect(mixpanel.init).toHaveBeenCalledWith(sessionInfo.mixpanelToken, undefined, 'tsEmbed');
|
|
@@ -74,7 +75,7 @@ describe('Unit test for mixpanel', () => {
|
|
|
74
75
|
mixpanelToken: 'abc123',
|
|
75
76
|
userGUID: '12345',
|
|
76
77
|
isPublicUser: false,
|
|
77
|
-
};
|
|
78
|
+
} as SessionInfo;
|
|
78
79
|
initMixpanel(sessionInfo);
|
|
79
80
|
expect(mixpanel.track).toHaveBeenCalledTimes(2);
|
|
80
81
|
});
|
package/src/mixpanel-service.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as mixpanel from 'mixpanel-browser';
|
|
2
2
|
import { logger } from './utils/logger';
|
|
3
|
+
import { SessionInfo } from './utils/sessionInfoService';
|
|
3
4
|
|
|
4
5
|
export const EndPoints = {
|
|
5
6
|
CONFIG: '/callosum/v1/system/config',
|
|
@@ -55,8 +56,9 @@ function emptyQueue() {
|
|
|
55
56
|
*
|
|
56
57
|
* @param sessionInfo
|
|
57
58
|
*/
|
|
58
|
-
export function initMixpanel(sessionInfo:
|
|
59
|
+
export function initMixpanel(sessionInfo: SessionInfo): void {
|
|
59
60
|
if (!sessionInfo || !sessionInfo.mixpanelToken) {
|
|
61
|
+
logger.error('Mixpanel token not found in session info');
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
62
64
|
// On a public cluster the user is anonymous, so don't set the identify to
|
package/src/react/index.spec.tsx
CHANGED
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
|
|
25
25
|
import { version } from '../../package.json';
|
|
26
26
|
|
|
27
|
+
import * as auth from '../auth';
|
|
28
|
+
import * as sessionService from '../utils/sessionInfoService';
|
|
29
|
+
|
|
27
30
|
const thoughtSpotHost = 'localhost';
|
|
28
31
|
|
|
29
32
|
beforeAll(() => {
|
|
@@ -31,6 +34,10 @@ beforeAll(() => {
|
|
|
31
34
|
thoughtSpotHost,
|
|
32
35
|
authType: AuthType.None,
|
|
33
36
|
});
|
|
37
|
+
jest.spyOn(auth, 'postLoginService').mockReturnValue(true);
|
|
38
|
+
jest.spyOn(sessionService, 'getSessionInfoSync').mockReturnValue({
|
|
39
|
+
userGUID: 'abcd',
|
|
40
|
+
});
|
|
34
41
|
spyOn(window, 'alert');
|
|
35
42
|
});
|
|
36
43
|
|
package/src/react/index.tsx
CHANGED
|
@@ -26,9 +26,10 @@ describe('Unit test for authService', () => {
|
|
|
26
26
|
status: 200,
|
|
27
27
|
ok: true,
|
|
28
28
|
}));
|
|
29
|
-
const response = await fetchSessionInfoService(
|
|
30
|
-
expect(response.
|
|
29
|
+
const response = await fetchSessionInfoService(thoughtSpotHost);
|
|
30
|
+
expect(response.success).toBe(true);
|
|
31
31
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
32
|
+
expect(fetch).toBeCalledWith(`${thoughtSpotHost}${EndPoints.SESSION_INFO}`, {});
|
|
32
33
|
});
|
|
33
34
|
|
|
34
35
|
test('fetchAuthTokenService', async () => {
|
|
@@ -108,8 +109,12 @@ describe('Unit test for authService', () => {
|
|
|
108
109
|
status: 500,
|
|
109
110
|
ok: false,
|
|
110
111
|
}));
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
try {
|
|
113
|
+
await fetchSessionInfoService(authVerificationUrl);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
expect(e.message).toContain('Failed to fetch session info');
|
|
116
|
+
}
|
|
117
|
+
expect(logger.error).toHaveBeenCalledWith('Failed to fetch http://localhost:3000/callosum/v1/session/info', 'error');
|
|
113
118
|
});
|
|
114
119
|
|
|
115
120
|
test('verifyTokenService', async () => {
|
|
@@ -2,6 +2,7 @@ import { logger } from '../logger';
|
|
|
2
2
|
|
|
3
3
|
export const EndPoints = {
|
|
4
4
|
AUTH_VERIFICATION: '/callosum/v1/session/info',
|
|
5
|
+
SESSION_INFO: '/callosum/v1/session/info',
|
|
5
6
|
SAML_LOGIN_TEMPLATE: (targetUrl: string) => `/callosum/v1/saml/login?targetURLPath=${targetUrl}`,
|
|
6
7
|
OIDC_LOGIN_TEMPLATE: (targetUrl: string) => `/callosum/v1/oidc/login?targetURLPath=${targetUrl}`,
|
|
7
8
|
TOKEN_LOGIN: '/callosum/v1/session/login/token',
|
|
@@ -7,23 +7,33 @@ import { EndPoints } from './authService';
|
|
|
7
7
|
* @param url
|
|
8
8
|
* @param options
|
|
9
9
|
*/
|
|
10
|
-
function
|
|
10
|
+
function tokenizedFailureLoggedFetch(url: string, options: RequestInit = {}): Promise<Response> {
|
|
11
11
|
return tokenizedFetch(url, options).then(async (r) => {
|
|
12
12
|
if (!r.ok && r.type !== 'opaqueredirect' && r.type !== 'opaque') {
|
|
13
|
-
logger.error(
|
|
13
|
+
logger.error(`Failed to fetch ${url}`, await r.text?.());
|
|
14
14
|
}
|
|
15
15
|
return r;
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
+
* Fetches the session info from the ThoughtSpot server.
|
|
20
21
|
*
|
|
21
|
-
* @param
|
|
22
|
+
* @param thoughtspotHost
|
|
23
|
+
* @returns {Promise<any>}
|
|
24
|
+
* @example
|
|
25
|
+
* ```js
|
|
26
|
+
* const response = await sessionInfoService();
|
|
27
|
+
* ```
|
|
22
28
|
*/
|
|
23
|
-
export function fetchSessionInfoService(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
export async function fetchSessionInfoService(thoughtspotHost: string): Promise<any> {
|
|
30
|
+
const sessionInfoPath = `${thoughtspotHost}${EndPoints.SESSION_INFO}`;
|
|
31
|
+
const response = await tokenizedFailureLoggedFetch(sessionInfoPath);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Failed to fetch session info: ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
return data;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -31,7 +41,7 @@ export function fetchSessionInfoService(authVerificationUrl: string): Promise<an
|
|
|
31
41
|
* @param thoughtSpotHost
|
|
32
42
|
*/
|
|
33
43
|
export async function fetchLogoutService(thoughtSpotHost: string): Promise<any> {
|
|
34
|
-
return
|
|
44
|
+
return tokenizedFailureLoggedFetch(`${thoughtSpotHost}${EndPoints.LOGOUT}`, {
|
|
35
45
|
credentials: 'include',
|
|
36
46
|
method: 'POST',
|
|
37
47
|
headers: {
|
|
@@ -39,3 +49,23 @@ export async function fetchLogoutService(thoughtSpotHost: string): Promise<any>
|
|
|
39
49
|
},
|
|
40
50
|
});
|
|
41
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Is active service to check if the user is logged in.
|
|
55
|
+
*
|
|
56
|
+
* @param thoughtSpotHost
|
|
57
|
+
* @version SDK: 1.28.4 | ThoughtSpot: *
|
|
58
|
+
*/
|
|
59
|
+
export async function isActiveService(thoughtSpotHost: string): Promise<boolean> {
|
|
60
|
+
const isActiveUrl = `${thoughtSpotHost}${EndPoints.IS_ACTIVE}`;
|
|
61
|
+
try {
|
|
62
|
+
const res = await tokenizedFetch(isActiveUrl, {
|
|
63
|
+
credentials: 'include',
|
|
64
|
+
});
|
|
65
|
+
return res.ok;
|
|
66
|
+
} catch (e) {
|
|
67
|
+
logger.warn(`Is Logged In Service failed : ${e.message}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
@@ -4,9 +4,11 @@ import * as auth from '../auth';
|
|
|
4
4
|
import * as base from '../embed/base';
|
|
5
5
|
import * as embedConfigInstance from '../embed/embedConfig';
|
|
6
6
|
import { EmbedEvent, AuthType } from '../types';
|
|
7
|
+
import * as sessionInfoService from './sessionInfoService';
|
|
7
8
|
|
|
8
9
|
describe('Unit test for process data', () => {
|
|
9
10
|
beforeAll(() => {
|
|
11
|
+
jest.spyOn(auth, 'postLoginService').mockImplementation(() => Promise.resolve({}));
|
|
10
12
|
base.init({
|
|
11
13
|
thoughtSpotHost: 'https://tshost',
|
|
12
14
|
authType: AuthType.None,
|
|
@@ -58,15 +60,14 @@ describe('Unit test for process data', () => {
|
|
|
58
60
|
isPublicUser: false,
|
|
59
61
|
};
|
|
60
62
|
const e = { type: EmbedEvent.AuthInit, data: sessionInfo };
|
|
61
|
-
jest.spyOn(auth, 'initSession').mockReturnValue(null);
|
|
62
63
|
jest.spyOn(base, 'notifyAuthSuccess');
|
|
64
|
+
jest.spyOn(sessionInfoService, 'getSessionInfoSync').mockReturnValue(sessionInfo);
|
|
63
65
|
expect(processDataInstance.processEventData(e.type, e, '', null)).toEqual({
|
|
64
66
|
type: e.type,
|
|
65
67
|
data: {
|
|
66
68
|
userGUID: sessionInfo.userGUID,
|
|
67
69
|
},
|
|
68
70
|
});
|
|
69
|
-
expect(auth.initSession).toBeCalledWith(sessionInfo);
|
|
70
71
|
expect(base.notifyAuthSuccess).toBeCalled();
|
|
71
72
|
});
|
|
72
73
|
|
package/src/utils/processData.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
notifyAuthSuccess,
|
|
6
6
|
notifyLogout,
|
|
7
7
|
} from '../embed/base';
|
|
8
|
-
import { AuthFailureType
|
|
8
|
+
import { AuthFailureType } from '../auth';
|
|
9
9
|
import { AuthType, CustomActionPayload, EmbedEvent } from '../types';
|
|
10
10
|
import { AnswerService } from './graphql/answerService/answerService';
|
|
11
11
|
import { resetCachedAuthToken } from '../authToken';
|
|
@@ -34,8 +34,6 @@ export function processCustomAction(e: any, thoughtSpotHost: string) {
|
|
|
34
34
|
* @param e
|
|
35
35
|
*/
|
|
36
36
|
function processAuthInit(e: any) {
|
|
37
|
-
// Store user session details sent by app.
|
|
38
|
-
initSession(e.data);
|
|
39
37
|
notifyAuthSuccess();
|
|
40
38
|
|
|
41
39
|
// Expose only allowed details (eg: userGUID) back to SDK users.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { getEmbedConfig } from '../embed/embedConfig';
|
|
2
|
+
import { fetchSessionInfoService } from './authService';
|
|
3
|
+
|
|
4
|
+
export type SessionInfo = {
|
|
5
|
+
releaseVersion: string;
|
|
6
|
+
userGUID: string;
|
|
7
|
+
currentOrgId: number;
|
|
8
|
+
privileges: string[];
|
|
9
|
+
mixpanelToken: string;
|
|
10
|
+
isPublicUser: boolean;
|
|
11
|
+
clusterId: string;
|
|
12
|
+
clusterName: string;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let sessionInfo: null | SessionInfo = null;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns the session info object and caches it for future use.
|
|
20
|
+
* Once fetched the session info object is cached and returned from the cache on
|
|
21
|
+
* subsequent calls.
|
|
22
|
+
*
|
|
23
|
+
* @example ```js
|
|
24
|
+
* const sessionInfo = await getSessionInfo();
|
|
25
|
+
* console.log(sessionInfo);
|
|
26
|
+
* ```
|
|
27
|
+
* @version SDK: 1.28.3 | ThoughtSpot: *
|
|
28
|
+
* @returns {Promise<SessionInfo>} The session info object.
|
|
29
|
+
*/
|
|
30
|
+
export async function getSessionInfo(): Promise<SessionInfo> {
|
|
31
|
+
if (!sessionInfo) {
|
|
32
|
+
const host = getEmbedConfig().thoughtSpotHost;
|
|
33
|
+
const sessionResponse = await fetchSessionInfoService(host);
|
|
34
|
+
const processedSessionInfo = getSessionDetails(sessionResponse);
|
|
35
|
+
sessionInfo = processedSessionInfo;
|
|
36
|
+
}
|
|
37
|
+
return sessionInfo;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns the cached session info object. If the client is not authenticated the
|
|
42
|
+
* function will return null.
|
|
43
|
+
*
|
|
44
|
+
* @example ```js
|
|
45
|
+
* const sessionInfo = getSessionInfoSync();
|
|
46
|
+
* if (sessionInfo) {
|
|
47
|
+
* console.log(sessionInfo);
|
|
48
|
+
* } else {
|
|
49
|
+
* console.log('Not authenticated');
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
* @returns {SessionInfo | null} The session info object.
|
|
53
|
+
* @version SDK: 1.28.3 | ThoughtSpot: *
|
|
54
|
+
*/
|
|
55
|
+
export function getSessionInfoSync(): SessionInfo | null {
|
|
56
|
+
return sessionInfo;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Processes the session info response and returns the session info object.
|
|
61
|
+
*
|
|
62
|
+
* @param sessionInfoResp {any} Response from the session info API.
|
|
63
|
+
* @returns {SessionInfo} The session info object.
|
|
64
|
+
* @example ```js
|
|
65
|
+
* const sessionInfoResp = await fetch(sessionInfoPath);
|
|
66
|
+
* const sessionInfo = getSessionDetails(sessionInfoResp);
|
|
67
|
+
* console.log(sessionInfo);
|
|
68
|
+
* ```
|
|
69
|
+
* @version SDK: 1.28.3 | ThoughtSpot: *
|
|
70
|
+
*/
|
|
71
|
+
export const getSessionDetails = (sessionInfoResp: any): SessionInfo => {
|
|
72
|
+
const devMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.devSdkKey;
|
|
73
|
+
const prodMixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.prodSdkKey;
|
|
74
|
+
const mixpanelToken = sessionInfoResp.configInfo.mixpanelConfig.production
|
|
75
|
+
? prodMixpanelToken
|
|
76
|
+
: devMixpanelToken;
|
|
77
|
+
return {
|
|
78
|
+
userGUID: sessionInfoResp.userGUID,
|
|
79
|
+
mixpanelToken,
|
|
80
|
+
isPublicUser: sessionInfoResp.configInfo.isPublicUser,
|
|
81
|
+
releaseVersion: sessionInfoResp.releaseVersion,
|
|
82
|
+
clusterId: sessionInfoResp.configInfo.selfClusterId,
|
|
83
|
+
clusterName: sessionInfoResp.configInfo.selfClusterName,
|
|
84
|
+
...sessionInfoResp,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resets the cached session info object and forces a new fetch on the next call.
|
|
90
|
+
*
|
|
91
|
+
* @example ```js
|
|
92
|
+
* resetCachedSessionInfo();
|
|
93
|
+
* const sessionInfo = await getSessionInfo();
|
|
94
|
+
* console.log(sessionInfo);
|
|
95
|
+
* ```
|
|
96
|
+
* @version SDK: 1.28.3 | ThoughtSpot ts7.april.cl, 7.2.1
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
export function resetCachedSessionInfo(): void {
|
|
100
|
+
sessionInfo = null;
|
|
101
|
+
}
|