@configura/web-api 2.0.0-alpha.2 → 2.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CatalogueAPI.d.ts +73 -43
- package/dist/CatalogueAPI.js +37 -2
- package/dist/CfgProduct.d.ts +73 -11
- package/dist/CfgProduct.js +130 -29
- package/dist/CfgReferencePathHelper.d.ts +16 -3
- package/dist/CfgReferencePathHelper.js +14 -1
- package/dist/ConfigurationConverter.d.ts +13 -4
- package/dist/ConfigurationConverter.js +106 -12
- package/dist/io/CfgHistoryManager.d.ts +37 -4
- package/dist/io/CfgHistoryManager.js +76 -8
- package/dist/io/CfgHistoryToProdConfConnector.d.ts +7 -10
- package/dist/io/CfgHistoryToProdConfConnector.js +29 -38
- package/dist/io/CfgIOManager.d.ts +5 -0
- package/dist/io/CfgIOManager.js +20 -1
- package/dist/io/CfgIOProdConfConnector.d.ts +21 -17
- package/dist/io/CfgIOProdConfConnector.js +54 -37
- package/dist/io/CfgIOWarningSupplier.d.ts +4 -0
- package/dist/io/CfgIOWarningSupplier.js +1 -0
- package/dist/io/CfgObservableStateManager.d.ts +4 -0
- package/dist/io/CfgObservableStateManager.js +4 -0
- package/dist/io/CfgObservableStateToProdConfConnector.d.ts +4 -4
- package/dist/io/CfgObservableStateToProdConfConnector.js +4 -4
- package/dist/io/CfgWindowMessageManager.d.ts +2 -2
- package/dist/io/CfgWindowMessageManager.js +9 -2
- package/dist/io/CfgWindowMessageToProdConfConnector.d.ts +4 -4
- package/dist/io/CfgWindowMessageToProdConfConnector.js +4 -4
- package/dist/productConfiguration/CfgFeature.d.ts +8 -6
- package/dist/productConfiguration/CfgFeature.js +19 -6
- package/dist/productConfiguration/CfgOption.d.ts +5 -5
- package/dist/productConfiguration/CfgOption.js +11 -5
- package/dist/productConfiguration/CfgProductConfiguration.d.ts +27 -15
- package/dist/productConfiguration/CfgProductConfiguration.js +54 -21
- package/dist/productConfiguration/filters.d.ts +2 -2
- package/dist/productConfiguration/productParamsGenerator.d.ts +3 -3
- package/dist/productConfiguration/utilitiesProductConfiguration.d.ts +1 -1
- package/dist/productConfiguration/utilitiesProductConfiguration.js +11 -4
- package/dist/productLoader.d.ts +3 -3
- package/dist/syncGroups/SyncGroupsHandler.d.ts +4 -1
- package/dist/syncGroups/SyncGroupsHandler.js +6 -2
- package/dist/syncGroups/SyncGroupsTransaction.js +34 -21
- package/dist/tasks/TaskHandler.d.ts +2 -2
- package/dist/tasks/TaskHandler.js +2 -1
- package/dist/tests/testData/dummyProductForTest.d.ts +2 -2
- package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +1 -0
- package/dist/tests/testData/testDataCachedGetProduct.js +1 -0
- package/dist/tests/testData/testDataCachedPostValidate.js +1 -0
- package/dist/tests/testData/testDataProductAggregatedPrice.js +1 -0
- package/dist/tests/testData/testDataUpcharge.js +1 -0
- package/dist/utilitiesCatalogueData.d.ts +14 -9
- package/dist/utilitiesCatalogueData.js +7 -0
- package/dist/utilitiesCataloguePermission.d.ts +4 -4
- package/package.json +3 -3
package/dist/CfgProduct.js
CHANGED
|
@@ -9,13 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { AggregatedLoadingObservable, assert, assertDefined, augmentErrorMessage, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
|
|
11
11
|
import { CfgMeasureDefinition } from "./CfgMeasure.js";
|
|
12
|
-
import {
|
|
12
|
+
import { convertDtoProductConfToV1 } from "./ConfigurationConverter.js";
|
|
13
13
|
import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
|
|
14
14
|
import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
|
|
15
15
|
import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
|
|
16
16
|
import { wrapWithCache } from "./productLoader.js";
|
|
17
17
|
import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
|
|
18
|
-
import { comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
18
|
+
import { compareCfgProductData, comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
19
19
|
function completeSettings(incompleteSettings) {
|
|
20
20
|
var _a;
|
|
21
21
|
return {
|
|
@@ -52,7 +52,7 @@ function isDescriptionMatch(l, r) {
|
|
|
52
52
|
* the class that should be used and interacted with.
|
|
53
53
|
*/
|
|
54
54
|
export class _CfgProductInternal {
|
|
55
|
-
constructor(initSuccess, initFail, _productLoaderRaw, prodParams, settings, optional, selected, rootFeatureRefs,
|
|
55
|
+
constructor(initSuccess, initFail, _productLoaderRaw, prodParams, settings, optional, selected, rootFeatureRefs, rawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, parent, root, _additionalProductRef, _syncGroupHandler) {
|
|
56
56
|
var _a;
|
|
57
57
|
this._productLoaderRaw = _productLoaderRaw;
|
|
58
58
|
this.prodParams = prodParams;
|
|
@@ -67,14 +67,24 @@ export class _CfgProductInternal {
|
|
|
67
67
|
this._destroyed = false;
|
|
68
68
|
this.additionalProducts = [];
|
|
69
69
|
this.changeObservable = new Observable();
|
|
70
|
+
/** Mark this and its descendants as destroyed and remove all listeners */
|
|
70
71
|
this.destroy = () => {
|
|
72
|
+
var _a;
|
|
71
73
|
this._destroyed = true;
|
|
72
74
|
this.changeObservable.stopAllListen();
|
|
73
75
|
this.configuration.stopAllListenForChange();
|
|
74
|
-
for (const additionalProduct of this.additionalProducts
|
|
76
|
+
for (const additionalProduct of (_a = this.additionalProducts) !== null && _a !== void 0 ? _a : []) {
|
|
75
77
|
additionalProduct.destroy();
|
|
76
78
|
}
|
|
77
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Reset will reset the product to its initial state
|
|
82
|
+
*/
|
|
83
|
+
this.reset = () => __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
if (this._initialClone !== undefined) {
|
|
85
|
+
yield this.copyFrom(this._initialClone, true);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
78
88
|
this._notifyAllOfChange = (bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
|
|
79
89
|
if (bubbleMode === CfgProductBubbleMode.Stop) {
|
|
80
90
|
return;
|
|
@@ -164,12 +174,14 @@ export class _CfgProductInternal {
|
|
|
164
174
|
}
|
|
165
175
|
return conf;
|
|
166
176
|
};
|
|
167
|
-
this.setDtoConf = (s, doValidate, productLoaderForGroupedLoad) => this
|
|
177
|
+
this.setDtoConf = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
return yield this.setApiSelection(convertDtoProductConfToV1(s), doValidate, productLoaderForGroupedLoad);
|
|
179
|
+
});
|
|
168
180
|
this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
|
|
169
|
-
return this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
|
|
181
|
+
return yield this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
|
|
170
182
|
});
|
|
171
183
|
this.copyFrom = (source, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
|
|
172
|
-
return yield this._setApiSelectionWithOtherProduct(
|
|
184
|
+
return yield this._setApiSelectionWithOtherProduct(convertDtoProductConfToV1(source.getDtoConf(false, false)), doValidate, productLoaderForGroupedLoad, source);
|
|
173
185
|
});
|
|
174
186
|
this._setApiSelectionWithOtherProduct = (s, doValidate, productLoaderForGroupedLoad, sourceProduct) => __awaiter(this, void 0, void 0, function* () {
|
|
175
187
|
// Wrap with cache will make getProduct for this function call use the same server call
|
|
@@ -179,9 +191,16 @@ export class _CfgProductInternal {
|
|
|
179
191
|
productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
|
|
180
192
|
let change = false;
|
|
181
193
|
if (sourceProduct !== undefined) {
|
|
182
|
-
this._rawProductData
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
if (!compareCfgProductData(this._rawProductData, sourceProduct.rawProductData)) {
|
|
195
|
+
this._rawProductData = sourceProduct.rawProductData;
|
|
196
|
+
change = true;
|
|
197
|
+
}
|
|
198
|
+
if (this.configuration._internal.addRawFeatures(sourceProduct.configuration.rawFeatures, false)) {
|
|
199
|
+
change = true;
|
|
200
|
+
}
|
|
201
|
+
if (this.configuration._internal.populateFeatures(sourceProduct.configuration.rootFeatureRefs)) {
|
|
202
|
+
change = true;
|
|
203
|
+
}
|
|
185
204
|
}
|
|
186
205
|
const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
|
|
187
206
|
if (configurationChange) {
|
|
@@ -203,8 +222,8 @@ export class _CfgProductInternal {
|
|
|
203
222
|
const sourceProductAdditionalProducts = sourceProduct === null || sourceProduct === void 0 ? void 0 : sourceProduct.additionalProducts;
|
|
204
223
|
assert(!sourceProductAdditionalProducts ||
|
|
205
224
|
additionalProductsCount === sourceProductAdditionalProducts.length, `Passed sourceProduct does not have the same number of additional products as this.`);
|
|
206
|
-
if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionAdditionalProduct,
|
|
207
|
-
var
|
|
225
|
+
if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionAdditionalProduct) => __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
var _b;
|
|
208
227
|
const refKey = apiSelectionAdditionalProduct.refKey;
|
|
209
228
|
assertDefined(refKey, "Additional product api configurations must have refKey.");
|
|
210
229
|
const i = additionalProducts.findIndex((a) => refKey === a.refKey);
|
|
@@ -212,12 +231,12 @@ export class _CfgProductInternal {
|
|
|
212
231
|
let sourceProductAdditionalProduct = undefined;
|
|
213
232
|
if (sourceProductAdditionalProducts !== undefined) {
|
|
214
233
|
sourceProductAdditionalProduct =
|
|
215
|
-
(
|
|
234
|
+
(_b = sourceProductAdditionalProducts.find((a) => refKey === a.refKey)) === null || _b === void 0 ? void 0 : _b._internal;
|
|
216
235
|
assertDefined(sourceProductAdditionalProduct, "Additional product not found in sourceProduct");
|
|
217
236
|
}
|
|
218
237
|
const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
|
|
219
|
-
return additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
|
|
220
|
-
}))).some((b) => b)) {
|
|
238
|
+
return yield additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
|
|
239
|
+
})))).some((b) => b)) {
|
|
221
240
|
change = true;
|
|
222
241
|
}
|
|
223
242
|
if (doValidate && configurationChange) {
|
|
@@ -310,7 +329,10 @@ export class _CfgProductInternal {
|
|
|
310
329
|
const token = this.loadingObservable.startChildLoading();
|
|
311
330
|
this._revalidateInProgressToken = token;
|
|
312
331
|
try {
|
|
313
|
-
const response = yield productLoader.postValidate(correctDefaultsOnCatalogueParams(this.prodParams), {
|
|
332
|
+
const response = yield productLoader.postValidate(correctDefaultsOnCatalogueParams(this.prodParams), {
|
|
333
|
+
selOptions: configuration._internal.getApiSelection(),
|
|
334
|
+
knownFeatureCodes: configuration.rawFeatures.map((f) => f.code),
|
|
335
|
+
});
|
|
314
336
|
// The revalidateInProgressToken is used to know if some other revalidate
|
|
315
337
|
// call has happened after this call, thereby making this call obsolete.
|
|
316
338
|
// This is a bit crude in that it does not actually cancel previous validate
|
|
@@ -329,9 +351,10 @@ export class _CfgProductInternal {
|
|
|
329
351
|
if (this._destroyed) {
|
|
330
352
|
return false;
|
|
331
353
|
}
|
|
332
|
-
const { productData, rootFeatureRefs } = response;
|
|
354
|
+
const { productData, rootFeatureRefs, features } = response;
|
|
333
355
|
const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
|
|
334
356
|
this._rawProductData = productData;
|
|
357
|
+
configuration._internal.addRawFeatures(features, true);
|
|
335
358
|
if (rootFeatureRefs !== undefined) {
|
|
336
359
|
configuration._internal.populateFeatures(rootFeatureRefs);
|
|
337
360
|
}
|
|
@@ -348,7 +371,7 @@ export class _CfgProductInternal {
|
|
|
348
371
|
return true;
|
|
349
372
|
}
|
|
350
373
|
catch (e) {
|
|
351
|
-
throw augmentErrorMessage(e,
|
|
374
|
+
throw augmentErrorMessage(e, `Validate product configuration request (${this.prodParams.partNumber}) failure`);
|
|
352
375
|
}
|
|
353
376
|
finally {
|
|
354
377
|
this.loadingObservable.stopChildLoading(token);
|
|
@@ -421,19 +444,25 @@ export class _CfgProductInternal {
|
|
|
421
444
|
this.root = root !== null && root !== void 0 ? root : this;
|
|
422
445
|
this.key = makeProductKey(Object.assign(Object.assign({}, prodParams), { partNumber: (_a = _additionalProductRef === null || _additionalProductRef === void 0 ? void 0 : _additionalProductRef.refKey) !== null && _a !== void 0 ? _a : prodParams.partNumber }));
|
|
423
446
|
this._selected = optional ? selected : undefined;
|
|
424
|
-
this.isAdditionalProduct =
|
|
425
|
-
this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs,
|
|
447
|
+
this.isAdditionalProduct = _additionalProductRef !== undefined;
|
|
448
|
+
this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs, rawFeatures, apiSelection, this, this.root);
|
|
426
449
|
}
|
|
427
450
|
get selected() {
|
|
428
451
|
return this._selected !== false;
|
|
429
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Please note that cloning an additional product will make the clone believe is is
|
|
455
|
+
* an additional product, even if it has no parent and root.
|
|
456
|
+
* Providing the parent and root of what you clone as arguments is unwise as it will
|
|
457
|
+
* make changes you do on the clone be propagated up to the original non-clone root product.
|
|
458
|
+
*/
|
|
430
459
|
clone(parent, root) {
|
|
431
460
|
return __awaiter(this, void 0, void 0, function* () {
|
|
432
461
|
const product = yield new Promise((initSuccess, initFail) => {
|
|
433
462
|
var _a;
|
|
434
463
|
const p = new _CfgProductInternal(() => {
|
|
435
464
|
initSuccess(p);
|
|
436
|
-
}, initFail, this._productLoaderRaw, this.prodParams, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.
|
|
465
|
+
}, initFail, this._productLoaderRaw, this.prodParams, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.rawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration._internal.getApiSelection(), new AggregatedLoadingObservable(), parent, root, this._additionalProductRef, (_a = this._syncGroupHandler) === null || _a === void 0 ? void 0 : _a.clone());
|
|
437
466
|
});
|
|
438
467
|
for (const additionalProduct of this.additionalProducts) {
|
|
439
468
|
product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
|
|
@@ -441,12 +470,21 @@ export class _CfgProductInternal {
|
|
|
441
470
|
return product;
|
|
442
471
|
});
|
|
443
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Internal use. Used when this product is an additional product, and
|
|
475
|
+
* changing a parent product has made the settings for this product
|
|
476
|
+
* change.
|
|
477
|
+
*/
|
|
444
478
|
_updateAdditionalProdRef(p) {
|
|
445
479
|
this._additionalProductRef = p;
|
|
446
480
|
if (p.optional !== this.optional) {
|
|
447
481
|
this._selected = p.optional ? false : undefined;
|
|
448
482
|
}
|
|
449
483
|
}
|
|
484
|
+
get hasRootFeaturesChanged() {
|
|
485
|
+
return (this._configuration._internal.hasRootFeaturesChanged ||
|
|
486
|
+
this.additionalProducts.some((p) => p._internal.hasRootFeaturesChanged));
|
|
487
|
+
}
|
|
450
488
|
get description() {
|
|
451
489
|
var _a, _b;
|
|
452
490
|
return (_b = (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.refDescription) !== null && _b !== void 0 ? _b : this._rawProductData.description;
|
|
@@ -530,7 +568,19 @@ export class _CfgProductInternal {
|
|
|
530
568
|
if (this._selected === v) {
|
|
531
569
|
return false;
|
|
532
570
|
}
|
|
571
|
+
// Vitally important that this happens before the call to reset. An optional
|
|
572
|
+
// additional product is always deselected at start, so this way the reset won't cause
|
|
573
|
+
// infinite loops
|
|
533
574
|
this._selected = v;
|
|
575
|
+
if (!v) {
|
|
576
|
+
yield this.reset();
|
|
577
|
+
}
|
|
578
|
+
if (v) {
|
|
579
|
+
const syncGroupHandler = this.syncGroupHandler;
|
|
580
|
+
if (syncGroupHandler !== undefined) {
|
|
581
|
+
yield syncGroupHandler.init(this.root, wrapWithCache(this._productLoaderRaw));
|
|
582
|
+
}
|
|
583
|
+
}
|
|
534
584
|
yield this._notifyAllOfChange(bubbleMode, true);
|
|
535
585
|
return true;
|
|
536
586
|
});
|
|
@@ -594,22 +644,26 @@ prodParams, settings, optional, loadingObservable, parent, root, additionalProdu
|
|
|
594
644
|
: undefined;
|
|
595
645
|
try {
|
|
596
646
|
const productResponse = yield productLoaderForGroupedLoad.getProduct(correctDefaultsOnCatalogueParams(prodParams));
|
|
597
|
-
const { productData, rootFeatureRefs, features:
|
|
647
|
+
const { productData, rootFeatureRefs, features: rawFeatures, uuid, unit, } = productResponse;
|
|
598
648
|
const product = yield new Promise((initSuccess, initFail) => {
|
|
649
|
+
var _a;
|
|
599
650
|
const p = new _CfgProductInternal(() => {
|
|
600
651
|
// We absolutely do not want anyone to assign to this._configuration. So we want that field private.
|
|
601
652
|
// But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
|
|
602
653
|
// Feel free to find a nicer more readable solution :)
|
|
603
654
|
initSuccess(p);
|
|
604
|
-
}, initFail, productLoaderRaw, prodParams, settings, optional, !optional, rootFeatureRefs,
|
|
655
|
+
}, initFail, productLoaderRaw, prodParams, settings, optional, !optional, rootFeatureRefs, rawFeatures, uuid, unit, productData, (_a = productData.partsData.selOptions) !== null && _a !== void 0 ? _a : [], loadingObservable, parent, root, additionalProductRef, syncGroupHandler);
|
|
605
656
|
});
|
|
606
657
|
yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
|
|
607
|
-
|
|
608
|
-
|
|
658
|
+
product._initialClone = yield product.clone();
|
|
659
|
+
if (syncGroupHandler !== undefined) {
|
|
660
|
+
// As syncGroupHandler is only set for root product we know that we will init with root
|
|
661
|
+
yield syncGroupHandler.init(product, productLoaderForGroupedLoad);
|
|
662
|
+
}
|
|
609
663
|
return product;
|
|
610
664
|
}
|
|
611
665
|
catch (e) {
|
|
612
|
-
throw augmentErrorMessage(e,
|
|
666
|
+
throw augmentErrorMessage(e, `Load product request (${prodParams.partNumber}) failure`);
|
|
613
667
|
}
|
|
614
668
|
});
|
|
615
669
|
export class CfgProduct {
|
|
@@ -648,11 +702,43 @@ export class CfgProduct {
|
|
|
648
702
|
* Gets what selections has been made on the product, recursively including product
|
|
649
703
|
* configuration, optional products and additional products. Used when a full view of all
|
|
650
704
|
* selections on a product is needed, such as when doing Render or Export.
|
|
705
|
+
* @deprecated getDtoConf provides a newer format.
|
|
706
|
+
* @see getDtoConf
|
|
707
|
+
*/
|
|
708
|
+
this.getApiSelection = () => convertDtoProductConfToV1(this._internal.getDtoConf(false, false), true);
|
|
709
|
+
/**
|
|
710
|
+
* Applies the configuration (selections) in the passed object onto the product recursively
|
|
711
|
+
* including product configuration, optional products and additional products.
|
|
712
|
+
* @param doValidate Makes a server side validation call. These are necessary to ensure that
|
|
713
|
+
* the right models are loaded.
|
|
714
|
+
* @deprecated setDtoConf uses a newer format.
|
|
715
|
+
*/
|
|
716
|
+
this.setApiSelection = (configuration, doValidate = true) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setApiSelection(configuration, doValidate); });
|
|
717
|
+
/**
|
|
718
|
+
* A newer alternative version of getApiSelection. This returns the configuration (selections)
|
|
719
|
+
* on the product, recursively including product configuration, optional products and additional
|
|
720
|
+
* products.
|
|
721
|
+
* This version has the following advantages over getApiSelection:
|
|
722
|
+
* - The format is clearer, designed to be readable
|
|
723
|
+
* - Makes less assumptions about the structure in the Product being unchanging over time. In
|
|
724
|
+
* particular, the Feature codes are included in the data, so that changes to what Features
|
|
725
|
+
* are used in a Product is less likely to lead to unexpected results.
|
|
726
|
+
* - You can request ExtendedData and/or ProductParams to be included in the result. This extra
|
|
727
|
+
* data is ignored when passed back into the API, but it can be very useful for external
|
|
728
|
+
* applications.
|
|
729
|
+
* The other version (getApiSelection) has the advantage of using a format directly compatible with the API:s.
|
|
730
|
+
* @param includeExtendedData Includes extra data which is not an actual part of the configuration,
|
|
731
|
+
* i.e. units and groupCodes
|
|
732
|
+
* @param includeProductParams Includes what Product this was generated for, and the same for any
|
|
733
|
+
* Additional Products.
|
|
651
734
|
*/
|
|
652
|
-
this.getApiSelection = () => convertDtoConfProdToV1(this._internal.getDtoConf(false, false), true);
|
|
653
735
|
this.getDtoConf = (includeExtendedData = false, includeProductParams = false) => this._internal.getDtoConf(includeExtendedData, includeProductParams);
|
|
654
|
-
|
|
655
|
-
|
|
736
|
+
/**
|
|
737
|
+
* A newer alternative version of setApiSelection.
|
|
738
|
+
* @param doValidate Makes a server side validation call. These are necessary to ensure that
|
|
739
|
+
* the right models are loaded.
|
|
740
|
+
*/
|
|
741
|
+
this.setDtoConf = (configuration, doValidate = true) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setDtoConf(configuration, doValidate); });
|
|
656
742
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
657
743
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
658
744
|
this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
|
|
@@ -731,6 +817,21 @@ export class CfgProduct {
|
|
|
731
817
|
get visible() {
|
|
732
818
|
return this._internal.visible;
|
|
733
819
|
}
|
|
820
|
+
// A similar text to the one below exists in global-message-managers.md and should be kept in sync.
|
|
821
|
+
/**
|
|
822
|
+
* Functional selection is a Catalogues feature where selecting Options on Features result in that you
|
|
823
|
+
* "jump" to another Product as a result of the Validate call. You normally do not notice that a functional
|
|
824
|
+
* selection has occurred except for the styleNr changing. Functional selection can change which Features
|
|
825
|
+
* from the original product call are used as root Features. This can in turn affect if serialized
|
|
826
|
+
* configuration can be applied or not.
|
|
827
|
+
*
|
|
828
|
+
* The SDK can currently only apply serialized configuration if the list of root Features has not changed.
|
|
829
|
+
* For this reason, when functional selection has happened, extracting data for external systems might work
|
|
830
|
+
* well, but reapplying back into Stage will probably fail.
|
|
831
|
+
*/
|
|
832
|
+
get hasRootFeaturesChanged() {
|
|
833
|
+
return this._internal.hasRootFeaturesChanged;
|
|
834
|
+
}
|
|
734
835
|
get rawProductData() {
|
|
735
836
|
return this._internal.rawProductData;
|
|
736
837
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DtoCatalogueParamsWithCidAndLang, DtoProductParamsWithCidAndLang } from "./CatalogueAPI";
|
|
2
2
|
/**
|
|
3
3
|
* These methods aims to provide a default suggested way of building
|
|
4
4
|
* URLs to Products and Catalogues. By using consistent URL:s copy-paste
|
|
@@ -8,7 +8,20 @@ import { DtoCatalogueParamsWithLang, DtoProductParamsWithLang } from "./Catalogu
|
|
|
8
8
|
* Internally at Configura we try to stick with this format.
|
|
9
9
|
*/
|
|
10
10
|
export declare class CfgReferencePathHelper {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Use to generate URLs in our reference format. This is the format Configura uses in our integrations.
|
|
13
|
+
* @param browsingRootUrl The URL where Stage browsing begins
|
|
14
|
+
* @param catParams What catalogue to generate URL for.
|
|
15
|
+
* @returns An URL to a catalogue
|
|
16
|
+
*/
|
|
17
|
+
static getCataloguePath: (browsingRootUrl: string, catParams: DtoCatalogueParamsWithCidAndLang) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Use to generate URLs in our reference format. This is the format Configura uses in our integrations.
|
|
20
|
+
* @param browsingRootUrl The URL where Stage browsing begins
|
|
21
|
+
* @param productParams What product to generate URL for.
|
|
22
|
+
* @param separator Optional, defaults to "product", but can be changed to indicate another function.
|
|
23
|
+
* @returns An URL to a product
|
|
24
|
+
*/
|
|
25
|
+
static getProductPath: (browsingRootUrl: string, productParams: DtoProductParamsWithCidAndLang, separator?: string) => string;
|
|
13
26
|
}
|
|
14
27
|
//# sourceMappingURL=CfgReferencePathHelper.d.ts.map
|
|
@@ -9,5 +9,18 @@ import { encodeURIComponents } from "@configura/web-utilities";
|
|
|
9
9
|
*/
|
|
10
10
|
export class CfgReferencePathHelper {
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Use to generate URLs in our reference format. This is the format Configura uses in our integrations.
|
|
14
|
+
* @param browsingRootUrl The URL where Stage browsing begins
|
|
15
|
+
* @param catParams What catalogue to generate URL for.
|
|
16
|
+
* @returns An URL to a catalogue
|
|
17
|
+
*/
|
|
12
18
|
CfgReferencePathHelper.getCataloguePath = (browsingRootUrl, catParams) => `${browsingRootUrl}/${encodeURIComponents(catParams.cid, catParams.lang, catParams.enterprise, catParams.prdCat, catParams.prdCatVersion, catParams.vendor, catParams.priceList)}`;
|
|
13
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Use to generate URLs in our reference format. This is the format Configura uses in our integrations.
|
|
21
|
+
* @param browsingRootUrl The URL where Stage browsing begins
|
|
22
|
+
* @param productParams What product to generate URL for.
|
|
23
|
+
* @param separator Optional, defaults to "product", but can be changed to indicate another function.
|
|
24
|
+
* @returns An URL to a product
|
|
25
|
+
*/
|
|
26
|
+
CfgReferencePathHelper.getProductPath = (browsingRootUrl, productParams, separator = "product") => `${CfgReferencePathHelper.getCataloguePath(browsingRootUrl, productParams)}/${separator}/${encodeURIComponent(productParams.partNumber)}`;
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
1
|
+
import { DtoAdditionalProductConf, DtoAdditionalProductConfiguration, DtoFeatureConf, DtoProductConf, DtoSelectedOption } from "./CatalogueAPI.js";
|
|
2
|
+
export declare const isDtoProductConfAdditional: (value: DtoProductConf) => value is DtoAdditionalProductConf;
|
|
3
|
+
export declare const convertDtoProductConfToV1: (conf: DtoProductConf, silenceWarnings?: boolean) => DtoAdditionalProductConfiguration;
|
|
4
|
+
export declare const convertDtoFeatureConfsToSelOptions: (features: DtoFeatureConf[], silenceWarnings?: boolean) => DtoSelectedOption[];
|
|
5
|
+
/**
|
|
6
|
+
* Serializes and compacts the configuration into a format especially suited for URLs
|
|
7
|
+
*/
|
|
8
|
+
export declare const dtoProductConfigurationToCompactString: (conf: DtoProductConf) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Deserializes and inflates the configuration from the compacted format
|
|
11
|
+
*/
|
|
12
|
+
export declare const compactStringToDtoProductConf: (versionAndConf: string) => DtoProductConf;
|
|
13
|
+
export declare const stripExtendedDataFromDtoProductConf: (conf: DtoProductConf) => DtoProductConf;
|
|
5
14
|
//# sourceMappingURL=ConfigurationConverter.d.ts.map
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const isDtoProductConfAdditional = (value) => "refKey" in value;
|
|
2
2
|
// As this has potential to flood the terminal we only inform once
|
|
3
3
|
let hasInformedAboutProdParams = false;
|
|
4
4
|
let hasInformedAboutGroupCode = false;
|
|
5
5
|
let hasInformedAboutUnit = false;
|
|
6
|
-
export const
|
|
6
|
+
export const convertDtoProductConfToV1 = (conf, silenceWarnings = false) => {
|
|
7
7
|
var _a, _b;
|
|
8
8
|
if (!silenceWarnings && conf.prodParams !== undefined && !hasInformedAboutProdParams) {
|
|
9
9
|
hasInformedAboutProdParams = true;
|
|
10
|
-
console.info("Incoming
|
|
10
|
+
console.info("Incoming DtoProductConf contains prodParams. These will be ignored.");
|
|
11
11
|
}
|
|
12
12
|
const result = {
|
|
13
13
|
selOptions: ((_a = conf.features) !== null && _a !== void 0 ? _a : []).map((f) => ({
|
|
14
14
|
code: "!~!",
|
|
15
|
-
next:
|
|
15
|
+
next: convertDtoFeatureConfToApiSelection(f, silenceWarnings),
|
|
16
16
|
})),
|
|
17
|
-
additionalProducts: ((_b = conf.additionalProducts) !== null && _b !== void 0 ? _b : []).map((p) =>
|
|
17
|
+
additionalProducts: ((_b = conf.additionalProducts) !== null && _b !== void 0 ? _b : []).map((p) => convertDtoProductConfToV1(p, silenceWarnings)),
|
|
18
18
|
selected: true,
|
|
19
19
|
};
|
|
20
|
-
if (
|
|
20
|
+
if (isDtoProductConfAdditional(conf)) {
|
|
21
21
|
result.refKey = conf.refKey;
|
|
22
22
|
result.selected = conf.selected;
|
|
23
23
|
}
|
|
24
24
|
return result;
|
|
25
25
|
};
|
|
26
|
-
export const
|
|
26
|
+
export const convertDtoFeatureConfsToSelOptions = (features, silenceWarnings = false) => (features !== null && features !== void 0 ? features : []).map((f) => ({
|
|
27
27
|
code: "!~!",
|
|
28
|
-
next:
|
|
28
|
+
next: convertDtoFeatureConfToApiSelection(f, silenceWarnings),
|
|
29
29
|
}));
|
|
30
|
-
const
|
|
30
|
+
const convertDtoFeatureConfToApiSelection = (feature, silenceWarnings) => {
|
|
31
31
|
const { groupCode, options, unit } = feature;
|
|
32
32
|
if (!silenceWarnings && groupCode !== undefined && !hasInformedAboutGroupCode) {
|
|
33
33
|
hasInformedAboutGroupCode = true;
|
|
@@ -39,13 +39,13 @@ const convertDtoConfFeatureToApiSelection = (feature, silenceWarnings) => {
|
|
|
39
39
|
}
|
|
40
40
|
const result = {};
|
|
41
41
|
for (const option of (options !== null && options !== void 0 ? options : []).filter((o) => o.selected)) {
|
|
42
|
-
result[option.code] =
|
|
42
|
+
result[option.code] = convertDtoOptionConfToSelectedOption(option, silenceWarnings);
|
|
43
43
|
}
|
|
44
44
|
return result;
|
|
45
45
|
};
|
|
46
|
-
const
|
|
46
|
+
const convertDtoOptionConfToSelectedOption = (option, silenceWarnings) => {
|
|
47
47
|
const { features, code, numericValue } = option;
|
|
48
|
-
const selectionTrees = (features !== null && features !== void 0 ? features : []).map((f) =>
|
|
48
|
+
const selectionTrees = (features !== null && features !== void 0 ? features : []).map((f) => convertDtoFeatureConfToApiSelection(f, silenceWarnings));
|
|
49
49
|
const mergedSelectionTree = {};
|
|
50
50
|
let anyItems = false;
|
|
51
51
|
for (const selectionTree of selectionTrees) {
|
|
@@ -70,3 +70,97 @@ const convertDtoConfOptionToSelectedOption = (option, silenceWarnings) => {
|
|
|
70
70
|
}
|
|
71
71
|
return selectedOption;
|
|
72
72
|
};
|
|
73
|
+
const versionedRegex = /^v(\d+)(.+)$/;
|
|
74
|
+
const jsonKeyRegex = /"([^"]+)":/g;
|
|
75
|
+
const swapForUrlAdaptedRegex = /[-~_{}"]/g;
|
|
76
|
+
const shortToLong = new Map();
|
|
77
|
+
const longToShort = new Map();
|
|
78
|
+
// The replacement scheme here assumes this will only be used for keys
|
|
79
|
+
// in DtoProductConf and the knowledge that it contains no one
|
|
80
|
+
// one char keys.
|
|
81
|
+
for (const [long, short] of [
|
|
82
|
+
["configuration", "c"],
|
|
83
|
+
["additionalProducts", "a"],
|
|
84
|
+
["prodParams", "p"],
|
|
85
|
+
["refKey", "r"],
|
|
86
|
+
["selected", "s"],
|
|
87
|
+
["features", "f"],
|
|
88
|
+
["code", "d"],
|
|
89
|
+
["options", "o"],
|
|
90
|
+
["numericValue", "n"],
|
|
91
|
+
["groupCode", "g"],
|
|
92
|
+
["unit", "u"],
|
|
93
|
+
]) {
|
|
94
|
+
shortToLong.set(short, long);
|
|
95
|
+
longToShort.set(long, short);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* As certain chars are abundant in JSON but less abundant in the actual data
|
|
99
|
+
* we swap these so that frequent characters do not need to be URL-encoded.
|
|
100
|
+
*/
|
|
101
|
+
const jsonStringSwapCharsForUrl = (data) => data.replace(swapForUrlAdaptedRegex, (char) => {
|
|
102
|
+
switch (char) {
|
|
103
|
+
case "{":
|
|
104
|
+
return "~";
|
|
105
|
+
case "}":
|
|
106
|
+
return "-";
|
|
107
|
+
case '"':
|
|
108
|
+
return "_";
|
|
109
|
+
case "~":
|
|
110
|
+
return "{";
|
|
111
|
+
case "-":
|
|
112
|
+
return "}";
|
|
113
|
+
case "_":
|
|
114
|
+
return '"';
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unexpected char "${char}" in swap for URL`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const compactDtoProductConfJsonKeys = (data) => data.replace(jsonKeyRegex, (_, key) => { var _a; return `"${(_a = longToShort.get(key)) !== null && _a !== void 0 ? _a : key}":`; });
|
|
120
|
+
const expandDtoProductConfJsonKeys = (data) => data.replace(jsonKeyRegex, (_, key) => { var _a; return `"${(_a = shortToLong.get(key)) !== null && _a !== void 0 ? _a : key}":`; });
|
|
121
|
+
/**
|
|
122
|
+
* Serializes and compacts the configuration into a format especially suited for URLs
|
|
123
|
+
*/
|
|
124
|
+
export const dtoProductConfigurationToCompactString = (conf) => "v1" +
|
|
125
|
+
jsonStringSwapCharsForUrl(compactDtoProductConfJsonKeys(JSON.stringify(conf, undefined, "")));
|
|
126
|
+
/**
|
|
127
|
+
* Deserializes and inflates the configuration from the compacted format
|
|
128
|
+
*/
|
|
129
|
+
export const compactStringToDtoProductConf = (versionAndConf) => {
|
|
130
|
+
const match = versionedRegex.exec(versionAndConf);
|
|
131
|
+
if (match === null) {
|
|
132
|
+
throw new Error("Could not match version string");
|
|
133
|
+
}
|
|
134
|
+
const [, version, conf] = match;
|
|
135
|
+
if (version !== "1") {
|
|
136
|
+
throw new Error("Unknown packed URL version");
|
|
137
|
+
}
|
|
138
|
+
if (conf === "") {
|
|
139
|
+
throw new Error("No conf found");
|
|
140
|
+
}
|
|
141
|
+
return JSON.parse(expandDtoProductConfJsonKeys(jsonStringSwapCharsForUrl(conf)));
|
|
142
|
+
};
|
|
143
|
+
export const stripExtendedDataFromDtoProductConf = (conf) => {
|
|
144
|
+
var _a, _b;
|
|
145
|
+
return ({
|
|
146
|
+
features: (_a = conf.features) === null || _a === void 0 ? void 0 : _a.map(stripExtendedDataFromDtoFeatureConf),
|
|
147
|
+
additionalProducts: (_b = conf.additionalProducts) === null || _b === void 0 ? void 0 : _b.map(stripExtendedDataFromDtoAdditionalProductConfiguration),
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
const stripExtendedDataFromDtoAdditionalProductConfiguration = (conf) => (Object.assign(Object.assign({}, stripExtendedDataFromDtoProductConf(conf)), { refKey: conf.refKey, selected: conf.selected }));
|
|
151
|
+
const stripExtendedDataFromDtoFeatureConf = (conf) => {
|
|
152
|
+
var _a;
|
|
153
|
+
return ({
|
|
154
|
+
code: conf.code,
|
|
155
|
+
options: (_a = conf.options) === null || _a === void 0 ? void 0 : _a.map(stripExtendedDataFromDtoOptionConf),
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
const stripExtendedDataFromDtoOptionConf = (conf) => {
|
|
159
|
+
var _a;
|
|
160
|
+
return ({
|
|
161
|
+
code: conf.code,
|
|
162
|
+
selected: conf.selected,
|
|
163
|
+
numericValue: conf.numericValue,
|
|
164
|
+
features: (_a = conf.features) === null || _a === void 0 ? void 0 : _a.map(stripExtendedDataFromDtoFeatureConf),
|
|
165
|
+
});
|
|
166
|
+
};
|
|
@@ -2,25 +2,57 @@ import { CfgWindowEventManager } from "./CfgWindowEventManager.js";
|
|
|
2
2
|
declare type CfgHistoryManagerMessageData = {
|
|
3
3
|
initial: boolean;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* How the history is updated.
|
|
7
|
+
* @param DoNotWrite Only listens to initial URL-values.
|
|
8
|
+
* @param Replace Replaces the current history frame at updates.
|
|
9
|
+
* @param Push Adds history frames at updates.
|
|
10
|
+
* @param ReplaceAndUpdateUrl Replaces the current history frame at updates and updates the browser URL
|
|
11
|
+
* @param PushAndUpdateUrl Adds history frames at updates and updates the browser URL
|
|
12
|
+
*/
|
|
13
|
+
export declare enum HistoryMode {
|
|
14
|
+
DoNotWrite = 0,
|
|
15
|
+
Replace = 1,
|
|
16
|
+
Push = 2,
|
|
17
|
+
ReplaceAndUpdateUrl = 3,
|
|
18
|
+
PushAndUpdateUrl = 4
|
|
19
|
+
}
|
|
5
20
|
/**
|
|
6
21
|
* The collected data used when sending. For history this both contains the message, which is
|
|
7
|
-
* used as the "state" in the history-frame, and the qsKeyValues, which are
|
|
22
|
+
* used as the "state" in the history-frame, and the qsKeyValues, which are query string
|
|
23
|
+
* key-values used in the URL.
|
|
8
24
|
* When navigating back and forth the stage (message) is used to restore the old state. When
|
|
9
25
|
* opening a window with no prior history the Query String will be used.
|
|
10
26
|
*/
|
|
11
27
|
export declare type CfgHistoryManagerSendData<D> = {
|
|
12
28
|
message: D;
|
|
13
|
-
|
|
29
|
+
mode: HistoryMode;
|
|
14
30
|
qsKeyValues: Map<string, string | undefined>;
|
|
15
31
|
};
|
|
16
32
|
/**
|
|
17
|
-
* This class
|
|
18
|
-
*
|
|
33
|
+
* This class is used to coordinate writing and reading to the browser history.
|
|
34
|
+
* It handles messages sent from the connectors.
|
|
19
35
|
*/
|
|
20
36
|
export declare class CfgHistoryManager extends CfgWindowEventManager<"popstate", CfgHistoryManagerSendData<CfgHistoryManagerMessageData>> {
|
|
21
37
|
private static _instance;
|
|
22
38
|
static get instance(): CfgHistoryManager;
|
|
23
39
|
private constructor();
|
|
40
|
+
private _aggregatedQsKeyValues;
|
|
41
|
+
private _urlUpdateObservable;
|
|
42
|
+
/**
|
|
43
|
+
* @returns The current browser URL updated with the latest updates from
|
|
44
|
+
* the Connectors.
|
|
45
|
+
*/
|
|
46
|
+
getUrl(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Listen for updated URL:s. This doesn't have to mean the URL in the
|
|
49
|
+
* browser has been updated.
|
|
50
|
+
*/
|
|
51
|
+
listenForUrl(listener: (url: string) => void): void;
|
|
52
|
+
/**
|
|
53
|
+
* Stop listen.
|
|
54
|
+
*/
|
|
55
|
+
stopListenForUrl(listener: (url: string) => void): void;
|
|
24
56
|
/**
|
|
25
57
|
* Write to the history
|
|
26
58
|
*/
|
|
@@ -46,6 +78,7 @@ export declare class CfgHistoryManager extends CfgWindowEventManager<"popstate",
|
|
|
46
78
|
private static _makeUpdatedState;
|
|
47
79
|
protected readonly eventType = "popstate";
|
|
48
80
|
protected getDataFromEvent(event: PopStateEvent): unknown;
|
|
81
|
+
static getMessageFromCurrentHistoryState(messageKey: string): unknown | undefined;
|
|
49
82
|
}
|
|
50
83
|
export {};
|
|
51
84
|
//# sourceMappingURL=CfgHistoryManager.d.ts.map
|