@thoughtspot/visual-embed-sdk 1.24.0-dev → 1.24.0-preRender.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/package.json +2 -3
- package/cjs/src/embed/app.d.ts +3 -1
- package/cjs/src/embed/app.d.ts.map +1 -1
- package/cjs/src/embed/app.js +8 -2
- package/cjs/src/embed/app.js.map +1 -1
- package/cjs/src/embed/base.d.ts +2 -0
- package/cjs/src/embed/base.d.ts.map +1 -1
- package/cjs/src/embed/base.js +2 -0
- package/cjs/src/embed/base.js.map +1 -1
- package/cjs/src/embed/liveboard.d.ts +3 -2
- package/cjs/src/embed/liveboard.d.ts.map +1 -1
- package/cjs/src/embed/liveboard.js +6 -5
- package/cjs/src/embed/liveboard.js.map +1 -1
- package/cjs/src/embed/sage.d.ts +4 -1
- package/cjs/src/embed/sage.d.ts.map +1 -1
- package/cjs/src/embed/sage.js +9 -2
- package/cjs/src/embed/sage.js.map +1 -1
- package/cjs/src/embed/search-bar.d.ts +1 -0
- package/cjs/src/embed/search-bar.d.ts.map +1 -1
- package/cjs/src/embed/search-bar.js +1 -0
- package/cjs/src/embed/search-bar.js.map +1 -1
- package/cjs/src/embed/search.d.ts +5 -1
- package/cjs/src/embed/search.d.ts.map +1 -1
- package/cjs/src/embed/search.js +10 -2
- package/cjs/src/embed/search.js.map +1 -1
- package/cjs/src/embed/ts-embed.d.ts +9 -3
- package/cjs/src/embed/ts-embed.d.ts.map +1 -1
- package/cjs/src/embed/ts-embed.js +50 -20
- package/cjs/src/embed/ts-embed.js.map +1 -1
- package/cjs/src/embed/ts-embed.spec.js +47 -0
- package/cjs/src/embed/ts-embed.spec.js.map +1 -1
- package/cjs/src/index.d.ts +3 -2
- package/cjs/src/index.d.ts.map +1 -1
- package/cjs/src/index.js +3 -1
- package/cjs/src/index.js.map +1 -1
- package/cjs/src/mixpanel-service.d.ts.map +1 -1
- package/cjs/src/mixpanel-service.js +2 -0
- package/cjs/src/mixpanel-service.js.map +1 -1
- package/cjs/src/mixpanel-service.spec.js +1 -0
- package/cjs/src/mixpanel-service.spec.js.map +1 -1
- package/cjs/src/react/index.d.ts +5 -1
- package/cjs/src/react/index.d.ts.map +1 -1
- package/cjs/src/react/index.js +8 -8
- package/cjs/src/react/index.js.map +1 -1
- package/cjs/src/types.d.ts +46 -18
- package/cjs/src/types.d.ts.map +1 -1
- package/cjs/src/types.js +7 -13
- package/cjs/src/types.js.map +1 -1
- package/cjs/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
- package/cjs/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
- package/cjs/src/utils/graphql/answerService/answer-queries.js +80 -0
- package/cjs/src/utils/graphql/answerService/answer-queries.js.map +1 -0
- package/cjs/src/utils/graphql/answerService/answerService.d.ts +61 -0
- package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
- package/cjs/src/utils/graphql/answerService/answerService.js +182 -0
- package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -0
- package/cjs/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
- package/cjs/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
- package/cjs/src/utils/graphql/answerService/answerService.spec.js +201 -0
- package/cjs/src/utils/graphql/answerService/answerService.spec.js.map +1 -0
- package/cjs/src/utils/graphql/graphql-request.d.ts +15 -0
- package/cjs/src/utils/graphql/graphql-request.d.ts.map +1 -0
- package/cjs/src/utils/graphql/graphql-request.js +40 -0
- package/cjs/src/utils/graphql/graphql-request.js.map +1 -0
- package/cjs/src/utils/graphql/sourceService.d.ts +8 -0
- package/cjs/src/utils/graphql/sourceService.d.ts.map +1 -0
- package/cjs/src/utils/graphql/sourceService.js +69 -0
- package/cjs/src/utils/graphql/sourceService.js.map +1 -0
- package/cjs/src/utils/graphql/sourceService.spec.d.ts +2 -0
- package/cjs/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
- package/cjs/src/utils/graphql/sourceService.spec.js +12 -0
- package/cjs/src/utils/graphql/sourceService.spec.js.map +1 -0
- package/cjs/src/utils/processData.d.ts.map +1 -1
- package/cjs/src/utils/processData.js +7 -11
- package/cjs/src/utils/processData.js.map +1 -1
- package/cjs/src/utils/processData.spec.js +13 -17
- package/cjs/src/utils/processData.spec.js.map +1 -1
- package/cjs/src/utils.d.ts +6 -0
- package/cjs/src/utils.d.ts.map +1 -1
- package/cjs/src/utils.js +26 -1
- package/cjs/src/utils.js.map +1 -1
- package/dist/src/embed/app.d.ts +3 -1
- package/dist/src/embed/app.d.ts.map +1 -1
- package/dist/src/embed/base.d.ts +2 -0
- package/dist/src/embed/base.d.ts.map +1 -1
- package/dist/src/embed/liveboard.d.ts +3 -2
- package/dist/src/embed/liveboard.d.ts.map +1 -1
- package/dist/src/embed/sage.d.ts +4 -1
- package/dist/src/embed/sage.d.ts.map +1 -1
- package/dist/src/embed/search-bar.d.ts +1 -0
- package/dist/src/embed/search-bar.d.ts.map +1 -1
- package/dist/src/embed/search.d.ts +5 -1
- package/dist/src/embed/search.d.ts.map +1 -1
- package/dist/src/embed/ts-embed.d.ts +9 -3
- package/dist/src/embed/ts-embed.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mixpanel-service.d.ts.map +1 -1
- package/dist/src/react/index.d.ts +5 -1
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +46 -18
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
- package/dist/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
- package/dist/src/utils/graphql/answerService/answerService.d.ts +61 -0
- package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
- package/dist/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
- package/dist/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
- package/dist/src/utils/graphql/graphql-request.d.ts +15 -0
- package/dist/src/utils/graphql/graphql-request.d.ts.map +1 -0
- package/dist/src/utils/graphql/sourceService.d.ts +8 -0
- package/dist/src/utils/graphql/sourceService.d.ts.map +1 -0
- package/dist/src/utils/graphql/sourceService.spec.d.ts +2 -0
- package/dist/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
- package/dist/src/utils/processData.d.ts.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/tsembed-react.es.js +468 -107
- package/dist/tsembed-react.js +471 -106
- package/dist/tsembed.es.js +511 -103
- package/dist/tsembed.js +511 -102
- package/dist/visual-embed-sdk-react-full.d.ts +132 -28
- package/dist/visual-embed-sdk-react.d.ts +132 -28
- package/dist/visual-embed-sdk.d.ts +127 -27
- package/lib/package.json +2 -3
- package/lib/src/embed/app.d.ts +3 -1
- package/lib/src/embed/app.d.ts.map +1 -1
- package/lib/src/embed/app.js +8 -2
- package/lib/src/embed/app.js.map +1 -1
- package/lib/src/embed/base.d.ts +2 -0
- package/lib/src/embed/base.d.ts.map +1 -1
- package/lib/src/embed/base.js +2 -0
- package/lib/src/embed/base.js.map +1 -1
- package/lib/src/embed/liveboard.d.ts +3 -2
- package/lib/src/embed/liveboard.d.ts.map +1 -1
- package/lib/src/embed/liveboard.js +6 -5
- package/lib/src/embed/liveboard.js.map +1 -1
- package/lib/src/embed/sage.d.ts +4 -1
- package/lib/src/embed/sage.d.ts.map +1 -1
- package/lib/src/embed/sage.js +9 -2
- package/lib/src/embed/sage.js.map +1 -1
- package/lib/src/embed/search-bar.d.ts +1 -0
- package/lib/src/embed/search-bar.d.ts.map +1 -1
- package/lib/src/embed/search-bar.js +1 -0
- package/lib/src/embed/search-bar.js.map +1 -1
- package/lib/src/embed/search.d.ts +5 -1
- package/lib/src/embed/search.d.ts.map +1 -1
- package/lib/src/embed/search.js +10 -2
- package/lib/src/embed/search.js.map +1 -1
- package/lib/src/embed/ts-embed.d.ts +9 -3
- package/lib/src/embed/ts-embed.d.ts.map +1 -1
- package/lib/src/embed/ts-embed.js +50 -20
- package/lib/src/embed/ts-embed.js.map +1 -1
- package/lib/src/embed/ts-embed.spec.js +47 -0
- package/lib/src/embed/ts-embed.spec.js.map +1 -1
- package/lib/src/index.d.ts +3 -2
- package/lib/src/index.d.ts.map +1 -1
- package/lib/src/index.js +2 -1
- package/lib/src/index.js.map +1 -1
- package/lib/src/mixpanel-service.d.ts.map +1 -1
- package/lib/src/mixpanel-service.js +2 -0
- package/lib/src/mixpanel-service.js.map +1 -1
- package/lib/src/mixpanel-service.spec.js +1 -0
- package/lib/src/mixpanel-service.spec.js.map +1 -1
- package/lib/src/react/index.d.ts +5 -1
- package/lib/src/react/index.d.ts.map +1 -1
- package/lib/src/react/index.js +7 -7
- package/lib/src/react/index.js.map +1 -1
- package/lib/src/types.d.ts +46 -18
- package/lib/src/types.d.ts.map +1 -1
- package/lib/src/types.js +6 -12
- package/lib/src/types.js.map +1 -1
- package/lib/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
- package/lib/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
- package/lib/src/utils/graphql/answerService/answer-queries.js +77 -0
- package/lib/src/utils/graphql/answerService/answer-queries.js.map +1 -0
- package/lib/src/utils/graphql/answerService/answerService.d.ts +61 -0
- package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
- package/lib/src/utils/graphql/answerService/answerService.js +177 -0
- package/lib/src/utils/graphql/answerService/answerService.js.map +1 -0
- package/lib/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
- package/lib/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
- package/lib/src/utils/graphql/answerService/answerService.spec.js +199 -0
- package/lib/src/utils/graphql/answerService/answerService.spec.js.map +1 -0
- package/lib/src/utils/graphql/graphql-request.d.ts +15 -0
- package/lib/src/utils/graphql/graphql-request.d.ts.map +1 -0
- package/lib/src/utils/graphql/graphql-request.js +36 -0
- package/lib/src/utils/graphql/graphql-request.js.map +1 -0
- package/lib/src/utils/graphql/sourceService.d.ts +8 -0
- package/lib/src/utils/graphql/sourceService.d.ts.map +1 -0
- package/lib/src/utils/graphql/sourceService.js +65 -0
- package/lib/src/utils/graphql/sourceService.js.map +1 -0
- package/lib/src/utils/graphql/sourceService.spec.d.ts +2 -0
- package/lib/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
- package/lib/src/utils/graphql/sourceService.spec.js +10 -0
- package/lib/src/utils/graphql/sourceService.spec.js.map +1 -0
- package/lib/src/utils/processData.d.ts.map +1 -1
- package/lib/src/utils/processData.js +8 -12
- package/lib/src/utils/processData.js.map +1 -1
- package/lib/src/utils/processData.spec.js +14 -18
- package/lib/src/utils/processData.spec.js.map +1 -1
- package/lib/src/utils.d.ts +6 -0
- package/lib/src/utils.d.ts.map +1 -1
- package/lib/src/utils.js +23 -0
- package/lib/src/utils.js.map +1 -1
- package/lib/src/visual-embed-sdk.d.ts +133 -28
- package/package.json +2 -3
- package/src/embed/app.ts +12 -2
- package/src/embed/base.ts +2 -0
- package/src/embed/liveboard.ts +7 -5
- package/src/embed/sage.ts +13 -2
- package/src/embed/search-bar.tsx +2 -0
- package/src/embed/search.ts +14 -2
- package/src/embed/ts-embed.spec.ts +49 -0
- package/src/embed/ts-embed.ts +61 -22
- package/src/index.ts +5 -0
- package/src/mixpanel-service.spec.ts +1 -0
- package/src/mixpanel-service.ts +1 -0
- package/src/react/index.tsx +40 -37
- package/src/types.ts +50 -19
- package/src/utils/graphql/answerService/answer-queries.ts +80 -0
- package/src/utils/graphql/answerService/answerService.spec.ts +231 -0
- package/src/utils/graphql/answerService/answerService.ts +234 -0
- package/src/utils/graphql/graphql-request.ts +45 -0
- package/src/utils/graphql/sourceService.spec.ts +10 -0
- package/src/utils/graphql/sourceService.ts +71 -0
- package/src/utils/processData.spec.ts +15 -25
- package/src/utils/processData.ts +13 -15
- package/src/utils.ts +25 -0
- package/src/utils/answerService.spec.ts +0 -41
- package/src/utils/answerService.ts +0 -63
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const bachSessionId = `
|
|
2
|
+
id {
|
|
3
|
+
sessionId
|
|
4
|
+
genNo
|
|
5
|
+
acSession {
|
|
6
|
+
sessionId
|
|
7
|
+
genNo
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
export const getUnaggregatedAnswerSession = `
|
|
13
|
+
mutation GetUnAggregatedAnswerSession($session: BachSessionIdInput!, $columns: [UserPointSelectionInput!]!) {
|
|
14
|
+
Answer__getUnaggregatedAnswer(session: $session, columns: $columns) {
|
|
15
|
+
${bachSessionId}
|
|
16
|
+
answer {
|
|
17
|
+
visualizations {
|
|
18
|
+
... on TableViz {
|
|
19
|
+
columns {
|
|
20
|
+
column {
|
|
21
|
+
id
|
|
22
|
+
name
|
|
23
|
+
referencedColumns {
|
|
24
|
+
guid
|
|
25
|
+
displayName
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
export const removeColumns = `
|
|
37
|
+
mutation RemoveColumns($session: BachSessionIdInput!, $logicalColumnIds: [GUID!], $columnIds: [GUID!]) {
|
|
38
|
+
Answer__removeColumns(
|
|
39
|
+
session: $session
|
|
40
|
+
logicalColumnIds: $logicalColumnIds
|
|
41
|
+
columnIds: $columnIds
|
|
42
|
+
) {
|
|
43
|
+
${bachSessionId}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const addColumns = `
|
|
49
|
+
mutation AddColumns($session: BachSessionIdInput!, $columns: [AnswerColumnInfo!]!) {
|
|
50
|
+
Answer__addColumn(session: $session, columns: $columns) {
|
|
51
|
+
${bachSessionId}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
export const getAnswerData = `
|
|
57
|
+
query GetTableWithHeadlineData($session: BachSessionIdInput!, $deadline: Int!, $dataPaginationParams: DataPaginationParamsInput!) {
|
|
58
|
+
getAnswer(session: $session) {
|
|
59
|
+
${bachSessionId}
|
|
60
|
+
answer {
|
|
61
|
+
id
|
|
62
|
+
visualizations {
|
|
63
|
+
id
|
|
64
|
+
... on TableViz {
|
|
65
|
+
columns {
|
|
66
|
+
column {
|
|
67
|
+
id
|
|
68
|
+
name
|
|
69
|
+
type
|
|
70
|
+
aggregationType
|
|
71
|
+
dataType
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
data(deadline: $deadline, pagination: $dataPaginationParams)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import 'jest-fetch-mock';
|
|
2
|
+
import { VizPoint } from 'src/types';
|
|
3
|
+
import { AnswerService } from './answerService';
|
|
4
|
+
import { getAnswerData, removeColumns } from './answer-queries';
|
|
5
|
+
|
|
6
|
+
const defaultSession = {
|
|
7
|
+
sessionId: 'id',
|
|
8
|
+
genNo: 1,
|
|
9
|
+
acSession: {
|
|
10
|
+
sessionId: 'ac',
|
|
11
|
+
genNo: 0,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param answer
|
|
17
|
+
* @param point
|
|
18
|
+
*/
|
|
19
|
+
function createAnswerService(answer = {}, point?: VizPoint[]) {
|
|
20
|
+
return new AnswerService(
|
|
21
|
+
defaultSession,
|
|
22
|
+
answer,
|
|
23
|
+
'https://tshost',
|
|
24
|
+
point,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('Answer service tests', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
fetchMock.resetMocks();
|
|
31
|
+
});
|
|
32
|
+
test('Execute query should execute the supplied graphql on the session', async () => {
|
|
33
|
+
fetchMock.mockResponseOnce(JSON.stringify({
|
|
34
|
+
data: {
|
|
35
|
+
Bla: {
|
|
36
|
+
id: {},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
const answerService = createAnswerService();
|
|
41
|
+
answerService.executeQuery(
|
|
42
|
+
'query Bla {}',
|
|
43
|
+
{ a: 1 },
|
|
44
|
+
);
|
|
45
|
+
expect(fetchMock).toBeCalledWith('https://tshost/prism/?op=Bla', expect.objectContaining({
|
|
46
|
+
body: JSON.stringify({
|
|
47
|
+
operationName: 'Bla',
|
|
48
|
+
query: 'query Bla {}',
|
|
49
|
+
variables: {
|
|
50
|
+
session: defaultSession,
|
|
51
|
+
a: 1,
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
}));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Should return error when failure', async () => {
|
|
58
|
+
fetchMock.mockRejectOnce(new Error('testError'));
|
|
59
|
+
const answerService = createAnswerService();
|
|
60
|
+
const data = await answerService.executeQuery(
|
|
61
|
+
'query Bla {}',
|
|
62
|
+
{ a: 1 },
|
|
63
|
+
);
|
|
64
|
+
expect(data.message).toBe('testError');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('fetchData should call the right graphql, and return data', async () => {
|
|
68
|
+
fetchMock.mockResponseOnce(JSON.stringify({
|
|
69
|
+
data: {
|
|
70
|
+
getAnswer: {
|
|
71
|
+
id: {},
|
|
72
|
+
answer: {
|
|
73
|
+
visualizations: [{
|
|
74
|
+
columns: [{
|
|
75
|
+
column: {
|
|
76
|
+
id: 'id1',
|
|
77
|
+
},
|
|
78
|
+
}],
|
|
79
|
+
data: {
|
|
80
|
+
foo: 1,
|
|
81
|
+
},
|
|
82
|
+
}],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
const answerService = createAnswerService();
|
|
88
|
+
const data = await answerService.fetchData(20, 10);
|
|
89
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
90
|
+
'https://tshost/prism/?op=GetTableWithHeadlineData',
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
body: JSON.stringify({
|
|
93
|
+
operationName: 'GetTableWithHeadlineData',
|
|
94
|
+
query: getAnswerData,
|
|
95
|
+
variables: {
|
|
96
|
+
session: defaultSession,
|
|
97
|
+
deadline: 0,
|
|
98
|
+
dataPaginationParams: {
|
|
99
|
+
isClientPaginated: true,
|
|
100
|
+
offset: 20,
|
|
101
|
+
size: 10,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
expect(data.columns[0].column.id).toBe('id1');
|
|
108
|
+
expect(data.data.foo).toBe(1);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('fetchCSVBlob should call the right API', async () => {
|
|
112
|
+
fetchMock.once('Bla');
|
|
113
|
+
const answerService = createAnswerService();
|
|
114
|
+
answerService.fetchCSVBlob(undefined, true);
|
|
115
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
116
|
+
`https://tshost/prism/download/answer/csv?sessionId=${defaultSession.sessionId}&genNo=${defaultSession.genNo}&userLocale=en-us&exportFileName=data&omitInfo=true`,
|
|
117
|
+
expect.objectContaining({}),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('getUnderlyingDataForPoint should call the right APIs', async () => {
|
|
122
|
+
fetchMock.mockResponses(
|
|
123
|
+
JSON.stringify({
|
|
124
|
+
data: {
|
|
125
|
+
getSourceDetailById: [{
|
|
126
|
+
columns: [{
|
|
127
|
+
id: 'id1',
|
|
128
|
+
name: 'col1',
|
|
129
|
+
}, {
|
|
130
|
+
id: 'id2',
|
|
131
|
+
name: 'col2',
|
|
132
|
+
}, {
|
|
133
|
+
id: 'id3',
|
|
134
|
+
name: 'col3',
|
|
135
|
+
}],
|
|
136
|
+
}],
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
JSON.stringify({
|
|
140
|
+
data: {
|
|
141
|
+
Answer__getUnaggregatedAnswer: {
|
|
142
|
+
id: {
|
|
143
|
+
...defaultSession,
|
|
144
|
+
},
|
|
145
|
+
answer: {
|
|
146
|
+
visualizations: [{
|
|
147
|
+
columns: [{
|
|
148
|
+
column: {
|
|
149
|
+
id: 'oid1',
|
|
150
|
+
name: 'col1',
|
|
151
|
+
referencedColumns: [{
|
|
152
|
+
guid: 'id1',
|
|
153
|
+
}],
|
|
154
|
+
},
|
|
155
|
+
}],
|
|
156
|
+
}],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
JSON.stringify({
|
|
162
|
+
data: {
|
|
163
|
+
Answer__addColumn: {
|
|
164
|
+
id: {
|
|
165
|
+
genNo: 2,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
JSON.stringify({
|
|
171
|
+
data: {
|
|
172
|
+
Answer__removeColumns: {
|
|
173
|
+
id: {
|
|
174
|
+
genNo: 3,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
const answerService = createAnswerService({
|
|
181
|
+
sources: [{
|
|
182
|
+
header: {
|
|
183
|
+
guid: 'source1',
|
|
184
|
+
},
|
|
185
|
+
}],
|
|
186
|
+
}, [{
|
|
187
|
+
selectedAttributes: [{
|
|
188
|
+
column: {
|
|
189
|
+
id: 'oid1', // output column id
|
|
190
|
+
name: 'col1',
|
|
191
|
+
dataType: 'CHAR',
|
|
192
|
+
},
|
|
193
|
+
value: '1',
|
|
194
|
+
}, {
|
|
195
|
+
column: {
|
|
196
|
+
id: 'oid3',
|
|
197
|
+
name: 'col3',
|
|
198
|
+
dataType: 'DATE',
|
|
199
|
+
},
|
|
200
|
+
value: 12345,
|
|
201
|
+
}],
|
|
202
|
+
selectedMeasures: [],
|
|
203
|
+
}]);
|
|
204
|
+
const underlying = await answerService.getUnderlyingDataForPoint(['col2']);
|
|
205
|
+
expect(fetchMock).toHaveBeenCalledTimes(4);
|
|
206
|
+
expect(underlying.getSession().genNo).toBe(3);
|
|
207
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
208
|
+
'https://tshost/prism/?op=RemoveColumns',
|
|
209
|
+
expect.objectContaining({
|
|
210
|
+
body: JSON.stringify({
|
|
211
|
+
operationName: 'RemoveColumns',
|
|
212
|
+
query: removeColumns,
|
|
213
|
+
variables: {
|
|
214
|
+
session: {
|
|
215
|
+
...defaultSession,
|
|
216
|
+
genNo: 2,
|
|
217
|
+
},
|
|
218
|
+
logicalColumnIds: [
|
|
219
|
+
'id1',
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('getUnderlyingDataForPoint should throw when no point is selected', async () => {
|
|
228
|
+
const answerService = createAnswerService({}, null);
|
|
229
|
+
await expect(answerService.getUnderlyingDataForPoint(['col2'])).rejects.toThrow();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import type { ColumnValue, VizPoint } from '../../../types';
|
|
2
|
+
import { deepMerge, removeTypename } from '../../../utils';
|
|
3
|
+
import { graphqlQuery } from '../graphql-request';
|
|
4
|
+
import { getSourceDetail } from '../sourceService';
|
|
5
|
+
import * as queries from './answer-queries';
|
|
6
|
+
|
|
7
|
+
export interface SessionInterface {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
genNo: number;
|
|
10
|
+
acSession: { sessionId: string; genNo: number };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line no-shadow
|
|
14
|
+
export enum OperationType {
|
|
15
|
+
GetChartWithData = 'GetChartWithData',
|
|
16
|
+
GetTableWithHeadlineData = 'GetTableWithHeadlineData',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface UnderlyingDataPoint {
|
|
20
|
+
columnId: string;
|
|
21
|
+
dataValue: any;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Class representing the answer service provided with the
|
|
26
|
+
* custom action payload. This service could be used to run
|
|
27
|
+
* graphql queries in the context of the answer on which the
|
|
28
|
+
* custom action was triggered.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```js
|
|
32
|
+
* embed.on(EmbedEvent.CustomAction, e => {
|
|
33
|
+
* const underlying = await e.answerService.getUnderlyingDataForPoint([
|
|
34
|
+
* 'col name 1'
|
|
35
|
+
* ]);
|
|
36
|
+
* const data = await underlying.fetchData(0, 100);
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
* @version
|
|
40
|
+
* ThoughtSpot: 9.9.0.cl / SDK: 1.25.0
|
|
41
|
+
*/
|
|
42
|
+
export class AnswerService {
|
|
43
|
+
constructor(
|
|
44
|
+
private session: SessionInterface,
|
|
45
|
+
private answer: any,
|
|
46
|
+
private thoughtSpotHost: string,
|
|
47
|
+
private selectedPoints?: VizPoint[],
|
|
48
|
+
) {
|
|
49
|
+
this.session = removeTypename(session);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public async getSourceDetail() {
|
|
53
|
+
const sourceId = this.answer.sources[0].header.guid;
|
|
54
|
+
return getSourceDetail(
|
|
55
|
+
this.thoughtSpotHost,
|
|
56
|
+
sourceId,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async removeColumns(columnIds: string[]) {
|
|
61
|
+
return this.executeQuery(
|
|
62
|
+
queries.removeColumns,
|
|
63
|
+
{
|
|
64
|
+
logicalColumnIds: columnIds,
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async addColumns(columnIds: string[]) {
|
|
70
|
+
return this.executeQuery(
|
|
71
|
+
queries.addColumns,
|
|
72
|
+
{
|
|
73
|
+
columns: columnIds.map((colId) => ({ logicalColumnId: colId })),
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async fetchData(offset = 0, size = 1000) {
|
|
79
|
+
const { answer } = await this.executeQuery(
|
|
80
|
+
queries.getAnswerData,
|
|
81
|
+
{
|
|
82
|
+
deadline: 0,
|
|
83
|
+
dataPaginationParams: {
|
|
84
|
+
isClientPaginated: true,
|
|
85
|
+
offset,
|
|
86
|
+
size,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
const { columns, data } = answer.visualizations[0];
|
|
91
|
+
return {
|
|
92
|
+
columns,
|
|
93
|
+
data,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
*
|
|
99
|
+
* @param userLocale
|
|
100
|
+
* @param omitInfo Omit the download Info on top of the CSV
|
|
101
|
+
* @returns Response
|
|
102
|
+
*/
|
|
103
|
+
public async fetchCSVBlob(userLocale = 'en-us', omitInfo = false): Promise<Response> {
|
|
104
|
+
if (omitInfo) {
|
|
105
|
+
console.warn('omitInfo not supported yet.');
|
|
106
|
+
}
|
|
107
|
+
const fetchUrl = `${this.thoughtSpotHost}/prism/download/answer/csv?sessionId=${this.session.sessionId}&genNo=${this.session.genNo}&userLocale=${userLocale}&exportFileName=data&omitInfo=${omitInfo}`;
|
|
108
|
+
return fetch(fetchUrl, {
|
|
109
|
+
credentials: 'include',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public async getUnderlyingDataForPoint(
|
|
114
|
+
outputColumnNames: string[],
|
|
115
|
+
selectedPoints?: UnderlyingDataPoint[],
|
|
116
|
+
): Promise<AnswerService> {
|
|
117
|
+
if (!selectedPoints && !this.selectedPoints) {
|
|
118
|
+
throw new Error('Needs to be triggered in context of a point');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!selectedPoints) {
|
|
122
|
+
selectedPoints = getSelectedPointsForUnderlyingDataQuery(
|
|
123
|
+
this.selectedPoints,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sourceDetail = await this.getSourceDetail();
|
|
128
|
+
const ouputColumnGuids = getGuidsFromColumnNames(sourceDetail, outputColumnNames);
|
|
129
|
+
const unAggAnswer = await graphqlQuery({
|
|
130
|
+
query: queries.getUnaggregatedAnswerSession,
|
|
131
|
+
variables: {
|
|
132
|
+
session: this.session,
|
|
133
|
+
columns: selectedPoints,
|
|
134
|
+
},
|
|
135
|
+
thoughtSpotHost: this.thoughtSpotHost,
|
|
136
|
+
});
|
|
137
|
+
const unaggAnswerSession = new AnswerService(
|
|
138
|
+
unAggAnswer.id,
|
|
139
|
+
unAggAnswer.answer,
|
|
140
|
+
this.thoughtSpotHost,
|
|
141
|
+
);
|
|
142
|
+
const currentColumns: Set<string> = new Set(
|
|
143
|
+
unAggAnswer.answer.visualizations[0].columns
|
|
144
|
+
.map(
|
|
145
|
+
(c: any) => c.column.referencedColumns[0].guid,
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const columnsToAdd = [...ouputColumnGuids].filter((col) => !currentColumns.has(col));
|
|
150
|
+
if (columnsToAdd.length) {
|
|
151
|
+
await unaggAnswerSession.addColumns(columnsToAdd);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const columnsToRemove = [...currentColumns].filter((col) => !ouputColumnGuids.has(col));
|
|
155
|
+
if (columnsToRemove.length) {
|
|
156
|
+
await unaggAnswerSession.removeColumns(columnsToRemove);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return unaggAnswerSession;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public async executeQuery(query: string, variables: any): Promise<any> {
|
|
163
|
+
const data = await graphqlQuery({
|
|
164
|
+
query,
|
|
165
|
+
variables: {
|
|
166
|
+
session: this.session,
|
|
167
|
+
...variables,
|
|
168
|
+
},
|
|
169
|
+
thoughtSpotHost: this.thoughtSpotHost,
|
|
170
|
+
isCompositeQuery: false,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
this.session = deepMerge(this.session, data?.id || {}) as unknown as SessionInterface;
|
|
174
|
+
return data;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public getSession() {
|
|
178
|
+
return this.session;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
*
|
|
184
|
+
* @param sourceDetail
|
|
185
|
+
* @param colNames
|
|
186
|
+
*/
|
|
187
|
+
function getGuidsFromColumnNames(sourceDetail: any, colNames: string[]) {
|
|
188
|
+
const cols = sourceDetail.columns.reduce((colSet: any, col: any) => {
|
|
189
|
+
colSet[col.name] = col;
|
|
190
|
+
return colSet;
|
|
191
|
+
}, {});
|
|
192
|
+
|
|
193
|
+
return new Set(colNames.map((colName) => {
|
|
194
|
+
const col = cols[colName];
|
|
195
|
+
return col.id;
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
*
|
|
201
|
+
* @param selectedPoints
|
|
202
|
+
*/
|
|
203
|
+
function getSelectedPointsForUnderlyingDataQuery(
|
|
204
|
+
selectedPoints: VizPoint[],
|
|
205
|
+
): UnderlyingDataPoint[] {
|
|
206
|
+
const underlyingDataPoint: UnderlyingDataPoint[] = [];
|
|
207
|
+
/**
|
|
208
|
+
*
|
|
209
|
+
* @param colVal
|
|
210
|
+
*/
|
|
211
|
+
function addPointFromColVal(colVal: ColumnValue) {
|
|
212
|
+
const dataType = colVal.column.dataType;
|
|
213
|
+
const id = colVal.column.id;
|
|
214
|
+
let dataValue;
|
|
215
|
+
if (dataType === 'DATE') {
|
|
216
|
+
dataValue = [{
|
|
217
|
+
epochRange: {
|
|
218
|
+
startEpoch: colVal.value,
|
|
219
|
+
},
|
|
220
|
+
}];
|
|
221
|
+
} else {
|
|
222
|
+
dataValue = [{ value: colVal.value }];
|
|
223
|
+
}
|
|
224
|
+
underlyingDataPoint.push({
|
|
225
|
+
columnId: colVal.column.id,
|
|
226
|
+
dataValue,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
selectedPoints.forEach((p) => {
|
|
231
|
+
p.selectedAttributes.forEach(addPointFromColVal);
|
|
232
|
+
});
|
|
233
|
+
return underlyingDataPoint;
|
|
234
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getOperationNameFromQuery } from '../../utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param root0
|
|
6
|
+
* @param root0.query
|
|
7
|
+
* @param root0.variables
|
|
8
|
+
* @param root0.thoughtSpotHost
|
|
9
|
+
* @param root0.isCompositeQuery
|
|
10
|
+
*/
|
|
11
|
+
export async function graphqlQuery({
|
|
12
|
+
query,
|
|
13
|
+
variables,
|
|
14
|
+
thoughtSpotHost,
|
|
15
|
+
isCompositeQuery = false,
|
|
16
|
+
}: {
|
|
17
|
+
query: string,
|
|
18
|
+
variables: any,
|
|
19
|
+
thoughtSpotHost: string,
|
|
20
|
+
isCompositeQuery?: boolean
|
|
21
|
+
}) {
|
|
22
|
+
const operationName = getOperationNameFromQuery(query);
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${thoughtSpotHost}/prism/?op=${operationName}`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'content-type': 'application/json;charset=UTF-8',
|
|
28
|
+
'x-requested-by': 'ThoughtSpot',
|
|
29
|
+
accept: '*/*',
|
|
30
|
+
'accept-language': 'en-us',
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
operationName,
|
|
34
|
+
query,
|
|
35
|
+
variables,
|
|
36
|
+
}),
|
|
37
|
+
credentials: 'include',
|
|
38
|
+
});
|
|
39
|
+
const result = await response.json();
|
|
40
|
+
const dataValues = Object.values(result.data);
|
|
41
|
+
return (isCompositeQuery) ? result.data : dataValues[0];
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import 'jest-fetch-mock';
|
|
2
|
+
import { getSourceDetail } from './sourceService';
|
|
3
|
+
|
|
4
|
+
describe('Source service tests', () => {
|
|
5
|
+
test('Should return source detail and cache it', async () => {
|
|
6
|
+
await getSourceDetail('https://tshost', 'id');
|
|
7
|
+
await getSourceDetail('https://tshost', 'id');
|
|
8
|
+
expect(fetchMock).toBeCalledTimes(1);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { graphqlQuery } from './graphql-request';
|
|
2
|
+
|
|
3
|
+
export const getSourceDetailQuery = `
|
|
4
|
+
query GetSourceDetail($ids: [GUID!]!) {
|
|
5
|
+
getSourceDetailById(ids: $ids, type: LOGICAL_TABLE) {
|
|
6
|
+
id
|
|
7
|
+
name
|
|
8
|
+
description
|
|
9
|
+
authorName
|
|
10
|
+
authorDisplayName
|
|
11
|
+
isExternal
|
|
12
|
+
type
|
|
13
|
+
created
|
|
14
|
+
modified
|
|
15
|
+
columns {
|
|
16
|
+
id
|
|
17
|
+
name
|
|
18
|
+
author
|
|
19
|
+
authorDisplayName
|
|
20
|
+
description
|
|
21
|
+
dataType
|
|
22
|
+
type
|
|
23
|
+
modified
|
|
24
|
+
ownerName
|
|
25
|
+
owner
|
|
26
|
+
dataRecency
|
|
27
|
+
sources {
|
|
28
|
+
tableId
|
|
29
|
+
tableName
|
|
30
|
+
columnId
|
|
31
|
+
columnName
|
|
32
|
+
__typename
|
|
33
|
+
}
|
|
34
|
+
synonyms
|
|
35
|
+
cohortAnswerId
|
|
36
|
+
__typename
|
|
37
|
+
}
|
|
38
|
+
relationships
|
|
39
|
+
destinationRelationships
|
|
40
|
+
dataSourceId
|
|
41
|
+
__typename
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const sourceDetailCache = new Map<string, any>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param thoughtSpotHost
|
|
51
|
+
* @param sourceId
|
|
52
|
+
*/
|
|
53
|
+
export async function getSourceDetail(
|
|
54
|
+
thoughtSpotHost: string,
|
|
55
|
+
sourceId: string,
|
|
56
|
+
): Promise<any> {
|
|
57
|
+
if (sourceDetailCache.has(sourceId)) {
|
|
58
|
+
return sourceDetailCache.get(sourceId);
|
|
59
|
+
}
|
|
60
|
+
const details = await graphqlQuery({
|
|
61
|
+
query: getSourceDetailQuery,
|
|
62
|
+
variables: {
|
|
63
|
+
ids: [sourceId],
|
|
64
|
+
},
|
|
65
|
+
thoughtSpotHost,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const souceDetails = details[0];
|
|
69
|
+
sourceDetailCache.set(sourceId, souceDetails);
|
|
70
|
+
return souceDetails;
|
|
71
|
+
}
|