• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thoughtspot / visual-embed-sdk / #2592

10 Oct 2025 10:28AM UTC coverage: 93.789% (-0.4%) from 94.158%
#2592

Pull #328

sastaachar
SCAL-269016 : sepration
Pull Request #328: SCAL-269016 : Intercept v2

1295 of 1475 branches covered (87.8%)

Branch coverage included in aggregate %.

100 of 107 new or added lines in 9 files covered. (93.46%)

41 existing lines in 4 files now uncovered.

3099 of 3210 relevant lines covered (96.54%)

93.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/src/api-intercept.ts
1
import { getThoughtSpotHost } from "./config";
14✔
2
import { getEmbedConfig } from "./embed/embedConfig";
14✔
3
import { InterceptedApiType, BaseViewConfig, EmbedConfig, InterceptV2Flags, EmbedEvent } from "./types";
14✔
4
import { logger } from "./utils/logger";
14✔
5

6

7
const defaultUrls: Record<Exclude<InterceptedApiType, InterceptedApiType.ALL>, string[]> = {
14✔
8
    [InterceptedApiType.METADATA]: [
9
        '/prism/?op=CreateAnswerSession',
10
        '/prism/?op=GetV2SourceDetail',
11
    ] as string[],
12
    [InterceptedApiType.ANSWER_DATA]: [
13
        '/prism/?op=GetChartWithData',
14
        '/prism/?op=GetTableWithHeadlineData',
15
    ] as string[],
16
    [InterceptedApiType.LIVEBOARD_DATA]: [
17
        '/prism/?op=LoadContextBook'
18
    ] as string[],
19
};
20

21
const formatInterceptUrl = (url: string) => {
14✔
22
    const host = getThoughtSpotHost(getEmbedConfig());
5✔
23
    if (url.startsWith('/')) return `${host}${url}`;
5✔
NEW
24
    return url;
×
25
}
26

27
export const processApiIntercept = async (eventData: any) => {
14✔
28

29
    return JSON.parse(eventData.data);
1✔
30
}
31

32
interface LegacyInterceptFlags {
33
    isOnBeforeGetVizDataInterceptEnabled: boolean;
34
}
35

36
const processInterceptUrls = (interceptUrls: (string | InterceptedApiType)[]) => {
14✔
37
    let processedUrls = [...interceptUrls];
2✔
38
    Object.entries(defaultUrls).forEach(([apiType, apiTypeUrls]) => {
2✔
39
        if (!processedUrls.includes(apiType)) return;
6✔
40
        processedUrls = processedUrls.filter(url => url !== apiType);
3✔
41
        processedUrls = [...processedUrls, ...apiTypeUrls];
2✔
42
    })
43
    return processedUrls.map(url => formatInterceptUrl(url));
5✔
44
}
45
export const getInterceptInitData = (embedConfig: EmbedConfig, viewConfig: BaseViewConfig): InterceptV2Flags => {
14✔
46

47
    const enableApiIntercept = (embedConfig.enableApiIntercept || viewConfig.enableApiIntercept) && (viewConfig.enableApiIntercept !== false);
164✔
48

49
    if (!enableApiIntercept) return {
164✔
50
        enableApiIntercept: false,
51
    };
52

53
    const combinedUrls = [...(embedConfig.interceptUrls || []), ...(viewConfig.interceptUrls || [])];
3!
54

55
    if ((viewConfig as LegacyInterceptFlags).isOnBeforeGetVizDataInterceptEnabled) {
3✔
56
        combinedUrls.push(InterceptedApiType.ANSWER_DATA);
1✔
57
    }
58

59
    const shouldInterceptAll = combinedUrls.includes(InterceptedApiType.ALL);
3✔
60
    const interceptUrls = shouldInterceptAll ? [InterceptedApiType.ALL] : processInterceptUrls(combinedUrls);
3✔
61

62
    const interceptTimeout = embedConfig.interceptTimeout || viewConfig.interceptTimeout;
3✔
63

64
    return {
3✔
65
        interceptUrls,
66
        interceptTimeout,
67
        enableApiIntercept,
68
    };
69
}
70

71
/**
72
 * 
73
 * @param fetchInit 
74
 */
75
const parseInterceptData = (eventDataString: any) => {
14✔
76

77
    try {
2✔
78
        const { input, init } = JSON.parse(eventDataString);
2✔
79

80
        init.body = JSON.parse(init.body);
1✔
81

82
        const parsedInit = { input, init };
1✔
83
        return [parsedInit, null];
1✔
84
    } catch (error) {
85
        return [null, error];
1✔
86
    }
87
}
88

89
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 }> }) => {
14✔
90

91
    const { eventData, executeEvent, viewConfig, getUnsavedAnswerTml } = params;
2✔
92

93
    const [interceptData, bodyParseError] = parseInterceptData(eventData.data);
2✔
94

95
    if (bodyParseError) {
2✔
96
        executeEvent(EmbedEvent.Error, {
1✔
97
            error: 'Error parsing api intercept body',
98
        });
99
        logger.error('Error parsing request body', bodyParseError);
1✔
100
        return;
1✔
101
    }
102

103
    const { input: requestUrl, init } = interceptData;
1✔
104

105
    const sessionId = init?.body?.variables?.session?.sessionId;
1!
106
    const vizId = init?.body?.variables?.contextBookId;
1!
107

108
    if (defaultUrls.ANSWER_DATA.includes(requestUrl) && (viewConfig as LegacyInterceptFlags).isOnBeforeGetVizDataInterceptEnabled) {
1✔
109
        const answerTml = await getUnsavedAnswerTml({ sessionId, vizId });
1✔
110
        executeEvent(EmbedEvent.OnBeforeGetVizDataIntercept, { data: { data: answerTml } });
1✔
111
    }
112

113
    executeEvent(EmbedEvent.ApiIntercept, interceptData);
1✔
114
}
115

116
export const processLegacyInterceptResponse = (payload: any) => {
14✔
117

118
    const title = payload?.data?.errorText;
1!
119
    const desc = payload?.data?.errorDescription;
1!
120

121
    const payloadToSend = [{
1✔
122
        data: {},
123
        errors: [
124
            {
125
                errorObj: {
126
                    title,
127
                    desc
128
                }
129
            }
130
        ],
131
    }];
132

133
    return payloadToSend;
1✔
134
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc