@gooddata/sdk-backend-base 9.6.0-alpha.2 → 9.6.0-alpha.21

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 (64) hide show
  1. package/esm/cachingBackend/index.js +269 -260
  2. package/esm/cachingBackend/index.js.map +1 -1
  3. package/esm/customBackend/execution.js +107 -94
  4. package/esm/customBackend/execution.js.map +1 -1
  5. package/esm/customBackend/index.js +102 -95
  6. package/esm/customBackend/index.js.map +1 -1
  7. package/esm/customBackend/workspace.js +3 -0
  8. package/esm/customBackend/workspace.js.map +1 -1
  9. package/esm/decoratedBackend/analyticalWorkspace.js +3 -0
  10. package/esm/decoratedBackend/analyticalWorkspace.js.map +1 -1
  11. package/esm/decoratedBackend/attributes.js +1 -0
  12. package/esm/decoratedBackend/attributes.js.map +1 -1
  13. package/esm/decoratedBackend/catalog.js +5 -0
  14. package/esm/decoratedBackend/catalog.js.map +1 -1
  15. package/esm/decoratedBackend/dashboards.d.ts +2 -1
  16. package/esm/decoratedBackend/dashboards.d.ts.map +1 -1
  17. package/esm/decoratedBackend/dashboards.js +5 -0
  18. package/esm/decoratedBackend/dashboards.js.map +1 -1
  19. package/esm/decoratedBackend/elements.js +32 -6
  20. package/esm/decoratedBackend/elements.js.map +1 -1
  21. package/esm/decoratedBackend/execution.js +32 -13
  22. package/esm/decoratedBackend/execution.js.map +1 -1
  23. package/esm/decoratedBackend/index.js +4 -0
  24. package/esm/decoratedBackend/index.js.map +1 -1
  25. package/esm/decoratedBackend/organization.js +3 -0
  26. package/esm/decoratedBackend/organization.js.map +1 -1
  27. package/esm/decoratedBackend/organizations.js +2 -0
  28. package/esm/decoratedBackend/organizations.js.map +1 -1
  29. package/esm/decoratedBackend/securitySettings.js +2 -0
  30. package/esm/decoratedBackend/securitySettings.js.map +1 -1
  31. package/esm/decoratedBackend/workspaceSettings.js +1 -0
  32. package/esm/decoratedBackend/workspaceSettings.js.map +1 -1
  33. package/esm/dummyBackend/index.js +34 -10
  34. package/esm/dummyBackend/index.js.map +1 -1
  35. package/esm/eventingBackend/index.js +47 -44
  36. package/esm/eventingBackend/index.js.map +1 -1
  37. package/esm/ldmFactories/builder.js +2 -0
  38. package/esm/ldmFactories/builder.js.map +1 -1
  39. package/esm/ldmFactories/catalog/attributeFactory.js +1 -1
  40. package/esm/ldmFactories/catalog/attributeFactory.js.map +1 -1
  41. package/esm/ldmFactories/catalog/measureFactory.js +1 -1
  42. package/esm/ldmFactories/catalog/measureFactory.js.map +1 -1
  43. package/esm/ldmFactories/dashboard/insightWidgetFactory.js +5 -3
  44. package/esm/ldmFactories/dashboard/insightWidgetFactory.js.map +1 -1
  45. package/esm/ldmFactories/dashboard/kpiWidgetFactory.js +17 -9
  46. package/esm/ldmFactories/dashboard/kpiWidgetFactory.js.map +1 -1
  47. package/esm/ldmFactories/dashboard/widgetFactory.js +18 -18
  48. package/esm/ldmFactories/dashboard/widgetFactory.js.map +1 -1
  49. package/esm/normalizingBackend/index.js +79 -58
  50. package/esm/normalizingBackend/index.js.map +1 -1
  51. package/esm/normalizingBackend/normalizer.js +317 -297
  52. package/esm/normalizingBackend/normalizer.js.map +1 -1
  53. package/esm/sdk-backend-base.d.ts +2 -0
  54. package/esm/toolkit/auth.js +33 -32
  55. package/esm/toolkit/auth.js.map +1 -1
  56. package/esm/toolkit/execution.js +2 -0
  57. package/esm/toolkit/execution.js.map +1 -1
  58. package/esm/toolkit/paging.js +48 -38
  59. package/esm/toolkit/paging.js.map +1 -1
  60. package/esm/toolkit/pluginUrlValidation.js +1 -2
  61. package/esm/toolkit/pluginUrlValidation.js.map +1 -1
  62. package/esm/workspaceSettingsBackend/index.js +11 -2
  63. package/esm/workspaceSettingsBackend/index.js.map +1 -1
  64. package/package.json +8 -10
@@ -8,109 +8,121 @@ import keyBy from "lodash/keyBy.js";
8
8
  * @internal
9
9
  */
10
10
  export class Denormalizer {
11
+ state;
11
12
  static from(state) {
12
13
  return new Denormalizer(state);
13
14
  }
15
+ originalAttributes;
16
+ originalMeasures;
14
17
  constructor(state) {
15
18
  this.state = state;
16
- /**
17
- * Given the current normalization state, this method transforms the dimension descriptors of the
18
- * normalized execution, so that all the customization of the original execution definition is restored
19
- * into them:
20
- *
21
- * - Reverse lookup of local identifiers happens
22
- * - Attribute / Measure name is set according to the defined alias
23
- * - Measure format is set according to the format in definition
24
- *
25
- * @param normalizedDims - normalized dimension descriptors
26
- * @returns new descriptors
27
- */
28
- this.denormalizeDimDescriptors = (normalizedDims) => {
29
- var _a;
30
- const measureGroup = (_a = normalizedDims
31
- .find((normalizedDim) => normalizedDim.headers.find((header) => isMeasureGroupDescriptor(header)))) === null || _a === void 0 ? void 0 : _a.headers.find((header) => isMeasureGroupDescriptor(header));
32
- return cloneDeepWith(normalizedDims, (value) => {
33
- var _a;
34
- if (isAttributeDescriptor(value)) {
35
- const localIdentifier = this.originalLocalId(value.attributeHeader.localIdentifier);
36
- const attribute = this.originalAttributes[localIdentifier];
37
- const name = attributeAlias(attribute) || value.attributeHeader.formOf.name;
38
- return {
39
- attributeHeader: Object.assign(Object.assign({}, value.attributeHeader), { localIdentifier, formOf: Object.assign(Object.assign({}, value.attributeHeader.formOf), { name }) }),
40
- };
41
- }
42
- else if (isMeasureDescriptor(value)) {
43
- const localIdentifier = this.originalLocalId(value.measureHeaderItem.localIdentifier);
44
- const measure = this.originalMeasures[localIdentifier];
45
- const masterMeasureId = measureMasterIdentifier(measure);
46
- const masterMeasureInheritFormat = isMeasureGroupDescriptor(measureGroup) &&
47
- ((_a = measureGroup.measureGroupHeader.items.find((item) => this.originalLocalId(item.measureHeaderItem.localIdentifier) === masterMeasureId)) === null || _a === void 0 ? void 0 : _a.measureHeaderItem.format);
48
- /**
49
- * Measure format is taken by priority from:
50
- * 1) the chosen format of the measure (undefined for inherited format and derived measures)
51
- * 2) the chosen format of the master measure (undefined for inherited format and master)
52
- * 3) the inherited format of master measure (undefined for master)
53
- * 4) the inherited format for master measure or default "#,#.##" for derived
54
- */
55
- const format = measureFormat(measure) ||
56
- (masterMeasureId && measureFormat(this.originalMeasures[masterMeasureId])) ||
57
- masterMeasureInheritFormat ||
58
- value.measureHeaderItem.format;
59
- const name = this.originalMeasureTitle(measure, value.measureHeaderItem.name);
60
- return {
61
- measureHeaderItem: Object.assign(Object.assign({}, value.measureHeaderItem), { localIdentifier,
62
- format,
63
- name }),
64
- };
65
- }
66
- // let the default logic handle this
67
- return undefined;
68
- });
69
- };
70
- /**
71
- * Derived measures or arithmetic measures have the 'name' in result header defaulted to measure
72
- * localId. This method deals with it. It creates a copy of headers with the measure headers denormalized,
73
- * values replaced with the contents of alias or title (whichever comes first).
74
- *
75
- * @param headerItems - headers to denormalize, copy will be done
76
- * @returns new headers
77
- */
78
- this.denormalizeHeaders = (headerItems) => {
79
- return cloneDeepWith(headerItems, (value) => {
80
- if (isResultMeasureHeader(value)) {
81
- return {
82
- measureHeaderItem: {
83
- name: this.fillOriginalMeasureTitle(value.measureHeaderItem.name),
84
- order: value.measureHeaderItem.order,
85
- },
86
- };
87
- }
88
- // let the default logic handle this
89
- return undefined;
90
- });
91
- };
92
- this.originalLocalId = (normalized) => {
93
- const value = this.state.n2oMap[normalized];
94
- invariant(value, `mapping from normalized to real localId does not exist. Normalized: ${normalized}`);
95
- return value;
96
- };
97
- this.fillOriginalMeasureTitle = (name) => {
98
- const originalLocalId = this.state.n2oMap[name];
99
- if (!originalLocalId) {
100
- return name;
101
- }
102
- const measure = this.originalMeasures[originalLocalId];
103
- if (!measure) {
104
- return name;
105
- }
106
- return this.originalMeasureTitle(measure, name);
107
- };
108
- this.originalMeasureTitle = (originalDef, nameFromBackend) => {
109
- return measureAlias(originalDef) || measureTitle(originalDef) || nameFromBackend;
110
- };
111
19
  this.originalAttributes = keyBy(state.original.attributes, attributeLocalId);
112
20
  this.originalMeasures = keyBy(state.original.measures, measureLocalId);
113
21
  }
22
+ /**
23
+ * Given the current normalization state, this method transforms the dimension descriptors of the
24
+ * normalized execution, so that all the customization of the original execution definition is restored
25
+ * into them:
26
+ *
27
+ * - Reverse lookup of local identifiers happens
28
+ * - Attribute / Measure name is set according to the defined alias
29
+ * - Measure format is set according to the format in definition
30
+ *
31
+ * @param normalizedDims - normalized dimension descriptors
32
+ * @returns new descriptors
33
+ */
34
+ denormalizeDimDescriptors = (normalizedDims) => {
35
+ const measureGroup = normalizedDims
36
+ .find((normalizedDim) => normalizedDim.headers.find((header) => isMeasureGroupDescriptor(header)))
37
+ ?.headers.find((header) => isMeasureGroupDescriptor(header));
38
+ return cloneDeepWith(normalizedDims, (value) => {
39
+ if (isAttributeDescriptor(value)) {
40
+ const localIdentifier = this.originalLocalId(value.attributeHeader.localIdentifier);
41
+ const attribute = this.originalAttributes[localIdentifier];
42
+ const name = attributeAlias(attribute) || value.attributeHeader.formOf.name;
43
+ return {
44
+ attributeHeader: {
45
+ ...value.attributeHeader,
46
+ localIdentifier,
47
+ formOf: {
48
+ ...value.attributeHeader.formOf,
49
+ name,
50
+ },
51
+ },
52
+ };
53
+ }
54
+ else if (isMeasureDescriptor(value)) {
55
+ const localIdentifier = this.originalLocalId(value.measureHeaderItem.localIdentifier);
56
+ const measure = this.originalMeasures[localIdentifier];
57
+ const masterMeasureId = measureMasterIdentifier(measure);
58
+ const masterMeasureInheritFormat = isMeasureGroupDescriptor(measureGroup) &&
59
+ measureGroup.measureGroupHeader.items.find((item) => this.originalLocalId(item.measureHeaderItem.localIdentifier) === masterMeasureId)?.measureHeaderItem.format;
60
+ /**
61
+ * Measure format is taken by priority from:
62
+ * 1) the chosen format of the measure (undefined for inherited format and derived measures)
63
+ * 2) the chosen format of the master measure (undefined for inherited format and master)
64
+ * 3) the inherited format of master measure (undefined for master)
65
+ * 4) the inherited format for master measure or default "#,#.##" for derived
66
+ */
67
+ const format = measureFormat(measure) ||
68
+ (masterMeasureId && measureFormat(this.originalMeasures[masterMeasureId])) ||
69
+ masterMeasureInheritFormat ||
70
+ value.measureHeaderItem.format;
71
+ const name = this.originalMeasureTitle(measure, value.measureHeaderItem.name);
72
+ return {
73
+ measureHeaderItem: {
74
+ ...value.measureHeaderItem,
75
+ localIdentifier,
76
+ format,
77
+ name,
78
+ },
79
+ };
80
+ }
81
+ // let the default logic handle this
82
+ return undefined;
83
+ });
84
+ };
85
+ /**
86
+ * Derived measures or arithmetic measures have the 'name' in result header defaulted to measure
87
+ * localId. This method deals with it. It creates a copy of headers with the measure headers denormalized,
88
+ * values replaced with the contents of alias or title (whichever comes first).
89
+ *
90
+ * @param headerItems - headers to denormalize, copy will be done
91
+ * @returns new headers
92
+ */
93
+ denormalizeHeaders = (headerItems) => {
94
+ return cloneDeepWith(headerItems, (value) => {
95
+ if (isResultMeasureHeader(value)) {
96
+ return {
97
+ measureHeaderItem: {
98
+ name: this.fillOriginalMeasureTitle(value.measureHeaderItem.name),
99
+ order: value.measureHeaderItem.order,
100
+ },
101
+ };
102
+ }
103
+ // let the default logic handle this
104
+ return undefined;
105
+ });
106
+ };
107
+ originalLocalId = (normalized) => {
108
+ const value = this.state.n2oMap[normalized];
109
+ invariant(value, `mapping from normalized to real localId does not exist. Normalized: ${normalized}`);
110
+ return value;
111
+ };
112
+ fillOriginalMeasureTitle = (name) => {
113
+ const originalLocalId = this.state.n2oMap[name];
114
+ if (!originalLocalId) {
115
+ return name;
116
+ }
117
+ const measure = this.originalMeasures[originalLocalId];
118
+ if (!measure) {
119
+ return name;
120
+ }
121
+ return this.originalMeasureTitle(measure, name);
122
+ };
123
+ originalMeasureTitle = (originalDef, nameFromBackend) => {
124
+ return measureAlias(originalDef) || measureTitle(originalDef) || nameFromBackend;
125
+ };
114
126
  }
115
127
  /**
116
128
  * The normalization of execution definition means stripping away all the detail that is unnecessary for the
@@ -132,229 +144,237 @@ export class Denormalizer {
132
144
  * @internal
133
145
  */
134
146
  export class Normalizer {
147
+ original;
148
+ options;
135
149
  static normalize(def, options) {
136
150
  const n = new Normalizer(def, options);
137
151
  return n.normalize();
138
152
  }
153
+ normalized;
154
+ /**
155
+ * original to normalized local id map
156
+ */
157
+ o2nMap = {};
158
+ /**
159
+ * normalized to original local id map
160
+ */
161
+ n2oMap = {};
162
+ originalMeasures = new Map();
163
+ alreadyNormalized = [];
139
164
  constructor(original, options = {}) {
140
165
  this.original = original;
141
166
  this.options = options;
142
- /**
143
- * original to normalized local id map
144
- */
145
- this.o2nMap = {};
146
- /**
147
- * normalized to original local id map
148
- */
149
- this.n2oMap = {};
150
- this.originalMeasures = new Map();
151
- this.alreadyNormalized = [];
152
- /**
153
- * Creates a new mapping between original local ID and the proposed normalized local ID. This method
154
- * ensures uniqueness of the normalized local ID. If the proposed normalized local ID is taken, it will
155
- * append a suffix to make a unique local ID.
156
- *
157
- * This can happen if the original execution definition contains same measure / attribute multiple times,
158
- * each time using different local ID.
159
- */
160
- this.createUniqueMapping = (original, normalized) => {
161
- invariant(!this.o2nMap[original], `duplicate localId used in execution definition: ${original}`);
162
- let uniqueNormalized = normalized;
163
- let attempt = 1;
164
- while (this.n2oMap[uniqueNormalized]) {
165
- uniqueNormalized = `${normalized}_${attempt}`;
166
- attempt += 1;
167
+ const copy = cloneDeep(this.original);
168
+ // throw away noop filters
169
+ const filters = copy.filters.filter((f) => {
170
+ if (isAttributeFilter(f)) {
171
+ return !filterIsEmpty(f) || isPositiveAttributeFilter(f);
167
172
  }
168
- this.o2nMap[original] = uniqueNormalized;
169
- this.n2oMap[uniqueNormalized] = original;
170
- return uniqueNormalized;
171
- };
172
- this.normalizedLocalId = (original) => {
173
- const value = this.o2nMap[original];
174
- invariant(value, `dangling localId reference. Target: ${original}`);
175
- return value;
176
- };
177
- this.maybeNormalizedLocalId = (original) => {
178
- return this.o2nMap[original];
179
- };
180
- this.normalizeAttributes = () => {
181
- this.normalized.attributes.forEach((attr) => {
182
- if (!this.options.keepRemovableProperties) {
183
- delete attr.attribute.alias;
184
- }
185
- const originalLocalId = attributeLocalId(attr);
186
- const normalizedLocalId = attributeLocalId(modifyAttribute(attr, (m) => m.noAlias().defaultLocalId()));
187
- attr.attribute.localIdentifier = this.createUniqueMapping(originalLocalId, normalizedLocalId);
188
- });
189
- };
190
- this.normalizeTotal = (total) => {
191
- var _a, _b;
192
- // safely normalize total, when not found in the converted
193
- // attributes/measures, keep the original identifier
194
- return Object.assign(Object.assign({}, total), { attributeIdentifier: (_a = this.maybeNormalizedLocalId(total.attributeIdentifier)) !== null && _a !== void 0 ? _a : total.attributeIdentifier, measureIdentifier: (_b = this.maybeNormalizedLocalId(total.measureIdentifier)) !== null && _b !== void 0 ? _b : total.measureIdentifier });
195
- };
196
- this.normalizeTotals = () => {
197
- this.normalized.buckets.forEach((bucket) => {
198
- if (bucket.totals) {
199
- bucket.totals = bucket.totals.map(this.normalizeTotal);
200
- }
201
- });
202
- };
203
- /**
204
- * Simple measure normalization will toss away noop filters. There is nothing else to do.
205
- */
206
- this.normalizeSimple = (def) => {
207
- var _a;
208
- if (!def.measureDefinition.filters) {
209
- return;
173
+ else if (isMeasureValueFilter(f)) {
174
+ return measureValueFilterCondition(f) !== undefined;
210
175
  }
211
- def.measureDefinition.filters = (_a = def.measureDefinition.filters) === null || _a === void 0 ? void 0 : _a.filter((filter) => {
212
- return !isNegativeAttributeFilter(filter) || !filterIsEmpty(filter);
213
- });
214
- };
215
- this.normalizePoP = (def, path) => {
216
- const masterMeasure = def.popMeasureDefinition.measureIdentifier;
217
- const normalizedId = this.maybeNormalizedLocalId(masterMeasure) || this.normalizeMeasureByLocalId(masterMeasure, path);
218
- def.popMeasureDefinition.measureIdentifier = normalizedId;
219
- };
220
- this.normalizePreviousPeriod = (def, path) => {
221
- const masterMeasure = def.previousPeriodMeasure.measureIdentifier;
222
- const normalizedId = this.maybeNormalizedLocalId(masterMeasure) || this.normalizeMeasureByLocalId(masterMeasure, path);
223
- def.previousPeriodMeasure.measureIdentifier = normalizedId;
224
- };
225
- this.normalizeArithmetic = (def, path) => {
226
- const normalizedIds = def.arithmeticMeasure.measureIdentifiers.map((operand) => {
227
- return this.maybeNormalizedLocalId(operand) || this.normalizeMeasureByLocalId(operand, path);
228
- });
229
- def.arithmeticMeasure.measureIdentifiers = normalizedIds;
176
+ return true;
177
+ });
178
+ this.normalized = {
179
+ ...copy,
180
+ filters,
230
181
  };
231
- this.normalizeMeasure = (measure, path = new Set()) => {
232
- const localId = measureLocalId(measure);
233
- const definition = measure.measure.definition;
234
- /*
235
- * don't do anything if the measure is already normalized;
236
- *
237
- * this can happen as master / derived / arithmetic measures can be mixed in the measures array
238
- * in any order & the recursive algorithm goes after the leaves first and then gets to the measure
239
- * once again as it walks the array
240
- */
241
- if (this.alreadyNormalized.find((m) => m === measure)) {
242
- // if already normalized, the measure has been mutated and the localId is the normalized one
243
- return localId;
244
- }
245
- /*
246
- * circular dependency detection and bail-out.
247
- */
248
- invariant(!path.has(localId), `circular dependency on measure with localId ${localId}`);
249
- path.add(localId);
250
- if (isPoPMeasureDefinition(definition)) {
251
- this.normalizePoP(definition, path);
182
+ copy.measures.forEach((measure) => this.originalMeasures.set(measureLocalId(measure), measure));
183
+ }
184
+ /**
185
+ * Creates a new mapping between original local ID and the proposed normalized local ID. This method
186
+ * ensures uniqueness of the normalized local ID. If the proposed normalized local ID is taken, it will
187
+ * append a suffix to make a unique local ID.
188
+ *
189
+ * This can happen if the original execution definition contains same measure / attribute multiple times,
190
+ * each time using different local ID.
191
+ */
192
+ createUniqueMapping = (original, normalized) => {
193
+ invariant(!this.o2nMap[original], `duplicate localId used in execution definition: ${original}`);
194
+ let uniqueNormalized = normalized;
195
+ let attempt = 1;
196
+ while (this.n2oMap[uniqueNormalized]) {
197
+ uniqueNormalized = `${normalized}_${attempt}`;
198
+ attempt += 1;
199
+ }
200
+ this.o2nMap[original] = uniqueNormalized;
201
+ this.n2oMap[uniqueNormalized] = original;
202
+ return uniqueNormalized;
203
+ };
204
+ normalizedLocalId = (original) => {
205
+ const value = this.o2nMap[original];
206
+ invariant(value, `dangling localId reference. Target: ${original}`);
207
+ return value;
208
+ };
209
+ maybeNormalizedLocalId = (original) => {
210
+ return this.o2nMap[original];
211
+ };
212
+ normalizeAttributes = () => {
213
+ this.normalized.attributes.forEach((attr) => {
214
+ if (!this.options.keepRemovableProperties) {
215
+ delete attr.attribute.alias;
252
216
  }
253
- else if (isPreviousPeriodMeasureDefinition(definition)) {
254
- this.normalizePreviousPeriod(definition, path);
217
+ const originalLocalId = attributeLocalId(attr);
218
+ const normalizedLocalId = attributeLocalId(modifyAttribute(attr, (m) => m.noAlias().defaultLocalId()));
219
+ attr.attribute.localIdentifier = this.createUniqueMapping(originalLocalId, normalizedLocalId);
220
+ });
221
+ };
222
+ normalizeTotal = (total) => {
223
+ // safely normalize total, when not found in the converted
224
+ // attributes/measures, keep the original identifier
225
+ return {
226
+ ...total,
227
+ attributeIdentifier: this.maybeNormalizedLocalId(total.attributeIdentifier) ?? total.attributeIdentifier,
228
+ measureIdentifier: this.maybeNormalizedLocalId(total.measureIdentifier) ?? total.measureIdentifier,
229
+ };
230
+ };
231
+ normalizeTotals = () => {
232
+ this.normalized.buckets.forEach((bucket) => {
233
+ if (bucket.totals) {
234
+ bucket.totals = bucket.totals.map(this.normalizeTotal);
255
235
  }
256
- else if (isArithmeticMeasureDefinition(definition)) {
257
- this.normalizeArithmetic(definition, path);
236
+ });
237
+ };
238
+ /**
239
+ * Simple measure normalization will toss away noop filters. There is nothing else to do.
240
+ */
241
+ normalizeSimple = (def) => {
242
+ if (!def.measureDefinition.filters) {
243
+ return;
244
+ }
245
+ def.measureDefinition.filters = def.measureDefinition.filters?.filter((filter) => {
246
+ return !isNegativeAttributeFilter(filter) || !filterIsEmpty(filter);
247
+ });
248
+ };
249
+ normalizePoP = (def, path) => {
250
+ const masterMeasure = def.popMeasureDefinition.measureIdentifier;
251
+ const normalizedId = this.maybeNormalizedLocalId(masterMeasure) || this.normalizeMeasureByLocalId(masterMeasure, path);
252
+ def.popMeasureDefinition.measureIdentifier = normalizedId;
253
+ };
254
+ normalizePreviousPeriod = (def, path) => {
255
+ const masterMeasure = def.previousPeriodMeasure.measureIdentifier;
256
+ const normalizedId = this.maybeNormalizedLocalId(masterMeasure) || this.normalizeMeasureByLocalId(masterMeasure, path);
257
+ def.previousPeriodMeasure.measureIdentifier = normalizedId;
258
+ };
259
+ normalizeArithmetic = (def, path) => {
260
+ const normalizedIds = def.arithmeticMeasure.measureIdentifiers.map((operand) => {
261
+ return this.maybeNormalizedLocalId(operand) || this.normalizeMeasureByLocalId(operand, path);
262
+ });
263
+ def.arithmeticMeasure.measureIdentifiers = normalizedIds;
264
+ };
265
+ normalizeMeasure = (measure, path = new Set()) => {
266
+ const localId = measureLocalId(measure);
267
+ const definition = measure.measure.definition;
268
+ /*
269
+ * don't do anything if the measure is already normalized;
270
+ *
271
+ * this can happen as master / derived / arithmetic measures can be mixed in the measures array
272
+ * in any order & the recursive algorithm goes after the leaves first and then gets to the measure
273
+ * once again as it walks the array
274
+ */
275
+ if (this.alreadyNormalized.find((m) => m === measure)) {
276
+ // if already normalized, the measure has been mutated and the localId is the normalized one
277
+ return localId;
278
+ }
279
+ /*
280
+ * circular dependency detection and bail-out.
281
+ */
282
+ invariant(!path.has(localId), `circular dependency on measure with localId ${localId}`);
283
+ path.add(localId);
284
+ if (isPoPMeasureDefinition(definition)) {
285
+ this.normalizePoP(definition, path);
286
+ }
287
+ else if (isPreviousPeriodMeasureDefinition(definition)) {
288
+ this.normalizePreviousPeriod(definition, path);
289
+ }
290
+ else if (isArithmeticMeasureDefinition(definition)) {
291
+ this.normalizeArithmetic(definition, path);
292
+ }
293
+ else if (isMeasureDefinition(definition)) {
294
+ this.normalizeSimple(definition);
295
+ }
296
+ if (!this.options.keepRemovableProperties) {
297
+ delete measure.measure.alias;
298
+ delete measure.measure.title;
299
+ delete measure.measure.format;
300
+ }
301
+ const newLocalId = measureLocalId(modifyMeasure(measure, (m) => m.noAlias().noTitle().defaultFormat().defaultLocalId()));
302
+ const newUniqueLocalId = this.createUniqueMapping(localId, newLocalId);
303
+ measure.measure.localIdentifier = newUniqueLocalId;
304
+ this.alreadyNormalized.push(measure);
305
+ return newUniqueLocalId;
306
+ };
307
+ normalizeMeasureByLocalId = (localId, path) => {
308
+ const measure = this.originalMeasures.get(localId);
309
+ invariant(measure, `measure with localId ${localId} could not be found`);
310
+ return this.normalizeMeasure(measure, path);
311
+ };
312
+ normalizeMeasures = () => {
313
+ this.normalized.measures.forEach((measure) => this.normalizeMeasure(measure));
314
+ };
315
+ normalizeFilters = () => {
316
+ this.normalized.filters.forEach((filter) => {
317
+ if (isMeasureValueFilter(filter)) {
318
+ const ref = filter.measureValueFilter.measure;
319
+ if (isLocalIdRef(ref)) {
320
+ ref.localIdentifier = this.normalizedLocalId(ref.localIdentifier);
321
+ }
258
322
  }
259
- else if (isMeasureDefinition(definition)) {
260
- this.normalizeSimple(definition);
323
+ if (isRankingFilter(filter)) {
324
+ const { measure, attributes = [] } = filter.rankingFilter;
325
+ [...attributes, measure].filter(isLocalIdRef).forEach((ref) => {
326
+ ref.localIdentifier = this.normalizedLocalId(ref.localIdentifier);
327
+ });
261
328
  }
262
- if (!this.options.keepRemovableProperties) {
263
- delete measure.measure.alias;
264
- delete measure.measure.title;
265
- delete measure.measure.format;
329
+ });
330
+ };
331
+ normalizeSorts = () => {
332
+ this.normalized.sortBy.forEach((sort) => {
333
+ if (isAttributeSort(sort)) {
334
+ sort.attributeSortItem.attributeIdentifier = this.normalizedLocalId(sort.attributeSortItem.attributeIdentifier);
266
335
  }
267
- const newLocalId = measureLocalId(modifyMeasure(measure, (m) => m.noAlias().noTitle().defaultFormat().defaultLocalId()));
268
- const newUniqueLocalId = this.createUniqueMapping(localId, newLocalId);
269
- measure.measure.localIdentifier = newUniqueLocalId;
270
- this.alreadyNormalized.push(measure);
271
- return newUniqueLocalId;
272
- };
273
- this.normalizeMeasureByLocalId = (localId, path) => {
274
- const measure = this.originalMeasures.get(localId);
275
- invariant(measure, `measure with localId ${localId} could not be found`);
276
- return this.normalizeMeasure(measure, path);
277
- };
278
- this.normalizeMeasures = () => {
279
- this.normalized.measures.forEach((measure) => this.normalizeMeasure(measure));
280
- };
281
- this.normalizeFilters = () => {
282
- this.normalized.filters.forEach((filter) => {
283
- if (isMeasureValueFilter(filter)) {
284
- const ref = filter.measureValueFilter.measure;
285
- if (isLocalIdRef(ref)) {
286
- ref.localIdentifier = this.normalizedLocalId(ref.localIdentifier);
336
+ else if (isMeasureSort(sort)) {
337
+ sort.measureSortItem.locators.forEach((locator) => {
338
+ if (isAttributeLocator(locator)) {
339
+ locator.attributeLocatorItem.attributeIdentifier = this.normalizedLocalId(locator.attributeLocatorItem.attributeIdentifier);
287
340
  }
288
- }
289
- if (isRankingFilter(filter)) {
290
- const { measure, attributes = [] } = filter.rankingFilter;
291
- [...attributes, measure].filter(isLocalIdRef).forEach((ref) => {
292
- ref.localIdentifier = this.normalizedLocalId(ref.localIdentifier);
293
- });
294
- }
295
- });
296
- };
297
- this.normalizeSorts = () => {
298
- this.normalized.sortBy.forEach((sort) => {
299
- if (isAttributeSort(sort)) {
300
- sort.attributeSortItem.attributeIdentifier = this.normalizedLocalId(sort.attributeSortItem.attributeIdentifier);
301
- }
302
- else if (isMeasureSort(sort)) {
303
- sort.measureSortItem.locators.forEach((locator) => {
304
- if (isAttributeLocator(locator)) {
305
- locator.attributeLocatorItem.attributeIdentifier = this.normalizedLocalId(locator.attributeLocatorItem.attributeIdentifier);
306
- }
307
- else if (isMeasureLocator(locator)) {
308
- locator.measureLocatorItem.measureIdentifier = this.normalizedLocalId(locator.measureLocatorItem.measureIdentifier);
309
- }
310
- });
311
- }
312
- });
313
- };
314
- this.normalizeDimensions = () => {
315
- this.normalized.dimensions.forEach((dim) => {
316
- dim.itemIdentifiers = dim.itemIdentifiers.map((item) => {
317
- if (item === MeasureGroupIdentifier) {
318
- return item;
319
- }
320
- else {
321
- return this.normalizedLocalId(item);
341
+ else if (isMeasureLocator(locator)) {
342
+ locator.measureLocatorItem.measureIdentifier = this.normalizedLocalId(locator.measureLocatorItem.measureIdentifier);
322
343
  }
323
344
  });
324
- if (dim.totals) {
325
- dim.totals.forEach((total) => {
326
- total.attributeIdentifier = this.normalizedLocalId(total.attributeIdentifier);
327
- total.measureIdentifier = this.normalizedLocalId(total.measureIdentifier);
328
- });
345
+ }
346
+ });
347
+ };
348
+ normalizeDimensions = () => {
349
+ this.normalized.dimensions.forEach((dim) => {
350
+ dim.itemIdentifiers = dim.itemIdentifiers.map((item) => {
351
+ if (item === MeasureGroupIdentifier) {
352
+ return item;
353
+ }
354
+ else {
355
+ return this.normalizedLocalId(item);
329
356
  }
330
357
  });
331
- };
332
- this.normalize = () => {
333
- this.normalizeAttributes();
334
- this.normalizeMeasures();
335
- this.normalizeFilters();
336
- this.normalizeSorts();
337
- this.normalizeDimensions();
338
- this.normalizeTotals();
339
- return {
340
- normalized: this.normalized,
341
- original: this.original,
342
- n2oMap: this.n2oMap,
343
- };
344
- };
345
- const copy = cloneDeep(this.original);
346
- // throw away noop filters
347
- const filters = copy.filters.filter((f) => {
348
- if (isAttributeFilter(f)) {
349
- return !filterIsEmpty(f) || isPositiveAttributeFilter(f);
350
- }
351
- else if (isMeasureValueFilter(f)) {
352
- return measureValueFilterCondition(f) !== undefined;
358
+ if (dim.totals) {
359
+ dim.totals.forEach((total) => {
360
+ total.attributeIdentifier = this.normalizedLocalId(total.attributeIdentifier);
361
+ total.measureIdentifier = this.normalizedLocalId(total.measureIdentifier);
362
+ });
353
363
  }
354
- return true;
355
364
  });
356
- this.normalized = Object.assign(Object.assign({}, copy), { filters });
357
- copy.measures.forEach((measure) => this.originalMeasures.set(measureLocalId(measure), measure));
358
- }
365
+ };
366
+ normalize = () => {
367
+ this.normalizeAttributes();
368
+ this.normalizeMeasures();
369
+ this.normalizeFilters();
370
+ this.normalizeSorts();
371
+ this.normalizeDimensions();
372
+ this.normalizeTotals();
373
+ return {
374
+ normalized: this.normalized,
375
+ original: this.original,
376
+ n2oMap: this.n2oMap,
377
+ };
378
+ };
359
379
  }
360
380
  //# sourceMappingURL=normalizer.js.map