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