@labdigital/commercetools-mock 0.9.1 → 0.10.1
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/README.md +8 -0
- package/dist/index.d.ts +354 -188
- package/dist/index.global.js +2346 -2209
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1968 -1829
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2171 -2032
- package/dist/index.mjs.map +1 -1
- package/package.json +30 -21
- package/src/constants.ts +4 -2
- package/src/ctMock.ts +27 -86
- package/src/helpers.ts +10 -11
- package/src/index.test.ts +1 -1
- package/src/lib/haversine.ts +2 -2
- package/src/lib/masking.ts +3 -1
- package/src/lib/predicateParser.ts +93 -92
- package/src/lib/projectionSearchFilter.test.ts +28 -36
- package/src/lib/projectionSearchFilter.ts +88 -103
- package/src/oauth/store.ts +3 -3
- package/src/priceSelector.test.ts +16 -35
- package/src/priceSelector.ts +6 -9
- package/src/product-projection-search.ts +49 -57
- package/src/projectAPI.test.ts +7 -0
- package/src/projectAPI.ts +17 -22
- package/src/repositories/abstract.ts +102 -51
- package/src/repositories/cart-discount.ts +4 -5
- package/src/repositories/cart.ts +56 -46
- package/src/repositories/category.ts +23 -26
- package/src/repositories/channel.ts +5 -6
- package/src/repositories/custom-object.ts +41 -32
- package/src/repositories/customer-group.ts +4 -5
- package/src/repositories/customer.ts +42 -5
- package/src/repositories/discount-code.ts +5 -6
- package/src/repositories/errors.ts +10 -14
- package/src/repositories/extension.ts +16 -15
- package/src/repositories/helpers.ts +10 -15
- package/src/repositories/index.ts +75 -0
- package/src/repositories/inventory-entry.ts +5 -6
- package/src/repositories/my-order.ts +2 -2
- package/src/repositories/order-edit.ts +39 -0
- package/src/repositories/order.test.ts +16 -11
- package/src/repositories/order.ts +21 -14
- package/src/repositories/payment.ts +9 -10
- package/src/repositories/product-discount.ts +5 -25
- package/src/repositories/product-projection.ts +12 -5
- package/src/repositories/product-selection.ts +40 -0
- package/src/repositories/product-type.ts +38 -60
- package/src/repositories/product.ts +128 -85
- package/src/repositories/project.ts +16 -33
- package/src/repositories/quote-request.ts +28 -0
- package/src/repositories/quote.ts +28 -0
- package/src/repositories/review.ts +34 -0
- package/src/repositories/shipping-method.ts +25 -28
- package/src/repositories/shopping-list.ts +6 -6
- package/src/repositories/staged-quote.ts +29 -0
- package/src/repositories/standalone-price.ts +36 -0
- package/src/repositories/state.ts +16 -17
- package/src/repositories/store.ts +13 -29
- package/src/repositories/subscription.ts +4 -5
- package/src/repositories/tax-category.ts +9 -26
- package/src/repositories/type.ts +24 -27
- package/src/repositories/zone.ts +9 -11
- package/src/server.ts +5 -0
- package/src/services/abstract.ts +43 -12
- package/src/services/cart-discount.ts +3 -4
- package/src/services/cart.test.ts +9 -11
- package/src/services/cart.ts +42 -38
- package/src/services/category.test.ts +1 -2
- package/src/services/category.ts +3 -4
- package/src/services/channel.ts +3 -4
- package/src/services/custom-object.test.ts +6 -6
- package/src/services/custom-object.ts +4 -5
- package/src/services/customer-group.ts +3 -4
- package/src/services/customer.test.ts +136 -0
- package/src/services/customer.ts +5 -6
- package/src/services/discount-code.ts +3 -4
- package/src/services/extension.ts +3 -4
- package/src/services/index.ts +74 -0
- package/src/services/inventory-entry.test.ts +9 -13
- package/src/services/inventory-entry.ts +3 -4
- package/src/services/my-cart.test.ts +2 -0
- package/src/services/my-cart.ts +4 -5
- package/src/services/my-customer.ts +3 -4
- package/src/services/my-order.ts +4 -5
- package/src/services/my-payment.ts +3 -4
- package/src/services/order.test.ts +28 -26
- package/src/services/order.ts +4 -5
- package/src/services/payment.ts +3 -4
- package/src/services/product-discount.ts +3 -20
- package/src/services/product-projection.test.ts +76 -8
- package/src/services/product-projection.ts +4 -5
- package/src/services/product-type.ts +3 -20
- package/src/services/product.test.ts +200 -90
- package/src/services/product.ts +3 -4
- package/src/services/project.ts +5 -6
- package/src/services/shipping-method.ts +3 -4
- package/src/services/shopping-list.ts +3 -4
- package/src/services/state.ts +3 -4
- package/src/services/store.test.ts +11 -2
- package/src/services/store.ts +4 -21
- package/src/services/subscription.ts +3 -4
- package/src/services/tax-category.ts +3 -20
- package/src/services/type.ts +3 -4
- package/src/services/zone.ts +3 -4
- package/src/storage/abstract.ts +82 -0
- package/src/{storage.ts → storage/in-memory.ts} +79 -147
- package/src/storage/index.ts +2 -0
- package/src/types.ts +52 -83
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProductProjection } from '@commercetools/platform-sdk'
|
|
2
|
+
import { cloneObject } from '../helpers'
|
|
2
3
|
import { applyPriceSelector } from '../priceSelector'
|
|
3
4
|
import { parseFilterExpression } from './projectionSearchFilter'
|
|
4
5
|
|
|
5
6
|
describe('Search filter', () => {
|
|
6
|
-
const
|
|
7
|
+
const exampleProduct: ProductProjection = {
|
|
8
|
+
id: '7401d82f-1378-47ba-996a-85beeb87ac87',
|
|
9
|
+
version: 2,
|
|
10
|
+
createdAt: '2022-07-22T10:02:40.851Z',
|
|
11
|
+
lastModifiedAt: '2022-07-22T10:02:44.427Z',
|
|
12
|
+
key: 'test-product',
|
|
13
|
+
productType: {
|
|
14
|
+
typeId: 'product-type',
|
|
15
|
+
id: 'b9b4b426-938b-4ccb-9f36-c6f933e8446e',
|
|
16
|
+
},
|
|
7
17
|
name: {
|
|
8
18
|
'nl-NL': 'test',
|
|
9
19
|
},
|
|
@@ -47,27 +57,9 @@ describe('Search filter', () => {
|
|
|
47
57
|
},
|
|
48
58
|
}
|
|
49
59
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
createdAt: '2022-07-22T10:02:40.851Z',
|
|
54
|
-
lastModifiedAt: '2022-07-22T10:02:44.427Z',
|
|
55
|
-
key: 'test-product',
|
|
56
|
-
productType: {
|
|
57
|
-
typeId: 'product-type',
|
|
58
|
-
id: 'b9b4b426-938b-4ccb-9f36-c6f933e8446e',
|
|
59
|
-
},
|
|
60
|
-
masterData: {
|
|
61
|
-
current: productData,
|
|
62
|
-
staged: productData,
|
|
63
|
-
published: true,
|
|
64
|
-
hasStagedChanges: false,
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const match = (pattern: string, product?: Product) => {
|
|
69
|
-
const matchFunc = parseFilterExpression(pattern, false)
|
|
70
|
-
const clone = JSON.parse(JSON.stringify(product ?? exampleProduct))
|
|
60
|
+
const match = (pattern: string, product?: ProductProjection) => {
|
|
61
|
+
const matchFunc = parseFilterExpression(pattern)
|
|
62
|
+
const clone = cloneObject(product ?? exampleProduct)
|
|
71
63
|
return {
|
|
72
64
|
isMatch: matchFunc(clone, false),
|
|
73
65
|
product: clone,
|
|
@@ -81,7 +73,9 @@ describe('Search filter', () => {
|
|
|
81
73
|
})
|
|
82
74
|
|
|
83
75
|
test('by product type id', async () => {
|
|
84
|
-
expect(
|
|
76
|
+
expect(
|
|
77
|
+
match(`productType.id:"b9b4b426-938b-4ccb-9f36-c6f933e8446e"`).isMatch
|
|
78
|
+
).toBeTruthy()
|
|
85
79
|
})
|
|
86
80
|
|
|
87
81
|
test('by SKU', async () => {
|
|
@@ -129,20 +123,22 @@ describe('Search filter', () => {
|
|
|
129
123
|
|
|
130
124
|
test('by price range - or', async () => {
|
|
131
125
|
expect(
|
|
132
|
-
match(
|
|
126
|
+
match(
|
|
127
|
+
`variants.price.centAmount:range (2 TO 1500 ), (1500 TO 3000), (3000 TO 6000)`
|
|
128
|
+
).isMatch
|
|
133
129
|
).toBeTruthy()
|
|
134
130
|
})
|
|
135
131
|
|
|
136
132
|
test('by scopedPrice range', async () => {
|
|
137
133
|
let result
|
|
138
|
-
let products:
|
|
134
|
+
let products: ProductProjection[]
|
|
139
135
|
|
|
140
136
|
// No currency given
|
|
141
137
|
result = match(`variants.scopedPrice.value.centAmount:range (1500 TO 2000)`)
|
|
142
138
|
expect(result.isMatch).toBeFalsy()
|
|
143
139
|
|
|
144
140
|
// Currency match
|
|
145
|
-
products = [
|
|
141
|
+
products = [cloneObject(exampleProduct)]
|
|
146
142
|
applyPriceSelector(products, { currency: 'EUR' })
|
|
147
143
|
|
|
148
144
|
result = match(
|
|
@@ -151,18 +147,14 @@ describe('Search filter', () => {
|
|
|
151
147
|
)
|
|
152
148
|
expect(result.isMatch).toBeTruthy()
|
|
153
149
|
expect(result.product).toMatchObject({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
sku: 'MYSKU',
|
|
158
|
-
scopedPrice: { value: { centAmount: 1789 } },
|
|
159
|
-
},
|
|
160
|
-
},
|
|
150
|
+
masterVariant: {
|
|
151
|
+
sku: 'MYSKU',
|
|
152
|
+
scopedPrice: { value: { centAmount: 1789 } },
|
|
161
153
|
},
|
|
162
154
|
})
|
|
163
155
|
|
|
164
156
|
// Currency mismatch
|
|
165
|
-
products = [
|
|
157
|
+
products = [cloneObject(exampleProduct)]
|
|
166
158
|
applyPriceSelector(products, { currency: 'USD' })
|
|
167
159
|
|
|
168
160
|
result = match(
|
|
@@ -172,7 +164,7 @@ describe('Search filter', () => {
|
|
|
172
164
|
expect(result.isMatch).toBeFalsy()
|
|
173
165
|
|
|
174
166
|
// Price has no country so mismatch
|
|
175
|
-
products = [
|
|
167
|
+
products = [cloneObject(exampleProduct)]
|
|
176
168
|
applyPriceSelector(products, { currency: 'EUR', country: 'NL' })
|
|
177
169
|
result = match(
|
|
178
170
|
`variants.scopedPrice.value.centAmount:range (1500 TO 2000)`,
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
* This module implements the commercetools product projection filter expression.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ProductProjection, ProductVariant } from '@commercetools/platform-sdk'
|
|
6
6
|
import perplex from 'perplex'
|
|
7
7
|
import Parser from 'pratt'
|
|
8
|
-
import { nestedLookup } from '../helpers'
|
|
9
8
|
import { Writable } from '../types'
|
|
9
|
+
import { nestedLookup } from '../helpers'
|
|
10
10
|
|
|
11
11
|
type MatchFunc = (target: any) => boolean
|
|
12
12
|
|
|
13
|
-
type
|
|
14
|
-
p: Writable<
|
|
13
|
+
type ProductProjectionFilter = (
|
|
14
|
+
p: Writable<ProductProjection>,
|
|
15
15
|
markMatchingVariants: boolean
|
|
16
16
|
) => boolean
|
|
17
17
|
|
|
18
|
-
type
|
|
18
|
+
type TypeSymbol = {
|
|
19
19
|
type: 'Symbol'
|
|
20
20
|
kind: 'int' | 'string' | 'any'
|
|
21
21
|
value: any
|
|
@@ -38,7 +38,10 @@ type TermExpressionSet = {
|
|
|
38
38
|
type: 'TermExpression'
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
type ExpressionSet =
|
|
41
|
+
type ExpressionSet =
|
|
42
|
+
| RangeExpressionSet
|
|
43
|
+
| FilterExpressionSet
|
|
44
|
+
| TermExpressionSet
|
|
42
45
|
|
|
43
46
|
export type RangeExpression = {
|
|
44
47
|
type: 'RangeExpression'
|
|
@@ -53,29 +56,28 @@ export type FilterExpression = {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
|
-
* Returns a function (
|
|
59
|
+
* Returns a function (ProductProjectionFilter).
|
|
57
60
|
* NOTE: The filter can alter the resources in-place (FIXME)
|
|
58
61
|
*/
|
|
59
62
|
export const parseFilterExpression = (
|
|
60
|
-
filter: string
|
|
61
|
-
|
|
62
|
-
): ProductFilter => {
|
|
63
|
+
filter: string
|
|
64
|
+
): ProductProjectionFilter => {
|
|
63
65
|
const exprFunc = generateMatchFunc(filter)
|
|
64
66
|
const [source] = filter.split(':', 1)
|
|
65
67
|
|
|
66
68
|
if (source.startsWith('variants.')) {
|
|
67
|
-
return filterVariants(source,
|
|
69
|
+
return filterVariants(source, exprFunc)
|
|
68
70
|
}
|
|
69
71
|
return filterProduct(source, exprFunc)
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
const getLexer = (value: string) =>
|
|
73
|
-
|
|
74
|
+
const getLexer = (value: string) =>
|
|
75
|
+
new perplex(value)
|
|
74
76
|
.token('MISSING', /missing(?![-_a-z0-9]+)/i)
|
|
75
77
|
.token('EXISTS', /exists(?![-_a-z0-9]+)/i)
|
|
76
78
|
.token('RANGE', /range(?![-_a-z0-9]+)/i)
|
|
77
79
|
.token('TO', /to(?![-_a-z0-9]+)/i)
|
|
78
|
-
.token('IDENTIFIER', /[-_
|
|
80
|
+
.token('IDENTIFIER', /[-_.a-z]+/i)
|
|
79
81
|
|
|
80
82
|
.token('FLOAT', /\d+\.\d+/)
|
|
81
83
|
.token('INT', /\d+/)
|
|
@@ -89,22 +91,19 @@ const getLexer = (value: string) => {
|
|
|
89
91
|
.token(')', ')')
|
|
90
92
|
.token('"', '"')
|
|
91
93
|
.token('WS', /\s+/, true) // skip
|
|
92
|
-
}
|
|
93
94
|
|
|
94
95
|
const parseFilter = (filter: string): ExpressionSet => {
|
|
95
96
|
const lexer = getLexer(filter)
|
|
96
97
|
const parser = new Parser(lexer)
|
|
97
98
|
.builder()
|
|
98
|
-
.nud('IDENTIFIER', 100, t =>
|
|
99
|
-
return t.token.match
|
|
100
|
-
})
|
|
99
|
+
.nud('IDENTIFIER', 100, (t) => t.token.match)
|
|
101
100
|
.led(':', 100, ({ left, bp }) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
const parsed: any = parser.parse({ terminals: [bp - 1] })
|
|
102
|
+
const expressions: RangeExpression[] | FilterExpression[] | TypeSymbol[] =
|
|
103
|
+
!Array.isArray(parsed) ? [parsed] : parsed
|
|
105
104
|
|
|
106
105
|
// Make sure we only have one type of expression (cannot mix)
|
|
107
|
-
const unique = new Set(expressions.map(expr => expr.type))
|
|
106
|
+
const unique = new Set(expressions.map((expr) => expr.type))
|
|
108
107
|
if (unique.size > 1) {
|
|
109
108
|
throw new Error('Invalid expression')
|
|
110
109
|
}
|
|
@@ -112,7 +111,7 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
112
111
|
// Convert plain symbols to a filter expression. For example
|
|
113
112
|
// variants.attribute.foobar:4 where 4 is a Symbol should result
|
|
114
113
|
// in a comparison
|
|
115
|
-
if (expressions.some(expr => expr.type == 'Symbol')) {
|
|
114
|
+
if (expressions.some((expr) => expr.type == 'Symbol')) {
|
|
116
115
|
return {
|
|
117
116
|
source: left as string,
|
|
118
117
|
type: 'FilterExpression',
|
|
@@ -123,11 +122,9 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
123
122
|
|
|
124
123
|
return {
|
|
125
124
|
type: 'FilterExpression',
|
|
126
|
-
match: (obj: any): boolean =>
|
|
127
|
-
return obj === e.value
|
|
128
|
-
},
|
|
125
|
+
match: (obj: any): boolean => obj === e.value,
|
|
129
126
|
}
|
|
130
|
-
})
|
|
127
|
+
}),
|
|
131
128
|
}
|
|
132
129
|
}
|
|
133
130
|
|
|
@@ -137,44 +134,50 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
137
134
|
children: expressions,
|
|
138
135
|
}
|
|
139
136
|
})
|
|
140
|
-
.nud(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
137
|
+
.nud(
|
|
138
|
+
'STRING',
|
|
139
|
+
20,
|
|
140
|
+
(t) =>
|
|
141
|
+
({
|
|
142
|
+
type: 'Symbol',
|
|
143
|
+
kind: 'string',
|
|
144
|
+
// @ts-ignore
|
|
145
|
+
value: t.token.groups[1],
|
|
146
|
+
} as TypeSymbol)
|
|
147
|
+
)
|
|
148
|
+
.nud(
|
|
149
|
+
'INT',
|
|
150
|
+
5,
|
|
151
|
+
(t) =>
|
|
152
|
+
({
|
|
153
|
+
type: 'Symbol',
|
|
154
|
+
kind: 'int',
|
|
155
|
+
value: parseInt(t.token.match, 10),
|
|
156
|
+
} as TypeSymbol)
|
|
157
|
+
)
|
|
158
|
+
.nud('STAR', 5, (_) => ({
|
|
159
|
+
type: 'Symbol',
|
|
160
|
+
kind: 'any',
|
|
161
|
+
value: null,
|
|
162
|
+
}))
|
|
163
|
+
.nud(
|
|
164
|
+
'EXISTS',
|
|
165
|
+
10,
|
|
166
|
+
({ bp }) =>
|
|
167
|
+
({
|
|
168
|
+
type: 'FilterExpression',
|
|
169
|
+
match: (obj: any): boolean => obj !== undefined,
|
|
170
|
+
} as FilterExpression)
|
|
171
|
+
)
|
|
172
|
+
.nud(
|
|
173
|
+
'MISSING',
|
|
174
|
+
10,
|
|
175
|
+
({ bp }) =>
|
|
176
|
+
({
|
|
177
|
+
type: 'FilterExpression',
|
|
178
|
+
match: (obj: any): boolean => obj === undefined,
|
|
179
|
+
} as FilterExpression)
|
|
180
|
+
)
|
|
178
181
|
.led('COMMA', 200, ({ left, token, bp }) => {
|
|
179
182
|
const expr: any = parser.parse({ terminals: [bp - 1] })
|
|
180
183
|
if (Array.isArray(expr)) {
|
|
@@ -183,7 +186,7 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
183
186
|
return [left, expr]
|
|
184
187
|
}
|
|
185
188
|
})
|
|
186
|
-
.nud('(', 100, t => {
|
|
189
|
+
.nud('(', 100, (t) => {
|
|
187
190
|
const expr: any = parser.parse({ terminals: [')'] })
|
|
188
191
|
lexer.expect(')')
|
|
189
192
|
return expr
|
|
@@ -211,21 +214,13 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
211
214
|
let func = undefined
|
|
212
215
|
|
|
213
216
|
if (range.start !== null && range.stop !== null) {
|
|
214
|
-
func = (obj: any): boolean =>
|
|
215
|
-
return obj >= range.start && obj <= range.stop
|
|
216
|
-
}
|
|
217
|
+
func = (obj: any): boolean => obj >= range.start && obj <= range.stop
|
|
217
218
|
} else if (range.start === null && range.stop !== null) {
|
|
218
|
-
func = (obj: any): boolean =>
|
|
219
|
-
return obj <= range.stop
|
|
220
|
-
}
|
|
219
|
+
func = (obj: any): boolean => obj <= range.stop
|
|
221
220
|
} else if (range.start !== null && range.stop === null) {
|
|
222
|
-
func = (obj: any): boolean =>
|
|
223
|
-
return obj >= range.start
|
|
224
|
-
}
|
|
221
|
+
func = (obj: any): boolean => obj >= range.start
|
|
225
222
|
} else {
|
|
226
|
-
func = (obj: any): boolean =>
|
|
227
|
-
return true
|
|
228
|
-
}
|
|
223
|
+
func = (obj: any): boolean => true
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
return {
|
|
@@ -244,8 +239,8 @@ const parseFilter = (filter: string): ExpressionSet => {
|
|
|
244
239
|
const generateMatchFunc = (filter: string) => {
|
|
245
240
|
const result = parseFilter(filter)
|
|
246
241
|
if (!result) {
|
|
247
|
-
const lines = filter.split('\n')
|
|
248
|
-
const column = lines[lines.length - 1].length
|
|
242
|
+
// const lines = filter.split('\n')
|
|
243
|
+
// const column = lines[lines.length - 1].length
|
|
249
244
|
throw new Error(`Syntax error while parsing '${filter}'.`)
|
|
250
245
|
}
|
|
251
246
|
if (result.type == 'TermExpression') {
|
|
@@ -254,7 +249,7 @@ const generateMatchFunc = (filter: string) => {
|
|
|
254
249
|
|
|
255
250
|
return (obj: any) => {
|
|
256
251
|
if (!result.children) return false
|
|
257
|
-
return result.children.some(c => c.match(obj))
|
|
252
|
+
return result.children.some((c) => c.match(obj))
|
|
258
253
|
}
|
|
259
254
|
}
|
|
260
255
|
|
|
@@ -268,23 +263,20 @@ export const generateFacetFunc = (filter: string): ExpressionSet => {
|
|
|
268
263
|
return parseFilter(filter)
|
|
269
264
|
}
|
|
270
265
|
|
|
271
|
-
const filterProduct =
|
|
272
|
-
|
|
266
|
+
const filterProduct =
|
|
267
|
+
(source: string, exprFunc: MatchFunc): ProductProjectionFilter =>
|
|
268
|
+
(p: ProductProjection, markMatchingVariants: boolean): boolean => {
|
|
273
269
|
const value = nestedLookup(p, source)
|
|
274
270
|
return exprFunc(value)
|
|
275
271
|
}
|
|
276
|
-
}
|
|
277
272
|
|
|
278
|
-
const filterVariants =
|
|
279
|
-
source: string,
|
|
280
|
-
|
|
281
|
-
exprFunc: MatchFunc
|
|
282
|
-
): ProductFilter => {
|
|
283
|
-
return (p: Product, markMatchingVariants: boolean): boolean => {
|
|
273
|
+
const filterVariants =
|
|
274
|
+
(source: string, exprFunc: MatchFunc): ProductProjectionFilter =>
|
|
275
|
+
(p: ProductProjection, markMatchingVariants: boolean): boolean => {
|
|
284
276
|
const [, ...paths] = source.split('.')
|
|
285
277
|
const path = paths.join('.')
|
|
286
278
|
|
|
287
|
-
const variants = getVariants(p
|
|
279
|
+
const variants = getVariants(p) as Writable<ProductVariant>[]
|
|
288
280
|
for (const variant of variants) {
|
|
289
281
|
const value = resolveVariantValue(variant, path)
|
|
290
282
|
|
|
@@ -294,7 +286,7 @@ const filterVariants = (
|
|
|
294
286
|
// set to true. For the other variants in the same product projection
|
|
295
287
|
// this field is set to false.
|
|
296
288
|
if (markMatchingVariants) {
|
|
297
|
-
variants.forEach(v => (v.isMatchingVariant = false))
|
|
289
|
+
variants.forEach((v) => (v.isMatchingVariant = false))
|
|
298
290
|
variant.isMatchingVariant = true
|
|
299
291
|
}
|
|
300
292
|
return true
|
|
@@ -303,7 +295,6 @@ const filterVariants = (
|
|
|
303
295
|
|
|
304
296
|
return false
|
|
305
297
|
}
|
|
306
|
-
}
|
|
307
298
|
|
|
308
299
|
export const resolveVariantValue = (obj: ProductVariant, path: string): any => {
|
|
309
300
|
if (path === undefined) {
|
|
@@ -335,13 +326,7 @@ export const resolveVariantValue = (obj: ProductVariant, path: string): any => {
|
|
|
335
326
|
return nestedLookup(obj, path)
|
|
336
327
|
}
|
|
337
328
|
|
|
338
|
-
export const getVariants = (p:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
: p.masterData.current?.masterVariant,
|
|
343
|
-
...(staged
|
|
344
|
-
? p.masterData.staged?.variants
|
|
345
|
-
: p.masterData.current?.variants),
|
|
346
|
-
]
|
|
347
|
-
}
|
|
329
|
+
export const getVariants = (p: ProductProjection): ProductVariant[] => [
|
|
330
|
+
p.masterVariant,
|
|
331
|
+
...(p.variants ?? []),
|
|
332
|
+
]
|
package/src/oauth/store.ts
CHANGED
|
@@ -9,9 +9,9 @@ type Token = {
|
|
|
9
9
|
|
|
10
10
|
export class OAuth2Store {
|
|
11
11
|
tokens: Token[] = []
|
|
12
|
-
validate
|
|
12
|
+
validate = true
|
|
13
13
|
|
|
14
|
-
constructor(validate
|
|
14
|
+
constructor(validate = true) {
|
|
15
15
|
this.validate = validate
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -29,7 +29,7 @@ export class OAuth2Store {
|
|
|
29
29
|
validateToken(token: string) {
|
|
30
30
|
if (!this.validate) return true
|
|
31
31
|
|
|
32
|
-
const foundToken = this.tokens.find(t => t.access_token === token)
|
|
32
|
+
const foundToken = this.tokens.find((t) => t.access_token === token)
|
|
33
33
|
if (foundToken) {
|
|
34
34
|
return true
|
|
35
35
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProductProjection } from '@commercetools/platform-sdk'
|
|
2
2
|
import { applyPriceSelector } from './priceSelector'
|
|
3
3
|
|
|
4
4
|
describe('priceSelector', () => {
|
|
5
|
-
let product:
|
|
5
|
+
let product: ProductProjection
|
|
6
6
|
|
|
7
7
|
beforeEach(() => {
|
|
8
|
-
|
|
8
|
+
product = {
|
|
9
|
+
id: '7401d82f-1378-47ba-996a-85beeb87ac87',
|
|
10
|
+
version: 2,
|
|
11
|
+
createdAt: '2022-07-22T10:02:40.851Z',
|
|
12
|
+
lastModifiedAt: '2022-07-22T10:02:44.427Z',
|
|
13
|
+
productType: {
|
|
14
|
+
typeId: 'product-type',
|
|
15
|
+
id: 'b9b4b426-938b-4ccb-9f36-c6f933e8446e',
|
|
16
|
+
},
|
|
9
17
|
name: {
|
|
10
18
|
'nl-NL': 'test',
|
|
11
19
|
},
|
|
@@ -48,49 +56,22 @@ describe('priceSelector', () => {
|
|
|
48
56
|
],
|
|
49
57
|
},
|
|
50
58
|
}
|
|
51
|
-
|
|
52
|
-
product = {
|
|
53
|
-
id: '7401d82f-1378-47ba-996a-85beeb87ac87',
|
|
54
|
-
version: 2,
|
|
55
|
-
createdAt: '2022-07-22T10:02:40.851Z',
|
|
56
|
-
lastModifiedAt: '2022-07-22T10:02:44.427Z',
|
|
57
|
-
productType: {
|
|
58
|
-
typeId: 'product-type',
|
|
59
|
-
id: 'b9b4b426-938b-4ccb-9f36-c6f933e8446e',
|
|
60
|
-
},
|
|
61
|
-
masterData: {
|
|
62
|
-
current: productData,
|
|
63
|
-
staged: productData,
|
|
64
|
-
published: true,
|
|
65
|
-
hasStagedChanges: false,
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
59
|
})
|
|
69
60
|
|
|
70
61
|
test('currency (match)', async () => {
|
|
71
62
|
applyPriceSelector([product], { currency: 'EUR' })
|
|
72
63
|
|
|
73
64
|
expect(product).toMatchObject({
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
sku: 'MYSKU',
|
|
78
|
-
scopedPrice: { value: { centAmount: 1789 } },
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
staged: {
|
|
82
|
-
masterVariant: {
|
|
83
|
-
sku: 'MYSKU',
|
|
84
|
-
scopedPrice: { value: { centAmount: 1789 } },
|
|
85
|
-
},
|
|
86
|
-
},
|
|
65
|
+
masterVariant: {
|
|
66
|
+
sku: 'MYSKU',
|
|
67
|
+
scopedPrice: { value: { centAmount: 1789 } },
|
|
87
68
|
},
|
|
88
69
|
})
|
|
89
70
|
})
|
|
90
71
|
|
|
91
72
|
test('currency, country (no match)', async () => {
|
|
92
73
|
applyPriceSelector([product], { currency: 'EUR', country: 'US' })
|
|
93
|
-
expect(product.
|
|
94
|
-
expect(product.
|
|
74
|
+
expect(product.masterVariant.scopedPrice).toBeUndefined()
|
|
75
|
+
expect(product.masterVariant.scopedPrice).toBeUndefined()
|
|
95
76
|
})
|
|
96
77
|
})
|
package/src/priceSelector.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InvalidInputError,
|
|
3
3
|
Price,
|
|
4
|
-
|
|
4
|
+
ProductProjection,
|
|
5
5
|
ProductVariant,
|
|
6
6
|
} from '@commercetools/platform-sdk'
|
|
7
7
|
import { CommercetoolsError } from './exceptions'
|
|
@@ -20,23 +20,20 @@ export type PriceSelector = {
|
|
|
20
20
|
* the scopedPrice attribute
|
|
21
21
|
*/
|
|
22
22
|
export const applyPriceSelector = (
|
|
23
|
-
products:
|
|
23
|
+
products: ProductProjection[],
|
|
24
24
|
selector: PriceSelector
|
|
25
25
|
) => {
|
|
26
26
|
validatePriceSelector(selector)
|
|
27
27
|
|
|
28
28
|
for (const product of products) {
|
|
29
29
|
const variants: Writable<ProductVariant>[] = [
|
|
30
|
-
product.
|
|
31
|
-
...(product.
|
|
32
|
-
|
|
33
|
-
product.masterData.current?.masterVariant,
|
|
34
|
-
...(product.masterData.current?.variants || []),
|
|
35
|
-
].filter(x => x != undefined)
|
|
30
|
+
product.masterVariant,
|
|
31
|
+
...(product.variants ?? []),
|
|
32
|
+
].filter((x) => x != undefined)
|
|
36
33
|
|
|
37
34
|
for (const variant of variants) {
|
|
38
35
|
const scopedPrices =
|
|
39
|
-
variant.prices?.filter(p => priceSelectorFilter(p, selector)) ?? []
|
|
36
|
+
variant.prices?.filter((p) => priceSelectorFilter(p, selector)) ?? []
|
|
40
37
|
|
|
41
38
|
if (scopedPrices.length > 0) {
|
|
42
39
|
const price = scopedPrices[0]
|