@cubejs-client/core 1.3.15 → 1.3.16
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/dist/{cubejs-client-core.js → cubejs-client-core.cjs.js} +1016 -411
- package/dist/cubejs-client-core.cjs.js.map +1 -0
- package/dist/cubejs-client-core.umd.js +2901 -12088
- package/dist/cubejs-client-core.umd.js.map +1 -1
- package/dist/src/HttpTransport.d.ts +54 -0
- package/dist/src/HttpTransport.d.ts.map +1 -0
- package/dist/src/HttpTransport.js +55 -0
- package/dist/src/Meta.d.ts +62 -0
- package/dist/src/Meta.d.ts.map +1 -0
- package/dist/src/Meta.js +150 -0
- package/dist/src/ProgressResult.d.ts +8 -0
- package/dist/src/ProgressResult.d.ts.map +1 -0
- package/dist/src/ProgressResult.js +11 -0
- package/dist/src/RequestError.d.ts +6 -0
- package/dist/src/RequestError.d.ts.map +1 -0
- package/dist/src/RequestError.js +7 -0
- package/dist/src/ResultSet.d.ts +430 -0
- package/dist/src/ResultSet.d.ts.map +1 -0
- package/dist/src/ResultSet.js +952 -0
- package/dist/src/SqlQuery.d.ts +17 -0
- package/dist/src/SqlQuery.d.ts.map +1 -0
- package/dist/src/SqlQuery.js +11 -0
- package/dist/src/index.d.ts +194 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +411 -0
- package/dist/src/index.umd.d.ts +3 -0
- package/dist/src/index.umd.d.ts.map +1 -0
- package/dist/src/index.umd.js +6 -0
- package/dist/src/time.d.ts +70 -0
- package/dist/src/time.d.ts.map +1 -0
- package/dist/src/time.js +249 -0
- package/dist/src/types.d.ts +424 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +19 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +294 -0
- package/dist/test/CubeApi.test.d.ts +7 -0
- package/dist/test/CubeApi.test.d.ts.map +1 -0
- package/dist/test/CubeApi.test.js +279 -0
- package/dist/test/HttpTransport.test.d.ts +2 -0
- package/dist/test/HttpTransport.test.d.ts.map +1 -0
- package/dist/test/HttpTransport.test.js +244 -0
- package/dist/test/ResultSet.test.d.ts +7 -0
- package/dist/test/ResultSet.test.d.ts.map +1 -0
- package/dist/test/ResultSet.test.js +1725 -0
- package/dist/test/compare-date-range.test.d.ts +2 -0
- package/dist/test/compare-date-range.test.d.ts.map +1 -0
- package/dist/test/compare-date-range.test.js +742 -0
- package/dist/test/data-blending.test.d.ts +2 -0
- package/dist/test/data-blending.test.d.ts.map +1 -0
- package/dist/test/data-blending.test.js +423 -0
- package/dist/test/default-heuristics.test.d.ts +2 -0
- package/dist/test/default-heuristics.test.d.ts.map +1 -0
- package/dist/test/default-heuristics.test.js +108 -0
- package/dist/test/drill-down.test.d.ts +2 -0
- package/dist/test/drill-down.test.d.ts.map +1 -0
- package/dist/test/drill-down.test.js +373 -0
- package/dist/test/fixtures/datablending/load-responses.json +261 -0
- package/dist/test/granularity.test.d.ts +2 -0
- package/dist/test/granularity.test.d.ts.map +1 -0
- package/dist/test/granularity.test.js +218 -0
- package/dist/test/helpers.d.ts +283 -0
- package/dist/test/helpers.d.ts.map +1 -0
- package/dist/test/helpers.js +974 -0
- package/dist/test/index.test.d.ts +7 -0
- package/dist/test/index.test.d.ts.map +1 -0
- package/dist/test/index.test.js +370 -0
- package/dist/test/table.test.d.ts +2 -0
- package/dist/test/table.test.d.ts.map +1 -0
- package/dist/test/table.test.js +757 -0
- package/dist/test/utils.test.d.ts +2 -0
- package/dist/test/utils.test.d.ts.map +1 -0
- package/dist/test/utils.test.js +32 -0
- package/package.json +26 -21
- package/dist/cubejs-client-core.esm.js +0 -1639
- package/dist/cubejs-client-core.esm.js.map +0 -1
- package/dist/cubejs-client-core.js.map +0 -1
- package/index.d.ts +0 -1338
- package/src/HttpTransport.js +0 -60
- package/src/HttpTransport.test.js +0 -117
- package/src/Meta.js +0 -142
- package/src/ProgressResult.js +0 -13
- package/src/RequestError.js +0 -7
- package/src/ResultSet.js +0 -746
- package/src/SqlQuery.js +0 -13
- package/src/index.js +0 -398
- package/src/index.test.js +0 -454
- package/src/index.umd.js +0 -8
- package/src/tests/ResultSet.test.js +0 -1655
- package/src/tests/compare-date-range.test.js +0 -753
- package/src/tests/data-blending.test.js +0 -432
- package/src/tests/default-heuristics.test.js +0 -118
- package/src/tests/drill-down.test.js +0 -402
- package/src/tests/fixtures/datablending/load-responses.json +0 -261
- package/src/tests/granularity.test.js +0 -225
- package/src/tests/table.test.js +0 -791
- package/src/tests/utils.test.js +0 -35
- package/src/time.js +0 -296
- package/src/utils.js +0 -368
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { clone, equals, fromPairs, indexBy, prop, toPairs } from 'ramda';
|
|
2
|
+
import { DEFAULT_GRANULARITY } from './time';
|
|
3
|
+
export function removeEmptyQueryFields(_query) {
|
|
4
|
+
const query = _query || {};
|
|
5
|
+
return fromPairs(toPairs(query).flatMap(([key, value]) => {
|
|
6
|
+
if (['measures', 'dimensions', 'segments', 'timeDimensions', 'filters'].includes(key)) {
|
|
7
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (key === 'order' && value) {
|
|
12
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
else if (!Object.keys(value).length) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return [[key, value]];
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
export function validateQuery(_query) {
|
|
23
|
+
const query = _query || {};
|
|
24
|
+
return removeEmptyQueryFields({
|
|
25
|
+
...query,
|
|
26
|
+
filters: (query.filters || []).filter((f) => 'operator' in f),
|
|
27
|
+
timeDimensions: (query.timeDimensions || []).filter((td) => !(!td.dateRange && !td.granularity)),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function areQueriesEqual(query1, query2) {
|
|
31
|
+
return (equals(Object.entries(query1?.order || {}), Object.entries(query2?.order || {})) && equals(query1, query2));
|
|
32
|
+
}
|
|
33
|
+
export function defaultOrder(query) {
|
|
34
|
+
const granularity = (query.timeDimensions || []).find((d) => d.granularity);
|
|
35
|
+
if (granularity) {
|
|
36
|
+
return {
|
|
37
|
+
[granularity.dimension]: 'asc',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else if ((query.measures || []).length > 0 &&
|
|
41
|
+
(query.dimensions || []).length > 0) {
|
|
42
|
+
return {
|
|
43
|
+
[query.measures[0]]: 'desc',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else if ((query.dimensions || []).length > 0) {
|
|
47
|
+
return {
|
|
48
|
+
[query.dimensions[0]]: 'asc',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
export function defaultHeuristics(newState, oldQuery, options) {
|
|
54
|
+
const { query, ...props } = clone(newState);
|
|
55
|
+
const { meta, sessionGranularity } = options;
|
|
56
|
+
const granularity = sessionGranularity || DEFAULT_GRANULARITY;
|
|
57
|
+
let state = {
|
|
58
|
+
shouldApplyHeuristicOrder: false,
|
|
59
|
+
pivotConfig: null,
|
|
60
|
+
query,
|
|
61
|
+
...props,
|
|
62
|
+
};
|
|
63
|
+
let newQuery = null;
|
|
64
|
+
if (!areQueriesEqual(query, oldQuery)) {
|
|
65
|
+
newQuery = query;
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(newQuery) || Array.isArray(oldQuery)) {
|
|
68
|
+
return {
|
|
69
|
+
shouldApplyHeuristicOrder: false,
|
|
70
|
+
pivotConfig: null,
|
|
71
|
+
...newState,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (newQuery) {
|
|
75
|
+
if ((oldQuery.timeDimensions || []).length === 1 &&
|
|
76
|
+
(newQuery.timeDimensions || []).length === 1 &&
|
|
77
|
+
newQuery.timeDimensions[0].granularity &&
|
|
78
|
+
oldQuery.timeDimensions[0].granularity !==
|
|
79
|
+
newQuery.timeDimensions[0].granularity) {
|
|
80
|
+
state = {
|
|
81
|
+
...state,
|
|
82
|
+
sessionGranularity: newQuery.timeDimensions[0].granularity,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (((oldQuery.measures || []).length === 0 &&
|
|
86
|
+
(newQuery.measures || []).length > 0) ||
|
|
87
|
+
((oldQuery.measures || []).length === 1 &&
|
|
88
|
+
(newQuery.measures || []).length === 1 &&
|
|
89
|
+
oldQuery.measures[0] !== newQuery.measures[0])) {
|
|
90
|
+
const [td] = newQuery.timeDimensions || [];
|
|
91
|
+
const defaultTimeDimension = meta.defaultTimeDimensionNameFor(newQuery.measures[0]);
|
|
92
|
+
newQuery = {
|
|
93
|
+
...newQuery,
|
|
94
|
+
timeDimensions: defaultTimeDimension
|
|
95
|
+
? [
|
|
96
|
+
{
|
|
97
|
+
dimension: defaultTimeDimension,
|
|
98
|
+
granularity: td?.granularity || granularity,
|
|
99
|
+
dateRange: td?.dateRange,
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
: [],
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
...state,
|
|
106
|
+
pivotConfig: null,
|
|
107
|
+
shouldApplyHeuristicOrder: true,
|
|
108
|
+
query: newQuery,
|
|
109
|
+
chartType: defaultTimeDimension ? 'line' : 'number',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if ((oldQuery.dimensions || []).length === 0 &&
|
|
113
|
+
(newQuery.dimensions || []).length > 0) {
|
|
114
|
+
newQuery = {
|
|
115
|
+
...newQuery,
|
|
116
|
+
timeDimensions: (newQuery.timeDimensions || []).map((td) => ({
|
|
117
|
+
...td,
|
|
118
|
+
granularity: undefined,
|
|
119
|
+
})),
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
...state,
|
|
123
|
+
pivotConfig: null,
|
|
124
|
+
shouldApplyHeuristicOrder: true,
|
|
125
|
+
query: newQuery,
|
|
126
|
+
chartType: 'table',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if ((oldQuery.dimensions || []).length > 0 &&
|
|
130
|
+
(newQuery.dimensions || []).length === 0) {
|
|
131
|
+
newQuery = {
|
|
132
|
+
...newQuery,
|
|
133
|
+
timeDimensions: (newQuery.timeDimensions || []).map((td) => ({
|
|
134
|
+
...td,
|
|
135
|
+
granularity: td.granularity || granularity,
|
|
136
|
+
})),
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
...state,
|
|
140
|
+
pivotConfig: null,
|
|
141
|
+
shouldApplyHeuristicOrder: true,
|
|
142
|
+
query: newQuery,
|
|
143
|
+
chartType: (newQuery.timeDimensions || []).length ? 'line' : 'number',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (((oldQuery.dimensions || []).length > 0 ||
|
|
147
|
+
(oldQuery.measures || []).length > 0) &&
|
|
148
|
+
(newQuery.dimensions || []).length === 0 &&
|
|
149
|
+
(newQuery.measures || []).length === 0) {
|
|
150
|
+
newQuery = {
|
|
151
|
+
...newQuery,
|
|
152
|
+
timeDimensions: [],
|
|
153
|
+
filters: [],
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
...state,
|
|
157
|
+
pivotConfig: null,
|
|
158
|
+
shouldApplyHeuristicOrder: true,
|
|
159
|
+
query: newQuery,
|
|
160
|
+
sessionGranularity: null,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return state;
|
|
164
|
+
}
|
|
165
|
+
if (state.chartType) {
|
|
166
|
+
const newChartType = state.chartType;
|
|
167
|
+
if ((newChartType === 'line' || newChartType === 'area') &&
|
|
168
|
+
(oldQuery.timeDimensions || []).length === 1 &&
|
|
169
|
+
!oldQuery.timeDimensions[0].granularity) {
|
|
170
|
+
const [td] = oldQuery.timeDimensions;
|
|
171
|
+
return {
|
|
172
|
+
...state,
|
|
173
|
+
pivotConfig: null,
|
|
174
|
+
query: {
|
|
175
|
+
...oldQuery,
|
|
176
|
+
timeDimensions: [{ ...td, granularity }],
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if ((newChartType === 'pie' ||
|
|
181
|
+
newChartType === 'table' ||
|
|
182
|
+
newChartType === 'number') &&
|
|
183
|
+
(oldQuery.timeDimensions || []).length === 1 &&
|
|
184
|
+
oldQuery.timeDimensions[0].granularity) {
|
|
185
|
+
const [td] = oldQuery.timeDimensions;
|
|
186
|
+
return {
|
|
187
|
+
...state,
|
|
188
|
+
pivotConfig: null,
|
|
189
|
+
shouldApplyHeuristicOrder: true,
|
|
190
|
+
query: {
|
|
191
|
+
...oldQuery,
|
|
192
|
+
timeDimensions: [{ ...td, granularity: undefined }],
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return state;
|
|
198
|
+
}
|
|
199
|
+
export function isQueryPresent(query) {
|
|
200
|
+
if (!query) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return (Array.isArray(query) ? query : [query]).every((q) => q.measures?.length || q.dimensions?.length || q.timeDimensions?.length);
|
|
204
|
+
}
|
|
205
|
+
export function movePivotItem(pivotConfig, sourceIndex, destinationIndex, sourceAxis, destinationAxis) {
|
|
206
|
+
const nextPivotConfig = {
|
|
207
|
+
...pivotConfig,
|
|
208
|
+
x: [...(pivotConfig.x || [])],
|
|
209
|
+
y: [...(pivotConfig.y || [])],
|
|
210
|
+
};
|
|
211
|
+
const id = pivotConfig[sourceAxis][sourceIndex];
|
|
212
|
+
const lastIndex = nextPivotConfig[destinationAxis].length - 1;
|
|
213
|
+
if (id === 'measures') {
|
|
214
|
+
destinationIndex = lastIndex + 1;
|
|
215
|
+
}
|
|
216
|
+
else if (sourceAxis === destinationAxis &&
|
|
217
|
+
destinationIndex >= lastIndex &&
|
|
218
|
+
nextPivotConfig[destinationAxis][lastIndex] === 'measures') {
|
|
219
|
+
destinationIndex = lastIndex - 1;
|
|
220
|
+
}
|
|
221
|
+
else if (sourceAxis !== destinationAxis &&
|
|
222
|
+
destinationIndex > lastIndex &&
|
|
223
|
+
nextPivotConfig[destinationAxis][lastIndex] === 'measures') {
|
|
224
|
+
destinationIndex = lastIndex;
|
|
225
|
+
}
|
|
226
|
+
nextPivotConfig[sourceAxis].splice(sourceIndex, 1);
|
|
227
|
+
nextPivotConfig[destinationAxis].splice(destinationIndex, 0, id);
|
|
228
|
+
return nextPivotConfig;
|
|
229
|
+
}
|
|
230
|
+
export function moveItemInArray(list, sourceIndex, destinationIndex) {
|
|
231
|
+
const result = [...list];
|
|
232
|
+
const [removed] = result.splice(sourceIndex, 1);
|
|
233
|
+
result.splice(destinationIndex, 0, removed);
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
export function flattenFilters(filters = []) {
|
|
237
|
+
return filters.reduce((memo, filter) => {
|
|
238
|
+
if ('or' in filter) {
|
|
239
|
+
return [...memo, ...flattenFilters(filter.or)];
|
|
240
|
+
}
|
|
241
|
+
if ('and' in filter) {
|
|
242
|
+
return [...memo, ...flattenFilters(filter.and)];
|
|
243
|
+
}
|
|
244
|
+
return [...memo, filter];
|
|
245
|
+
}, []);
|
|
246
|
+
}
|
|
247
|
+
export function getQueryMembers(query = {}) {
|
|
248
|
+
const keys = ['measures', 'dimensions', 'segments'];
|
|
249
|
+
const members = new Set();
|
|
250
|
+
keys.forEach((key) => (query[key] || []).forEach((member) => members.add(member)));
|
|
251
|
+
(query.timeDimensions || []).forEach((td) => members.add(td.dimension));
|
|
252
|
+
const filters = flattenFilters(query.filters);
|
|
253
|
+
filters.forEach((filter) => {
|
|
254
|
+
const member = filter.dimension || filter.member;
|
|
255
|
+
if (typeof member === 'string') {
|
|
256
|
+
members.add(member);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return [...members];
|
|
260
|
+
}
|
|
261
|
+
export function getOrderMembersFromOrder(orderMembers, order) {
|
|
262
|
+
const ids = new Set();
|
|
263
|
+
const indexedOrderMembers = indexBy(prop('id'), orderMembers);
|
|
264
|
+
const entries = Array.isArray(order) ? order : Object.entries(order || {});
|
|
265
|
+
const nextOrderMembers = [];
|
|
266
|
+
entries.forEach(([memberId, currentOrder]) => {
|
|
267
|
+
if (currentOrder !== 'none' && indexedOrderMembers[memberId]) {
|
|
268
|
+
ids.add(memberId);
|
|
269
|
+
nextOrderMembers.push({
|
|
270
|
+
...indexedOrderMembers[memberId],
|
|
271
|
+
order: currentOrder,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
orderMembers.forEach((member) => {
|
|
276
|
+
if (!ids.has(member.id)) {
|
|
277
|
+
nextOrderMembers.push({
|
|
278
|
+
...member,
|
|
279
|
+
order: member.order || 'none',
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return nextOrderMembers;
|
|
284
|
+
}
|
|
285
|
+
export function aliasSeries(values, index, pivotConfig, duplicateMeasures = new Set()) {
|
|
286
|
+
const nonNullValues = values.filter((value) => value != null);
|
|
287
|
+
if (pivotConfig?.aliasSeries?.[index]) {
|
|
288
|
+
return [pivotConfig.aliasSeries[index], ...nonNullValues];
|
|
289
|
+
}
|
|
290
|
+
else if (duplicateMeasures.has(nonNullValues[0])) {
|
|
291
|
+
return [index, ...nonNullValues];
|
|
292
|
+
}
|
|
293
|
+
return nonNullValues;
|
|
294
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CubeApi.test.d.ts","sourceRoot":"","sources":["../../test/CubeApi.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT License
|
|
3
|
+
* @copyright Cube Dev, Inc.
|
|
4
|
+
* @fileoverview Test signal parameter in CubeApi
|
|
5
|
+
*/
|
|
6
|
+
/* globals describe,test,expect,jest,beforeEach */
|
|
7
|
+
/* eslint-disable import/first */
|
|
8
|
+
import { CubeApi as CubeApiOriginal } from '../src';
|
|
9
|
+
import HttpTransport from '../src/HttpTransport';
|
|
10
|
+
import { DescriptiveQueryRequest, DescriptiveQueryRequestCompact, DescriptiveQueryResponse, NumericCastedData } from './helpers';
|
|
11
|
+
import ResultSet from '../src/ResultSet';
|
|
12
|
+
class CubeApi extends CubeApiOriginal {
|
|
13
|
+
getTransport() {
|
|
14
|
+
return this.transport;
|
|
15
|
+
}
|
|
16
|
+
makeRequest(method, params) {
|
|
17
|
+
return this.request(method, params);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
describe('CubeApi Constructor', () => {
|
|
21
|
+
test('throw error if no api url', async () => {
|
|
22
|
+
try {
|
|
23
|
+
const _cubeApi = new CubeApi('token', {});
|
|
24
|
+
throw new Error('Should not get here');
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
expect(e.message).toBe('The `apiUrl` option is required');
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('CubeApi Load', () => {
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
jest.restoreAllMocks();
|
|
35
|
+
});
|
|
36
|
+
test('simple query, no options', async () => {
|
|
37
|
+
// Create a spy on the request method
|
|
38
|
+
jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
39
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
40
|
+
status: 200,
|
|
41
|
+
text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)),
|
|
42
|
+
json: () => Promise.resolve(DescriptiveQueryResponse)
|
|
43
|
+
}, async () => undefined))
|
|
44
|
+
}));
|
|
45
|
+
const cubeApi = new CubeApi('token', {
|
|
46
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
47
|
+
});
|
|
48
|
+
const res = await cubeApi.load(DescriptiveQueryRequest);
|
|
49
|
+
expect(res).toBeInstanceOf(ResultSet);
|
|
50
|
+
expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data);
|
|
51
|
+
});
|
|
52
|
+
test('simple query + { mutexKey, castNumerics }', async () => {
|
|
53
|
+
// Create a spy on the request method
|
|
54
|
+
jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
55
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
56
|
+
status: 200,
|
|
57
|
+
text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)),
|
|
58
|
+
json: () => Promise.resolve(DescriptiveQueryResponse)
|
|
59
|
+
}, async () => undefined))
|
|
60
|
+
}));
|
|
61
|
+
const cubeApi = new CubeApi({
|
|
62
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
63
|
+
});
|
|
64
|
+
const res = await cubeApi.load(DescriptiveQueryRequest, { mutexKey: 'mutexKey', castNumerics: true });
|
|
65
|
+
expect(res).toBeInstanceOf(ResultSet);
|
|
66
|
+
expect(res.rawData()).toEqual(NumericCastedData);
|
|
67
|
+
});
|
|
68
|
+
test('simple query + compact response format', async () => {
|
|
69
|
+
// Create a spy on the request method
|
|
70
|
+
jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
71
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
72
|
+
status: 200,
|
|
73
|
+
text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)),
|
|
74
|
+
json: () => Promise.resolve(DescriptiveQueryResponse)
|
|
75
|
+
}, async () => undefined))
|
|
76
|
+
}));
|
|
77
|
+
const cubeApi = new CubeApi('token', {
|
|
78
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
79
|
+
});
|
|
80
|
+
const res = await cubeApi.load(DescriptiveQueryRequestCompact, undefined, undefined, 'compact');
|
|
81
|
+
expect(res).toBeInstanceOf(ResultSet);
|
|
82
|
+
expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data);
|
|
83
|
+
});
|
|
84
|
+
test('2 queries', async () => {
|
|
85
|
+
// Create a spy on the request method
|
|
86
|
+
jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
87
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
88
|
+
status: 200,
|
|
89
|
+
text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)),
|
|
90
|
+
json: () => Promise.resolve(DescriptiveQueryResponse)
|
|
91
|
+
}, async () => undefined))
|
|
92
|
+
}));
|
|
93
|
+
const cubeApi = new CubeApi('token', {
|
|
94
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
95
|
+
});
|
|
96
|
+
const res = await cubeApi.load([DescriptiveQueryRequest, DescriptiveQueryRequest]);
|
|
97
|
+
expect(res).toBeInstanceOf(ResultSet);
|
|
98
|
+
expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data);
|
|
99
|
+
});
|
|
100
|
+
test('2 queries + compact response format', async () => {
|
|
101
|
+
// Create a spy on the request method
|
|
102
|
+
jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
103
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
104
|
+
status: 200,
|
|
105
|
+
text: () => Promise.resolve(JSON.stringify(DescriptiveQueryResponse)),
|
|
106
|
+
json: () => Promise.resolve(DescriptiveQueryResponse)
|
|
107
|
+
}, async () => undefined))
|
|
108
|
+
}));
|
|
109
|
+
const cubeApi = new CubeApi('token', {
|
|
110
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
111
|
+
});
|
|
112
|
+
const res = await cubeApi.load([DescriptiveQueryRequestCompact, DescriptiveQueryRequestCompact], undefined, undefined, 'compact');
|
|
113
|
+
expect(res).toBeInstanceOf(ResultSet);
|
|
114
|
+
expect(res.rawData()).toEqual(DescriptiveQueryResponse.results[0].data);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('CubeApi with Abort Signal', () => {
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
jest.clearAllMocks();
|
|
120
|
+
jest.restoreAllMocks();
|
|
121
|
+
});
|
|
122
|
+
test('should pass signal from constructor to request', async () => {
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
const { signal } = controller;
|
|
125
|
+
// Create a spy on the request method
|
|
126
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
127
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
128
|
+
status: 200,
|
|
129
|
+
text: () => Promise.resolve('{"results":[]}'),
|
|
130
|
+
json: () => Promise.resolve({ results: [] })
|
|
131
|
+
}, async () => undefined))
|
|
132
|
+
}));
|
|
133
|
+
const cubeApi = new CubeApi('token', {
|
|
134
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
135
|
+
signal
|
|
136
|
+
});
|
|
137
|
+
// Create a second spy on the load method to verify signal is passed to HttpTransport
|
|
138
|
+
jest.spyOn(cubeApi, 'load');
|
|
139
|
+
await cubeApi.load({
|
|
140
|
+
measures: ['Orders.count']
|
|
141
|
+
});
|
|
142
|
+
// Check if the signal was passed to request method through load
|
|
143
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
144
|
+
// The request method should receive the signal in the call
|
|
145
|
+
// Create a request in the same way as CubeApi.load does
|
|
146
|
+
cubeApi.makeRequest('load', {
|
|
147
|
+
query: { measures: ['Orders.count'] },
|
|
148
|
+
queryType: 'multi'
|
|
149
|
+
});
|
|
150
|
+
// Verify the transport is using the signal
|
|
151
|
+
expect(cubeApi.getTransport().signal).toBe(signal);
|
|
152
|
+
});
|
|
153
|
+
test('should pass signal from options to request', async () => {
|
|
154
|
+
const controller = new AbortController();
|
|
155
|
+
const { signal } = controller;
|
|
156
|
+
// Mock for this specific test
|
|
157
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
158
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
159
|
+
status: 200,
|
|
160
|
+
text: () => Promise.resolve('{"results":[]}'),
|
|
161
|
+
json: () => Promise.resolve({ results: [] })
|
|
162
|
+
}, async () => undefined))
|
|
163
|
+
}));
|
|
164
|
+
const cubeApi = new CubeApi('token', {
|
|
165
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1'
|
|
166
|
+
});
|
|
167
|
+
await cubeApi.load({ measures: ['Orders.count'] }, { signal });
|
|
168
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
169
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal);
|
|
170
|
+
});
|
|
171
|
+
test('options signal should override constructor signal', async () => {
|
|
172
|
+
const constructorController = new AbortController();
|
|
173
|
+
const optionsController = new AbortController();
|
|
174
|
+
// Mock for this specific test
|
|
175
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
176
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
177
|
+
status: 200,
|
|
178
|
+
text: () => Promise.resolve('{"results":[]}'),
|
|
179
|
+
json: () => Promise.resolve({ results: [] })
|
|
180
|
+
}, async () => undefined))
|
|
181
|
+
}));
|
|
182
|
+
const cubeApi = new CubeApi('token', {
|
|
183
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1',
|
|
184
|
+
signal: constructorController.signal
|
|
185
|
+
});
|
|
186
|
+
await cubeApi.load({ measures: ['Orders.count'] }, { signal: optionsController.signal });
|
|
187
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
188
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(optionsController.signal);
|
|
189
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).not.toBe(constructorController.signal);
|
|
190
|
+
});
|
|
191
|
+
test('should pass signal to meta request', async () => {
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const { signal } = controller;
|
|
194
|
+
// Mock for meta with proper format - include dimensions, segments, and measures with required properties
|
|
195
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
196
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
197
|
+
status: 200,
|
|
198
|
+
text: () => Promise.resolve(JSON.stringify({
|
|
199
|
+
cubes: [{
|
|
200
|
+
name: 'Orders',
|
|
201
|
+
title: 'Orders',
|
|
202
|
+
measures: [{
|
|
203
|
+
name: 'count',
|
|
204
|
+
title: 'Count',
|
|
205
|
+
shortTitle: 'Count',
|
|
206
|
+
type: 'number'
|
|
207
|
+
}],
|
|
208
|
+
dimensions: [{
|
|
209
|
+
name: 'status',
|
|
210
|
+
title: 'Status',
|
|
211
|
+
type: 'string'
|
|
212
|
+
}],
|
|
213
|
+
segments: []
|
|
214
|
+
}]
|
|
215
|
+
})),
|
|
216
|
+
json: () => Promise.resolve({
|
|
217
|
+
cubes: [{
|
|
218
|
+
name: 'Orders',
|
|
219
|
+
title: 'Orders',
|
|
220
|
+
measures: [{
|
|
221
|
+
name: 'count',
|
|
222
|
+
title: 'Count',
|
|
223
|
+
shortTitle: 'Count',
|
|
224
|
+
type: 'number'
|
|
225
|
+
}],
|
|
226
|
+
dimensions: [{
|
|
227
|
+
name: 'status',
|
|
228
|
+
title: 'Status',
|
|
229
|
+
type: 'string'
|
|
230
|
+
}],
|
|
231
|
+
segments: []
|
|
232
|
+
}]
|
|
233
|
+
})
|
|
234
|
+
}, async () => undefined))
|
|
235
|
+
}));
|
|
236
|
+
const cubeApi = new CubeApi('token', {
|
|
237
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1'
|
|
238
|
+
});
|
|
239
|
+
await cubeApi.meta({ signal });
|
|
240
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
241
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal);
|
|
242
|
+
});
|
|
243
|
+
test('should pass signal to sql request', async () => {
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
const { signal } = controller;
|
|
246
|
+
// Mock for SQL response
|
|
247
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
248
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
249
|
+
status: 200,
|
|
250
|
+
text: () => Promise.resolve('{"sql":{"sql":"SELECT * FROM orders"}}'),
|
|
251
|
+
json: () => Promise.resolve({ sql: { sql: 'SELECT * FROM orders' } })
|
|
252
|
+
}, async () => undefined))
|
|
253
|
+
}));
|
|
254
|
+
const cubeApi = new CubeApi('token', {
|
|
255
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1'
|
|
256
|
+
});
|
|
257
|
+
await cubeApi.sql({ measures: ['Orders.count'] }, { signal });
|
|
258
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
259
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal);
|
|
260
|
+
});
|
|
261
|
+
test('should pass signal to dryRun request', async () => {
|
|
262
|
+
const controller = new AbortController();
|
|
263
|
+
const { signal } = controller;
|
|
264
|
+
// Mock for dryRun response
|
|
265
|
+
const requestSpy = jest.spyOn(HttpTransport.prototype, 'request').mockImplementation(() => ({
|
|
266
|
+
subscribe: (cb) => Promise.resolve(cb({
|
|
267
|
+
status: 200,
|
|
268
|
+
text: () => Promise.resolve('{"queryType":"regular"}'),
|
|
269
|
+
json: () => Promise.resolve({ queryType: 'regular' })
|
|
270
|
+
}, async () => undefined))
|
|
271
|
+
}));
|
|
272
|
+
const cubeApi = new CubeApi('token', {
|
|
273
|
+
apiUrl: 'http://localhost:4000/cubejs-api/v1'
|
|
274
|
+
});
|
|
275
|
+
await cubeApi.dryRun({ measures: ['Orders.count'] }, { signal });
|
|
276
|
+
expect(requestSpy).toHaveBeenCalled();
|
|
277
|
+
expect(requestSpy.mock.calls[0]?.[1]?.signal).toBe(signal);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpTransport.test.d.ts","sourceRoot":"","sources":["../../test/HttpTransport.test.ts"],"names":[],"mappings":""}
|