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

thoughtspot / rise / #385

15 Jul 2025 10:03PM UTC coverage: 87.066% (+1.0%) from 86.09%
#385

Pull #21

sagar1993
review comments 2
Pull Request #21: ResponseFormat to gql directive

95 of 120 branches covered (79.17%)

Branch coverage included in aggregate %.

36 of 38 new or added lines in 2 files covered. (94.74%)

1 existing line in 1 file now uncovered.

181 of 197 relevant lines covered (91.88%)

9.35 hits per line

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

90.28
/src/common.ts
1
import _ from 'lodash';
1✔
2

3
const FORWARD_RESPONSE_HEADERS = [
1✔
4
    'set-cookie',
5
    'x-callosum-incident-id',
6
    'x-callosum-ip',
7
    'x-callosum-request-time-us',
8
    'x-callosum-trace-id',
9
    'x-content-type-options',
10
    'x-nginx-localhost',
11
    'x-ua-compatible',
12
    'x-xss-protection',
13
];
14

15
export const commonDefs = `
1✔
16
    scalar JSON
17
    input RiseSetter {
18
        field: String
19
        path: String!
20
    }
21
`;
22

23
export interface RiseDirectiveOptions {
24
    name: string;
25
    baseURL: string;
26
    apiType: 'rest' | 'gql';
27
    headers: Record<string, string>;
28
    forwardheaders: string[];
29
    contenttype: string | undefined;
30
    ErrorClass: new (...args: any[]) => Error;
31
}
32

33
export function getReqHeaders(riseDirective: RiseDirectiveOptions, options, context) {
1✔
34
    let {
35
        headers = {},
18✔
36
        contenttype = options.contenttype || 'application/json',
45✔
37
        forwardheaders = [],
21✔
38
    } = riseDirective;
21✔
39

40
    headers = {
21✔
41
        'Content-Type': contenttype,
42
        ...options.headers,
43
        ...headers,
44
    };
45
    forwardheaders.push(...options.forwardheaders);
21✔
46
    forwardheaders = forwardheaders.map((h) => h.toLowerCase());
32✔
47
    return {
21✔
48
        ...headers,
49
        ..._.pickBy(context.req.headers, (v, h) => forwardheaders.includes(h.toLowerCase())),
43✔
50
    };
51
}
52

53
export function processResHeaders(response, context) {
1✔
54
    // Setting the headers returned from response
55
    const responseHeaders: any = response.headers.raw();
18✔
56
    FORWARD_RESPONSE_HEADERS.forEach((key) => {
18✔
57
    if (responseHeaders[key]) {
162!
58
        context.res.setHeader(key, responseHeaders[key]);
×
59
    }
60
    });
61
}
62

63
export class RestError extends Error {
1✔
64
    public code: number;
65

66
    public errors: any;
67

68
    constructor(message = 'Error occured', code = 400, errors = null) {
2!
69
        super(message);
1✔
70
        this.code = code;
1✔
71
        this.errors = errors;
1✔
72
    }
73
}
74

75
// The function is used to map the keys of the object to the new keys
76
// By default, it will return the original object if the key is not in the keyMap
77
// Example:
78
// const obj = { a: 1, b: 2, c: 3 }
79
// const keyMap = { a: 'd', b: 'e', f: 'g' }
80
// const newObj = mapKeysDeep(obj, keyMap)
81
// newObj will be { d: 1, e: 2, c: 3 }
82
// The function will not modify the original object
83
export function mapKeysDeep(obj: any, keyMap: Record<string, string> = {}): any {
1!
84
    // If keyMap is empty, return original object
85
    if (!keyMap || Object.keys(keyMap).length === 0) {
32✔
86
        return obj;
8✔
87
    }
88
    // If obj is an array, map each item in the array
89
    if (Array.isArray(obj)) {
24✔
90
        return obj.map((item) => mapKeysDeep(item, keyMap));
2✔
91
    }
92
    // If obj is an object, map each key in the object
93
    if (obj !== null && typeof obj === 'object') {
23✔
94
        const mapped: Record<string, any> = {};
10✔
95
        Object.keys(obj).forEach((key) => {
10✔
96
            const newKey = keyMap[key] || key;
18✔
97
            mapped[newKey] = mapKeysDeep(obj[key], keyMap);
18✔
98
        });
99
        return mapped;
10✔
100
    }
101
    return obj;
13✔
102
}
103

104
/**
105
 * Parses responseKeyFormat from string to object format
106
 * @param responseKeyFormat - The response key format, can be string (JSON) or object
107
 * @returns Parsed key mapping object or empty object if parsing fails
108
 */
109
export function parseResponseKeyFormat(
1✔
110
    responseKeyFormat: string | Record<string, string> | undefined,
111
): Record<string, string> {
112
    if (!responseKeyFormat) {
11✔
113
        return {};
6✔
114
    }
115
    // If it's already an object, return it directly
116
    if (typeof responseKeyFormat === 'object') {
5!
NEW
117
        return responseKeyFormat;
×
118
    }
119
    // If it's a string, try to parse it as JSON
120
    if (typeof responseKeyFormat === 'string') {
5✔
121
        try {
5✔
122
            const parsed = JSON.parse(responseKeyFormat);
5✔
123
            console.debug('[Rise] Parsed responseKeyFormat:', parsed);
3✔
124
            return parsed;
3✔
125
        } catch (error) {
126
            console.error('[Rise] Failed to parse responseKeyFormat as JSON:', error);
2✔
127
            console.error('[Rise] Invalid responseKeyFormat:', responseKeyFormat);
2✔
128
            return {};
2✔
129
        }
130
    }
NEW
131
    return {};
×
132
}
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