Skip to content

Commit db61cd2

Browse files
committed
SCAL-269016 : rebase
1 parent 6a522e9 commit db61cd2

File tree

10 files changed

+317
-53
lines changed

10 files changed

+317
-53
lines changed

src/api-intercept.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { getThoughtSpotHost } from "./config";
2+
import { getEmbedConfig } from "./embed/embedConfig";
3+
import { InterceptedApiType, BaseViewConfig, EmbedConfig, InterceptV2Flags, EmbedEvent } from "./types";
4+
5+
6+
7+
const defaultUrls: Record<Exclude<InterceptedApiType, InterceptedApiType.ALL>, string[]> = {
8+
[InterceptedApiType.METADATA]: [
9+
'/prism/?op=CreateAnswerSession',
10+
'/prism/?op=GetV2SourceDetail',
11+
] as string[],
12+
[InterceptedApiType.DATA]: [
13+
'/prism/?op=GetChartWithData',
14+
'/prism/?op=GetTableWithHeadlineData',
15+
'/prism/?op=LoadContextBook'
16+
] as string[],
17+
};
18+
19+
const formatInterceptUrl = (url: string) => {
20+
const host = getThoughtSpotHost(getEmbedConfig());
21+
if (url.startsWith('/')) return `${host}${url}`;
22+
return url;
23+
}
24+
25+
export const processApiIntercept = async (eventData: any) => {
26+
27+
return JSON.parse(eventData.data);
28+
}
29+
30+
interface LegacyInterceptFlags {
31+
isOnBeforeGetVizDataInterceptEnabled: boolean;
32+
}
33+
34+
const processInterceptUrls = (combinedUrls: (string | InterceptedApiType)[]) => {
35+
Object.entries(defaultUrls).forEach(([apiType, apiTypeUrls]) => {
36+
if (!combinedUrls.includes(apiType)) return;
37+
combinedUrls = combinedUrls.filter(url => url !== apiType);
38+
combinedUrls = [...combinedUrls, ...apiTypeUrls];
39+
})
40+
return combinedUrls.map(url => formatInterceptUrl(url));
41+
}
42+
export const getInterceptInitData = (embedConfig: EmbedConfig, viewConfig: BaseViewConfig): InterceptV2Flags => {
43+
44+
const enableApiIntercept = (embedConfig.enableApiIntercept || viewConfig.enableApiIntercept) && (viewConfig.enableApiIntercept !== false);
45+
46+
if (!enableApiIntercept) return {
47+
enableApiIntercept: false,
48+
};
49+
50+
const combinedUrls = [...(embedConfig.interceptUrls || []), ...(viewConfig.interceptUrls || [])];
51+
52+
if ((viewConfig as LegacyInterceptFlags).isOnBeforeGetVizDataInterceptEnabled) {
53+
combinedUrls.push(InterceptedApiType.DATA);
54+
}
55+
56+
const shouldInterceptAll = combinedUrls.includes(InterceptedApiType.ALL);
57+
const interceptUrls = shouldInterceptAll ? [InterceptedApiType.ALL] : processInterceptUrls(combinedUrls);
58+
59+
const interceptTimeout = embedConfig.interceptTimeout || viewConfig.interceptTimeout;
60+
61+
return {
62+
interceptUrls,
63+
interceptTimeout,
64+
enableApiIntercept,
65+
};
66+
}
67+
68+
/**
69+
*
70+
* @param fetchInit
71+
*/
72+
const parseInterceptData = (eventDataString: any) => {
73+
74+
try {
75+
const { input, init } = JSON.parse(eventDataString);
76+
77+
init.body = JSON.parse(init.body);
78+
79+
const parsedInit = { input, init };
80+
return [parsedInit, null];
81+
} catch (error) {
82+
return [null, error];
83+
}
84+
}
85+
86+
export const handleInterceptEvent = async (params: { eventData: any, executeEvent: (eventType: EmbedEvent, data: any) => void, embedConfig: EmbedConfig, viewConfig: BaseViewConfig, getUnsavedAnswerTml: (props: { sessionId?: string, vizId?: string }) => Promise<{ tml: string }> }) => {
87+
88+
const { eventData, executeEvent, viewConfig, getUnsavedAnswerTml } = params;
89+
90+
const [interceptData, bodyParseError] = parseInterceptData(eventData.data);
91+
92+
if (bodyParseError) {
93+
executeEvent(EmbedEvent.Error, {
94+
error: 'Error parsing api intercept body',
95+
});
96+
logger.error('Error parsing request body', bodyParseError);
97+
return;
98+
}
99+
100+
const { input: requestUrl, init } = interceptData;
101+
102+
const sessionId = init?.body?.variables?.session?.sessionId;
103+
const vizId = init?.body?.variables?.contextBookId;
104+
105+
if (defaultUrls.DATA.includes(requestUrl) && (viewConfig as LegacyInterceptFlags).isOnBeforeGetVizDataInterceptEnabled) {
106+
const answerTml = await getUnsavedAnswerTml({ sessionId, vizId });
107+
executeEvent(EmbedEvent.OnBeforeGetVizDataIntercept, { data: { data: answerTml } });
108+
}
109+
110+
executeEvent(EmbedEvent.ApiIntercept, interceptData);
111+
}
112+
113+
export const processLegacyInterceptResponse = (payload: any) => {
114+
115+
const title = payload?.data?.errorText;
116+
const desc = payload?.data?.errorDescription;
117+
118+
const payloadToSend = [{
119+
data: {},
120+
errors: [
121+
{
122+
errorObj: {
123+
title,
124+
desc
125+
}
126+
}
127+
],
128+
}];
129+
130+
return payloadToSend;
131+
}

src/embed/app.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
AllEmbedViewConfig,
2020
} from '../types';
2121
import { V1Embed } from './ts-embed';
22+
import { getInterceptInitData } from '../api-intercept';
2223

2324
/**
2425
* Pages within the ThoughtSpot app that can be embedded.
@@ -728,7 +729,9 @@ export class AppEmbed extends V1Embed {
728729
params[Param.enableAskSage] = enableAskSage;
729730
}
730731

731-
if (isOnBeforeGetVizDataInterceptEnabled) {
732+
const { enableApiIntercept } = getInterceptInitData(this.embedConfig, this.viewConfig);
733+
734+
if (isOnBeforeGetVizDataInterceptEnabled && !enableApiIntercept) {
732735

733736
params[
734737
Param.IsOnBeforeGetVizDataInterceptEnabled

src/embed/hostEventClient/contracts.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum UIPassthroughEvent {
77
GetAvailableUIPassthroughs = 'getAvailableUiPassthroughs',
88
GetAnswerConfig = 'getAnswerPageConfig',
99
GetLiveboardConfig = 'getPinboardPageConfig',
10+
GetUnsavedAnswerTML = 'getUnsavedAnswerTML',
1011
}
1112

1213
// UI Passthrough Contract
@@ -63,6 +64,15 @@ export type UIPassthroughContractBase = {
6364
request: any;
6465
response: any;
6566
};
67+
[UIPassthroughEvent.GetUnsavedAnswerTML]: {
68+
request: {
69+
sessionId?: string;
70+
vizId?: string;
71+
};
72+
response: {
73+
tml: string;
74+
};
75+
};
6676
};
6777

6878
// UI Passthrough Request and Response

src/embed/search.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ERROR_MESSAGE } from '../errors';
2626
import { getAuthPromise } from './base';
2727
import { getReleaseVersion } from '../auth';
2828
import { getEmbedConfig } from './embedConfig';
29+
import { getInterceptInitData } from '../api-intercept';
2930

3031
/**
3132
* Configuration for search options.
@@ -442,7 +443,8 @@ export class SearchEmbed extends TsEmbed {
442443
queryParams[Param.HideSearchBar] = true;
443444
}
444445

445-
if (isOnBeforeGetVizDataInterceptEnabled) {
446+
const { enableApiIntercept } = getInterceptInitData(this.embedConfig, this.viewConfig);
447+
if (isOnBeforeGetVizDataInterceptEnabled && !enableApiIntercept) {
446448

447449
queryParams[Param.IsOnBeforeGetVizDataInterceptEnabled] = isOnBeforeGetVizDataInterceptEnabled;
448450
}

src/embed/ts-embed.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ describe('Unit test case for ts embed', () => {
10451045
type: EmbedEvent.APP_INIT,
10461046
data: {},
10471047
};
1048-
1048+
10491049
// Create a SearchEmbed with valid custom actions to test
10501050
// CustomActionsValidationResult
10511051
const searchEmbed = new SearchEmbed(getRootEl(), {
@@ -1067,7 +1067,7 @@ describe('Unit test case for ts embed', () => {
10671067
}
10681068
]
10691069
});
1070-
1070+
10711071
searchEmbed.render();
10721072
const mockPort: any = {
10731073
postMessage: jest.fn(),
@@ -1116,7 +1116,7 @@ describe('Unit test case for ts embed', () => {
11161116
customVariablesForThirdPartyTools: {},
11171117
},
11181118
});
1119-
1119+
11201120
// Verify that CustomActionsValidationResult structure is
11211121
// correct
11221122
const appInitData = mockPort.postMessage.mock.calls[0][0].data;
@@ -1137,7 +1137,7 @@ describe('Unit test case for ts embed', () => {
11371137
})
11381138
])
11391139
);
1140-
1140+
11411141
// Verify actions are sorted by name (alphabetically)
11421142
expect(appInitData.customActions[0].name).toBe('Another Valid Action');
11431143
expect(appInitData.customActions[1].name).toBe('Valid Action');
@@ -2488,7 +2488,7 @@ describe('Unit test case for ts embed', () => {
24882488
});
24892489

24902490
afterAll((): void => {
2491-
window.location = location as any;
2491+
(window.location as any) = location;
24922492
});
24932493

24942494
it('get url params for TS', () => {
@@ -3406,7 +3406,7 @@ describe('Unit test case for ts embed', () => {
34063406
new Error('Auth failed'),
34073407
);
34083408
const searchEmbed = new SearchEmbed(getRootEl(), defaultViewConfig);
3409-
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
3409+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
34103410
await searchEmbed.render();
34113411
await executeAfterWait(() => {
34123412
expect(getRootEl().innerHTML).toContain('Not logged in');
@@ -3415,7 +3415,7 @@ describe('Unit test case for ts embed', () => {
34153415
window.dispatchEvent(onlineEvent);
34163416
}).not.toThrow();
34173417
});
3418-
3418+
34193419
errorSpy.mockReset();
34203420
});
34213421

0 commit comments

Comments
 (0)