@thoughtspot/visual-embed-sdk
Version:
ThoughtSpot Embed SDK
178 lines • 7.05 kB
JavaScript
import { getThoughtSpotHost } from "./config";
import { getEmbedConfig } from "./embed/embedConfig";
import { ERROR_MESSAGE } from "./errors";
import { InterceptedApiType, EmbedEvent, EmbedErrorCodes, ErrorDetailsTypes } from "./types";
import { embedEventStatus } from "./utils";
import { logger } from "./utils/logger";
const DefaultInterceptUrlsMap = {
[InterceptedApiType.AnswerData]: [
'/prism/?op=GetChartWithData',
'/prism/?op=GetTableWithHeadlineData',
'/prism/?op=GetTableWithData',
],
[InterceptedApiType.LiveboardData]: [
'/prism/?op=LoadContextBook'
],
};
const formatInterceptUrl = (url) => {
const host = getThoughtSpotHost(getEmbedConfig());
if (url.startsWith('/'))
return `${host}${url}`;
return url;
};
/**
* Converts user passed url values to proper urls
* [ANSER_DATA] => ['https://host/pris/op?=op']
* @param interceptUrls
* @returns
*/
const processInterceptUrls = (interceptUrls) => {
let processedUrls = [...interceptUrls];
Object.entries(DefaultInterceptUrlsMap).forEach(([apiType, apiTypeUrls]) => {
if (!processedUrls.includes(apiType))
return;
processedUrls = processedUrls.filter(url => url !== apiType);
processedUrls = [...processedUrls, ...apiTypeUrls];
});
return processedUrls.map(url => formatInterceptUrl(url));
};
/**
* Returns the data to be sent to embed to setup intercepts
* the urls to intercept, timeout etc
* @param viewConfig
* @returns
*/
export const getInterceptInitData = (viewConfig) => {
const combinedUrls = [...(viewConfig.interceptUrls || [])];
if (viewConfig.isOnBeforeGetVizDataInterceptEnabled) {
combinedUrls.push(InterceptedApiType.AnswerData);
}
const shouldInterceptAll = combinedUrls.includes(InterceptedApiType.ALL);
const interceptUrls = shouldInterceptAll ? [InterceptedApiType.ALL] : processInterceptUrls(combinedUrls);
const interceptTimeout = viewConfig.interceptTimeout;
return {
interceptUrls,
interceptTimeout,
};
};
const parseJson = (jsonString) => {
try {
const json = JSON.parse(jsonString);
return [json, null];
}
catch (error) {
return [null, error];
}
};
/**
* Parse the api intercept data and return the parsed data and error if any
* Embed returns the input and init from the fetch call
*/
const parseInterceptData = (eventDataString) => {
try {
const [parsedData, error] = parseJson(eventDataString);
if (error) {
return [null, error];
}
const { input, init } = parsedData;
const [parsedBody, bodyParseError] = parseJson(init.body);
if (!bodyParseError) {
init.body = parsedBody;
}
const parsedInit = { input, init };
return [parsedInit, null];
}
catch (error) {
return [null, error];
}
};
const getUrlType = (url) => {
for (const [apiType, apiTypeUrls] of Object.entries(DefaultInterceptUrlsMap)) {
if (apiTypeUrls.includes(url))
return apiType;
}
// TODO: have a unknown type maybe ??
return InterceptedApiType.ALL;
};
/**
* Handle Api intercept event and simulate legacy onBeforeGetVizDataIntercept event
*
* embed sends -> ApiIntercept -> we send
* ApiIntercept
* OnBeforeGetVizDataIntercept (if url is part of DefaultUrlMap.AnswerData)
*
* @param params
* @returns
*/
export const handleInterceptEvent = async (params) => {
var _a, _b, _c, _d, _e;
const { eventData, executeEvent, viewConfig, getUnsavedAnswerTml } = params;
const [interceptData, bodyParseError] = parseInterceptData(eventData.data);
if (bodyParseError) {
const errorDetails = {
errorType: ErrorDetailsTypes.API,
message: ERROR_MESSAGE.ERROR_PARSING_API_INTERCEPT_BODY,
code: EmbedErrorCodes.PARSING_API_INTERCEPT_BODY_ERROR,
error: ERROR_MESSAGE.ERROR_PARSING_API_INTERCEPT_BODY,
};
executeEvent(EmbedEvent.Error, errorDetails);
logger.error('Error parsing request body', bodyParseError);
return;
}
const { input: requestUrl, init } = interceptData;
const sessionId = (_c = (_b = (_a = init === null || init === void 0 ? void 0 : init.body) === null || _a === void 0 ? void 0 : _a.variables) === null || _b === void 0 ? void 0 : _b.session) === null || _c === void 0 ? void 0 : _c.sessionId;
const vizId = (_e = (_d = init === null || init === void 0 ? void 0 : init.body) === null || _d === void 0 ? void 0 : _d.variables) === null || _e === void 0 ? void 0 : _e.contextBookId;
const answerDataUrls = DefaultInterceptUrlsMap[InterceptedApiType.AnswerData];
const legacyInterceptEnabled = viewConfig.isOnBeforeGetVizDataInterceptEnabled;
const isAnswerDataUrl = answerDataUrls.includes(requestUrl);
const sendLegacyIntercept = isAnswerDataUrl && legacyInterceptEnabled;
if (sendLegacyIntercept) {
const answerTml = await getUnsavedAnswerTml({ sessionId, vizId });
// Build the legacy payload for backwards compatibility
const legacyPayload = {
data: {
data: answerTml,
status: embedEventStatus.END,
type: EmbedEvent.OnBeforeGetVizDataIntercept
}
};
executeEvent(EmbedEvent.OnBeforeGetVizDataIntercept, legacyPayload);
}
const urlType = getUrlType(requestUrl);
executeEvent(EmbedEvent.ApiIntercept, { ...interceptData, urlType });
};
/**
* Support both the legacy and new format of the api intercept response
* @param payload
* @returns
*/
export const processApiInterceptResponse = (payload) => {
var _a;
const isLegacyFormat = (_a = payload === null || payload === void 0 ? void 0 : payload.data) === null || _a === void 0 ? void 0 : _a.error;
if (isLegacyFormat) {
return processLegacyInterceptResponse(payload);
}
return payload;
};
export const processLegacyInterceptResponse = (payload) => {
var _a, _b, _c, _d, _e;
const errorText = (_b = (_a = payload === null || payload === void 0 ? void 0 : payload.data) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.errorText;
const errorDescription = (_d = (_c = payload === null || payload === void 0 ? void 0 : payload.data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.errorDescription;
const payloadToSend = {
execute: (_e = payload === null || payload === void 0 ? void 0 : payload.data) === null || _e === void 0 ? void 0 : _e.execute,
response: {
body: {
errors: [
{
title: errorText,
description: errorDescription,
isUserError: true,
},
],
data: {},
},
},
};
return { data: payloadToSend };
};
//# sourceMappingURL=api-intercept.js.map