@thoughtspot/visual-embed-sdk 1.20.1 → 1.21.0-alpha.0
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/README.md +1 -1
- package/dist/src/auth.d.ts +48 -3
- package/dist/src/auth.d.ts.map +1 -1
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/embed/app.d.ts +17 -5
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/base.d.ts +20 -9
- package/dist/src/embed/base.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +17 -5
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/search-bar.d.ts +3 -0
- package/dist/src/embed/search-bar.d.ts.map +1 -1
- package/dist/src/embed/search.d.ts +9 -5
- package/dist/src/embed/search.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +64 -6
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mixpanel-service.d.ts +8 -0
- package/dist/src/mixpanel-service.d.ts.map +1 -1
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/react/util.d.ts +4 -0
- package/dist/src/react/util.d.ts.map +1 -1
- package/dist/src/test/test-utils.d.ts +11 -2
- package/dist/src/test/test-utils.d.ts.map +1 -1
- package/dist/src/types.d.ts +273 -76
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/answerService.d.ts +7 -0
- package/dist/src/utils/answerService.d.ts.map +1 -1
- package/dist/src/utils/authService.d.ts +30 -0
- package/dist/src/utils/authService.d.ts.map +1 -1
- package/dist/src/utils/processData.d.ts +12 -0
- package/dist/src/utils/processData.d.ts.map +1 -1
- package/dist/src/utils/processTrigger.d.ts +7 -0
- package/dist/src/utils/processTrigger.d.ts.map +1 -1
- package/dist/src/utils.d.ts +12 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed.es.js +667 -264
- package/dist/tsembed.js +659 -263
- package/lib/package.json +3 -8
- package/lib/src/auth.d.ts +48 -3
- package/lib/src/auth.d.ts.map +1 -1
- package/lib/src/auth.js +70 -25
- package/lib/src/auth.js.map +1 -1
- package/lib/src/auth.spec.js +14 -5
- package/lib/src/auth.spec.js.map +1 -1
- package/lib/src/config.d.ts +1 -0
- package/lib/src/config.d.ts.map +1 -1
- package/lib/src/config.js +5 -3
- package/lib/src/config.js.map +1 -1
- package/lib/src/config.spec.js.map +1 -1
- package/lib/src/embed/app.d.ts +17 -5
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +25 -15
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/app.spec.js +12 -12
- package/lib/src/embed/app.spec.js.map +1 -1
- package/lib/src/embed/base.d.ts +20 -9
- package/lib/src/embed/base.d.ts.map +1 -1
- package/lib/src/embed/base.js +31 -15
- package/lib/src/embed/base.js.map +1 -1
- 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.d.ts +17 -5
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +49 -37
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/liveboard.spec.js +37 -30
- package/lib/src/embed/liveboard.spec.js.map +1 -1
- package/lib/src/embed/pinboard.spec.js +14 -26
- package/lib/src/embed/pinboard.spec.js.map +1 -1
- package/lib/src/embed/search-bar.d.ts +3 -0
- package/lib/src/embed/search-bar.d.ts.map +1 -1
- package/lib/src/embed/search-bar.js +5 -6
- package/lib/src/embed/search-bar.js.map +1 -1
- package/lib/src/embed/search.d.ts +9 -5
- package/lib/src/embed/search.d.ts.map +1 -1
- package/lib/src/embed/search.js +18 -14
- package/lib/src/embed/search.js.map +1 -1
- package/lib/src/embed/search.spec.js +16 -19
- package/lib/src/embed/search.spec.js.map +1 -1
- package/lib/src/embed/searchEmbed-basic-auth.spec.js +4 -0
- package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +64 -6
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +147 -74
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +46 -24
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/errors.d.ts.map +1 -1
- package/lib/src/errors.js.map +1 -1
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -2
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.d.ts +8 -0
- package/lib/src/mixpanel-service.d.ts.map +1 -1
- package/lib/src/mixpanel-service.js +13 -1
- package/lib/src/mixpanel-service.js.map +1 -1
- package/lib/src/mixpanel-service.spec.js.map +1 -1
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +6 -6
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/react/index.spec.js +15 -6
- package/lib/src/react/index.spec.js.map +1 -1
- package/lib/src/react/util.d.ts +4 -0
- package/lib/src/react/util.d.ts.map +1 -1
- package/lib/src/react/util.js +4 -0
- package/lib/src/react/util.js.map +1 -1
- package/lib/src/test/test-utils.d.ts +11 -2
- package/lib/src/test/test-utils.d.ts.map +1 -1
- package/lib/src/test/test-utils.js +36 -25
- package/lib/src/test/test-utils.js.map +1 -1
- package/lib/src/types.d.ts +273 -76
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +200 -54
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/answerService.d.ts +7 -0
- package/lib/src/utils/answerService.d.ts.map +1 -1
- package/lib/src/utils/answerService.js +7 -0
- package/lib/src/utils/answerService.js.map +1 -1
- package/lib/src/utils/answerService.spec.js.map +1 -1
- package/lib/src/utils/authService.d.ts +30 -0
- package/lib/src/utils/authService.d.ts.map +1 -1
- package/lib/src/utils/authService.js +39 -2
- package/lib/src/utils/authService.js.map +1 -1
- package/lib/src/utils/authService.spec.js.map +1 -1
- package/lib/src/utils/processData.d.ts +12 -0
- package/lib/src/utils/processData.d.ts.map +1 -1
- package/lib/src/utils/processData.js +33 -5
- package/lib/src/utils/processData.js.map +1 -1
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils/processTrigger.d.ts +7 -0
- package/lib/src/utils/processTrigger.d.ts.map +1 -1
- package/lib/src/utils/processTrigger.js +17 -3
- package/lib/src/utils/processTrigger.js.map +1 -1
- package/lib/src/utils/processTrigger.spec.js.map +1 -1
- package/lib/src/utils.d.ts +12 -0
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +24 -19
- package/lib/src/utils.js.map +1 -1
- package/lib/src/utils.spec.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +437 -103
- package/package.json +3 -8
- package/src/auth.spec.ts +68 -150
- package/src/auth.ts +115 -109
- package/src/config.spec.ts +2 -4
- package/src/config.ts +5 -3
- package/src/embed/app.spec.ts +25 -14
- package/src/embed/app.ts +47 -35
- package/src/embed/base.spec.ts +3 -9
- package/src/embed/base.ts +51 -53
- package/src/embed/embed.spec.ts +5 -6
- package/src/embed/liveboard.spec.ts +56 -37
- package/src/embed/liveboard.ts +66 -64
- package/src/embed/pinboard.spec.ts +26 -29
- package/src/embed/search-bar.tsx +10 -8
- package/src/embed/search.spec.ts +31 -21
- package/src/embed/search.ts +26 -25
- package/src/embed/searchEmbed-basic-auth.spec.ts +22 -28
- package/src/embed/ts-embed.spec.ts +99 -144
- package/src/embed/ts-embed.ts +181 -159
- package/src/errors.ts +3 -6
- package/src/index.ts +4 -10
- package/src/mixpanel-service.spec.ts +1 -3
- package/src/mixpanel-service.ts +13 -1
- package/src/react/index.spec.tsx +37 -13
- package/src/react/index.tsx +38 -57
- package/src/react/util.ts +8 -4
- package/src/test/test-utils.ts +43 -39
- package/src/types.ts +270 -78
- package/src/utils/answerService.spec.ts +3 -5
- package/src/utils/answerService.ts +21 -17
- package/src/utils/authService.spec.ts +26 -41
- package/src/utils/authService.ts +47 -21
- package/src/utils/processData.spec.ts +26 -59
- package/src/utils/processData.ts +36 -14
- package/src/utils/processTrigger.spec.ts +1 -6
- package/src/utils/processTrigger.ts +18 -9
- package/src/utils.spec.ts +8 -12
- package/src/utils.ts +25 -26
package/src/auth.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import EventEmitter from 'eventemitter3';
|
|
2
2
|
import { initMixpanel } from './mixpanel-service';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AuthType, DOMSelector, EmbedConfig, EmbedEvent, Param,
|
|
5
|
+
} from './types';
|
|
4
6
|
import { getDOMNode, getRedirectUrl } from './utils';
|
|
5
7
|
// eslint-disable-next-line import/no-cycle
|
|
6
8
|
import {
|
|
@@ -25,15 +27,12 @@ const sessionInfoPromise = new Promise((resolve) => {
|
|
|
25
27
|
});
|
|
26
28
|
let releaseVersion = '';
|
|
27
29
|
|
|
28
|
-
export const SSO_REDIRECTION_MARKER_GUID =
|
|
29
|
-
'5e16222e-ef02-43e9-9fbd-24226bf3ce5b';
|
|
30
|
+
export const SSO_REDIRECTION_MARKER_GUID = '5e16222e-ef02-43e9-9fbd-24226bf3ce5b';
|
|
30
31
|
|
|
31
32
|
export const EndPoints = {
|
|
32
33
|
AUTH_VERIFICATION: '/callosum/v1/session/info',
|
|
33
|
-
SAML_LOGIN_TEMPLATE: (targetUrl: string) =>
|
|
34
|
-
|
|
35
|
-
OIDC_LOGIN_TEMPLATE: (targetUrl: string) =>
|
|
36
|
-
`/callosum/v1/oidc/login?targetURLPath=${targetUrl}`,
|
|
34
|
+
SAML_LOGIN_TEMPLATE: (targetUrl: string) => `/callosum/v1/saml/login?targetURLPath=${targetUrl}`,
|
|
35
|
+
OIDC_LOGIN_TEMPLATE: (targetUrl: string) => `/callosum/v1/oidc/login?targetURLPath=${targetUrl}`,
|
|
37
36
|
TOKEN_LOGIN: '/callosum/v1/session/login/token',
|
|
38
37
|
BASIC_LOGIN: '/callosum/v1/session/login',
|
|
39
38
|
LOGOUT: '/callosum/v1/session/logout',
|
|
@@ -42,6 +41,7 @@ export const EndPoints = {
|
|
|
42
41
|
/**
|
|
43
42
|
* Enum for auth failure types. This is the parameter passed to the listner
|
|
44
43
|
* of {@link AuthStatus.FAILURE}.
|
|
44
|
+
*
|
|
45
45
|
* @group Authentication / Init
|
|
46
46
|
*/
|
|
47
47
|
export enum AuthFailureType {
|
|
@@ -53,6 +53,7 @@ export enum AuthFailureType {
|
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Enum for auth status emitted by the emitter returned from {@link init}.
|
|
56
|
+
*
|
|
56
57
|
* @group Authentication / Init
|
|
57
58
|
*/
|
|
58
59
|
export enum AuthStatus {
|
|
@@ -76,6 +77,7 @@ export enum AuthStatus {
|
|
|
76
77
|
* Emitted when inPopup: true in the SAMLRedirect flow.
|
|
77
78
|
* And, we are waiting for popup to be triggered either programatically
|
|
78
79
|
* or by the trigger button.
|
|
80
|
+
*
|
|
79
81
|
* @version SDK: 1.19.0
|
|
80
82
|
*/
|
|
81
83
|
WAITING_FOR_POPUP = 'WAITING_FOR_POPUP',
|
|
@@ -83,28 +85,37 @@ export enum AuthStatus {
|
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Event emitter returned from {@link init}.
|
|
88
|
+
*
|
|
86
89
|
* @group Authentication / Init
|
|
87
90
|
*/
|
|
88
91
|
export interface AuthEventEmitter {
|
|
89
92
|
/**
|
|
90
|
-
*
|
|
93
|
+
* Register a listener on Auth failure.
|
|
94
|
+
*
|
|
95
|
+
* @param event
|
|
96
|
+
* @param listener
|
|
97
|
+
*/
|
|
98
|
+
on(event: AuthStatus.FAILURE, listener: (failureType: AuthFailureType) => void): this;
|
|
99
|
+
/**
|
|
100
|
+
* Register a listener on Auth SDK success.
|
|
101
|
+
*
|
|
91
102
|
* @param event
|
|
92
103
|
* @param listener
|
|
93
104
|
*/
|
|
94
105
|
on(
|
|
95
|
-
event: AuthStatus.
|
|
96
|
-
listener: (failureType: AuthFailureType) => void,
|
|
97
|
-
): this;
|
|
98
|
-
on(
|
|
99
|
-
event:
|
|
100
|
-
| AuthStatus.SDK_SUCCESS
|
|
101
|
-
| AuthStatus.LOGOUT
|
|
102
|
-
| AuthStatus.WAITING_FOR_POPUP,
|
|
106
|
+
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
|
|
103
107
|
listener: () => void,
|
|
104
108
|
): this;
|
|
105
109
|
on(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
|
|
110
|
+
once(event: AuthStatus.FAILURE, listener: (failureType: AuthFailureType) => void): this;
|
|
111
|
+
once(
|
|
112
|
+
event: AuthStatus.SDK_SUCCESS | AuthStatus.LOGOUT | AuthStatus.WAITING_FOR_POPUP,
|
|
113
|
+
listener: () => void,
|
|
114
|
+
): this;
|
|
115
|
+
once(event: AuthStatus.SUCCESS, listener: (sessionInfo: any) => void): this;
|
|
106
116
|
/**
|
|
107
117
|
* Trigger an event on the emitter returned from init.
|
|
118
|
+
*
|
|
108
119
|
* @param {@link AuthEvent}
|
|
109
120
|
*/
|
|
110
121
|
emit(event: AuthEvent): void;
|
|
@@ -112,6 +123,7 @@ export interface AuthEventEmitter {
|
|
|
112
123
|
|
|
113
124
|
/**
|
|
114
125
|
* Events which can be triggered on the emitter returned from {@link init}.
|
|
126
|
+
*
|
|
115
127
|
* @group Authentication / Init
|
|
116
128
|
*/
|
|
117
129
|
export enum AuthEvent {
|
|
@@ -124,16 +136,24 @@ export enum AuthEvent {
|
|
|
124
136
|
|
|
125
137
|
let authEE: EventEmitter<AuthStatus | AuthEvent>;
|
|
126
138
|
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
*/
|
|
127
142
|
export function getAuthEE(): EventEmitter<AuthStatus | AuthEvent> {
|
|
128
143
|
return authEE;
|
|
129
144
|
}
|
|
130
145
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
* @param eventEmitter
|
|
149
|
+
*/
|
|
150
|
+
export function setAuthEE(eventEmitter: EventEmitter<AuthStatus | AuthEvent>): void {
|
|
134
151
|
authEE = eventEmitter;
|
|
135
152
|
}
|
|
136
153
|
|
|
154
|
+
/**
|
|
155
|
+
*
|
|
156
|
+
*/
|
|
137
157
|
export function notifyAuthSDKSuccess(): void {
|
|
138
158
|
if (!authEE) {
|
|
139
159
|
console.error('SDK not initialized');
|
|
@@ -142,6 +162,9 @@ export function notifyAuthSDKSuccess(): void {
|
|
|
142
162
|
authEE.emit(AuthStatus.SDK_SUCCESS);
|
|
143
163
|
}
|
|
144
164
|
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
145
168
|
export function notifyAuthSuccess(): void {
|
|
146
169
|
if (!authEE) {
|
|
147
170
|
console.error('SDK not initialized');
|
|
@@ -150,6 +173,10 @@ export function notifyAuthSuccess(): void {
|
|
|
150
173
|
authEE.emit(AuthStatus.SUCCESS, sessionInfo);
|
|
151
174
|
}
|
|
152
175
|
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param failureType
|
|
179
|
+
*/
|
|
153
180
|
export function notifyAuthFailure(failureType: AuthFailureType): void {
|
|
154
181
|
if (!authEE) {
|
|
155
182
|
console.error('SDK not initialized');
|
|
@@ -158,6 +185,9 @@ export function notifyAuthFailure(failureType: AuthFailureType): void {
|
|
|
158
185
|
authEE.emit(AuthStatus.FAILURE, failureType);
|
|
159
186
|
}
|
|
160
187
|
|
|
188
|
+
/**
|
|
189
|
+
*
|
|
190
|
+
*/
|
|
161
191
|
export function notifyLogout(): void {
|
|
162
192
|
if (!authEE) {
|
|
163
193
|
console.error('SDK not initialized');
|
|
@@ -168,6 +198,7 @@ export function notifyLogout(): void {
|
|
|
168
198
|
|
|
169
199
|
/**
|
|
170
200
|
* Check if we are logged into the ThoughtSpot cluster
|
|
201
|
+
*
|
|
171
202
|
* @param thoughtSpotHost The ThoughtSpot cluster hostname or IP
|
|
172
203
|
*/
|
|
173
204
|
async function isLoggedIn(thoughtSpotHost: string): Promise<boolean> {
|
|
@@ -191,24 +222,32 @@ export function getReleaseVersion() {
|
|
|
191
222
|
}
|
|
192
223
|
|
|
193
224
|
/**
|
|
194
|
-
* Return a promise that resolves with the session information when
|
|
195
|
-
* successful. And info is available.
|
|
225
|
+
* Return a promise that resolves with the session information when
|
|
226
|
+
* authentication is successful. And info is available.
|
|
227
|
+
*
|
|
196
228
|
* @group Global methods
|
|
197
229
|
*/
|
|
198
230
|
export function getSessionInfo(): Promise<any> {
|
|
199
231
|
return sessionInfoPromise;
|
|
200
232
|
}
|
|
201
233
|
|
|
234
|
+
/**
|
|
235
|
+
*
|
|
236
|
+
* @param sessionDetails
|
|
237
|
+
*/
|
|
202
238
|
export function initSession(sessionDetails: any) {
|
|
203
239
|
sessionInfo = sessionDetails;
|
|
204
240
|
initMixpanel(sessionInfo);
|
|
205
241
|
sessionInfoResolver(sessionInfo);
|
|
206
242
|
}
|
|
207
243
|
|
|
208
|
-
const DUPLICATE_TOKEN_ERR =
|
|
209
|
-
'
|
|
210
|
-
'See https://developers.thoughtspot.com/docs/?pageid=embed-auth#trusted-auth-embed for more details.';
|
|
244
|
+
const DUPLICATE_TOKEN_ERR = 'Duplicate token, please issue a new token every time getAuthToken callback is called.'
|
|
245
|
+
+ 'See https://developers.thoughtspot.com/docs/?pageid=embed-auth#trusted-auth-embed for more details.';
|
|
211
246
|
let prevAuthToken: string = null;
|
|
247
|
+
/**
|
|
248
|
+
*
|
|
249
|
+
* @param authtoken
|
|
250
|
+
*/
|
|
212
251
|
function alertForDuplicateToken(authtoken: string) {
|
|
213
252
|
if (prevAuthToken === authtoken) {
|
|
214
253
|
// eslint-disable-next-line no-alert
|
|
@@ -230,18 +269,14 @@ function isAtSSORedirectUrl(): boolean {
|
|
|
230
269
|
*/
|
|
231
270
|
function removeSSORedirectUrlMarker(): void {
|
|
232
271
|
// Note (sunny): This will leave a # around even if it was not in the URL
|
|
233
|
-
// to begin with. Trying to remove the hash by changing window.location will
|
|
234
|
-
// the page which we don't want. We'll live with adding an
|
|
235
|
-
// parent page URL until we find any use case where
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
'',
|
|
239
|
-
);
|
|
272
|
+
// to begin with. Trying to remove the hash by changing window.location will
|
|
273
|
+
// reload the page which we don't want. We'll live with adding an
|
|
274
|
+
// unnecessary hash to the parent page URL until we find any use case where
|
|
275
|
+
// that creates an issue.
|
|
276
|
+
window.location.hash = window.location.hash.replace(SSO_REDIRECTION_MARKER_GUID, '');
|
|
240
277
|
}
|
|
241
278
|
|
|
242
|
-
export const getAuthenticaionToken = async (
|
|
243
|
-
embedConfig: EmbedConfig,
|
|
244
|
-
): Promise<any> => {
|
|
279
|
+
export const getAuthenticaionToken = async (embedConfig: EmbedConfig): Promise<any> => {
|
|
245
280
|
const { authEndpoint, getAuthToken } = embedConfig;
|
|
246
281
|
let authToken = null;
|
|
247
282
|
if (getAuthToken) {
|
|
@@ -256,40 +291,30 @@ export const getAuthenticaionToken = async (
|
|
|
256
291
|
|
|
257
292
|
/**
|
|
258
293
|
* Perform token based authentication
|
|
294
|
+
*
|
|
259
295
|
* @param embedConfig The embed configuration
|
|
260
296
|
*/
|
|
261
|
-
export const doTokenAuth = async (
|
|
262
|
-
embedConfig: EmbedConfig,
|
|
263
|
-
): Promise<boolean> => {
|
|
297
|
+
export const doTokenAuth = async (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
264
298
|
const {
|
|
265
|
-
thoughtSpotHost,
|
|
266
|
-
username,
|
|
267
|
-
authEndpoint,
|
|
268
|
-
getAuthToken,
|
|
299
|
+
thoughtSpotHost, username, authEndpoint, getAuthToken,
|
|
269
300
|
} = embedConfig;
|
|
270
301
|
if (!authEndpoint && !getAuthToken) {
|
|
271
|
-
throw new Error(
|
|
272
|
-
'Either auth endpoint or getAuthToken function must be provided',
|
|
273
|
-
);
|
|
302
|
+
throw new Error('Either auth endpoint or getAuthToken function must be provided');
|
|
274
303
|
}
|
|
275
304
|
loggedInStatus = await isLoggedIn(thoughtSpotHost);
|
|
276
305
|
if (!loggedInStatus) {
|
|
277
306
|
const authToken = await getAuthenticaionToken(embedConfig);
|
|
278
307
|
let resp;
|
|
279
308
|
try {
|
|
280
|
-
resp = await fetchAuthPostService(
|
|
281
|
-
thoughtSpotHost,
|
|
282
|
-
username,
|
|
283
|
-
authToken,
|
|
284
|
-
);
|
|
309
|
+
resp = await fetchAuthPostService(thoughtSpotHost, username, authToken);
|
|
285
310
|
} catch (e) {
|
|
286
311
|
resp = await fetchAuthService(thoughtSpotHost, username, authToken);
|
|
287
312
|
}
|
|
288
313
|
// token login issues a 302 when successful
|
|
289
314
|
loggedInStatus = resp.ok || resp.type === 'opaqueredirect';
|
|
290
315
|
if (loggedInStatus && embedConfig.detectCookieAccessSlow) {
|
|
291
|
-
// When 3rd party cookie access is blocked, this will fail because
|
|
292
|
-
// not be sent with the call.
|
|
316
|
+
// When 3rd party cookie access is blocked, this will fail because
|
|
317
|
+
// cookies will not be sent with the call.
|
|
293
318
|
loggedInStatus = await isLoggedIn(thoughtSpotHost);
|
|
294
319
|
}
|
|
295
320
|
}
|
|
@@ -298,16 +323,13 @@ export const doTokenAuth = async (
|
|
|
298
323
|
|
|
299
324
|
/**
|
|
300
325
|
* Validate embedConfig parameters required for cookielessTokenAuth
|
|
326
|
+
*
|
|
301
327
|
* @param embedConfig The embed configuration
|
|
302
328
|
*/
|
|
303
|
-
export const doCookielessTokenAuth = async (
|
|
304
|
-
embedConfig: EmbedConfig,
|
|
305
|
-
): Promise<boolean> => {
|
|
329
|
+
export const doCookielessTokenAuth = async (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
306
330
|
const { authEndpoint, getAuthToken } = embedConfig;
|
|
307
331
|
if (!authEndpoint && !getAuthToken) {
|
|
308
|
-
throw new Error(
|
|
309
|
-
'Either auth endpoint or getAuthToken function must be provided',
|
|
310
|
-
);
|
|
332
|
+
throw new Error('Either auth endpoint or getAuthToken function must be provided');
|
|
311
333
|
}
|
|
312
334
|
return Promise.resolve(true);
|
|
313
335
|
};
|
|
@@ -318,19 +340,14 @@ export const doCookielessTokenAuth = async (
|
|
|
318
340
|
*
|
|
319
341
|
* Warning: This feature is primarily intended for developer testing. It is
|
|
320
342
|
* strongly advised not to use this authentication method in production.
|
|
343
|
+
*
|
|
321
344
|
* @param embedConfig The embed configuration
|
|
322
345
|
*/
|
|
323
|
-
export const doBasicAuth = async (
|
|
324
|
-
embedConfig: EmbedConfig,
|
|
325
|
-
): Promise<boolean> => {
|
|
346
|
+
export const doBasicAuth = async (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
326
347
|
const { thoughtSpotHost, username, password } = embedConfig;
|
|
327
348
|
const loggedIn = await isLoggedIn(thoughtSpotHost);
|
|
328
349
|
if (!loggedIn) {
|
|
329
|
-
const response = await fetchBasicAuthService(
|
|
330
|
-
thoughtSpotHost,
|
|
331
|
-
username,
|
|
332
|
-
password,
|
|
333
|
-
);
|
|
350
|
+
const response = await fetchBasicAuthService(thoughtSpotHost, username, password);
|
|
334
351
|
loggedInStatus = response.ok;
|
|
335
352
|
if (embedConfig.detectCookieAccessSlow) {
|
|
336
353
|
loggedInStatus = await isLoggedIn(thoughtSpotHost);
|
|
@@ -341,11 +358,13 @@ export const doBasicAuth = async (
|
|
|
341
358
|
return loggedInStatus;
|
|
342
359
|
};
|
|
343
360
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
361
|
+
/**
|
|
362
|
+
*
|
|
363
|
+
* @param ssoURL
|
|
364
|
+
* @param triggerContainer
|
|
365
|
+
* @param triggerText
|
|
366
|
+
*/
|
|
367
|
+
async function samlPopupFlow(ssoURL: string, triggerContainer: DOMSelector, triggerText: string) {
|
|
349
368
|
const openPopup = () => {
|
|
350
369
|
if (samlAuthWindow === null || samlAuthWindow.closed) {
|
|
351
370
|
samlAuthWindow = window.open(
|
|
@@ -360,15 +379,13 @@ async function samlPopupFlow(
|
|
|
360
379
|
authEE?.emit(AuthStatus.WAITING_FOR_POPUP);
|
|
361
380
|
const containerEl = getDOMNode(triggerContainer);
|
|
362
381
|
if (containerEl) {
|
|
363
|
-
containerEl.innerHTML =
|
|
364
|
-
'<button id="ts-auth-btn" class="ts-auth-btn" style="margin: auto;"></button>';
|
|
382
|
+
containerEl.innerHTML = '<button id="ts-auth-btn" class="ts-auth-btn" style="margin: auto;"></button>';
|
|
365
383
|
const authElem = document.getElementById('ts-auth-btn');
|
|
366
384
|
authElem.textContent = triggerText;
|
|
367
385
|
authElem.addEventListener('click', openPopup, { once: true });
|
|
368
386
|
}
|
|
369
|
-
samlCompletionPromise =
|
|
370
|
-
|
|
371
|
-
new Promise<void>((resolve, reject) => {
|
|
387
|
+
samlCompletionPromise = samlCompletionPromise
|
|
388
|
+
|| new Promise<void>((resolve, reject) => {
|
|
372
389
|
window.addEventListener('message', (e) => {
|
|
373
390
|
if (e.data.type === EmbedEvent.SAMLComplete) {
|
|
374
391
|
(e.source as Window).close();
|
|
@@ -383,12 +400,11 @@ async function samlPopupFlow(
|
|
|
383
400
|
|
|
384
401
|
/**
|
|
385
402
|
* Perform SAML authentication
|
|
403
|
+
*
|
|
386
404
|
* @param embedConfig The embed configuration
|
|
405
|
+
* @param ssoEndPoint
|
|
387
406
|
*/
|
|
388
|
-
const doSSOAuth = async (
|
|
389
|
-
embedConfig: EmbedConfig,
|
|
390
|
-
ssoEndPoint: string,
|
|
391
|
-
): Promise<void> => {
|
|
407
|
+
const doSSOAuth = async (embedConfig: EmbedConfig, ssoEndPoint: string): Promise<void> => {
|
|
392
408
|
const { thoughtSpotHost } = embedConfig;
|
|
393
409
|
const loggedIn = await isLoggedIn(thoughtSpotHost);
|
|
394
410
|
if (loggedIn) {
|
|
@@ -409,11 +425,7 @@ const doSSOAuth = async (
|
|
|
409
425
|
|
|
410
426
|
const ssoURL = `${thoughtSpotHost}${ssoEndPoint}`;
|
|
411
427
|
if (embedConfig.inPopup) {
|
|
412
|
-
await samlPopupFlow(
|
|
413
|
-
ssoURL,
|
|
414
|
-
embedConfig.authTriggerContainer,
|
|
415
|
-
embedConfig.authTriggerText,
|
|
416
|
-
);
|
|
428
|
+
await samlPopupFlow(ssoURL, embedConfig.authTriggerContainer, embedConfig.authTriggerText);
|
|
417
429
|
loggedInStatus = true;
|
|
418
430
|
return;
|
|
419
431
|
}
|
|
@@ -423,20 +435,18 @@ const doSSOAuth = async (
|
|
|
423
435
|
|
|
424
436
|
export const doSamlAuth = async (embedConfig: EmbedConfig) => {
|
|
425
437
|
const { thoughtSpotHost } = embedConfig;
|
|
426
|
-
// redirect for SSO, when the SSO authentication is done, this page will be
|
|
427
|
-
// again and the same JS will execute again.
|
|
438
|
+
// redirect for SSO, when the SSO authentication is done, this page will be
|
|
439
|
+
// loaded again and the same JS will execute again.
|
|
428
440
|
const ssoRedirectUrl = embedConfig.inPopup
|
|
429
441
|
? `${thoughtSpotHost}/v2/#/embed/saml-complete`
|
|
430
442
|
: getRedirectUrl(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
443
|
+
window.location.href,
|
|
444
|
+
SSO_REDIRECTION_MARKER_GUID,
|
|
445
|
+
embedConfig.redirectPath,
|
|
446
|
+
);
|
|
435
447
|
|
|
436
448
|
// bring back the page to the same URL
|
|
437
|
-
const ssoEndPoint = `${EndPoints.SAML_LOGIN_TEMPLATE(
|
|
438
|
-
encodeURIComponent(ssoRedirectUrl),
|
|
439
|
-
)}`;
|
|
449
|
+
const ssoEndPoint = `${EndPoints.SAML_LOGIN_TEMPLATE(encodeURIComponent(ssoRedirectUrl))}`;
|
|
440
450
|
|
|
441
451
|
await doSSOAuth(embedConfig, ssoEndPoint);
|
|
442
452
|
return loggedInStatus;
|
|
@@ -444,21 +454,18 @@ export const doSamlAuth = async (embedConfig: EmbedConfig) => {
|
|
|
444
454
|
|
|
445
455
|
export const doOIDCAuth = async (embedConfig: EmbedConfig) => {
|
|
446
456
|
const { thoughtSpotHost } = embedConfig;
|
|
447
|
-
// redirect for SSO, when the SSO authentication is done, this page will be
|
|
448
|
-
// again and the same JS will execute again.
|
|
449
|
-
const ssoRedirectUrl =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
);
|
|
457
|
+
// redirect for SSO, when the SSO authentication is done, this page will be
|
|
458
|
+
// loaded again and the same JS will execute again.
|
|
459
|
+
const ssoRedirectUrl = embedConfig.noRedirect || embedConfig.inPopup
|
|
460
|
+
? `${thoughtSpotHost}/v2/#/embed/saml-complete`
|
|
461
|
+
: getRedirectUrl(
|
|
462
|
+
window.location.href,
|
|
463
|
+
SSO_REDIRECTION_MARKER_GUID,
|
|
464
|
+
embedConfig.redirectPath,
|
|
465
|
+
);
|
|
457
466
|
|
|
458
467
|
// bring back the page to the same URL
|
|
459
|
-
const ssoEndPoint = `${EndPoints.OIDC_LOGIN_TEMPLATE(
|
|
460
|
-
encodeURIComponent(ssoRedirectUrl),
|
|
461
|
-
)}`;
|
|
468
|
+
const ssoEndPoint = `${EndPoints.OIDC_LOGIN_TEMPLATE(encodeURIComponent(ssoRedirectUrl))}`;
|
|
462
469
|
|
|
463
470
|
await doSSOAuth(embedConfig, ssoEndPoint);
|
|
464
471
|
return loggedInStatus;
|
|
@@ -473,11 +480,10 @@ export const logout = async (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
|
473
480
|
|
|
474
481
|
/**
|
|
475
482
|
* Perform authentication on the ThoughtSpot cluster
|
|
483
|
+
*
|
|
476
484
|
* @param embedConfig The embed configuration
|
|
477
485
|
*/
|
|
478
|
-
export const authenticate = async (
|
|
479
|
-
embedConfig: EmbedConfig,
|
|
480
|
-
): Promise<boolean> => {
|
|
486
|
+
export const authenticate = async (embedConfig: EmbedConfig): Promise<boolean> => {
|
|
481
487
|
const { authType } = embedConfig;
|
|
482
488
|
switch (authType) {
|
|
483
489
|
case AuthType.SSO:
|
package/src/config.spec.ts
CHANGED
|
@@ -59,8 +59,7 @@ describe('getThoughtSpotHost', () => {
|
|
|
59
59
|
expect(
|
|
60
60
|
getThoughtSpotHost({
|
|
61
61
|
...embedConfig,
|
|
62
|
-
thoughtSpotHost:
|
|
63
|
-
'http://1.2.3.4:8088/v2/?foo=bar&baz=42#myhash',
|
|
62
|
+
thoughtSpotHost: 'http://1.2.3.4:8088/v2/?foo=bar&baz=42#myhash',
|
|
64
63
|
}),
|
|
65
64
|
).toBe('http://1.2.3.4:8088/v2');
|
|
66
65
|
});
|
|
@@ -70,8 +69,7 @@ describe('getThoughtSpotHost', () => {
|
|
|
70
69
|
getThoughtSpotHost({
|
|
71
70
|
...embedConfig,
|
|
72
71
|
authType: AuthType.SAML,
|
|
73
|
-
thoughtSpotHost:
|
|
74
|
-
'http://1.2.3.4:8088/v2/?foo=bar&baz=42#myhash',
|
|
72
|
+
thoughtSpotHost: 'http://1.2.3.4:8088/v2/?foo=bar&baz=42#myhash',
|
|
75
73
|
}),
|
|
76
74
|
).toBe('http://1.2.3.4:8088/v2');
|
|
77
75
|
});
|
package/src/config.ts
CHANGED
|
@@ -23,6 +23,7 @@ const urlRegex = new RegExp(
|
|
|
23
23
|
/**
|
|
24
24
|
* Parse and construct the ThoughtSpot hostname or IP address
|
|
25
25
|
* from the embed configuration object.
|
|
26
|
+
*
|
|
26
27
|
* @param config
|
|
27
28
|
*/
|
|
28
29
|
export const getThoughtSpotHost = (config: EmbedConfig): string => {
|
|
@@ -51,9 +52,10 @@ export const getV2BasePath = (config: EmbedConfig): string => {
|
|
|
51
52
|
|
|
52
53
|
const tsHost = getThoughtSpotHost(config);
|
|
53
54
|
|
|
54
|
-
// This is to handle when e2e's. Search is run on pods for
|
|
55
|
-
// with baseUrl=https://localhost:8443.
|
|
56
|
-
// This is to handle when the developer is developing in their local
|
|
55
|
+
// This is to handle when e2e's. Search is run on pods for
|
|
56
|
+
// comp-blink-test-pipeline with baseUrl=https://localhost:8443.
|
|
57
|
+
// This is to handle when the developer is developing in their local
|
|
58
|
+
// environment.
|
|
57
59
|
if (tsHost.includes('://localhost') && !tsHost.includes(':8443')) {
|
|
58
60
|
return '';
|
|
59
61
|
}
|
package/src/embed/app.spec.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AppEmbed, AppViewConfig, Page } from './app';
|
|
2
2
|
import { init } from '../index';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Action, AuthType, HostEvent, RuntimeFilterOp,
|
|
5
|
+
} from '../types';
|
|
4
6
|
import {
|
|
5
7
|
executeAfterWait,
|
|
6
8
|
getDocumentBody,
|
|
@@ -11,6 +13,7 @@ import {
|
|
|
11
13
|
defaultParams,
|
|
12
14
|
defaultParamsForPinboardEmbed,
|
|
13
15
|
defaultParamsWithoutHiddenActions,
|
|
16
|
+
expectUrlMatchesWithParams,
|
|
14
17
|
} from '../test/test-utils';
|
|
15
18
|
import { version } from '../../package.json';
|
|
16
19
|
import * as config from '../config';
|
|
@@ -44,7 +47,8 @@ describe('App embed tests', () => {
|
|
|
44
47
|
const appEmbed = new AppEmbed(getRootEl(), defaultViewConfig);
|
|
45
48
|
appEmbed.render();
|
|
46
49
|
await executeAfterWait(() => {
|
|
47
|
-
|
|
50
|
+
expectUrlMatchesWithParams(
|
|
51
|
+
getIFrameSrc(),
|
|
48
52
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/home`,
|
|
49
53
|
);
|
|
50
54
|
});
|
|
@@ -57,7 +61,8 @@ describe('App embed tests', () => {
|
|
|
57
61
|
} as AppViewConfig);
|
|
58
62
|
appEmbed.render();
|
|
59
63
|
await executeAfterWait(() => {
|
|
60
|
-
|
|
64
|
+
expectUrlMatchesWithParams(
|
|
65
|
+
getIFrameSrc(),
|
|
61
66
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/home`,
|
|
62
67
|
);
|
|
63
68
|
});
|
|
@@ -70,7 +75,8 @@ describe('App embed tests', () => {
|
|
|
70
75
|
} as AppViewConfig);
|
|
71
76
|
appEmbed.render();
|
|
72
77
|
await executeAfterWait(() => {
|
|
73
|
-
|
|
78
|
+
expectUrlMatchesWithParams(
|
|
79
|
+
getIFrameSrc(),
|
|
74
80
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=true${defaultParams}${defaultParamsPost}#/home`,
|
|
75
81
|
);
|
|
76
82
|
});
|
|
@@ -101,7 +107,8 @@ describe('App embed tests', () => {
|
|
|
101
107
|
appEmbed.render();
|
|
102
108
|
|
|
103
109
|
await executeAfterWait(() => {
|
|
104
|
-
|
|
110
|
+
expectUrlMatchesWithParams(
|
|
111
|
+
getIFrameSrc(),
|
|
105
112
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/${route}`,
|
|
106
113
|
);
|
|
107
114
|
cleanUp();
|
|
@@ -117,7 +124,8 @@ describe('App embed tests', () => {
|
|
|
117
124
|
} as AppViewConfig);
|
|
118
125
|
appEmbed.render();
|
|
119
126
|
await executeAfterWait(() => {
|
|
120
|
-
|
|
127
|
+
expectUrlMatchesWithParams(
|
|
128
|
+
getIFrameSrc(),
|
|
121
129
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}${defaultParamsPost}#/foo/bar`,
|
|
122
130
|
);
|
|
123
131
|
});
|
|
@@ -138,7 +146,8 @@ describe('App embed tests', () => {
|
|
|
138
146
|
|
|
139
147
|
appEmbed.render();
|
|
140
148
|
await executeAfterWait(() => {
|
|
141
|
-
|
|
149
|
+
expectUrlMatchesWithParams(
|
|
150
|
+
getIFrameSrc(),
|
|
142
151
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&col1=sales&op1=EQ&val1=1000${defaultParams}${defaultParamsPost}#/home`,
|
|
143
152
|
);
|
|
144
153
|
});
|
|
@@ -155,7 +164,8 @@ describe('App embed tests', () => {
|
|
|
155
164
|
|
|
156
165
|
appEmbed.render();
|
|
157
166
|
await executeAfterWait(() => {
|
|
158
|
-
|
|
167
|
+
expectUrlMatchesWithParams(
|
|
168
|
+
getIFrameSrc(),
|
|
159
169
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=false&profileAndHelpInNavBarHidden=false&${defaultParamsWithoutHiddenActions}&disableAction=[%22save%22,%22update%22]&disableHint=Access%20denied&hideAction=[%22${Action.ReportError}%22,%22download%22]${defaultParamsPost}#/home`,
|
|
160
170
|
);
|
|
161
171
|
});
|
|
@@ -170,7 +180,8 @@ describe('App embed tests', () => {
|
|
|
170
180
|
|
|
171
181
|
appEmbed.render();
|
|
172
182
|
await executeAfterWait(() => {
|
|
173
|
-
|
|
183
|
+
expectUrlMatchesWithParams(
|
|
184
|
+
getIFrameSrc(),
|
|
174
185
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false${defaultParams}&tag=Finance${defaultParamsPost}#/home`,
|
|
175
186
|
);
|
|
176
187
|
});
|
|
@@ -184,7 +195,8 @@ describe('App embed tests', () => {
|
|
|
184
195
|
|
|
185
196
|
appEmbed.render();
|
|
186
197
|
await executeAfterWait(() => {
|
|
187
|
-
|
|
198
|
+
expectUrlMatchesWithParams(
|
|
199
|
+
getIFrameSrc(),
|
|
188
200
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&enableSearchAssist=true${defaultParams}${defaultParamsPost}#/home`,
|
|
189
201
|
);
|
|
190
202
|
});
|
|
@@ -193,9 +205,7 @@ describe('App embed tests', () => {
|
|
|
193
205
|
describe('Navigate to Page API', () => {
|
|
194
206
|
const path = 'pinboard/e0836cad-4fdf-42d4-bd97-567a6b2a6058';
|
|
195
207
|
beforeEach(() => {
|
|
196
|
-
jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(
|
|
197
|
-
() => 'http://tshost',
|
|
198
|
-
);
|
|
208
|
+
jest.spyOn(config, 'getThoughtSpotHost').mockImplementation(() => 'http://tshost');
|
|
199
209
|
});
|
|
200
210
|
|
|
201
211
|
test('when app is AppEmbed after navigateToPage function call, new path should be set to iframe', async () => {
|
|
@@ -207,7 +217,8 @@ describe('App embed tests', () => {
|
|
|
207
217
|
});
|
|
208
218
|
await appEmbed.render();
|
|
209
219
|
appEmbed.navigateToPage(path);
|
|
210
|
-
|
|
220
|
+
expectUrlMatchesWithParams(
|
|
221
|
+
getIFrameSrc(),
|
|
211
222
|
`http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&${defaultParamsForPinboardEmbed}${defaultParamsPost}#/${path}`,
|
|
212
223
|
);
|
|
213
224
|
});
|