@labdigital/commercetools-mock 2.29.1 → 2.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +175 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +175 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/searchQueryTypeChecker.test.ts +96 -0
- package/src/lib/searchQueryTypeChecker.ts +120 -0
- package/src/product-projection-search.ts +1 -1
- package/src/product-search.ts +123 -0
- package/src/repositories/product/index.ts +13 -0
- package/src/services/product.test.ts +76 -1
- package/src/services/product.ts +15 -1
- package/src/types.ts +2 -2
package/dist/index.cjs
CHANGED
|
@@ -4541,6 +4541,165 @@ var PaymentRepository = class extends AbstractResourceRepository {
|
|
|
4541
4541
|
}
|
|
4542
4542
|
};
|
|
4543
4543
|
|
|
4544
|
+
// src/lib/searchQueryTypeChecker.ts
|
|
4545
|
+
var validateSearchQuery = (query) => {
|
|
4546
|
+
if (isSearchAndExpression(query)) {
|
|
4547
|
+
query.and.forEach((expr) => validateSearchQuery(expr));
|
|
4548
|
+
} else if (isSearchOrExpression(query)) {
|
|
4549
|
+
query.or.forEach((expr) => validateSearchQuery(expr));
|
|
4550
|
+
} else if (isSearchNotExpression(query)) {
|
|
4551
|
+
validateSearchQuery(query.not);
|
|
4552
|
+
} else if (isSearchFilterExpression(query) || isSearchRangeExpression(query) || isSearchExactExpression(query) || isSearchExistsExpression(query) || isSearchFullTextExpression(query) || isSearchFullTextPrefixExpression(query) || isSearchPrefixExpression(query) || isSearchWildCardExpression(query) || isSearchAnyValue(query)) {
|
|
4553
|
+
return;
|
|
4554
|
+
} else {
|
|
4555
|
+
throw new Error("Unsupported search query expression");
|
|
4556
|
+
}
|
|
4557
|
+
};
|
|
4558
|
+
var isSearchAndExpression = (expr) => expr.and !== void 0;
|
|
4559
|
+
var isSearchOrExpression = (expr) => expr.or !== void 0;
|
|
4560
|
+
var isSearchNotExpression = (expr) => expr.not !== void 0;
|
|
4561
|
+
var isSearchFilterExpression = (expr) => expr.filter !== void 0;
|
|
4562
|
+
var isSearchRangeExpression = (expr) => expr.range !== void 0;
|
|
4563
|
+
var isSearchExactExpression = (expr) => expr.exact !== void 0;
|
|
4564
|
+
var isSearchExistsExpression = (expr) => expr.exists !== void 0;
|
|
4565
|
+
var isSearchFullTextExpression = (expr) => expr.fullText !== void 0;
|
|
4566
|
+
var isSearchFullTextPrefixExpression = (expr) => expr.fullTextPrefix !== void 0;
|
|
4567
|
+
var isSearchPrefixExpression = (expr) => expr.prefix !== void 0;
|
|
4568
|
+
var isSearchWildCardExpression = (expr) => expr.wildcard !== void 0;
|
|
4569
|
+
var isSearchAnyValue = (expr) => expr.value !== void 0;
|
|
4570
|
+
|
|
4571
|
+
// src/priceSelector.ts
|
|
4572
|
+
var applyPriceSelector = (products, selector, noScopedPrice = false) => {
|
|
4573
|
+
validatePriceSelector(selector);
|
|
4574
|
+
for (const product of products) {
|
|
4575
|
+
const variants = [
|
|
4576
|
+
product.masterVariant,
|
|
4577
|
+
...product.variants ?? []
|
|
4578
|
+
].filter((x) => x != void 0);
|
|
4579
|
+
for (const variant of variants) {
|
|
4580
|
+
const scopedPrices = variant.prices?.filter((p) => priceSelectorFilter(p, selector)) ?? [];
|
|
4581
|
+
if (scopedPrices.length > 0) {
|
|
4582
|
+
const price = scopedPrices[0];
|
|
4583
|
+
variant.price = scopedPrices[0];
|
|
4584
|
+
if (!noScopedPrice) {
|
|
4585
|
+
variant.scopedPriceDiscounted = false;
|
|
4586
|
+
variant.scopedPrice = {
|
|
4587
|
+
...price,
|
|
4588
|
+
currentValue: price.value
|
|
4589
|
+
};
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
};
|
|
4595
|
+
var validatePriceSelector = (selector) => {
|
|
4596
|
+
if ((selector.country || selector.channel || selector.customerGroup) && !selector.currency) {
|
|
4597
|
+
throw new CommercetoolsError(
|
|
4598
|
+
{
|
|
4599
|
+
code: "InvalidInput",
|
|
4600
|
+
message: "The price selecting parameters country, channel and customerGroup cannot be used without the currency."
|
|
4601
|
+
},
|
|
4602
|
+
400
|
|
4603
|
+
);
|
|
4604
|
+
}
|
|
4605
|
+
};
|
|
4606
|
+
var priceSelectorFilter = (price, selector) => {
|
|
4607
|
+
if ((selector.country || price.country) && selector.country !== price.country) {
|
|
4608
|
+
return false;
|
|
4609
|
+
}
|
|
4610
|
+
if ((selector.currency || price.value.currencyCode) && selector.currency !== price.value.currencyCode) {
|
|
4611
|
+
return false;
|
|
4612
|
+
}
|
|
4613
|
+
if ((selector.channel || price.channel?.id) && selector.channel !== price.channel?.id) {
|
|
4614
|
+
return false;
|
|
4615
|
+
}
|
|
4616
|
+
if ((selector.customerGroup || price.customerGroup?.id) && selector.customerGroup !== price.customerGroup?.id) {
|
|
4617
|
+
return false;
|
|
4618
|
+
}
|
|
4619
|
+
return true;
|
|
4620
|
+
};
|
|
4621
|
+
|
|
4622
|
+
// src/product-search.ts
|
|
4623
|
+
var ProductSearch = class {
|
|
4624
|
+
_storage;
|
|
4625
|
+
constructor(storage) {
|
|
4626
|
+
this._storage = storage;
|
|
4627
|
+
}
|
|
4628
|
+
search(projectKey, params) {
|
|
4629
|
+
const resources = this._storage.all(projectKey, "product").map(
|
|
4630
|
+
(r) => this.transform(r, params.productProjectionParameters?.staged ?? false)
|
|
4631
|
+
).filter((p) => {
|
|
4632
|
+
if (!params.productProjectionParameters?.staged) {
|
|
4633
|
+
return p.published;
|
|
4634
|
+
}
|
|
4635
|
+
return true;
|
|
4636
|
+
});
|
|
4637
|
+
if (params.query) {
|
|
4638
|
+
try {
|
|
4639
|
+
validateSearchQuery(params.query);
|
|
4640
|
+
} catch (err) {
|
|
4641
|
+
console.error(err);
|
|
4642
|
+
throw new CommercetoolsError(
|
|
4643
|
+
{
|
|
4644
|
+
code: "InvalidInput",
|
|
4645
|
+
message: err.message
|
|
4646
|
+
},
|
|
4647
|
+
400
|
|
4648
|
+
);
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
if (params.productProjectionParameters) {
|
|
4652
|
+
applyPriceSelector(resources, {
|
|
4653
|
+
country: params.productProjectionParameters.priceCountry,
|
|
4654
|
+
channel: params.productProjectionParameters.priceChannel,
|
|
4655
|
+
customerGroup: params.productProjectionParameters.priceCustomerGroup,
|
|
4656
|
+
currency: params.productProjectionParameters.priceCurrency
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4659
|
+
const offset = params.offset || 0;
|
|
4660
|
+
const limit = params.limit || 20;
|
|
4661
|
+
const productProjectionsResult = resources.slice(offset, offset + limit);
|
|
4662
|
+
const productProjectionsParameterGiven = !!params?.productProjectionParameters;
|
|
4663
|
+
const results = productProjectionsResult.map(
|
|
4664
|
+
(product) => ({
|
|
4665
|
+
productProjection: productProjectionsParameterGiven ? product : void 0,
|
|
4666
|
+
id: product.id
|
|
4667
|
+
/**
|
|
4668
|
+
* @TODO: possibly add support for optional matchingVariants
|
|
4669
|
+
* https://docs.commercetools.com/api/projects/product-search#productsearchmatchingvariants
|
|
4670
|
+
*/
|
|
4671
|
+
})
|
|
4672
|
+
);
|
|
4673
|
+
return {
|
|
4674
|
+
total: resources.length,
|
|
4675
|
+
offset,
|
|
4676
|
+
limit,
|
|
4677
|
+
results,
|
|
4678
|
+
facets: []
|
|
4679
|
+
};
|
|
4680
|
+
}
|
|
4681
|
+
transform(product, staged) {
|
|
4682
|
+
const obj = !staged ? product.masterData.current : product.masterData.staged;
|
|
4683
|
+
return {
|
|
4684
|
+
id: product.id,
|
|
4685
|
+
createdAt: product.createdAt,
|
|
4686
|
+
lastModifiedAt: product.lastModifiedAt,
|
|
4687
|
+
version: product.version,
|
|
4688
|
+
name: obj.name,
|
|
4689
|
+
key: product.key,
|
|
4690
|
+
description: obj.description,
|
|
4691
|
+
metaDescription: obj.metaDescription,
|
|
4692
|
+
slug: obj.slug,
|
|
4693
|
+
categories: obj.categories,
|
|
4694
|
+
masterVariant: obj.masterVariant,
|
|
4695
|
+
variants: obj.variants,
|
|
4696
|
+
productType: product.productType,
|
|
4697
|
+
hasStagedChanges: product.masterData.hasStagedChanges,
|
|
4698
|
+
published: product.masterData.published
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
};
|
|
4702
|
+
|
|
4544
4703
|
// src/repositories/product/helpers.ts
|
|
4545
4704
|
var import_deep_equal2 = __toESM(require("deep-equal"), 1);
|
|
4546
4705
|
var import_uuid10 = require("uuid");
|
|
@@ -5271,9 +5430,11 @@ var ProductUpdateHandler = class extends AbstractUpdateHandler {
|
|
|
5271
5430
|
|
|
5272
5431
|
// src/repositories/product/index.ts
|
|
5273
5432
|
var ProductRepository = class extends AbstractResourceRepository {
|
|
5433
|
+
_searchService;
|
|
5274
5434
|
constructor(storage) {
|
|
5275
5435
|
super("product", storage);
|
|
5276
5436
|
this.actions = new ProductUpdateHandler(storage);
|
|
5437
|
+
this._searchService = new ProductSearch(storage);
|
|
5277
5438
|
}
|
|
5278
5439
|
create(context, draft) {
|
|
5279
5440
|
if (!draft.masterVariant) {
|
|
@@ -5366,6 +5527,9 @@ var ProductRepository = class extends AbstractResourceRepository {
|
|
|
5366
5527
|
};
|
|
5367
5528
|
return this.saveNew(context, resource);
|
|
5368
5529
|
}
|
|
5530
|
+
search(context, searchRequest) {
|
|
5531
|
+
return this._searchService.search(context.projectKey, searchRequest);
|
|
5532
|
+
}
|
|
5369
5533
|
};
|
|
5370
5534
|
|
|
5371
5535
|
// src/repositories/product-discount.ts
|
|
@@ -5449,57 +5613,6 @@ var ProductDiscountUpdateHandler = class extends AbstractUpdateHandler {
|
|
|
5449
5613
|
}
|
|
5450
5614
|
};
|
|
5451
5615
|
|
|
5452
|
-
// src/priceSelector.ts
|
|
5453
|
-
var applyPriceSelector = (products, selector, noScopedPrice = false) => {
|
|
5454
|
-
validatePriceSelector(selector);
|
|
5455
|
-
for (const product of products) {
|
|
5456
|
-
const variants = [
|
|
5457
|
-
product.masterVariant,
|
|
5458
|
-
...product.variants ?? []
|
|
5459
|
-
].filter((x) => x != void 0);
|
|
5460
|
-
for (const variant of variants) {
|
|
5461
|
-
const scopedPrices = variant.prices?.filter((p) => priceSelectorFilter(p, selector)) ?? [];
|
|
5462
|
-
if (scopedPrices.length > 0) {
|
|
5463
|
-
const price = scopedPrices[0];
|
|
5464
|
-
variant.price = scopedPrices[0];
|
|
5465
|
-
if (!noScopedPrice) {
|
|
5466
|
-
variant.scopedPriceDiscounted = false;
|
|
5467
|
-
variant.scopedPrice = {
|
|
5468
|
-
...price,
|
|
5469
|
-
currentValue: price.value
|
|
5470
|
-
};
|
|
5471
|
-
}
|
|
5472
|
-
}
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
};
|
|
5476
|
-
var validatePriceSelector = (selector) => {
|
|
5477
|
-
if ((selector.country || selector.channel || selector.customerGroup) && !selector.currency) {
|
|
5478
|
-
throw new CommercetoolsError(
|
|
5479
|
-
{
|
|
5480
|
-
code: "InvalidInput",
|
|
5481
|
-
message: "The price selecting parameters country, channel and customerGroup cannot be used without the currency."
|
|
5482
|
-
},
|
|
5483
|
-
400
|
|
5484
|
-
);
|
|
5485
|
-
}
|
|
5486
|
-
};
|
|
5487
|
-
var priceSelectorFilter = (price, selector) => {
|
|
5488
|
-
if ((selector.country || price.country) && selector.country !== price.country) {
|
|
5489
|
-
return false;
|
|
5490
|
-
}
|
|
5491
|
-
if ((selector.currency || price.value.currencyCode) && selector.currency !== price.value.currencyCode) {
|
|
5492
|
-
return false;
|
|
5493
|
-
}
|
|
5494
|
-
if ((selector.channel || price.channel?.id) && selector.channel !== price.channel?.id) {
|
|
5495
|
-
return false;
|
|
5496
|
-
}
|
|
5497
|
-
if ((selector.customerGroup || price.customerGroup?.id) && selector.customerGroup !== price.customerGroup?.id) {
|
|
5498
|
-
return false;
|
|
5499
|
-
}
|
|
5500
|
-
return true;
|
|
5501
|
-
};
|
|
5502
|
-
|
|
5503
5616
|
// src/lib/projectionSearchFilter.ts
|
|
5504
5617
|
var parseFilterExpression = (filter) => {
|
|
5505
5618
|
const exprFunc = generateMatchFunc2(filter);
|
|
@@ -8004,6 +8117,17 @@ var ProductService = class extends AbstractService {
|
|
|
8004
8117
|
getBasePath() {
|
|
8005
8118
|
return "products";
|
|
8006
8119
|
}
|
|
8120
|
+
extraRoutes(router) {
|
|
8121
|
+
router.post("/search", this.search.bind(this));
|
|
8122
|
+
}
|
|
8123
|
+
search(request, response) {
|
|
8124
|
+
const searchBody = request.body;
|
|
8125
|
+
const resource = this.repository.search(
|
|
8126
|
+
getRepositoryContext(request),
|
|
8127
|
+
searchBody
|
|
8128
|
+
);
|
|
8129
|
+
return response.status(200).send(resource);
|
|
8130
|
+
}
|
|
8007
8131
|
};
|
|
8008
8132
|
|
|
8009
8133
|
// src/services/product-discount.ts
|