@sisense/sdk-pivot-query-client 2.17.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 (189) hide show
  1. package/LICENSE.md +35 -0
  2. package/README.md +2 -0
  3. package/dist/__test-helpers__/testUtils.d.ts +30 -0
  4. package/dist/__test-helpers__/testUtils.js +89 -0
  5. package/dist/builders/index.d.ts +2 -0
  6. package/dist/builders/index.js +2 -0
  7. package/dist/builders/pivot-data-builder.d.ts +34 -0
  8. package/dist/builders/pivot-data-builder.js +182 -0
  9. package/dist/builders/socket-builder.d.ts +18 -0
  10. package/dist/builders/socket-builder.js +72 -0
  11. package/dist/cjs/__test-helpers__/testUtils.d.ts +30 -0
  12. package/dist/cjs/__test-helpers__/testUtils.js +104 -0
  13. package/dist/cjs/builders/index.d.ts +2 -0
  14. package/dist/cjs/builders/index.js +7 -0
  15. package/dist/cjs/builders/pivot-data-builder.d.ts +34 -0
  16. package/dist/cjs/builders/pivot-data-builder.js +186 -0
  17. package/dist/cjs/builders/socket-builder.d.ts +18 -0
  18. package/dist/cjs/builders/socket-builder.js +79 -0
  19. package/dist/cjs/data-handling/DataService.d.ts +672 -0
  20. package/dist/cjs/data-handling/DataService.js +1364 -0
  21. package/dist/cjs/data-handling/DivergenceComparator.d.ts +7 -0
  22. package/dist/cjs/data-handling/DivergenceComparator.js +20 -0
  23. package/dist/cjs/data-handling/constants.d.ts +44 -0
  24. package/dist/cjs/data-handling/constants.js +43 -0
  25. package/dist/cjs/data-handling/index.d.ts +3 -0
  26. package/dist/cjs/data-handling/index.js +23 -0
  27. package/dist/cjs/data-handling/types.d.ts +104 -0
  28. package/dist/cjs/data-handling/types.js +2 -0
  29. package/dist/cjs/data-handling/utils/createPivotTreeNode.d.ts +13 -0
  30. package/dist/cjs/data-handling/utils/createPivotTreeNode.js +21 -0
  31. package/dist/cjs/data-handling/utils/index.d.ts +4 -0
  32. package/dist/cjs/data-handling/utils/index.js +14 -0
  33. package/dist/cjs/data-handling/utils/jaqlProcessor.d.ts +122 -0
  34. package/dist/cjs/data-handling/utils/jaqlProcessor.js +661 -0
  35. package/dist/cjs/data-handling/utils/pivotTransforms.d.ts +74 -0
  36. package/dist/cjs/data-handling/utils/pivotTransforms.js +373 -0
  37. package/dist/cjs/data-handling/utils/plugins/PluginService.d.ts +135 -0
  38. package/dist/cjs/data-handling/utils/plugins/PluginService.js +383 -0
  39. package/dist/cjs/data-handling/utils/plugins/getters.d.ts +23 -0
  40. package/dist/cjs/data-handling/utils/plugins/getters.js +70 -0
  41. package/dist/cjs/data-handling/utils/plugins/types.d.ts +75 -0
  42. package/dist/cjs/data-handling/utils/plugins/types.js +2 -0
  43. package/dist/cjs/data-handling/utils/plugins/validator.d.ts +13 -0
  44. package/dist/cjs/data-handling/utils/plugins/validator.js +169 -0
  45. package/dist/cjs/data-load/AbstractDataLoadService.d.ts +256 -0
  46. package/dist/cjs/data-load/AbstractDataLoadService.js +473 -0
  47. package/dist/cjs/data-load/DataLoadService.d.ts +63 -0
  48. package/dist/cjs/data-load/DataLoadService.js +152 -0
  49. package/dist/cjs/data-load/SisenseDataLoadService.d.ts +44 -0
  50. package/dist/cjs/data-load/SisenseDataLoadService.js +87 -0
  51. package/dist/cjs/data-load/TestDataLoadService.d.ts +27 -0
  52. package/dist/cjs/data-load/TestDataLoadService.js +76 -0
  53. package/dist/cjs/data-load/constants.d.ts +13 -0
  54. package/dist/cjs/data-load/constants.js +28 -0
  55. package/dist/cjs/data-load/index.d.ts +6 -0
  56. package/dist/cjs/data-load/index.js +14 -0
  57. package/dist/cjs/data-load/sockets/SisenseSocket.d.ts +81 -0
  58. package/dist/cjs/data-load/sockets/SisenseSocket.js +162 -0
  59. package/dist/cjs/data-load/sockets/TestSocket.d.ts +61 -0
  60. package/dist/cjs/data-load/sockets/TestSocket.js +90 -0
  61. package/dist/cjs/data-load/sockets/helpers.d.ts +4 -0
  62. package/dist/cjs/data-load/sockets/helpers.js +27 -0
  63. package/dist/cjs/data-load/sockets/index.d.ts +2 -0
  64. package/dist/cjs/data-load/sockets/index.js +8 -0
  65. package/dist/cjs/data-load/types.d.ts +90 -0
  66. package/dist/cjs/data-load/types.js +2 -0
  67. package/dist/cjs/errors/LoadingCanceledError.d.ts +7 -0
  68. package/dist/cjs/errors/LoadingCanceledError.js +24 -0
  69. package/dist/cjs/errors/index.d.ts +1 -0
  70. package/dist/cjs/errors/index.js +6 -0
  71. package/dist/cjs/index.d.ts +17 -0
  72. package/dist/cjs/index.js +32 -0
  73. package/dist/cjs/package.json +12 -0
  74. package/dist/cjs/pivot-query-client.d.ts +13 -0
  75. package/dist/cjs/pivot-query-client.js +26 -0
  76. package/dist/cjs/tree-structure/AbstractTreeService.d.ts +308 -0
  77. package/dist/cjs/tree-structure/AbstractTreeService.js +716 -0
  78. package/dist/cjs/tree-structure/HeaderTreeService.d.ts +180 -0
  79. package/dist/cjs/tree-structure/HeaderTreeService.js +280 -0
  80. package/dist/cjs/tree-structure/TreeCellMap.d.ts +104 -0
  81. package/dist/cjs/tree-structure/TreeCellMap.js +145 -0
  82. package/dist/cjs/tree-structure/TreeService.d.ts +8 -0
  83. package/dist/cjs/tree-structure/TreeService.js +12 -0
  84. package/dist/cjs/tree-structure/constants.d.ts +6 -0
  85. package/dist/cjs/tree-structure/constants.js +10 -0
  86. package/dist/cjs/tree-structure/index.d.ts +5 -0
  87. package/dist/cjs/tree-structure/index.js +10 -0
  88. package/dist/cjs/tree-structure/types.d.ts +93 -0
  89. package/dist/cjs/tree-structure/types.js +2 -0
  90. package/dist/cjs/tree-structure/utils/index.d.ts +1 -0
  91. package/dist/cjs/tree-structure/utils/index.js +9 -0
  92. package/dist/cjs/tree-structure/utils/treeNode.d.ts +147 -0
  93. package/dist/cjs/tree-structure/utils/treeNode.js +534 -0
  94. package/dist/cjs/utils/array.d.ts +13 -0
  95. package/dist/cjs/utils/array.js +25 -0
  96. package/dist/cjs/utils/cloneObject.d.ts +30 -0
  97. package/dist/cjs/utils/cloneObject.js +225 -0
  98. package/dist/cjs/utils/index.d.ts +3 -0
  99. package/dist/cjs/utils/index.js +9 -0
  100. package/dist/cjs/utils/throttle.d.ts +12 -0
  101. package/dist/cjs/utils/throttle.js +39 -0
  102. package/dist/cjs/utils/types.d.ts +12 -0
  103. package/dist/cjs/utils/types.js +2 -0
  104. package/dist/data-handling/DataService.d.ts +672 -0
  105. package/dist/data-handling/DataService.js +1357 -0
  106. package/dist/data-handling/DivergenceComparator.d.ts +7 -0
  107. package/dist/data-handling/DivergenceComparator.js +16 -0
  108. package/dist/data-handling/constants.d.ts +44 -0
  109. package/dist/data-handling/constants.js +40 -0
  110. package/dist/data-handling/index.d.ts +3 -0
  111. package/dist/data-handling/index.js +4 -0
  112. package/dist/data-handling/types.d.ts +104 -0
  113. package/dist/data-handling/types.js +1 -0
  114. package/dist/data-handling/utils/createPivotTreeNode.d.ts +13 -0
  115. package/dist/data-handling/utils/createPivotTreeNode.js +17 -0
  116. package/dist/data-handling/utils/index.d.ts +4 -0
  117. package/dist/data-handling/utils/index.js +4 -0
  118. package/dist/data-handling/utils/jaqlProcessor.d.ts +122 -0
  119. package/dist/data-handling/utils/jaqlProcessor.js +621 -0
  120. package/dist/data-handling/utils/pivotTransforms.d.ts +74 -0
  121. package/dist/data-handling/utils/pivotTransforms.js +335 -0
  122. package/dist/data-handling/utils/plugins/PluginService.d.ts +135 -0
  123. package/dist/data-handling/utils/plugins/PluginService.js +379 -0
  124. package/dist/data-handling/utils/plugins/getters.d.ts +23 -0
  125. package/dist/data-handling/utils/plugins/getters.js +65 -0
  126. package/dist/data-handling/utils/plugins/types.d.ts +75 -0
  127. package/dist/data-handling/utils/plugins/types.js +1 -0
  128. package/dist/data-handling/utils/plugins/validator.d.ts +13 -0
  129. package/dist/data-handling/utils/plugins/validator.js +165 -0
  130. package/dist/data-load/AbstractDataLoadService.d.ts +256 -0
  131. package/dist/data-load/AbstractDataLoadService.js +466 -0
  132. package/dist/data-load/DataLoadService.d.ts +63 -0
  133. package/dist/data-load/DataLoadService.js +148 -0
  134. package/dist/data-load/SisenseDataLoadService.d.ts +44 -0
  135. package/dist/data-load/SisenseDataLoadService.js +83 -0
  136. package/dist/data-load/TestDataLoadService.d.ts +27 -0
  137. package/dist/data-load/TestDataLoadService.js +69 -0
  138. package/dist/data-load/constants.d.ts +13 -0
  139. package/dist/data-load/constants.js +25 -0
  140. package/dist/data-load/index.d.ts +6 -0
  141. package/dist/data-load/index.js +6 -0
  142. package/dist/data-load/sockets/SisenseSocket.d.ts +81 -0
  143. package/dist/data-load/sockets/SisenseSocket.js +155 -0
  144. package/dist/data-load/sockets/TestSocket.d.ts +61 -0
  145. package/dist/data-load/sockets/TestSocket.js +83 -0
  146. package/dist/data-load/sockets/helpers.d.ts +4 -0
  147. package/dist/data-load/sockets/helpers.js +23 -0
  148. package/dist/data-load/sockets/index.d.ts +2 -0
  149. package/dist/data-load/sockets/index.js +3 -0
  150. package/dist/data-load/types.d.ts +90 -0
  151. package/dist/data-load/types.js +1 -0
  152. package/dist/errors/LoadingCanceledError.d.ts +7 -0
  153. package/dist/errors/LoadingCanceledError.js +20 -0
  154. package/dist/errors/index.d.ts +1 -0
  155. package/dist/errors/index.js +2 -0
  156. package/dist/index.d.ts +17 -0
  157. package/dist/index.js +9 -0
  158. package/dist/pivot-query-client.d.ts +13 -0
  159. package/dist/pivot-query-client.js +22 -0
  160. package/dist/tree-structure/AbstractTreeService.d.ts +308 -0
  161. package/dist/tree-structure/AbstractTreeService.js +712 -0
  162. package/dist/tree-structure/HeaderTreeService.d.ts +180 -0
  163. package/dist/tree-structure/HeaderTreeService.js +276 -0
  164. package/dist/tree-structure/TreeCellMap.d.ts +104 -0
  165. package/dist/tree-structure/TreeCellMap.js +141 -0
  166. package/dist/tree-structure/TreeService.d.ts +8 -0
  167. package/dist/tree-structure/TreeService.js +8 -0
  168. package/dist/tree-structure/constants.d.ts +6 -0
  169. package/dist/tree-structure/constants.js +7 -0
  170. package/dist/tree-structure/index.d.ts +5 -0
  171. package/dist/tree-structure/index.js +4 -0
  172. package/dist/tree-structure/types.d.ts +93 -0
  173. package/dist/tree-structure/types.js +1 -0
  174. package/dist/tree-structure/utils/index.d.ts +1 -0
  175. package/dist/tree-structure/utils/index.js +1 -0
  176. package/dist/tree-structure/utils/treeNode.d.ts +147 -0
  177. package/dist/tree-structure/utils/treeNode.js +515 -0
  178. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  179. package/dist/utils/array.d.ts +13 -0
  180. package/dist/utils/array.js +21 -0
  181. package/dist/utils/cloneObject.d.ts +30 -0
  182. package/dist/utils/cloneObject.js +221 -0
  183. package/dist/utils/index.d.ts +3 -0
  184. package/dist/utils/index.js +3 -0
  185. package/dist/utils/throttle.d.ts +12 -0
  186. package/dist/utils/throttle.js +35 -0
  187. package/dist/utils/types.d.ts +12 -0
  188. package/dist/utils/types.js +1 -0
  189. package/package.json +61 -0
