@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.
- package/esm/cachingBackend/index.js +269 -260
- package/esm/cachingBackend/index.js.map +1 -1
- package/esm/customBackend/execution.js +107 -94
- package/esm/customBackend/execution.js.map +1 -1
- package/esm/customBackend/index.js +102 -95
- package/esm/customBackend/index.js.map +1 -1
- package/esm/customBackend/workspace.js +3 -0
- package/esm/customBackend/workspace.js.map +1 -1
- package/esm/decoratedBackend/analyticalWorkspace.js +3 -0
- package/esm/decoratedBackend/analyticalWorkspace.js.map +1 -1
- package/esm/decoratedBackend/attributes.js +1 -0
- package/esm/decoratedBackend/attributes.js.map +1 -1
- package/esm/decoratedBackend/catalog.js +5 -0
- package/esm/decoratedBackend/catalog.js.map +1 -1
- package/esm/decoratedBackend/dashboards.d.ts +2 -1
- package/esm/decoratedBackend/dashboards.d.ts.map +1 -1
- package/esm/decoratedBackend/dashboards.js +5 -0
- package/esm/decoratedBackend/dashboards.js.map +1 -1
- package/esm/decoratedBackend/elements.js +32 -6
- package/esm/decoratedBackend/elements.js.map +1 -1
- package/esm/decoratedBackend/execution.js +32 -13
- package/esm/decoratedBackend/execution.js.map +1 -1
- package/esm/decoratedBackend/index.js +4 -0
- package/esm/decoratedBackend/index.js.map +1 -1
- package/esm/decoratedBackend/organization.js +3 -0
- package/esm/decoratedBackend/organization.js.map +1 -1
- package/esm/decoratedBackend/organizations.js +2 -0
- package/esm/decoratedBackend/organizations.js.map +1 -1
- package/esm/decoratedBackend/securitySettings.js +2 -0
- package/esm/decoratedBackend/securitySettings.js.map +1 -1
- package/esm/decoratedBackend/workspaceSettings.js +1 -0
- package/esm/decoratedBackend/workspaceSettings.js.map +1 -1
- package/esm/dummyBackend/index.js +34 -10
- package/esm/dummyBackend/index.js.map +1 -1
- package/esm/eventingBackend/index.js +47 -44
- package/esm/eventingBackend/index.js.map +1 -1
- package/esm/ldmFactories/builder.js +2 -0
- package/esm/ldmFactories/builder.js.map +1 -1
- package/esm/ldmFactories/catalog/attributeFactory.js +1 -1
- package/esm/ldmFactories/catalog/attributeFactory.js.map +1 -1
- package/esm/ldmFactories/catalog/measureFactory.js +1 -1
- package/esm/ldmFactories/catalog/measureFactory.js.map +1 -1
- package/esm/ldmFactories/dashboard/insightWidgetFactory.js +5 -3
- package/esm/ldmFactories/dashboard/insightWidgetFactory.js.map +1 -1
- package/esm/ldmFactories/dashboard/kpiWidgetFactory.js +17 -9
- package/esm/ldmFactories/dashboard/kpiWidgetFactory.js.map +1 -1
- package/esm/ldmFactories/dashboard/widgetFactory.js +18 -18
- package/esm/ldmFactories/dashboard/widgetFactory.js.map +1 -1
- package/esm/normalizingBackend/index.js +79 -58
- package/esm/normalizingBackend/index.js.map +1 -1
- package/esm/normalizingBackend/normalizer.js +317 -297
- package/esm/normalizingBackend/normalizer.js.map +1 -1
- package/esm/sdk-backend-base.d.ts +2 -0
- package/esm/toolkit/auth.js +33 -32
- package/esm/toolkit/auth.js.map +1 -1
- package/esm/toolkit/execution.js +2 -0
- package/esm/toolkit/execution.js.map +1 -1
- package/esm/toolkit/paging.js +48 -38
- package/esm/toolkit/paging.js.map +1 -1
- package/esm/toolkit/pluginUrlValidation.js +1 -2
- package/esm/toolkit/pluginUrlValidation.js.map +1 -1
- package/esm/workspaceSettingsBackend/index.js +11 -2
- package/esm/workspaceSettingsBackend/index.js.map +1 -1
- 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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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
|
-
|
|
257
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|