@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.
Files changed (231) hide show
  1. package/cjs/package.json +2 -3
  2. package/cjs/src/embed/app.d.ts +3 -1
  3. package/cjs/src/embed/app.d.ts.map +1 -1
  4. package/cjs/src/embed/app.js +8 -2
  5. package/cjs/src/embed/app.js.map +1 -1
  6. package/cjs/src/embed/base.d.ts +2 -0
  7. package/cjs/src/embed/base.d.ts.map +1 -1
  8. package/cjs/src/embed/base.js +2 -0
  9. package/cjs/src/embed/base.js.map +1 -1
  10. package/cjs/src/embed/liveboard.d.ts +3 -2
  11. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  12. package/cjs/src/embed/liveboard.js +6 -5
  13. package/cjs/src/embed/liveboard.js.map +1 -1
  14. package/cjs/src/embed/sage.d.ts +4 -1
  15. package/cjs/src/embed/sage.d.ts.map +1 -1
  16. package/cjs/src/embed/sage.js +9 -2
  17. package/cjs/src/embed/sage.js.map +1 -1
  18. package/cjs/src/embed/search-bar.d.ts +1 -0
  19. package/cjs/src/embed/search-bar.d.ts.map +1 -1
  20. package/cjs/src/embed/search-bar.js +1 -0
  21. package/cjs/src/embed/search-bar.js.map +1 -1
  22. package/cjs/src/embed/search.d.ts +5 -1
  23. package/cjs/src/embed/search.d.ts.map +1 -1
  24. package/cjs/src/embed/search.js +10 -2
  25. package/cjs/src/embed/search.js.map +1 -1
  26. package/cjs/src/embed/ts-embed.d.ts +9 -3
  27. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  28. package/cjs/src/embed/ts-embed.js +50 -20
  29. package/cjs/src/embed/ts-embed.js.map +1 -1
  30. package/cjs/src/embed/ts-embed.spec.js +47 -0
  31. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  32. package/cjs/src/index.d.ts +3 -2
  33. package/cjs/src/index.d.ts.map +1 -1
  34. package/cjs/src/index.js +3 -1
  35. package/cjs/src/index.js.map +1 -1
  36. package/cjs/src/mixpanel-service.d.ts.map +1 -1
  37. package/cjs/src/mixpanel-service.js +2 -0
  38. package/cjs/src/mixpanel-service.js.map +1 -1
  39. package/cjs/src/mixpanel-service.spec.js +1 -0
  40. package/cjs/src/mixpanel-service.spec.js.map +1 -1
  41. package/cjs/src/react/index.d.ts +5 -1
  42. package/cjs/src/react/index.d.ts.map +1 -1
  43. package/cjs/src/react/index.js +8 -8
  44. package/cjs/src/react/index.js.map +1 -1
  45. package/cjs/src/types.d.ts +46 -18
  46. package/cjs/src/types.d.ts.map +1 -1
  47. package/cjs/src/types.js +7 -13
  48. package/cjs/src/types.js.map +1 -1
  49. package/cjs/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
  50. package/cjs/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
  51. package/cjs/src/utils/graphql/answerService/answer-queries.js +80 -0
  52. package/cjs/src/utils/graphql/answerService/answer-queries.js.map +1 -0
  53. package/cjs/src/utils/graphql/answerService/answerService.d.ts +61 -0
  54. package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
  55. package/cjs/src/utils/graphql/answerService/answerService.js +182 -0
  56. package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -0
  57. package/cjs/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
  58. package/cjs/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
  59. package/cjs/src/utils/graphql/answerService/answerService.spec.js +201 -0
  60. package/cjs/src/utils/graphql/answerService/answerService.spec.js.map +1 -0
  61. package/cjs/src/utils/graphql/graphql-request.d.ts +15 -0
  62. package/cjs/src/utils/graphql/graphql-request.d.ts.map +1 -0
  63. package/cjs/src/utils/graphql/graphql-request.js +40 -0
  64. package/cjs/src/utils/graphql/graphql-request.js.map +1 -0
  65. package/cjs/src/utils/graphql/sourceService.d.ts +8 -0
  66. package/cjs/src/utils/graphql/sourceService.d.ts.map +1 -0
  67. package/cjs/src/utils/graphql/sourceService.js +69 -0
  68. package/cjs/src/utils/graphql/sourceService.js.map +1 -0
  69. package/cjs/src/utils/graphql/sourceService.spec.d.ts +2 -0
  70. package/cjs/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
  71. package/cjs/src/utils/graphql/sourceService.spec.js +12 -0
  72. package/cjs/src/utils/graphql/sourceService.spec.js.map +1 -0
  73. package/cjs/src/utils/processData.d.ts.map +1 -1
  74. package/cjs/src/utils/processData.js +7 -11
  75. package/cjs/src/utils/processData.js.map +1 -1
  76. package/cjs/src/utils/processData.spec.js +13 -17
  77. package/cjs/src/utils/processData.spec.js.map +1 -1
  78. package/cjs/src/utils.d.ts +6 -0
  79. package/cjs/src/utils.d.ts.map +1 -1
  80. package/cjs/src/utils.js +26 -1
  81. package/cjs/src/utils.js.map +1 -1
  82. package/dist/src/embed/app.d.ts +3 -1
  83. package/dist/src/embed/app.d.ts.map +1 -1
  84. package/dist/src/embed/base.d.ts +2 -0
  85. package/dist/src/embed/base.d.ts.map +1 -1
  86. package/dist/src/embed/liveboard.d.ts +3 -2
  87. package/dist/src/embed/liveboard.d.ts.map +1 -1
  88. package/dist/src/embed/sage.d.ts +4 -1
  89. package/dist/src/embed/sage.d.ts.map +1 -1
  90. package/dist/src/embed/search-bar.d.ts +1 -0
  91. package/dist/src/embed/search-bar.d.ts.map +1 -1
  92. package/dist/src/embed/search.d.ts +5 -1
  93. package/dist/src/embed/search.d.ts.map +1 -1
  94. package/dist/src/embed/ts-embed.d.ts +9 -3
  95. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  96. package/dist/src/index.d.ts +3 -2
  97. package/dist/src/index.d.ts.map +1 -1
  98. package/dist/src/mixpanel-service.d.ts.map +1 -1
  99. package/dist/src/react/index.d.ts +5 -1
  100. package/dist/src/react/index.d.ts.map +1 -1
  101. package/dist/src/types.d.ts +46 -18
  102. package/dist/src/types.d.ts.map +1 -1
  103. package/dist/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
  104. package/dist/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
  105. package/dist/src/utils/graphql/answerService/answerService.d.ts +61 -0
  106. package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
  107. package/dist/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
  108. package/dist/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
  109. package/dist/src/utils/graphql/graphql-request.d.ts +15 -0
  110. package/dist/src/utils/graphql/graphql-request.d.ts.map +1 -0
  111. package/dist/src/utils/graphql/sourceService.d.ts +8 -0
  112. package/dist/src/utils/graphql/sourceService.d.ts.map +1 -0
  113. package/dist/src/utils/graphql/sourceService.spec.d.ts +2 -0
  114. package/dist/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
  115. package/dist/src/utils/processData.d.ts.map +1 -1
  116. package/dist/src/utils.d.ts +6 -0
  117. package/dist/src/utils.d.ts.map +1 -1
  118. package/dist/tsembed-react.es.js +468 -107
  119. package/dist/tsembed-react.js +471 -106
  120. package/dist/tsembed.es.js +511 -103
  121. package/dist/tsembed.js +511 -102
  122. package/dist/visual-embed-sdk-react-full.d.ts +132 -28
  123. package/dist/visual-embed-sdk-react.d.ts +132 -28
  124. package/dist/visual-embed-sdk.d.ts +127 -27
  125. package/lib/package.json +2 -3
  126. package/lib/src/embed/app.d.ts +3 -1
  127. package/lib/src/embed/app.d.ts.map +1 -1
  128. package/lib/src/embed/app.js +8 -2
  129. package/lib/src/embed/app.js.map +1 -1
  130. package/lib/src/embed/base.d.ts +2 -0
  131. package/lib/src/embed/base.d.ts.map +1 -1
  132. package/lib/src/embed/base.js +2 -0
  133. package/lib/src/embed/base.js.map +1 -1
  134. package/lib/src/embed/liveboard.d.ts +3 -2
  135. package/lib/src/embed/liveboard.d.ts.map +1 -1
  136. package/lib/src/embed/liveboard.js +6 -5
  137. package/lib/src/embed/liveboard.js.map +1 -1
  138. package/lib/src/embed/sage.d.ts +4 -1
  139. package/lib/src/embed/sage.d.ts.map +1 -1
  140. package/lib/src/embed/sage.js +9 -2
  141. package/lib/src/embed/sage.js.map +1 -1
  142. package/lib/src/embed/search-bar.d.ts +1 -0
  143. package/lib/src/embed/search-bar.d.ts.map +1 -1
  144. package/lib/src/embed/search-bar.js +1 -0
  145. package/lib/src/embed/search-bar.js.map +1 -1
  146. package/lib/src/embed/search.d.ts +5 -1
  147. package/lib/src/embed/search.d.ts.map +1 -1
  148. package/lib/src/embed/search.js +10 -2
  149. package/lib/src/embed/search.js.map +1 -1
  150. package/lib/src/embed/ts-embed.d.ts +9 -3
  151. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  152. package/lib/src/embed/ts-embed.js +50 -20
  153. package/lib/src/embed/ts-embed.js.map +1 -1
  154. package/lib/src/embed/ts-embed.spec.js +47 -0
  155. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  156. package/lib/src/index.d.ts +3 -2
  157. package/lib/src/index.d.ts.map +1 -1
  158. package/lib/src/index.js +2 -1
  159. package/lib/src/index.js.map +1 -1
  160. package/lib/src/mixpanel-service.d.ts.map +1 -1
  161. package/lib/src/mixpanel-service.js +2 -0
  162. package/lib/src/mixpanel-service.js.map +1 -1
  163. package/lib/src/mixpanel-service.spec.js +1 -0
  164. package/lib/src/mixpanel-service.spec.js.map +1 -1
  165. package/lib/src/react/index.d.ts +5 -1
  166. package/lib/src/react/index.d.ts.map +1 -1
  167. package/lib/src/react/index.js +7 -7
  168. package/lib/src/react/index.js.map +1 -1
  169. package/lib/src/types.d.ts +46 -18
  170. package/lib/src/types.d.ts.map +1 -1
  171. package/lib/src/types.js +6 -12
  172. package/lib/src/types.js.map +1 -1
  173. package/lib/src/utils/graphql/answerService/answer-queries.d.ts +5 -0
  174. package/lib/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -0
  175. package/lib/src/utils/graphql/answerService/answer-queries.js +77 -0
  176. package/lib/src/utils/graphql/answerService/answer-queries.js.map +1 -0
  177. package/lib/src/utils/graphql/answerService/answerService.d.ts +61 -0
  178. package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -0
  179. package/lib/src/utils/graphql/answerService/answerService.js +177 -0
  180. package/lib/src/utils/graphql/answerService/answerService.js.map +1 -0
  181. package/lib/src/utils/graphql/answerService/answerService.spec.d.ts +2 -0
  182. package/lib/src/utils/graphql/answerService/answerService.spec.d.ts.map +1 -0
  183. package/lib/src/utils/graphql/answerService/answerService.spec.js +199 -0
  184. package/lib/src/utils/graphql/answerService/answerService.spec.js.map +1 -0
  185. package/lib/src/utils/graphql/graphql-request.d.ts +15 -0
  186. package/lib/src/utils/graphql/graphql-request.d.ts.map +1 -0
  187. package/lib/src/utils/graphql/graphql-request.js +36 -0
  188. package/lib/src/utils/graphql/graphql-request.js.map +1 -0
  189. package/lib/src/utils/graphql/sourceService.d.ts +8 -0
  190. package/lib/src/utils/graphql/sourceService.d.ts.map +1 -0
  191. package/lib/src/utils/graphql/sourceService.js +65 -0
  192. package/lib/src/utils/graphql/sourceService.js.map +1 -0
  193. package/lib/src/utils/graphql/sourceService.spec.d.ts +2 -0
  194. package/lib/src/utils/graphql/sourceService.spec.d.ts.map +1 -0
  195. package/lib/src/utils/graphql/sourceService.spec.js +10 -0
  196. package/lib/src/utils/graphql/sourceService.spec.js.map +1 -0
  197. package/lib/src/utils/processData.d.ts.map +1 -1
  198. package/lib/src/utils/processData.js +8 -12
  199. package/lib/src/utils/processData.js.map +1 -1
  200. package/lib/src/utils/processData.spec.js +14 -18
  201. package/lib/src/utils/processData.spec.js.map +1 -1
  202. package/lib/src/utils.d.ts +6 -0
  203. package/lib/src/utils.d.ts.map +1 -1
  204. package/lib/src/utils.js +23 -0
  205. package/lib/src/utils.js.map +1 -1
  206. package/lib/src/visual-embed-sdk.d.ts +133 -28
  207. package/package.json +2 -3
  208. package/src/embed/app.ts +12 -2
  209. package/src/embed/base.ts +2 -0
  210. package/src/embed/liveboard.ts +7 -5
  211. package/src/embed/sage.ts +13 -2
  212. package/src/embed/search-bar.tsx +2 -0
  213. package/src/embed/search.ts +14 -2
  214. package/src/embed/ts-embed.spec.ts +49 -0
  215. package/src/embed/ts-embed.ts +61 -22
  216. package/src/index.ts +5 -0
  217. package/src/mixpanel-service.spec.ts +1 -0
  218. package/src/mixpanel-service.ts +1 -0
  219. package/src/react/index.tsx +40 -37
  220. package/src/types.ts +50 -19
  221. package/src/utils/graphql/answerService/answer-queries.ts +80 -0
  222. package/src/utils/graphql/answerService/answerService.spec.ts +231 -0
  223. package/src/utils/graphql/answerService/answerService.ts +234 -0
  224. package/src/utils/graphql/graphql-request.ts +45 -0
  225. package/src/utils/graphql/sourceService.spec.ts +10 -0
  226. package/src/utils/graphql/sourceService.ts +71 -0
  227. package/src/utils/processData.spec.ts +15 -25
  228. package/src/utils/processData.ts +13 -15
  229. package/src/utils.ts +25 -0
  230. package/src/utils/answerService.spec.ts +0 -41
  231. 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
+ }