UNPKG

@thoughtspot/visual-embed-sdk

Version:
387 lines 16.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.reset = exports.exportTML = exports.executeTML = exports.renderInQueue = exports.logout = exports.disableAutoLogin = exports.init = exports.getIsInitCalled = exports.getInitPromise = exports.createAndSetInitPromise = exports.prefetch = exports.handleAuth = exports.notifyLogout = exports.notifyAuthSuccess = exports.notifyAuthSDKSuccess = exports.notifyAuthFailure = exports.getAuthPromise = exports.authPromise = void 0; const tslib_1 = require("tslib"); /* eslint-disable camelcase */ /* eslint-disable import/no-mutable-exports */ /** * Copyright (c) 2022 * * Base classes * @summary Base classes * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com> */ const eventemitter3_1 = tslib_1.__importDefault(require("eventemitter3")); const reporting_1 = require("../utils/reporting"); const logger_1 = require("../utils/logger"); const tokenizedFetch_1 = require("../tokenizedFetch"); const authService_1 = require("../utils/authService/authService"); const config_1 = require("../config"); const types_1 = require("../types"); const auth_1 = require("../auth"); Object.defineProperty(exports, "notifyAuthFailure", { enumerable: true, get: function () { return auth_1.notifyAuthFailure; } }); Object.defineProperty(exports, "notifyAuthSDKSuccess", { enumerable: true, get: function () { return auth_1.notifyAuthSDKSuccess; } }); Object.defineProperty(exports, "notifyAuthSuccess", { enumerable: true, get: function () { return auth_1.notifyAuthSuccess; } }); Object.defineProperty(exports, "notifyLogout", { enumerable: true, get: function () { return auth_1.notifyLogout; } }); require("../utils/with-resolvers-polyfill"); const mixpanel_service_1 = require("../mixpanel-service"); const embedConfig_1 = require("./embedConfig"); const utils_1 = require("../utils"); const resetServices_1 = require("../utils/resetServices"); const CONFIG_DEFAULTS = { loginFailedMessage: 'Not logged in', authTriggerText: 'Authorize', authType: types_1.AuthType.None, logLevel: types_1.LogLevel.ERROR, }; const getAuthPromise = () => exports.authPromise; exports.getAuthPromise = getAuthPromise; /** * Perform authentication on the ThoughtSpot app as applicable. */ const handleAuth = () => { exports.authPromise = (0, auth_1.authenticate)((0, embedConfig_1.getEmbedConfig)()); exports.authPromise.then((isLoggedIn) => { if (!isLoggedIn) { (0, auth_1.notifyAuthFailure)(auth_1.AuthFailureType.SDK); } else { // Post login service is called after successful login. (0, auth_1.postLoginService)(); (0, auth_1.notifyAuthSDKSuccess)(); } }, () => { (0, auth_1.notifyAuthFailure)(auth_1.AuthFailureType.SDK); }); return exports.authPromise; }; exports.handleAuth = handleAuth; const hostUrlToFeatureUrl = { [types_1.PrefetchFeatures.SearchEmbed]: (url, flags) => `${url}v2/?${flags}#/embed/answer`, [types_1.PrefetchFeatures.LiveboardEmbed]: (url, flags) => `${url}?${flags}`, [types_1.PrefetchFeatures.FullApp]: (url, flags) => `${url}?${flags}`, [types_1.PrefetchFeatures.VizEmbed]: (url, flags) => `${url}?${flags}`, }; /** * 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. * @param url The URL provided for prefetch * @param prefetchFeatures Specify features which needs to be prefetched. * @param additionalFlags This can be used to add any URL flag. * @version SDK: 1.4.0 | ThoughtSpot: ts7.sep.cl, 7.2.1 * @group Global methods */ const prefetch = (url, prefetchFeatures, additionalFlags) => { var _a; if (url === '') { // eslint-disable-next-line no-console logger_1.logger.warn('The prefetch method does not have a valid URL'); } else { const features = prefetchFeatures || [types_1.PrefetchFeatures.FullApp]; let hostUrl = url || (0, embedConfig_1.getEmbedConfig)().thoughtSpotHost; const prefetchFlags = { [types_1.Param.EmbedApp]: true, ...(_a = (0, embedConfig_1.getEmbedConfig)()) === null || _a === void 0 ? void 0 : _a.additionalFlags, ...additionalFlags, }; hostUrl = hostUrl[hostUrl.length - 1] === '/' ? hostUrl : `${hostUrl}/`; Array.from(new Set(features .map((feature) => hostUrlToFeatureUrl[feature](hostUrl, (0, utils_1.getQueryParamString)(prefetchFlags))))) .forEach((prefetchUrl, index) => { const iFrame = document.createElement('iframe'); iFrame.src = prefetchUrl; iFrame.style.width = '0'; iFrame.style.height = '0'; iFrame.style.border = '0'; // Make it 'fixed' to keep it in a different stacking context. // This should solve the focus behaviours inside the iframe from // interfering with main body. iFrame.style.position = 'fixed'; // Push it out of viewport. iFrame.style.top = '100vh'; iFrame.style.left = '100vw'; iFrame.classList.add('prefetchIframe'); iFrame.classList.add(`prefetchIframeNum-${index}`); document.body.appendChild(iFrame); }); } }; exports.prefetch = prefetch; /** * * @param embedConfig */ function sanity(embedConfig) { if (embedConfig.thoughtSpotHost === undefined) { throw new Error('ThoughtSpot host not provided'); } if (embedConfig.authType === types_1.AuthType.TrustedAuthToken) { if (!embedConfig.authEndpoint && typeof embedConfig.getAuthToken !== 'function') { throw new Error('Trusted auth should provide either authEndpoint or getAuthToken'); } } } /** * * @param embedConfig */ function backwardCompat(embedConfig) { const newConfig = { ...embedConfig }; if (embedConfig.noRedirect !== undefined && embedConfig.inPopup === undefined) { newConfig.inPopup = embedConfig.noRedirect; } return newConfig; } const initFlagKey = 'initFlagKey'; const createAndSetInitPromise = () => { const { promise: initPromise, resolve: initPromiseResolve, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore } = Promise.withResolvers(); const initFlagStore = { initPromise, isInitCalled: false, initPromiseResolve, }; (0, utils_1.storeValueInWindow)(initFlagKey, initFlagStore, { // In case of diff imports the promise might be already set ignoreIfAlreadyExists: true, }); }; exports.createAndSetInitPromise = createAndSetInitPromise; (0, exports.createAndSetInitPromise)(); const getInitPromise = () => { var _a; return (_a = (0, utils_1.getValueFromWindow)(initFlagKey)) === null || _a === void 0 ? void 0 : _a.initPromise; }; exports.getInitPromise = getInitPromise; const getIsInitCalled = () => { var _a; return !!((_a = (0, utils_1.getValueFromWindow)(initFlagKey)) === null || _a === void 0 ? void 0 : _a.isInitCalled); }; exports.getIsInitCalled = getIsInitCalled; /** * Initializes the Visual Embed SDK globally and perform * authentication if applicable. This function needs to be called before any ThoughtSpot * component like Liveboard etc can be embedded. But need not wait for AuthEvent.SUCCESS * to actually embed. That is handled internally. * @param embedConfig The configuration object containing ThoughtSpot host, * authentication mechanism and so on. * @example * ```js * const authStatus = init({ * thoughtSpotHost: 'https://my.thoughtspot.cloud', * authType: AuthType.None, * }); * authStatus.on(AuthStatus.FAILURE, (reason) => { // do something here }); * ``` * @returns {@link AuthEventEmitter} event emitter which emits events on authentication success, * failure and logout. See {@link AuthStatus} * @version SDK: 1.0.0 | ThoughtSpot ts7.april.cl, 7.2.1 * @group Authentication / Init */ const init = (embedConfig) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; sanity(embedConfig); (0, resetServices_1.resetAllCachedServices)(); embedConfig = (0, embedConfig_1.setEmbedConfig)(backwardCompat({ ...CONFIG_DEFAULTS, ...embedConfig, thoughtSpotHost: (0, config_1.getThoughtSpotHost)(embedConfig), })); (0, logger_1.setGlobalLogLevelOverride)(embedConfig.logLevel); (0, reporting_1.registerReportingObserver)(); const authEE = new eventemitter3_1.default(); (0, auth_1.setAuthEE)(authEE); (0, exports.handleAuth)(); const { password, ...configToTrack } = (0, embedConfig_1.getEmbedConfig)(); (0, mixpanel_service_1.uploadMixpanelEvent)(mixpanel_service_1.MIXPANEL_EVENT.VISUAL_SDK_CALLED_INIT, { ...configToTrack, usedCustomizationSheet: ((_b = (_a = embedConfig.customizations) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b.customCSSUrl) != null, usedCustomizationVariables: ((_e = (_d = (_c = embedConfig.customizations) === null || _c === void 0 ? void 0 : _c.style) === null || _d === void 0 ? void 0 : _d.customCSS) === null || _e === void 0 ? void 0 : _e.variables) != null, usedCustomizationRules: ((_h = (_g = (_f = embedConfig.customizations) === null || _f === void 0 ? void 0 : _f.style) === null || _g === void 0 ? void 0 : _g.customCSS) === null || _h === void 0 ? void 0 : _h.rules_UNSTABLE) != null, usedCustomizationStrings: !!((_k = (_j = embedConfig.customizations) === null || _j === void 0 ? void 0 : _j.content) === null || _k === void 0 ? void 0 : _k.strings), usedCustomizationIconSprite: !!((_l = embedConfig.customizations) === null || _l === void 0 ? void 0 : _l.iconSpriteUrl), }); if ((0, embedConfig_1.getEmbedConfig)().callPrefetch) { (0, exports.prefetch)((0, embedConfig_1.getEmbedConfig)().thoughtSpotHost); } // Resolves the promise created in the initPromiseKey (0, utils_1.getValueFromWindow)(initFlagKey).initPromiseResolve(authEE); (0, utils_1.getValueFromWindow)(initFlagKey).isInitCalled = true; return authEE; }; exports.init = init; /** * */ function disableAutoLogin() { (0, embedConfig_1.getEmbedConfig)().autoLogin = false; } exports.disableAutoLogin = disableAutoLogin; /** * Logs out from ThoughtSpot. This also sets the autoLogin flag to false, to * prevent the SDK from automatically logging in again. * * You can call the `init` method again to re login, if autoLogin is set to * true in this second call it will be honored. * @param doNotDisableAutoLogin This flag when passed will not disable autoLogin * @returns Promise which resolves when logout completes. * @version SDK: 1.10.1 | ThoughtSpot: 8.2.0.cl, 8.4.1-sw * @group Global methods */ const logout = (doNotDisableAutoLogin = false) => { if (!doNotDisableAutoLogin) { disableAutoLogin(); } return (0, auth_1.logout)((0, embedConfig_1.getEmbedConfig)()).then((isLoggedIn) => { (0, auth_1.notifyLogout)(); return isLoggedIn; }); }; exports.logout = logout; let renderQueue = Promise.resolve(); /** * Renders functions in a queue, resolves to next function only after the callback next * is called * @param fn The function being registered */ const renderInQueue = (fn) => { const { queueMultiRenders = false } = (0, embedConfig_1.getEmbedConfig)(); if (queueMultiRenders) { renderQueue = renderQueue.then(() => new Promise((res) => fn(res))); return renderQueue; } // Sending an empty function to keep it consistent with the above usage. return fn(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function }; exports.renderInQueue = renderInQueue; /** * Imports TML representation of the metadata objects into ThoughtSpot. * @param data * @returns imports TML data into ThoughtSpot * @example * ```js * executeTML({ * //Array of metadata Tmls in string format * metadata_tmls: [ * "'\''{\"guid\":\"9bd202f5-d431-44bf-9a07-b4f7be372125\", * \"liveboard\":{\"name\":\"Parameters Liveboard\"}}'\''" * ], * import_policy: 'PARTIAL', // Specifies the import policy for the TML import. * create_new: false, // If selected, creates TML objects with new GUIDs. * }).then(result => { * console.log(result); * }).catch(error => { * console.error(error); * }); *``` * @version SDK: 1.23.0 | ThoughtSpot: 9.4.0.cl * @group Global methods */ const executeTML = async (data) => { try { sanity((0, embedConfig_1.getEmbedConfig)()); } catch (err) { return Promise.reject(err); } const { thoughtSpotHost, authType } = (0, embedConfig_1.getEmbedConfig)(); const headers = { 'Content-Type': 'application/json', 'x-requested-by': 'ThoughtSpot', }; const payload = { metadata_tmls: data.metadata_tmls, import_policy: data.import_policy || 'PARTIAL', create_new: data.create_new || false, }; return (0, tokenizedFetch_1.tokenizedFetch)(`${thoughtSpotHost}${authService_1.EndPoints.EXECUTE_TML}`, { method: 'POST', headers, body: JSON.stringify(payload), credentials: 'include', }) .then((response) => { if (!response.ok) { throw new Error(`Failed to import TML data: ${response.status} - ${response.statusText}`); } return response.json(); }) .catch((error) => { throw error; }); }; exports.executeTML = executeTML; /** * Exports TML representation of the metadata objects from ThoughtSpot in JSON or YAML * format. * @param data * @returns exports TML data * @example * ```js * exportTML({ * metadata: [ * { * type: "LIVEBOARD", //Metadata Type * identifier: "9bd202f5-d431-44bf-9a07-b4f7be372125" //Metadata Id * } * ], * export_associated: false,//indicates whether to export associated metadata objects * export_fqn: false, //Adds FQNs of the referenced objects.For example, if you are * //exporting a Liveboard and its associated objects, the API * //returns the Liveboard TML data with the FQNs of the referenced * //worksheet. If the exported TML data includes FQNs, you don't need * //to manually add FQNs of the referenced objects during TML import. * edoc_format: "JSON" //It takes JSON or YAML value * }).then(result => { * console.log(result); * }).catch(error => { * console.error(error); * }); * ``` * @version SDK: 1.23.0 | ThoughtSpot: 9.4.0.cl * @group Global methods */ const exportTML = async (data) => { const { thoughtSpotHost, authType } = (0, embedConfig_1.getEmbedConfig)(); try { sanity((0, embedConfig_1.getEmbedConfig)()); } catch (err) { return Promise.reject(err); } const payload = { metadata: data.metadata, export_associated: data.export_associated || false, export_fqn: data.export_fqn || false, edoc_format: data.edoc_format || 'YAML', }; const headers = { 'Content-Type': 'application/json', 'x-requested-by': 'ThoughtSpot', }; return (0, tokenizedFetch_1.tokenizedFetch)(`${thoughtSpotHost}${authService_1.EndPoints.EXPORT_TML}`, { method: 'POST', headers, body: JSON.stringify(payload), credentials: 'include', }) .then((response) => { if (!response.ok) { throw new Error(`Failed to export TML: ${response.status} - ${response.statusText}`); } return response.json(); }) .catch((error) => { throw error; }); }; exports.exportTML = exportTML; // For testing purposes only /** * */ function reset() { (0, embedConfig_1.setEmbedConfig)({}); (0, auth_1.setAuthEE)(null); exports.authPromise = null; } exports.reset = reset; //# sourceMappingURL=base.js.map