@@ -0,0 +1,1357 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ /* eslint-disable @typescript-eslint/ban-types */
13
+ import EventEmitter from 'events';
14
+ import { MessageType } from '../data-load/constants.js';
15
+ import { LoadingCanceledError } from '../errors/index.js';
16
+ import { TreeService } from '../tree-structure/index.js';
17
+ import { treeNode } from '../tree-structure/utils/index.js';
18
+ import { cloneObject, findIndex, throttle } from '../utils/index.js';
19
+ import { PanelType, UserType } from './constants.js';
20
+ import { DivergenceComparator } from './DivergenceComparator.js';
21
+ import { createPivotTreeNode, jaqlProcessor, pivotTransforms, PluginService, } from './utils/index.js';
22
+ const getJaqlComposition = (jaql, rawMetadata) => {
23
+ const processedJaql = jaql || { metadata: [], grandTotals: null };
24
+ let { metadata } = processedJaql;
25
+ metadata = metadata.map((panel) => {
26
+ const field = panel.field || {};
27
+ // find format to compose from rawMetadata by field.index
28
+ const samePanel = rawMetadata.find(({ field: rawField }) => rawField && rawField.index === field.index);
29
+ const format = samePanel ? samePanel.format : null;
30
+ return format ? Object.assign(Object.assign({}, panel), { format }) : panel;
31
+ });
32
+ return Object.assign(Object.assign({}, processedJaql), { metadata });
33
+ };
34
+ export const DEFAULT_PAGE_SIZE = 100;
35
+ export const EVENT_DATA_LOAD = 'dataLoad';
36
+ export const EVENT_DATA_CHUNK_LOADED = 'dataChunkLoaded';
37
+ export const EVENT_DATABAR_CHUNK_LOADED = 'dataBarChunkLoaded';
38
+ export const EVENT_RANGEMINMAX_CHUNK_LOADED = 'rangeMinMaxChunkLoaded';
39
+ export const EVENT_GRAND_CHUNK_LOADED = 'grandChunkLoaded';
40
+ export const EVENT_DATA_FINISH_CHUNK_LOADED = 'dataFinishChunkLoaded';
41
+ export const EVENT_FINISH_CHUNK_LOADED = 'finishChunkLoaded';
42
+ export const EVENT_PROGRESS_ERROR = 'progressError';
43
+ export const EVENT_HEADER_CELL_FORMAT = 'headerCellFormat';
44
+ export const EVENT_DATA_CELL_FORMAT = 'dataCellFormat';
45
+ export const EVENT_TOTAL_ROWS_COUNT = 'totalRowsCountLoaded';
46
+ export const EVENT_TOTAL_COLUMNS_COUNT = 'totalColumnsCountLoaded';
47
+ export const NODES_CHUNK_SIZE_MAX = 1000;
48
+ // TODO Review if throttling is needed
49
+ export const THROTTLE_TIME_MS = 0;
50
+ const defaultOptions = {
51
+ throttle,
52
+ throttleTime: THROTTLE_TIME_MS,
53
+ PluginService,
54
+ skipInternalColorFormatting: false,
55
+ subtotalsForSingleRow: false,
56
+ };
57
+ export class DataService {
58
+ // eslint-disable-next-line max-lines-per-function
59
+ constructor(loadService, events, options) {
60
+ /**
61
+ *
62
+ *
63
+ * @private
64
+ *
65
+ * loaded data chunks group
66
+ */
67
+ this.tempDataChunks = [];
68
+ /**
69
+ *
70
+ * @private
71
+ *
72
+ * last not handled data chunks group index
73
+ */
74
+ this.lastNotHandledChunkIndex = 0;
75
+ /**
76
+ *
77
+ * @private
78
+ *
79
+ * last ready but not handled data chunks group index
80
+ */
81
+ this.lastReadyNotHandledChunkIndex = 0;
82
+ /**
83
+ *
84
+ * @private
85
+ *
86
+ * Current rows nodes
87
+ */
88
+ this.rowsGrand = undefined;
89
+ /**
90
+ *
91
+ @private */
92
+ this.rowsTreeService = undefined;
93
+ /**
94
+ *
95
+ @private */
96
+ this.columnsTreeService = undefined;
97
+ /**
98
+ *
99
+ @private */
100
+ this.cornerTreeService = undefined;
101
+ /**
102
+ @private */
103
+ this.pageSize = DEFAULT_PAGE_SIZE;
104
+ /**
105
+ *
106
+ @private */
107
+ this.isPaginated = false;
108
+ /**
109
+ *
110
+ * @private
111
+ *
112
+ * number of loaded pages
113
+ */
114
+ this.loadedPagesCount = 0;
115
+ /**
116
+ *
117
+ * @private
118
+ *
119
+ * state for "loadData" method
120
+ */
121
+ this.isLoadInProgress = false;
122
+ /**
123
+ *
124
+ @private */
125
+ this.loadAllPromise = undefined;
126
+ /**
127
+ *
128
+ @private */
129
+ this.loadAllPromiseResolve = undefined;
130
+ /**
131
+ *
132
+ @private */
133
+ this.loadAllPromiseReject = undefined;
134
+ /**
135
+ *
136
+ * @private
137
+ *
138
+ * defines if all data is loaded
139
+ */
140
+ this.isWholeDataLoaded = false;
141
+ /**
142
+ *
143
+ * @private
144
+ *
145
+ * defines that data finish event is happened
146
+ */
147
+ this.hasDataFinishEvent = false;
148
+ /**
149
+ *
150
+ * @private
151
+ *
152
+ * defines that data handler in progress
153
+ */
154
+ this.isDataHandlerThrottled = false;
155
+ /**
156
+ *
157
+ * @private
158
+ *
159
+ * jaql request pure metadata
160
+ */
161
+ this.metadata = [];
162
+ /**
163
+ *
164
+ * @private
165
+ *
166
+ * service for items indexation divergence
167
+ */
168
+ this.divergenceComparator = new DivergenceComparator();
169
+ /**
170
+ *
171
+ @private */
172
+ this.rawJaql = undefined;
173
+ /**
174
+ *
175
+ * @private
176
+ *
177
+ * triggered events data storage
178
+ */
179
+ this.triggeredEventsData = {};
180
+ /**
181
+ *
182
+ * @private
183
+ *
184
+ * plugins handlers
185
+ */
186
+ this.callPlugins = [];
187
+ /**
188
+ * Handler for pivot metadata received event
189
+ *
190
+ * @returns {void}
191
+ */
192
+ this.onMetadataHandler = () => {
193
+ const jaql = { metadata: this.getMetadata() };
194
+ const isSingleRowTree = this.isSingleRowTree();
195
+ if (isSingleRowTree) {
196
+ jaqlProcessor.normalizeSingleBranchTreeSortDetails(jaql);
197
+ }
198
+ };
199
+ this.onHeadersHandler = (data) => {
200
+ // bind methods for injecting them into tree service builders
201
+ const getJaql = this.getJaql.bind(this);
202
+ const getRawJaql = this.getRawJaql.bind(this);
203
+ const applyIndexDivergence = this.applyIndexDivergence.bind(this);
204
+ const emitHeaderCellFormatEvent = (item, panel, jaql) => this.emit(EVENT_HEADER_CELL_FORMAT, item, panel, jaql, this.options);
205
+ // build columns tree service
206
+ const buildColumnsTreeService = DataService.createColumnsTreeServiceBuilder({
207
+ getJaql,
208
+ getRawJaql,
209
+ pluginService: this.pluginService,
210
+ applyIndexDivergence,
211
+ }, {
212
+ emitHeaderCellFormatEvent,
213
+ });
214
+ const columnsTreeServiceBuildResult = buildColumnsTreeService(data);
215
+ this.columnsTreeService = columnsTreeServiceBuildResult.columnsTreeService;
216
+ this.columnsCount = columnsTreeServiceBuildResult.columnsCount;
217
+ this.totalColumnsCount = columnsTreeServiceBuildResult.totalColumnsCount;
218
+ this.emit(EVENT_TOTAL_COLUMNS_COUNT, {
219
+ columnsCount: this.columnsCount || 0,
220
+ totalColumnsCount: this.totalColumnsCount || 0,
221
+ });
222
+ // build corner tree service
223
+ const buildCornerTreeService = DataService.createCornerTreeServiceBuilder({
224
+ getJaql,
225
+ getRawJaql,
226
+ pluginService: this.pluginService,
227
+ columnsTreeService: this.columnsTreeService,
228
+ }, {
229
+ emitHeaderCellFormatEvent,
230
+ });
231
+ const { cornerTreeService } = buildCornerTreeService();
232
+ this.cornerTreeService = cornerTreeService;
233
+ };
234
+ this.onDataHandler = (data) => {
235
+ // store the data chunk into temporary storage
236
+ // this step is NOT throttled because we want to make
237
+ // ALL data chunks coming (regardless of the rate) is captured
238
+ this.tempDataChunks = this.tempDataChunks || [];
239
+ if (Array.isArray(data)) {
240
+ (data || []).forEach((item) => {
241
+ DataService.fillDataChunks(this.tempDataChunks, item, this.pageSize);
242
+ });
243
+ }
244
+ else {
245
+ DataService.fillDataChunks(this.tempDataChunks, data, this.pageSize);
246
+ }
247
+ // the data processing step CAN be throttled
248
+ // However, method onDataHandlerThrottle needs logic
249
+ // to maintain the LAST processed chunk
250
+ if (this.tempDataChunks.length) {
251
+ this.isDataHandlerThrottled = true;
252
+ // @ts-ignore
253
+ this.onDataHandlerThrottle();
254
+ }
255
+ };
256
+ this.onGrandHandler = (data) => {
257
+ const rows = this.preProcessTree([data], PanelType.ROWS);
258
+ const mainNode = rows[0];
259
+ const name = 'Rows grand total';
260
+ const grandData = mainNode ? mainNode.data : [];
261
+ const node = treeNode.create(name, undefined, grandData);
262
+ const grandTotalNode = createPivotTreeNode(node, PanelType.ROWS);
263
+ grandTotalNode.userType = UserType.GRAND_TOTAL;
264
+ this.postProcessTree([grandTotalNode]);
265
+ this.postProcessData([grandTotalNode]);
266
+ this.rowsGrand = grandTotalNode;
267
+ this.emit(EVENT_GRAND_CHUNK_LOADED, treeNode.wrapInRootNode(this.rowsGrand));
268
+ };
269
+ this.onFinishHandler = () => {
270
+ this.emit(EVENT_FINISH_CHUNK_LOADED, {
271
+ totalRows: typeof this.totalRowsCount === 'number' ? this.totalRowsCount : this.getTotalItemsCount(),
272
+ totalColumns: typeof this.totalColumnsCount === 'number'
273
+ ? this.totalColumnsCount
274
+ : this.getLoadedColumnsCount(), // backward compatibility
275
+ });
276
+ };
277
+ this.onTotalRowsHandler = (data) => {
278
+ const { rowsCount, queryRowsCount, limitReached } = data || {};
279
+ this.totalRowsCount = rowsCount;
280
+ this.totalRecordsCount = queryRowsCount;
281
+ this.limitReached = limitReached;
282
+ this.emit(EVENT_TOTAL_ROWS_COUNT, data);
283
+ };
284
+ this.onDataBarsHandler = (chunk) => {
285
+ const { data = [] } = chunk;
286
+ let offset = 0;
287
+ if (this.cornerTreeService) {
288
+ offset = this.cornerTreeService.getTreeChildLength();
289
+ }
290
+ this.dataBars = [...Array(offset), ...data];
291
+ this.emit(EVENT_DATABAR_CHUNK_LOADED, this.dataBars);
292
+ };
293
+ this.onRangeMinMaxHandler = (chunk) => {
294
+ const { data = [] } = chunk;
295
+ const jaql = this.getJaql();
296
+ const measuresTree = jaqlProcessor.getMetadataTree(jaql, PanelType.MEASURES);
297
+ const measures = treeNode.getChildren(measuresTree);
298
+ const rangeMinMax = [];
299
+ measures.forEach((measure, index) => {
300
+ // align indexes in case disabled measures
301
+ rangeMinMax[measure.measureJaqlIndex || 0] = data[index];
302
+ });
303
+ this.rangeMinMax = rangeMinMax;
304
+ this.emit(EVENT_RANGEMINMAX_CHUNK_LOADED, this.rangeMinMax);
305
+ };
306
+ this.onDataFinishHandler = ({ rowsCount } = {}) => {
307
+ this.finishRequest();
308
+ this.rowsCount = rowsCount;
309
+ if (this.tempDataChunks.length) {
310
+ const chunkLast = this.tempDataChunks[this.tempDataChunks.length - 1];
311
+ chunkLast.ready = true;
312
+ }
313
+ // @ts-ignore
314
+ this.onDataHandlerThrottle();
315
+ this.checkLoadPromise();
316
+ this.emit(EVENT_DATA_FINISH_CHUNK_LOADED, {
317
+ limitedRows: this.rowsCount,
318
+ limitedColumns: this.columnsCount,
319
+ });
320
+ };
321
+ this.onErrorHandler = (data) => {
322
+ if (process.env.NODE_ENV !== 'production') {
323
+ // eslint-disable-next-line no-console
324
+ console.warn(`DataService data loading error "${JSON.stringify(data)}"`);
325
+ }
326
+ if (!this.isLoadInProgress &&
327
+ !this.isWholeDataLoaded &&
328
+ !(data instanceof LoadingCanceledError)) {
329
+ // post load error
330
+ this.emit(EVENT_PROGRESS_ERROR, data);
331
+ }
332
+ };
333
+ this.loadService = loadService;
334
+ this.events = events || new EventEmitter();
335
+ this.options = Object.assign(Object.assign({}, defaultOptions), options);
336
+ this.attachEvent();
337
+ this.onDataHandlerThrottle = this.options.throttle(() => {
338
+ const chunkIndex = findIndex(this.tempDataChunks, (item) => item.ready && !item.handled, this.lastReadyNotHandledChunkIndex);
339
+ if (chunkIndex === -1) {
340
+ return;
341
+ }
342
+ this.lastReadyNotHandledChunkIndex = chunkIndex;
343
+ const chunk = this.tempDataChunks[chunkIndex];
344
+ const rows = this.preProcessTree(chunk.list, PanelType.ROWS);
345
+ const modifiedRows = this.modifyTree(rows, PanelType.ROWS);
346
+ this.postProcessTree(modifiedRows, { skipFormatEvent: true });
347
+ this.postProcessData(modifiedRows, { skipFormatEvent: true });
348
+ const wrappedModifiedRows = treeNode.wrapInRootNode(modifiedRows);
349
+ if (this.rowsTreeService) {
350
+ this.rowsTreeService.extend(wrappedModifiedRows);
351
+ }
352
+ else {
353
+ this.rowsTreeService = new TreeService(wrappedModifiedRows, true);
354
+ }
355
+ this.emit(EVENT_DATA_CHUNK_LOADED, this.getLoadedElementsCount());
356
+ chunk.handled = true;
357
+ const nextChunkIndex = findIndex(this.tempDataChunks, (item) => !item.handled, this.lastNotHandledChunkIndex);
358
+ if (nextChunkIndex !== -1) {
359
+ this.lastNotHandledChunkIndex = nextChunkIndex;
360
+ // Note: removed `setImmediate` as not support
361
+ this.onDataHandlerThrottle();
362
+ }
363
+ else {
364
+ this.isDataHandlerThrottled = false;
365
+ }
366
+ if (this.hasDataFinishEvent && this.tempDataChunks.length) {
367
+ const chunkLast = this.tempDataChunks[this.tempDataChunks.length - 1];
368
+ chunkLast.ready = true;
369
+ }
370
+ if (this.hasDataFinishEvent && nextChunkIndex === -1) {
371
+ this.finishRequest();
372
+ this.checkLoadPromise();
373
+ }
374
+ else {
375
+ this.checkLoadPromise();
376
+ }
377
+ }, this.options.throttleTime);
378
+ }
379
+ destroy() {
380
+ this.detachEvent();
381
+ this.rowsGrand = undefined;
382
+ this.rowsTreeService = undefined;
383
+ this.columnsTreeService = undefined;
384
+ this.cornerTreeService = undefined;
385
+ this.tempDataChunks = [];
386
+ this.lastNotHandledChunkIndex = 0;
387
+ this.lastReadyNotHandledChunkIndex = 0;
388
+ this.events.removeAllListeners();
389
+ }
390
+ /**
391
+ * Subscribe to event notification
392
+ *
393
+ * @param {string} eventName - event name to subscribe
394
+ * @param {Function} callback - event handler
395
+ * @returns {void}
396
+ */
397
+ on(eventName, callback) {
398
+ this.events.on(eventName, callback);
399
+ if (this.triggeredEventsData[eventName]) {
400
+ this.triggeredEventsData[eventName].forEach((payload) => {
401
+ // Note: removed `setImmediate` as not supported
402
+ this.events.emit(eventName, ...payload);
403
+ });
404
+ delete this.triggeredEventsData[eventName];
405
+ }
406
+ }
407
+ /**
408
+ * Emits event with payload
409
+ *
410
+ * @param {string} eventName - event name to emit
411
+ * @param {Array<any>} payload - event payload
412
+ * @returns {void}
413
+ */
414
+ emit(eventName, ...payload) {
415
+ const eventListeners = this.events.listeners(eventName);
416
+ if (eventListeners.length === 0) {
417
+ this.triggeredEventsData[eventName] = this.triggeredEventsData[eventName] || [];
418
+ this.triggeredEventsData[eventName].push(payload);
419
+ }
420
+ else {
421
+ this.events.emit(eventName, ...payload);
422
+ }
423
+ }
424
+ /**
425
+ * Unsubscribe from event notification
426
+ *
427
+ * @param {string} eventName - event name to unsubscribe from
428
+ * @param {Function} callback - event handler
429
+ * @returns {void}
430
+ */
431
+ off(eventName, callback) {
432
+ this.events.removeListener(eventName, callback);
433
+ if (this.triggeredEventsData[eventName]) {
434
+ delete this.triggeredEventsData[eventName];
435
+ }
436
+ }
437
+ /**
438
+ * Initialize start data loading
439
+ *
440
+ * @param {JaqlRequest} [jaql] - JAQL request object
441
+ * @param {object} [options] - additional options
442
+ * @param {number} [options.pageSize=100] - page size to wait
443
+ * @param {boolean} [options.isPaginated=false] - defines which type of treeService to return,
444
+ * partial or paginated
445
+ * @param {Array<JaqlPanel>} [options.metadata] - jaql's metadata
446
+ * @param {boolean} [options.cacheResult=false] - cache result for next call or not
447
+ * @returns {Promise<any>} - promise with initial data to render
448
+ */
449
+ // eslint-disable-next-line max-lines-per-function,sonarjs/cognitive-complexity
450
+ loadData(jaql, options) {
451
+ // console.log('loadData', jaql, options);
452
+ const { pageSize = DEFAULT_PAGE_SIZE, isPaginated = false, cacheResult = false, metadata, } = options || {};
453
+ const pageSizeIsNotChanged = this.pageSize === pageSize;
454
+ this.clearLoadAllPromise();
455
+ if (jaql) {
456
+ this.clearCache();
457
+ // TODO review this
458
+ // this.onDataHandlerThrottle.cancel();
459
+ }
460
+ this.pageSize = pageSize;
461
+ this.isPaginated = isPaginated;
462
+ this.emit(EVENT_DATA_LOAD);
463
+ if (this.loadPromiseCache && pageSizeIsNotChanged) {
464
+ const loadPromise = Promise.resolve(this.loadPromiseCache);
465
+ this.loadPromiseCache = undefined;
466
+ return loadPromise;
467
+ }
468
+ this.isLoadInProgress = true;
469
+ if (jaql) {
470
+ this.metadata = metadata || [];
471
+ this.rawJaql = getJaqlComposition(jaql, this.metadata);
472
+ // console.log('getJaqlComposition', jaql, this.metadata, this.rawJaql);
473
+ this.initPluginService(this.rawJaql);
474
+ this.loadService
475
+ .load(jaql)
476
+ .then((data) => {
477
+ if (data === null || data === undefined) {
478
+ return;
479
+ }
480
+ const isDataNotExist = !('data' in data);
481
+ const isDataTransferFinished = data.dataFinish[0];
482
+ if (isDataTransferFinished && isDataNotExist) {
483
+ // eslint-disable-next-line no-throw-literal
484
+ throw {
485
+ error: {
486
+ error: true,
487
+ details: 'metadata undefined.',
488
+ },
489
+ };
490
+ }
491
+ })
492
+ .catch((data) => {
493
+ if (this.loadPromiseReject) {
494
+ this.loadPromiseReject(data.error);
495
+ }
496
+ });
497
+ }
498
+ const loadPromise = new Promise((resolve, reject) => {
499
+ this.loadPromiseResolve = resolve;
500
+ this.loadPromiseReject = reject;
501
+ })
502
+ .then((loadedCount) => this.sendInitPage(loadedCount))
503
+ .then((data) => {
504
+ if (cacheResult) {
505
+ this.loadPromiseCache = data;
506
+ }
507
+ this.loadedPagesCount = 1;
508
+ return data;
509
+ })
510
+ .catch((err) => {
511
+ if (err instanceof LoadingCanceledError) {
512
+ this.clearLoadPromise();
513
+ }
514
+ throw err;
515
+ });
516
+ this.checkLoadPromise();
517
+ return loadPromise;
518
+ }
519
+ /**
520
+ * Sets Promise that resolves after all data loaded
521
+ *
522
+ * @returns {Promise<{ loadedRowsCount: number, totalItemsCount: number }>} - promise with
523
+ * loaded items count
524
+ */
525
+ loadAllData() {
526
+ if (this.isWholeDataLoaded) {
527
+ const allDataInfo = {
528
+ loadedRowsCount: this.getLoadedElementsCount(),
529
+ totalItemsCount: typeof this.totalRowsCount === 'number' ? this.totalRowsCount : this.getTotalItemsCount(),
530
+ totalRecordsCount: this.totalRecordsCount,
531
+ limitReached: this.limitReached,
532
+ dataBars: this.dataBars,
533
+ rangeMinMax: this.rangeMinMax,
534
+ };
535
+ if (this.columnsCount !== 0 && this.totalColumnsCount !== 0) {
536
+ allDataInfo.columnsCount = this.columnsCount;
537
+ allDataInfo.totalColumnsCount = this.totalColumnsCount;
538
+ }
539
+ return Promise.resolve(allDataInfo);
540
+ }
541
+ if (!this.loadAllPromise) {
542
+ this.loadAllPromise = new Promise((resolve, reject) => {
543
+ this.loadAllPromiseResolve = resolve;
544
+ this.loadAllPromiseReject = reject;
545
+ })
546
+ .then(() => ({
547
+ loadedRowsCount: this.getLoadedElementsCount(),
548
+ totalItemsCount: typeof this.totalRowsCount === 'number'
549
+ ? this.totalRowsCount
550
+ : this.getTotalItemsCount(),
551
+ totalRecordsCount: this.totalRecordsCount,
552
+ limitReached: this.limitReached,
553
+ dataBars: this.dataBars,
554
+ rangeMinMax: this.rangeMinMax,
555
+ }))
556
+ .catch((err) => {
557
+ if (err instanceof LoadingCanceledError) {
558
+ this.clearLoadAllPromise();
559
+ }
560
+ throw err;
561
+ });
562
+ }
563
+ return this.loadAllPromise;
564
+ }
565
+ /**
566
+ * public method for selecting page and setting new page size
567
+ *
568
+ * @param {number} selected - page number
569
+ * @param {number} pageSize - set page size (optional)
570
+ * @returns {Promise<{rowsTreeService, columnsTreeService, cornerTreeService, isLastPage}>}
571
+ * - returns private method's call with panel's treeServices
572
+ */
573
+ getSelectedPageData(selected, pageSize) {
574
+ return Promise.resolve(this.sendPaginatedPage(selected, pageSize));
575
+ }
576
+ /**
577
+ * public method for selecting page by indexes
578
+ *
579
+ * @param {number} from - first row's index
580
+ * @param {number} to - last row's index
581
+ * @param {boolean} lastPage - append grandTotals
582
+ * @returns {Promise<{rowsTreeService, columnsTreeService, cornerTreeService, isLastPage}>}
583
+ * - returns private method's call with panel's treeServices
584
+ */
585
+ getIndexedPageData(from, to, lastPage = false) {
586
+ return Promise.resolve(this.sendPartialPage(from, to, lastPage));
587
+ }
588
+ /**
589
+ * Returns current jaql request object
590
+ *
591
+ * @returns {JaqlRequest} - jaql request object
592
+ */
593
+ getJaql() {
594
+ return (this.loadService.getJaql() || {
595
+ metadata: [],
596
+ grandTotals: null,
597
+ });
598
+ }
599
+ /**
600
+ * Defines data structure
601
+ *
602
+ * @returns {boolean} - true - single branch tree
603
+ */
604
+ isSingleRowTree() {
605
+ return this.loadService.isSingleRowTree();
606
+ }
607
+ /**
608
+ * Set cell process handlers
609
+ *
610
+ * @param {Array<PluginConfig>} handlers - object with target and handler
611
+ * @returns {void}
612
+ */
613
+ setPluginHandlers(handlers) {
614
+ this.callPlugins = handlers;
615
+ }
616
+ /**
617
+ * Set global styles for all cells
618
+ *
619
+ * @param {GlobalStyles} [styles] - object with target and handler
620
+ * @returns {void}
621
+ */
622
+ setGlobalStyles(styles) {
623
+ this.globalStyles = styles;
624
+ }
625
+ /**
626
+ * Returns current raw jaql request object
627
+ *
628
+ * @returns {JaqlRequest} - composition of required jaql fields
629
+ * @private
630
+ */
631
+ getRawJaql() {
632
+ return this.rawJaql || this.getJaql();
633
+ }
634
+ /**
635
+ * Returns current jaql request metadata array
636
+ *
637
+ * @returns {Array<JaqlPanel>} - jaql request metadata
638
+ * @private
639
+ */
640
+ getMetadata() {
641
+ return this.metadata.length > 0 ? this.metadata : this.getJaql().metadata;
642
+ }
643
+ /**
644
+ * Pre-process initial tree structure
645
+ *
646
+ * @param {Array<TreeNode>} items - items to normalize
647
+ * @param {string} type - items types to normalize
648
+ * @param {object} options - additional options
649
+ * @param {number} [options.level=0] - tree level, for internal use only
650
+ * @param {object} [options.measurePath={}] - items types to normalize
651
+ * @returns {Array<PivotTreeNode>} - normalized list of items
652
+ */
653
+ preProcessTree(items, type, options) {
654
+ return pivotTransforms.preProcessTree(items, type, this.getJaql(), options);
655
+ }
656
+ /**
657
+ * Post-process final tree structure with formatting event
658
+ *
659
+ * @param {PivotTreeNode} items - list of PivotTreeNode items
660
+ * @param {object} [options] - additional options
661
+ * @param {boolean} [options.skipFormatEvent] - process tree but skip format event trigger
662
+ * @param {boolean} [options.onlyFormatEvents] - process tree with format event trigger only
663
+ * @param {Function} [options.iterateFn] - iterate function to call for each tree node
664
+ * @returns {void}
665
+ */
666
+ postProcessTree(items, options) {
667
+ const emitFn = (item, panel, jaql) => this.emit(EVENT_HEADER_CELL_FORMAT, item, panel, jaql, this.options);
668
+ const extendedOptions = Object.assign(Object.assign({}, (options || {})), { emitFn });
669
+ return pivotTransforms.postProcessTree(items, this.getRawJaql(), extendedOptions);
670
+ }
671
+ /**
672
+ * Post-process data list with formatting event
673
+ *
674
+ * @param {PivotTreeNode} items - list of PivotTreeNode rows items
675
+ * @param {object} [options] - additional options
676
+ * @param {boolean} [options.skipFormatEvent] - process data list but skip format event trigger
677
+ * @param {boolean} [options.onlyFormatEvents] - process data list with format event
678
+ * trigger only
679
+ * @returns {void}
680
+ */
681
+ // eslint-disable-next-line sonarjs/cognitive-complexity
682
+ postProcessData(items, options) {
683
+ const { skipFormatEvent = false, onlyFormatEvents = false } = options || {};
684
+ const columns = this.columnsTreeService ? this.columnsTreeService.getLastLevelNodes() : [];
685
+ const rows = treeNode.getLastLevelNodes(items);
686
+ const jaql = this.getRawJaql();
687
+ if (skipFormatEvent) {
688
+ return;
689
+ }
690
+ rows.forEach((rowTreeNode) => {
691
+ const rowItem = createPivotTreeNode(rowTreeNode);
692
+ columns.forEach((columnTreeNode) => {
693
+ const columnItem = createPivotTreeNode(columnTreeNode);
694
+ const measurePanel = jaqlProcessor.getMetadataPanelByIndex(jaql, columnItem.measureJaqlIndex, PanelType.MEASURES);
695
+ if (typeof rowItem.data !== 'undefined' && typeof columnItem.index !== 'undefined') {
696
+ const { data = [] } = rowItem;
697
+ const { index } = columnItem;
698
+ const value = data[index];
699
+ const item = typeof value === 'object' && value !== null && value !== undefined ? value : { value };
700
+ data[index] = item;
701
+ if (onlyFormatEvents || !skipFormatEvent) {
702
+ const { skipInternalColorFormatting = false } = this.options;
703
+ if (!skipInternalColorFormatting) {
704
+ pivotTransforms.applyColorFormatting(item, rowItem, columnItem, measurePanel);
705
+ }
706
+ this.emit(EVENT_DATA_CELL_FORMAT, item, rowItem, columnItem, measurePanel, jaql, this.options);
707
+ }
708
+ }
709
+ });
710
+ });
711
+ }
712
+ /**
713
+ * Modify tree structure for subtotals and grand totals and some customization
714
+ *
715
+ * @param {Array<PivotTreeNode>} items - base items to modify
716
+ * @param {string} type - items types to normalize
717
+ * @param {TreeNode} originalData - initial data object
718
+ * @returns {PivotTreeNode | Array<PivotTreeNode>} - normalized list of items
719
+ */
720
+ modifyTree(items, type, originalData) {
721
+ const jaql = this.getJaql();
722
+ return pivotTransforms.modifyTree(items, type, jaql, {
723
+ originalData,
724
+ applyIndexDivergence: this.applyIndexDivergence.bind(this),
725
+ subtotalsForSingleRow: this.options.subtotalsForSingleRow,
726
+ });
727
+ }
728
+ /**
729
+ * method calculates divergence by prev indexes
730
+ *
731
+ * @param {Array<PivotTreeNode>} items - data to modify divergence and apply
732
+ * @returns {void}
733
+ * @private
734
+ */
735
+ applyIndexDivergence(items) {
736
+ this.divergenceComparator = new DivergenceComparator(items, this.divergenceComparator);
737
+ }
738
+ /**
739
+ * method creates plugin service instance for pivot
740
+ *
741
+ * @param {JaqlRequest} jaql - data to modify divergence and apply
742
+ * @returns {void}
743
+ * @private
744
+ */
745
+ initPluginService(jaql) {
746
+ this.pluginService = new this.options.PluginService(this.callPlugins, jaql, this.globalStyles);
747
+ }
748
+ /**
749
+ * Cancel current loading process
750
+ *
751
+ * @returns {void}
752
+ */
753
+ cancelLoading() {
754
+ const loadingCanceledError = new LoadingCanceledError();
755
+ if (this.loadPromiseReject && typeof this.loadPromiseReject === 'function') {
756
+ this.loadPromiseReject(loadingCanceledError);
757
+ }
758
+ if (this.loadAllPromiseReject && typeof this.loadAllPromiseReject === 'function') {
759
+ this.loadAllPromiseReject(loadingCanceledError);
760
+ }
761
+ this.detachEvent();
762
+ }
763
+ /**
764
+ * Returns final page size, excluding grand totals rows for example
765
+ *
766
+ * @returns {number} - page size
767
+ * @private
768
+ */
769
+ getPageSize() {
770
+ return this.pageSize;
771
+ }
772
+ /**
773
+ *
774
+ * Send result for "loadData" function after promise resolve
775
+ *
776
+ * @param {number} loadedCount - count of already loaded rows
777
+ * @returns {{
778
+ * rowsTreeService: TreeServiceI,
779
+ * columnsTreeService: TreeServiceI,
780
+ * corner: TreeNode,
781
+ * values: TreeNode,
782
+ * cellsMetadata: Map<string, Metadata>
783
+ * }} - object with initial data
784
+ * @private
785
+ */
786
+ sendInitPage(loadedCount) {
787
+ if (this.loadService.hasError() || !this.loadService.hasData()) {
788
+ return DataService.getEmptyPage();
789
+ }
790
+ let partialRowsService;
791
+ let cellsMetadata;
792
+ const pageSize = this.getPageSize();
793
+ if (this.rowsTreeService) {
794
+ const from = 0;
795
+ const to = pageSize > loadedCount ? loadedCount : pageSize;
796
+ const cloneFn = DataService.cloneTreeNode;
797
+ const partialTree = this.rowsTreeService.getPartialTree(from, to, { cloneFn });
798
+ if (this.rowsGrand) {
799
+ partialTree.push(cloneObject(this.rowsGrand));
800
+ }
801
+ this.postProcessTree(partialTree, {
802
+ onlyFormatEvents: true,
803
+ iterateFn: treeNode.clearNodeCache,
804
+ });
805
+ this.postProcessData(partialTree, { onlyFormatEvents: true });
806
+ const deepLength = this.rowsTreeService ? this.rowsTreeService.getTreeDeepsLength() : 0;
807
+ const wrappedModifiedRows = treeNode.wrapInRootNode(partialTree);
808
+ if (this.pluginService && wrappedModifiedRows && wrappedModifiedRows.children) {
809
+ this.pluginService.applyToRows(wrappedModifiedRows.children);
810
+ cellsMetadata = this.pluginService.metadataCache;
811
+ }
812
+ partialRowsService = new TreeService(wrappedModifiedRows, true, deepLength);
813
+ partialRowsService.hasGrandTotals = !!this.rowsGrand;
814
+ }
815
+ const isLastPage = this.isWholeDataLoaded && pageSize >= loadedCount;
816
+ return {
817
+ rowsTreeService: partialRowsService,
818
+ columnsTreeService: this.columnsTreeService,
819
+ cornerTreeService: this.cornerTreeService,
820
+ isLastPage,
821
+ cellsMetadata,
822
+ };
823
+ }
824
+ /**
825
+ * Sets Page Size and returns Tree Services
826
+ *
827
+ * @param {number} selected - page number -- STARTING FROM 0
828
+ * @param {number} [newPageSize] - set new pageSize
829
+ * @returns {{
830
+ * rowsTreeService: null,
831
+ * columnsTreeService: TreeServiceI,
832
+ * cornerTreeService: TreeServiceI,
833
+ * isLastPage: boolean
834
+ * }} - returns panel's treeServices
835
+ * @private
836
+ */
837
+ // eslint-disable-next-line sonarjs/cognitive-complexity
838
+ sendPaginatedPage(selected, newPageSize) {
839
+ if (this.loadService.hasError() || !this.loadService.hasData()) {
840
+ return DataService.getEmptyPage();
841
+ }
842
+ if (newPageSize) {
843
+ this.pageSize = newPageSize;
844
+ }
845
+ const pageSize = this.getPageSize();
846
+ const loadedCount = this.getLoadedElementsCount();
847
+ if (selected * pageSize >= loadedCount) {
848
+ throw new Error('Selected page is out of elements range!');
849
+ }
850
+ if (selected < 0) {
851
+ throw new Error('Selected page must be >= 0');
852
+ }
853
+ let partialRowsService;
854
+ let cellsMetadata;
855
+ if (this.rowsTreeService) {
856
+ const from = selected >= 0 ? selected * pageSize : 0;
857
+ const to = (selected + 1) * pageSize > loadedCount ? loadedCount : (selected + 1) * pageSize;
858
+ const cloneFn = DataService.cloneTreeNode;
859
+ const partialTree = this.rowsTreeService.getPartialTree(from, to, { cloneFn });
860
+ if (this.rowsGrand) {
861
+ partialTree.push(cloneObject(this.rowsGrand));
862
+ }
863
+ this.postProcessTree(partialTree, {
864
+ onlyFormatEvents: true,
865
+ iterateFn: treeNode.clearNodeCache,
866
+ });
867
+ this.postProcessData(partialTree, { onlyFormatEvents: true });
868
+ const deepLength = this.rowsTreeService ? this.rowsTreeService.getTreeDeepsLength() : 0;
869
+ const wrappedModifiedRows = treeNode.wrapInRootNode(partialTree);
870
+ if (this.pluginService && wrappedModifiedRows && wrappedModifiedRows.children) {
871
+ this.pluginService.applyToRows(wrappedModifiedRows.children);
872
+ cellsMetadata = this.pluginService.metadataCache;
873
+ }
874
+ partialRowsService = new TreeService(wrappedModifiedRows, true, deepLength);
875
+ partialRowsService.hasGrandTotals = !!this.rowsGrand;
876
+ }
877
+ return {
878
+ rowsTreeService: partialRowsService,
879
+ columnsTreeService: this.columnsTreeService,
880
+ cornerTreeService: this.cornerTreeService,
881
+ isLastPage: true,
882
+ cellsMetadata,
883
+ };
884
+ }
885
+ /**
886
+ * Returns partial tree by indexes
887
+ *
888
+ * @param {number} paramFrom - start index
889
+ * @param {number} paramTo - stop index
890
+ * @param {boolean} lastPage - append grandTotals
891
+ * @returns {{
892
+ * rowsTreeService: TreeServiceI,
893
+ * columnsTreeService: TreeServiceI,
894
+ * cornerTreeService: TreeServiceI,
895
+ * isLastPage: boolean
896
+ * }} - returns panel's treeServices
897
+ * @private
898
+ */
899
+ sendPartialPage(paramFrom, paramTo, lastPage) {
900
+ if (this.loadService.hasError() || !this.loadService.hasData()) {
901
+ return DataService.getEmptyPage();
902
+ }
903
+ const loadedCount = this.getLoadedElementsCount();
904
+ let [from, to] = [Math.min(paramFrom, paramTo), Math.max(paramFrom, paramTo)];
905
+ from = from >= 0 ? from : 0;
906
+ to = to >= 0 ? to : 0;
907
+ to = to < loadedCount ? to : loadedCount;
908
+ let rowsTreeService;
909
+ let cellsMetadata;
910
+ if (from > loadedCount) {
911
+ rowsTreeService = undefined;
912
+ }
913
+ else {
914
+ let partialTree;
915
+ if (this.rowsTreeService) {
916
+ const cloneFn = DataService.cloneTreeNode;
917
+ partialTree = this.rowsTreeService.getPartialTree(from, to, { cloneFn });
918
+ if (lastPage && this.rowsGrand) {
919
+ partialTree.push(cloneObject(this.rowsGrand));
920
+ }
921
+ this.postProcessTree(partialTree, {
922
+ onlyFormatEvents: true,
923
+ iterateFn: treeNode.clearNodeCache,
924
+ });
925
+ this.postProcessData(partialTree, { onlyFormatEvents: true });
926
+ }
927
+ const deepLength = this.rowsTreeService ? this.rowsTreeService.getTreeDeepsLength() : 0;
928
+ const wrappedModifiedRows = treeNode.wrapInRootNode(partialTree);
929
+ if (this.pluginService && wrappedModifiedRows && wrappedModifiedRows.children) {
930
+ this.pluginService.applyToRows(wrappedModifiedRows.children);
931
+ cellsMetadata = this.pluginService.metadataCache;
932
+ }
933
+ rowsTreeService = new TreeService(wrappedModifiedRows, true, deepLength);
934
+ rowsTreeService.hasGrandTotals = !!(lastPage && this.rowsGrand);
935
+ }
936
+ return {
937
+ rowsTreeService,
938
+ columnsTreeService: this.columnsTreeService,
939
+ cornerTreeService: this.cornerTreeService,
940
+ isLastPage: true,
941
+ cellsMetadata,
942
+ };
943
+ }
944
+ /**
945
+ *
946
+ * Get current state of tree (not recommended to use before 'loadData')
947
+ *
948
+ * @returns {number} loadedCount - current loaded count of children
949
+ * @private
950
+ */
951
+ getLoadedElementsCount() {
952
+ return this.rowsTreeService ? this.rowsTreeService.getTreeChildLength() : 0;
953
+ }
954
+ /**
955
+ * Returns total amount items count handled on back-end side
956
+ *
957
+ * @deprecated
958
+ * @returns {number} - items count
959
+ * @private
960
+ */
961
+ getTotalItemsCount() {
962
+ return this.loadService.getTotalItemsCount();
963
+ }
964
+ /**
965
+ * Getter for pivot table loaded columns number
966
+ *
967
+ * @deprecated
968
+ * @returns {number} loadedCount - current loaded pivot table columns count
969
+ * @private
970
+ */
971
+ getLoadedColumnsCount() {
972
+ return this.columnsTreeService ? this.columnsTreeService.getTreeChildLength() : 0;
973
+ }
974
+ /**
975
+ * Attach events to load service
976
+ * This is a key function implementing the Observer pattern
977
+ * DataService acts as Observer and subscribes its handlers to various
978
+ * data loading events emitted by the load service.
979
+ *
980
+ *
981
+ * @returns {void}
982
+ * @private
983
+ */
984
+ attachEvent() {
985
+ if (this.loadService.listenerCount(MessageType.HEADERS)) {
986
+ this.loadService.offAll(MessageType.HEADERS);
987
+ }
988
+ this.loadService.on(MessageType.HEADERS, this.onHeadersHandler);
989
+ this.loadService.on(MessageType.METADATA, this.onMetadataHandler);
990
+ this.loadService.on(MessageType.DATA, this.onDataHandler);
991
+ this.loadService.on(MessageType.GRAND, this.onGrandHandler);
992
+ this.loadService.on(MessageType.DATA_FINISH, this.onDataFinishHandler);
993
+ this.loadService.on(MessageType.TOTAL_ROWS, this.onTotalRowsHandler);
994
+ this.loadService.on(MessageType.DATABARS, this.onDataBarsHandler);
995
+ this.loadService.on(MessageType.RANGE_MIN_MAX, this.onRangeMinMaxHandler);
996
+ this.loadService.on(MessageType.FINISH, this.onFinishHandler);
997
+ this.loadService.on(MessageType.ERROR, this.onErrorHandler);
998
+ }
999
+ /**
1000
+ * Dettach events from load service
1001
+ *
1002
+ * @returns {void}
1003
+ * @private
1004
+ */
1005
+ detachEvent() {
1006
+ this.loadService.off(MessageType.HEADERS, this.onHeadersHandler);
1007
+ this.loadService.off(MessageType.METADATA, this.onMetadataHandler);
1008
+ this.loadService.off(MessageType.DATA, this.onDataHandler);
1009
+ this.loadService.off(MessageType.GRAND, this.onGrandHandler);
1010
+ this.loadService.off(MessageType.DATA_FINISH, this.onDataFinishHandler);
1011
+ this.loadService.off(MessageType.TOTAL_ROWS, this.onTotalRowsHandler);
1012
+ this.loadService.off(MessageType.DATABARS, this.onDataBarsHandler);
1013
+ this.loadService.off(MessageType.RANGE_MIN_MAX, this.onRangeMinMaxHandler);
1014
+ this.loadService.off(MessageType.FINISH, this.onFinishHandler);
1015
+ this.loadService.off(MessageType.ERROR, this.onErrorHandler);
1016
+ }
1017
+ /**
1018
+ * Check for enough data loaded and resolve promise of "loadData" function
1019
+ *
1020
+ * @returns {void}
1021
+ * @private
1022
+ */
1023
+ checkLoadPromise() {
1024
+ if (!this.isLoadInProgress) {
1025
+ return;
1026
+ }
1027
+ const loadedCount = this.getLoadedElementsCount();
1028
+ if (loadedCount >= this.getPageSize() || this.isWholeDataLoaded) {
1029
+ this.isLoadInProgress = false;
1030
+ if (typeof this.loadPromiseResolve === 'function') {
1031
+ this.loadPromiseResolve(loadedCount);
1032
+ this.clearLoadPromise();
1033
+ }
1034
+ }
1035
+ }
1036
+ /**
1037
+ * @returns {void}
1038
+ * @private
1039
+ */
1040
+ clearLoadAllPromise() {
1041
+ this.loadAllPromise = undefined;
1042
+ this.loadAllPromiseReject = undefined;
1043
+ this.loadAllPromiseResolve = undefined;
1044
+ }
1045
+ /**
1046
+ * @returns {void}
1047
+ * @private
1048
+ */
1049
+ clearLoadPromise() {
1050
+ this.loadPromiseReject = undefined;
1051
+ this.loadPromiseResolve = undefined;
1052
+ }
1053
+ /**
1054
+ * Mark finish request
1055
+ *
1056
+ * @returns {void}
1057
+ * @private
1058
+ */
1059
+ finishRequest() {
1060
+ this.hasDataFinishEvent = true;
1061
+ // even if the finish request arrives but the data handling is still being throttled
1062
+ // we cannot resolve the loadAllPromiseResolve because there are still data chunks
1063
+ // in temp storage to be processed
1064
+ if (this.isDataHandlerThrottled) {
1065
+ return;
1066
+ }
1067
+ this.isWholeDataLoaded = true;
1068
+ if (this.loadAllPromiseResolve && typeof this.loadAllPromiseResolve === 'function') {
1069
+ this.loadAllPromiseResolve();
1070
+ this.clearLoadAllPromise();
1071
+ }
1072
+ }
1073
+ /**
1074
+ * Clear internal cache in case new JAQL request send
1075
+ *
1076
+ * @returns {void}
1077
+ * @private
1078
+ */
1079
+ clearCache() {
1080
+ this.rowsGrand = undefined;
1081
+ this.rowsTreeService = undefined;
1082
+ this.columnsTreeService = undefined;
1083
+ this.loadedPagesCount = 0;
1084
+ this.hasDataFinishEvent = false;
1085
+ this.isWholeDataLoaded = false;
1086
+ this.isLoadInProgress = false;
1087
+ this.tempDataChunks = [];
1088
+ this.lastNotHandledChunkIndex = 0;
1089
+ this.lastReadyNotHandledChunkIndex = 0;
1090
+ this.isDataHandlerThrottled = false;
1091
+ }
1092
+ static getEmptyPage() {
1093
+ return {
1094
+ isLastPage: true,
1095
+ };
1096
+ }
1097
+ static fillDataChunks(list, node, firstMaxSize = NODES_CHUNK_SIZE_MAX, maxSize = NODES_CHUNK_SIZE_MAX) {
1098
+ const isFirstChunk = list.length < 2;
1099
+ const maxChunkSize = isFirstChunk ? firstMaxSize : maxSize;
1100
+ let lastChunk = list[Math.max(0, list.length - 1)];
1101
+ let lastChunkNode;
1102
+ let createNewChunk = !lastChunk;
1103
+ if (!node ||
1104
+ (typeof node.children === 'undefined' &&
1105
+ typeof node.value === 'undefined' &&
1106
+ node.data &&
1107
+ !node.data.length)) {
1108
+ if (lastChunk) {
1109
+ lastChunk.ready = true;
1110
+ }
1111
+ return;
1112
+ }
1113
+ if (lastChunk) {
1114
+ const lastChunkNodeIndex = Math.max(0, lastChunk.list.length - 1);
1115
+ lastChunkNode = lastChunk.list[lastChunkNodeIndex];
1116
+ const lastChunkSizeStr = lastChunk.size ? `${lastChunk.size}` : '0';
1117
+ const lastChunkSize = parseInt(lastChunkSizeStr, 10);
1118
+ const afterLastChunkSize = lastChunkSize + (node.size || 1);
1119
+ if (lastChunk.handled ||
1120
+ (afterLastChunkSize > maxChunkSize &&
1121
+ !(lastChunkNode.isPart && lastChunkNode.value === node.value))) {
1122
+ createNewChunk = true;
1123
+ }
1124
+ }
1125
+ if (node &&
1126
+ node.isPart &&
1127
+ lastChunkNode &&
1128
+ (!lastChunkNode.isPart || lastChunkNode.value !== node.value)) {
1129
+ createNewChunk = true;
1130
+ if (lastChunkNode.value !== node.value) {
1131
+ lastChunkNode = undefined;
1132
+ }
1133
+ }
1134
+ if (createNewChunk) {
1135
+ if (lastChunk) {
1136
+ lastChunk.ready = true;
1137
+ }
1138
+ lastChunk = {
1139
+ list: [],
1140
+ size: 0,
1141
+ ready: false,
1142
+ handled: false,
1143
+ };
1144
+ list.push(lastChunk);
1145
+ }
1146
+ if (lastChunk) {
1147
+ if (lastChunkNode && lastChunkNode.isPart && node && node.isPart) {
1148
+ treeNode.deepMerge(lastChunkNode, node);
1149
+ lastChunk.size += node.size || 1;
1150
+ }
1151
+ else {
1152
+ lastChunk.list.push(node);
1153
+ lastChunk.size += node.size || 1;
1154
+ }
1155
+ }
1156
+ }
1157
+ static cloneTreeNode(obj, skipChildren) {
1158
+ // return cloneObject(obj);
1159
+ const node = obj;
1160
+ const { children, isMapped, // eslint-disable-line no-unused-vars
1161
+ minLevel, // eslint-disable-line no-unused-vars
1162
+ childCount, // eslint-disable-line no-unused-vars
1163
+ childDeep, // eslint-disable-line no-unused-vars
1164
+ // @ts-ignore
1165
+ parent,
1166
+ // @ts-ignore
1167
+ master,
1168
+ // @ts-ignore
1169
+ data,
1170
+ // @ts-ignore
1171
+ state,
1172
+ // @ts-ignore
1173
+ style,
1174
+ // @ts-ignore
1175
+ store } = node, restNode = __rest(node, ["children", "isMapped", "minLevel", "childCount", "childDeep", "parent", "master", "data", "state", "style", "store"]);
1176
+ const clone = cloneObject(restNode);
1177
+ if (parent) {
1178
+ clone.parent = parent;
1179
+ }
1180
+ if (master) {
1181
+ clone.master = master;
1182
+ }
1183
+ if (data) {
1184
+ clone.data = cloneObject(data);
1185
+ }
1186
+ if (state) {
1187
+ clone.state = cloneObject(state);
1188
+ }
1189
+ if (style) {
1190
+ clone.style = cloneObject(style);
1191
+ }
1192
+ if (store) {
1193
+ clone.store = cloneObject(store);
1194
+ }
1195
+ if (!skipChildren && children) {
1196
+ clone.children = children.map((ch) => {
1197
+ const clonedChild = DataService.cloneTreeNode(ch);
1198
+ // @ts-ignore
1199
+ clonedChild.parent = clone;
1200
+ return clonedChild;
1201
+ });
1202
+ }
1203
+ return clone;
1204
+ }
1205
+ /**
1206
+ * @typedef {Function} buildColumnsTreeService
1207
+ * @param {HeaderTreeNode} originalColumnsTree
1208
+ * @returns {BuildColumnsTreeServiceResult}
1209
+ */
1210
+ /**
1211
+ * @typedef {object} BuildColumnsTreeServiceResult
1212
+ * @property {TreeNode|undefined} columnsTree
1213
+ * @property {TreeService|undefined} columnsTreeService
1214
+ * @property {number} columnsCount
1215
+ * @property {number} totalColumnsCount
1216
+ */
1217
+ /**
1218
+ * Creates builder function for building columns tree service
1219
+ *
1220
+ * @param {object} handlers - build handlers
1221
+ * @param {Function} handlers.getJaql - jaql getter
1222
+ * @param {Function} [handlers.getRawJaql] - raw jaql getter
1223
+ * @param {PluginService} [handlers.pluginService] - plugin service
1224
+ * @param {Function} [handlers.applyIndexDivergence] - helper for calculation indexes divergence
1225
+ * @param {object} [events] - build event emitters
1226
+ * @param {Function} events.emitHeaderCellFormatEvent - emits EVENT_HEADER_CELL_FORMAT event
1227
+ * @returns {buildColumnsTreeService} - build function
1228
+ */
1229
+ static createColumnsTreeServiceBuilder(handlers, events) {
1230
+ return (originalColumnsTree) => {
1231
+ const jaql = handlers.getJaql();
1232
+ const rawJaql = handlers.getRawJaql
1233
+ ? handlers.getRawJaql()
1234
+ : getJaqlComposition(jaql, jaql.metadata || []);
1235
+ const valuesTree = jaqlProcessor.getMetadataTree(jaql, PanelType.MEASURES);
1236
+ const valuesTreeChildren = treeNode.getChildren(valuesTree);
1237
+ const { preProcessTree, modifyTree, postProcessTree } = pivotTransforms;
1238
+ let columnsTree;
1239
+ let columnsTreeService;
1240
+ let columnsCount = 0;
1241
+ let totalColumnsCount = 0;
1242
+ if (treeNode.hasChildren(originalColumnsTree)) {
1243
+ if (originalColumnsTree.columnsCount && originalColumnsTree.totalColumnsCount) {
1244
+ columnsCount = originalColumnsTree.columnsCount;
1245
+ totalColumnsCount = originalColumnsTree.totalColumnsCount;
1246
+ }
1247
+ const columns = preProcessTree(originalColumnsTree, PanelType.COLUMNS, jaql);
1248
+ const modifiedColumns = modifyTree(columns, PanelType.COLUMNS, jaql, {
1249
+ originalData: originalColumnsTree,
1250
+ applyIndexDivergence: handlers.applyIndexDivergence,
1251
+ });
1252
+ postProcessTree(modifiedColumns, rawJaql, {
1253
+ emitFn: events && events.emitHeaderCellFormatEvent,
1254
+ });
1255
+ const wrappedModifiedColumns = treeNode.wrapInRootNode(modifiedColumns);
1256
+ if (handlers.pluginService && wrappedModifiedColumns && wrappedModifiedColumns.children) {
1257
+ handlers.pluginService.applyToColumns(wrappedModifiedColumns.children);
1258
+ }
1259
+ columnsTree = wrappedModifiedColumns;
1260
+ columnsTreeService = new TreeService(wrappedModifiedColumns);
1261
+ if (valuesTreeChildren.length === 1) {
1262
+ columnsTreeService.setValueNode(valuesTreeChildren[0]);
1263
+ }
1264
+ }
1265
+ else if (treeNode.hasChildren(valuesTree)) {
1266
+ if (originalColumnsTree.columnsCount && originalColumnsTree.totalColumnsCount) {
1267
+ columnsCount = originalColumnsTree.columnsCount;
1268
+ totalColumnsCount = originalColumnsTree.totalColumnsCount;
1269
+ }
1270
+ // columns limit
1271
+ if (typeof originalColumnsTree.maxChilds === 'number') {
1272
+ columnsCount = originalColumnsTree.maxChilds;
1273
+ valuesTree.children = treeNode
1274
+ .getChildren(valuesTree)
1275
+ .filter((item, index) => index < originalColumnsTree.maxChilds);
1276
+ }
1277
+ if (handlers.pluginService && valuesTree.children) {
1278
+ handlers.pluginService.applyToColumns(valuesTree.children);
1279
+ }
1280
+ postProcessTree(treeNode.getChildren(valuesTree), rawJaql, {
1281
+ emitFn: events && events.emitHeaderCellFormatEvent,
1282
+ });
1283
+ columnsTree = valuesTree;
1284
+ columnsTreeService = new TreeService(valuesTree);
1285
+ }
1286
+ else {
1287
+ if (handlers.pluginService) {
1288
+ handlers.pluginService.resetRowStartFrom();
1289
+ }
1290
+ columnsTree = undefined;
1291
+ columnsTreeService = undefined;
1292
+ columnsCount = 0;
1293
+ totalColumnsCount = 0;
1294
+ }
1295
+ return {
1296
+ columnsTree,
1297
+ columnsTreeService,
1298
+ columnsCount,
1299
+ totalColumnsCount,
1300
+ };
1301
+ };
1302
+ }
1303
+ /**
1304
+ * @typedef {Function} buildCornerTreeService
1305
+ * @param {HeaderTreeNode} originalColumnsTree
1306
+ * @returns {BuildCornerTreeServiceResult}
1307
+ */
1308
+ /**
1309
+ * @typedef {object} BuildCornerTreeServiceResult
1310
+ * @property {TreeNode|undefined} columnsTree
1311
+ * @property {TreeService|undefined} columnsTreeService
1312
+ * @property {number} columnsCount
1313
+ * @property {number} totalColumnsCount
1314
+ */
1315
+ /**
1316
+ * Creates builder function for building corners tree service
1317
+ *
1318
+ * @param {object} handlers - build handlers
1319
+ * @param {Function} handlers.getJaql - jaql getter
1320
+ * @param {Function} [handlers.getRawJaql] - raw jaql getter
1321
+ * @param {PluginService} [handlers.pluginService] - plugin service
1322
+ * @param {TreeServiceI} [handlers.columnsTreeService] - column tree service
1323
+ * @param {object} [events] - build event emitters
1324
+ * @param {Function} events.emitHeaderCellFormatEvent - emits EVENT_HEADER_CELL_FORMAT event
1325
+ * @returns {buildCornerTreeService} - build function
1326
+ */
1327
+ static createCornerTreeServiceBuilder(handlers, events) {
1328
+ return () => {
1329
+ const jaql = handlers.getJaql();
1330
+ const rawJaql = handlers.getRawJaql
1331
+ ? handlers.getRawJaql()
1332
+ : getJaqlComposition(jaql, jaql.metadata || []);
1333
+ const cornerTree = jaqlProcessor.getMetadataTree(jaql, PanelType.ROWS);
1334
+ let cornerTreeService;
1335
+ if (treeNode.hasChildren(cornerTree)) {
1336
+ // add user type to tree node
1337
+ treeNode.iterateThroughTree(treeNode.getChildren(cornerTree), (item) => {
1338
+ item.userType = UserType.CORNER;
1339
+ const panel = jaqlProcessor.getMetadataPanelByIndex(rawJaql, item.jaqlIndex, item.metadataType || '');
1340
+ if (events && events.emitHeaderCellFormatEvent) {
1341
+ events.emitHeaderCellFormatEvent(item, panel, rawJaql);
1342
+ }
1343
+ });
1344
+ let cornerDeep;
1345
+ if (handlers.columnsTreeService) {
1346
+ cornerDeep = handlers.columnsTreeService.getTreeDeepsLength();
1347
+ }
1348
+ if (handlers.pluginService) {
1349
+ handlers.pluginService.applyToHeaders(cornerTree);
1350
+ }
1351
+ cornerTreeService = new TreeService(cornerTree, false, cornerDeep);
1352
+ }
1353
+ return { cornerTree, cornerTreeService };
1354
+ };
1355
+ }
1356
+ }
1357
+ export default DataService;