preset-sdk-url-params
Version:
Frontend SDK for embedding Preset data analytics into your own application
146 lines (140 loc) • 5.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports._initComms = _initComms;
exports.embedDashboard = embedDashboard;
var _switchboard = require("@superset-ui/switchboard");
var _const = require("./const");
var _guestTokenRefresh = require("./guestTokenRefresh");
var _polyfills = require("./polyfills");
/**
* The function to fetch a guest token from your Host App's backend server.
* The Host App backend must supply an API endpoint
* which returns a guest token with appropriate resource access.
*/
/**
* Embeds a Superset dashboard into the page using an iframe.
*/
async function embedDashboard({
id,
supersetDomain,
mountPoint,
fetchGuestToken,
dashboardUiConfig,
debug = false
}) {
function log(...info) {
if (debug) {
console.debug(`[preset-frontend-sdk][dashboard ${id}]`, ...info);
}
}
log('embedding');
// Polyfill replaceChildren
(0, _polyfills.applyReplaceChildrenPolyfill)();
if (supersetDomain.endsWith("/")) {
supersetDomain = supersetDomain.slice(0, -1);
}
function calculateConfig() {
let configNumber = 0;
if (dashboardUiConfig) {
if (dashboardUiConfig.hideTitle) {
configNumber += 1;
}
if (dashboardUiConfig.hideTab) {
configNumber += 2;
}
if (dashboardUiConfig.hideChartControls) {
configNumber += 8;
}
}
return configNumber;
}
function entriesToQS(entries) {
return entries.map(([key, value]) => `${key}=${value}`).join('&');
}
async function mountIframe() {
return new Promise(resolve => {
const iframe = document.createElement('iframe');
const dashboardConfig = dashboardUiConfig ? `uiConfig=${calculateConfig()}` : "";
const filterConfig = dashboardUiConfig?.filters || {};
const filterConfigKeys = Object.entries(filterConfig).map(([uiKey, uiValue]) => [_const.DASHBOARD_UI_FILTER_CONFIG_URL_PARAM_KEY[uiKey], uiValue]).filter(([key]) => key);
const filterConfigUrlParams = filterConfigKeys.length > 0 ? entriesToQS(filterConfigKeys) : null;
const filterConfigCustomUrlParams = filterConfig.urlParams ? entriesToQS(Object.entries(filterConfig.urlParams)) : null;
// setup the iframe's sandbox configuration
iframe.sandbox.add("allow-same-origin"); // needed for postMessage to work
iframe.sandbox.add("allow-scripts"); // obviously the iframe needs scripts
iframe.sandbox.add("allow-presentation"); // for fullscreen charts
iframe.sandbox.add("allow-downloads"); // for downloading charts as image
iframe.sandbox.add("allow-top-navigation"); // for links to open
iframe.sandbox.add("allow-forms"); // for forms to submit
iframe.sandbox.add("allow-popups"); // for exporting charts as csv
// add the event listener before setting src, to be 100% sure that we capture the load event
iframe.addEventListener('load', () => {
const switchboard = _initComms(iframe.contentWindow, supersetDomain, debug);
log('sent message channel to the iframe');
resolve(switchboard);
});
const qs = [dashboardConfig, filterConfigUrlParams, filterConfigCustomUrlParams].filter(Boolean).join('&');
const qsSuffix = qs ? `?${qs}` : '';
iframe.src = `${supersetDomain}/embedded/${id}${qsSuffix}`;
mountPoint?.replaceChildren(iframe);
log('placed the iframe');
});
}
const [guestToken, ourPort] = await Promise.all([fetchGuestToken(), mountIframe()]);
ourPort.emit('guestToken', {
guestToken
});
log('sent guest token');
let refreshGuestTokenInterval;
async function refreshGuestToken() {
log('(refreshGuestToken) refreshing guest token');
try {
const newGuestToken = await fetchGuestToken();
log('(refreshGuestToken) got new guest token', newGuestToken);
ourPort.emit('guestToken', {
guestToken: newGuestToken
});
log('sent new guest token to ourPort');
log('setting new refreshGuestTokenInterval', (0, _guestTokenRefresh.getGuestTokenRefreshTiming)(newGuestToken));
refreshGuestTokenInterval = setTimeout(refreshGuestToken, (0, _guestTokenRefresh.getGuestTokenRefreshTiming)(newGuestToken));
log('set new refreshGuestTokenInterval');
} catch (error) {
log('Error refreshing guest token', error);
}
}
log('setTimeout for refreshGuestToken', (0, _guestTokenRefresh.getGuestTokenRefreshTiming)(guestToken));
refreshGuestTokenInterval = setTimeout(refreshGuestToken, (0, _guestTokenRefresh.getGuestTokenRefreshTiming)(guestToken));
function unmount() {
log('unmounting');
mountPoint?.replaceChildren();
clearInterval(refreshGuestTokenInterval);
}
const getScrollSize = () => ourPort.get('getScrollSize');
return {
getScrollSize,
unmount
};
}
function _initComms(window, targetOrigin, debug = false) {
// MessageChannel allows us to send and receive messages smoothly between our window and the iframe
// See https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API
const commsChannel = new MessageChannel();
const ourPort = commsChannel.port1;
const theirPort = commsChannel.port2;
// Send one of the message channel ports to the iframe to initialize embedded comms
// See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
// we know the content window isn't null because we are in the load event handler.
window.postMessage({
type: _const.IFRAME_COMMS_MESSAGE_TYPE,
handshake: "port transfer"
}, targetOrigin, [theirPort]);
// return our port from the promise
return new _switchboard.Switchboard({
port: ourPort,
name: 'preset-frontend-sdk',
debug
});
}
//# sourceMappingURL=index.js.map