@labdigital/commercetools-mock 2.17.1 → 2.18.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 (178) hide show
  1. package/dist/index.cjs +4186 -3974
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +266 -413
  4. package/dist/index.d.ts +266 -413
  5. package/dist/index.js +4186 -3974
  6. package/dist/index.js.map +1 -1
  7. package/package.json +44 -46
  8. package/src/constants.ts +2 -2
  9. package/src/ctMock.test.ts +11 -11
  10. package/src/ctMock.ts +141 -127
  11. package/src/deprecation.ts +8 -0
  12. package/src/exceptions.ts +17 -15
  13. package/src/helpers.ts +32 -32
  14. package/src/index.test.ts +128 -128
  15. package/src/index.ts +3 -3
  16. package/src/lib/expandParser.ts +13 -13
  17. package/src/lib/haversine.test.ts +9 -9
  18. package/src/lib/haversine.ts +11 -11
  19. package/src/lib/masking.ts +11 -11
  20. package/src/lib/parser.ts +2 -2
  21. package/src/lib/password.ts +23 -3
  22. package/src/lib/predicateParser.test.ts +185 -183
  23. package/src/lib/predicateParser.ts +234 -234
  24. package/src/lib/projectionSearchFilter.test.ts +103 -101
  25. package/src/lib/projectionSearchFilter.ts +152 -150
  26. package/src/lib/proxy.ts +5 -5
  27. package/src/oauth/errors.ts +4 -4
  28. package/src/oauth/helpers.ts +6 -6
  29. package/src/oauth/server.test.ts +86 -86
  30. package/src/oauth/server.ts +158 -144
  31. package/src/oauth/store.ts +44 -43
  32. package/src/priceSelector.test.ts +35 -35
  33. package/src/priceSelector.ts +30 -30
  34. package/src/product-projection-search.ts +136 -134
  35. package/src/projectAPI.test.ts +7 -7
  36. package/src/projectAPI.ts +24 -22
  37. package/src/repositories/abstract.ts +168 -116
  38. package/src/repositories/associate-role.ts +90 -77
  39. package/src/repositories/attribute-group.ts +51 -40
  40. package/src/repositories/business-unit.ts +168 -148
  41. package/src/repositories/cart/actions.ts +489 -0
  42. package/src/repositories/cart/helpers.ts +30 -0
  43. package/src/repositories/cart/index.ts +180 -0
  44. package/src/repositories/cart-discount/actions.ts +148 -0
  45. package/src/repositories/cart-discount/index.ts +86 -0
  46. package/src/repositories/category/actions.ts +231 -0
  47. package/src/repositories/category/index.ts +52 -0
  48. package/src/repositories/channel.ts +88 -90
  49. package/src/repositories/custom-object.ts +46 -45
  50. package/src/repositories/customer/actions.ts +165 -0
  51. package/src/repositories/customer/index.ts +79 -0
  52. package/src/repositories/customer-group.ts +66 -55
  53. package/src/repositories/discount-code/actions.ts +149 -0
  54. package/src/repositories/discount-code/index.ts +50 -0
  55. package/src/repositories/errors.ts +10 -10
  56. package/src/repositories/extension.ts +64 -62
  57. package/src/repositories/helpers.ts +117 -118
  58. package/src/repositories/index.ts +80 -79
  59. package/src/repositories/inventory-entry/actions.ts +84 -0
  60. package/src/repositories/inventory-entry/index.ts +44 -0
  61. package/src/repositories/my-customer.ts +114 -0
  62. package/src/repositories/my-order.ts +8 -8
  63. package/src/repositories/order/actions.ts +281 -0
  64. package/src/repositories/{order.test.ts → order/index.test.ts} +77 -77
  65. package/src/repositories/order/index.ts +260 -0
  66. package/src/repositories/order-edit.ts +10 -23
  67. package/src/repositories/payment/actions.ts +305 -0
  68. package/src/repositories/payment/helpers.ts +17 -0
  69. package/src/repositories/payment/index.ts +56 -0
  70. package/src/repositories/product/actions.ts +943 -0
  71. package/src/repositories/product/helpers.ts +98 -0
  72. package/src/repositories/product/index.ts +130 -0
  73. package/src/repositories/product-discount.ts +127 -117
  74. package/src/repositories/product-projection.ts +56 -62
  75. package/src/repositories/product-selection.ts +31 -28
  76. package/src/repositories/product-type.ts +136 -134
  77. package/src/repositories/project.ts +133 -118
  78. package/src/repositories/quote-request.ts +7 -19
  79. package/src/repositories/quote.ts +7 -22
  80. package/src/repositories/review.ts +13 -26
  81. package/src/repositories/shipping-method/actions.ts +198 -0
  82. package/src/repositories/shipping-method/helpers.ts +10 -0
  83. package/src/repositories/shipping-method/index.ts +138 -0
  84. package/src/repositories/shopping-list/actions.ts +295 -0
  85. package/src/repositories/shopping-list/index.ts +122 -0
  86. package/src/repositories/staged-quote.ts +7 -20
  87. package/src/repositories/standalone-price.ts +57 -44
  88. package/src/repositories/state.ts +113 -68
  89. package/src/repositories/store.ts +106 -94
  90. package/src/repositories/subscription.ts +46 -22
  91. package/src/repositories/tax-category/actions.ts +94 -0
  92. package/src/repositories/tax-category/helpers.ts +8 -0
  93. package/src/repositories/tax-category/index.ts +25 -0
  94. package/src/repositories/type/actions.ts +162 -0
  95. package/src/repositories/type/index.ts +24 -0
  96. package/src/repositories/zone.ts +62 -58
  97. package/src/server.ts +9 -9
  98. package/src/services/abstract.ts +75 -72
  99. package/src/services/associate-roles.test.ts +27 -27
  100. package/src/services/associate-roles.ts +7 -7
  101. package/src/services/attribute-group.ts +7 -7
  102. package/src/services/business-units.test.ts +28 -28
  103. package/src/services/business-units.ts +7 -7
  104. package/src/services/cart-discount.test.ts +199 -199
  105. package/src/services/cart-discount.ts +7 -7
  106. package/src/services/cart.test.ts +261 -261
  107. package/src/services/cart.ts +22 -21
  108. package/src/services/category.test.ts +121 -121
  109. package/src/services/category.ts +7 -7
  110. package/src/services/channel.ts +7 -7
  111. package/src/services/custom-object.test.ts +130 -130
  112. package/src/services/custom-object.ts +34 -31
  113. package/src/services/customer-group.ts +7 -7
  114. package/src/services/customer.test.ts +205 -205
  115. package/src/services/customer.ts +23 -36
  116. package/src/services/discount-code.ts +7 -7
  117. package/src/services/extension.ts +7 -7
  118. package/src/services/index.ts +85 -81
  119. package/src/services/inventory-entry.test.ts +106 -106
  120. package/src/services/inventory-entry.ts +7 -7
  121. package/src/services/my-cart.test.ts +56 -56
  122. package/src/services/my-cart.ts +20 -20
  123. package/src/services/my-customer.test.ts +155 -104
  124. package/src/services/my-customer.ts +61 -75
  125. package/src/services/my-order.ts +16 -16
  126. package/src/services/my-payment.test.ts +40 -40
  127. package/src/services/my-payment.ts +7 -7
  128. package/src/services/my-shopping-list.ts +7 -7
  129. package/src/services/order.test.ts +243 -243
  130. package/src/services/order.ts +23 -18
  131. package/src/services/payment.test.ts +40 -40
  132. package/src/services/payment.ts +7 -7
  133. package/src/services/product-discount.ts +7 -7
  134. package/src/services/product-projection.test.ts +190 -190
  135. package/src/services/product-projection.ts +34 -32
  136. package/src/services/product-selection.test.ts +19 -19
  137. package/src/services/product-selection.ts +7 -7
  138. package/src/services/product-type.test.ts +38 -38
  139. package/src/services/product-type.ts +7 -7
  140. package/src/services/product.test.ts +658 -656
  141. package/src/services/product.ts +7 -7
  142. package/src/services/project.test.ts +24 -24
  143. package/src/services/project.ts +17 -17
  144. package/src/services/reviews.ts +7 -7
  145. package/src/services/shipping-method.test.ts +78 -78
  146. package/src/services/shipping-method.ts +16 -16
  147. package/src/services/shopping-list.test.ts +170 -170
  148. package/src/services/shopping-list.ts +7 -7
  149. package/src/services/standalone-price.test.ts +112 -112
  150. package/src/services/standalone-price.ts +7 -7
  151. package/src/services/state.test.ts +30 -30
  152. package/src/services/state.ts +7 -7
  153. package/src/services/store.test.ts +40 -40
  154. package/src/services/store.ts +7 -7
  155. package/src/services/subscription.ts +7 -7
  156. package/src/services/tax-category.test.ts +43 -43
  157. package/src/services/tax-category.ts +7 -7
  158. package/src/services/type.ts +7 -7
  159. package/src/services/zone.ts +7 -7
  160. package/src/shippingCalculator.test.ts +43 -43
  161. package/src/shippingCalculator.ts +23 -23
  162. package/src/storage/abstract.ts +36 -34
  163. package/src/storage/in-memory.ts +237 -233
  164. package/src/storage/index.ts +2 -2
  165. package/src/types.ts +91 -91
  166. package/src/repositories/cart-discount.ts +0 -219
  167. package/src/repositories/cart.ts +0 -659
  168. package/src/repositories/category.ts +0 -256
  169. package/src/repositories/customer.ts +0 -228
  170. package/src/repositories/discount-code.ts +0 -181
  171. package/src/repositories/inventory-entry.ts +0 -109
  172. package/src/repositories/order.ts +0 -514
  173. package/src/repositories/payment.ts +0 -342
  174. package/src/repositories/product.ts +0 -1106
  175. package/src/repositories/shipping-method.ts +0 -312
  176. package/src/repositories/shopping-list.ts +0 -392
  177. package/src/repositories/tax-category.ts +0 -111
  178. package/src/repositories/type.ts +0 -172
@@ -1,1106 +0,0 @@
1
- import type {
2
- Price,
3
- PriceDraft,
4
- Product,
5
- ProductData,
6
- ProductDraft,
7
- ProductPublishAction,
8
- ProductSetAttributeAction,
9
- ProductSetAttributeInAllVariantsAction,
10
- ProductSetDescriptionAction,
11
- ProductAddExternalImageAction,
12
- ProductRemoveImageAction,
13
- ProductSetKeyAction,
14
- ProductTypeReference,
15
- ProductUpdateAction,
16
- ProductVariant,
17
- ProductVariantDraft,
18
- ProductMoveImageToPositionAction,
19
- ProductChangePriceAction,
20
- ProductAddPriceAction,
21
- ProductRemovePriceAction,
22
- CategoryReference,
23
- InvalidJsonInputError,
24
- InvalidOperationError,
25
- TaxCategoryReference,
26
- StateReference,
27
- ProductChangeNameAction,
28
- ProductChangeSlugAction,
29
- ProductSetMetaTitleAction,
30
- ProductSetMetaDescriptionAction,
31
- ProductSetMetaKeywordsAction,
32
- ProductAddVariantAction,
33
- ProductRemoveVariantAction,
34
- ProductChangeMasterVariantAction,
35
- ProductSetTaxCategoryAction,
36
- ProductAddToCategoryAction,
37
- ProductRemoveFromCategoryAction,
38
- ProductTransitionStateAction,
39
- ChannelReference,
40
- } from '@commercetools/platform-sdk'
41
- import { v4 as uuidv4 } from 'uuid'
42
- import type { Writable } from '../types.js'
43
- import { getBaseResourceProperties } from '../helpers.js'
44
- import { AbstractResourceRepository, RepositoryContext } from './abstract.js'
45
- import {
46
- createTypedMoney,
47
- getReferenceFromResourceIdentifier,
48
- } from './helpers.js'
49
- import deepEqual from 'deep-equal'
50
- import { CommercetoolsError } from '../exceptions.js'
51
-
52
- export class ProductRepository extends AbstractResourceRepository<'product'> {
53
- getTypeId() {
54
- return 'product' as const
55
- }
56
-
57
- create(context: RepositoryContext, draft: ProductDraft): Product {
58
- if (!draft.masterVariant) {
59
- throw new Error('Missing master variant')
60
- }
61
-
62
- let productType: ProductTypeReference | undefined = undefined
63
- try {
64
- productType = getReferenceFromResourceIdentifier<ProductTypeReference>(
65
- draft.productType,
66
- context.projectKey,
67
- this._storage
68
- )
69
- } catch (err) {
70
- // For now accept missing product types (but warn)
71
- console.warn(
72
- `Error resolving product-type '${draft.productType.id}'. This will be throw an error in later releases.`
73
- )
74
- productType = {
75
- typeId: 'product-type',
76
- id: draft.productType.id || '',
77
- }
78
- }
79
-
80
- // Resolve Product categories
81
- const categoryReferences: CategoryReference[] = []
82
- draft.categories?.forEach((category) => {
83
- if (category) {
84
- categoryReferences.push(
85
- getReferenceFromResourceIdentifier<CategoryReference>(
86
- category,
87
- context.projectKey,
88
- this._storage
89
- )
90
- )
91
- } else {
92
- throw new CommercetoolsError<InvalidJsonInputError>(
93
- {
94
- code: 'InvalidJsonInput',
95
- message: 'Request body does not contain valid JSON.',
96
- detailedErrorMessage: 'categories: JSON object expected.',
97
- },
98
- 400
99
- )
100
- }
101
- })
102
-
103
- // Resolve Tax category
104
- let taxCategoryReference: TaxCategoryReference | undefined = undefined
105
- if (draft.taxCategory) {
106
- taxCategoryReference =
107
- getReferenceFromResourceIdentifier<TaxCategoryReference>(
108
- draft.taxCategory,
109
- context.projectKey,
110
- this._storage
111
- )
112
- }
113
-
114
- // Resolve Product State
115
- let productStateReference: StateReference | undefined = undefined
116
- if (draft.state) {
117
- productStateReference =
118
- getReferenceFromResourceIdentifier<StateReference>(
119
- draft.state,
120
- context.projectKey,
121
- this._storage
122
- )
123
- }
124
-
125
- const productData: ProductData = {
126
- name: draft.name,
127
- slug: draft.slug,
128
- description: draft.description,
129
- categories: categoryReferences,
130
- masterVariant: this.variantFromDraft(context, 1, draft.masterVariant),
131
- variants:
132
- draft.variants?.map((variant, index) =>
133
- this.variantFromDraft(context, index + 2, variant)
134
- ) ?? [],
135
- metaTitle: draft.metaTitle,
136
- metaDescription: draft.metaDescription,
137
- metaKeywords: draft.metaKeywords,
138
- searchKeywords: draft.searchKeywords ?? {},
139
- }
140
-
141
- const resource: Product = {
142
- ...getBaseResourceProperties(),
143
- key: draft.key,
144
- productType: productType,
145
- taxCategory: taxCategoryReference,
146
- state: productStateReference,
147
- masterData: {
148
- current: productData,
149
- staged: productData,
150
- hasStagedChanges: false,
151
- published: draft.publish ?? false,
152
- },
153
- }
154
-
155
- this.saveNew(context, resource)
156
-
157
- return resource
158
- }
159
-
160
- private variantFromDraft(
161
- context: RepositoryContext,
162
- variantId: number,
163
- variant: ProductVariantDraft
164
- ): ProductVariant {
165
- return {
166
- id: variantId,
167
- sku: variant?.sku,
168
- key: variant?.key,
169
- attributes: variant?.attributes ?? [],
170
- prices: variant?.prices?.map((p) => this.priceFromDraft(context, p)),
171
- assets: [],
172
- images: [],
173
- }
174
- }
175
-
176
- private priceFromDraft(context: RepositoryContext, draft: PriceDraft): Price {
177
- return {
178
- id: uuidv4(),
179
- key: draft.key,
180
- country: draft.country,
181
- value: createTypedMoney(draft.value),
182
- channel: draft.channel
183
- ? getReferenceFromResourceIdentifier<ChannelReference>(
184
- draft.channel,
185
- context.projectKey,
186
- this._storage
187
- )
188
- : undefined,
189
- }
190
- }
191
-
192
- actions: Partial<
193
- Record<
194
- ProductUpdateAction['action'],
195
- (
196
- context: RepositoryContext,
197
- resource: Writable<Product>,
198
- action: any
199
- ) => void
200
- >
201
- > = {
202
- publish: (
203
- context: RepositoryContext,
204
- resource: Writable<Product>,
205
- { scope }: ProductPublishAction
206
- ) => {
207
- resource.masterData.current = resource.masterData.staged
208
- resource.masterData.published = true
209
- checkForStagedChanges(resource)
210
- },
211
- unpublish: (
212
- context: RepositoryContext,
213
- resource: Writable<Product>
214
- // { action }: ProductUnpublishAction
215
- ) => {
216
- resource.masterData.published = false
217
- checkForStagedChanges(resource)
218
- },
219
- setAttribute: (
220
- context: RepositoryContext,
221
- resource: Writable<Product>,
222
- { variantId, sku, name, value, staged }: ProductSetAttributeAction
223
- ) => {
224
- const setAttr = (data: Writable<ProductData>) => {
225
- const { variant, isMasterVariant, variantIndex } = getVariant(
226
- data,
227
- variantId,
228
- sku
229
- )
230
- if (!variant) {
231
- throw new Error(
232
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
233
- )
234
- }
235
-
236
- if (!variant.attributes) {
237
- variant.attributes = []
238
- }
239
-
240
- const existingAttr = variant.attributes.find(
241
- (attr) => attr.name === name
242
- )
243
- if (existingAttr) {
244
- existingAttr.value = value
245
- } else {
246
- variant.attributes.push({
247
- name,
248
- value,
249
- })
250
- }
251
- if (isMasterVariant) {
252
- data.masterVariant = variant
253
- } else {
254
- data.variants[variantIndex] = variant
255
- }
256
- }
257
-
258
- // If true, only the staged Attribute is set. If false, both current and
259
- // staged Attribute is set. Default is true
260
- const onlyStaged = staged !== undefined ? staged : true
261
-
262
- // Write the attribute to the staged data
263
- setAttr(resource.masterData.staged)
264
-
265
- // Also write to published data is isStaged = false
266
- // if isStaged is false we set the attribute on both the staged and
267
- // published data.
268
- if (!onlyStaged) {
269
- setAttr(resource.masterData.current)
270
- }
271
- checkForStagedChanges(resource)
272
-
273
- return resource
274
- },
275
- setAttributeInAllVariants: (
276
- context: RepositoryContext,
277
- resource: Writable<Product>,
278
- { name, value, staged }: ProductSetAttributeInAllVariantsAction
279
- ) => {
280
- const setAttrInAllVariants = (data: Writable<ProductData>) => {
281
- if (!data.masterVariant.attributes) {
282
- data.masterVariant.attributes = []
283
- }
284
-
285
- const existingAttr = data.masterVariant.attributes?.find(
286
- (attr) => attr.name === name
287
- )
288
-
289
- if (existingAttr) {
290
- existingAttr.value = value
291
- } else {
292
- data.masterVariant.attributes.push({
293
- name,
294
- value,
295
- })
296
- }
297
-
298
- data.variants.forEach((variant) => {
299
- if (!variant.attributes) {
300
- variant.attributes = []
301
- }
302
-
303
- const existingAttr = variant.attributes.find(
304
- (attr) => attr.name === name
305
- )
306
- if (existingAttr) {
307
- existingAttr.value = value
308
- } else {
309
- variant.attributes.push({
310
- name,
311
- value,
312
- })
313
- }
314
- })
315
- }
316
-
317
- // If true, only the staged Attribute is set. If false, both current and
318
- // staged Attribute is set. Default is true
319
- const onlyStaged = staged !== undefined ? staged : true
320
-
321
- // Write the attribute to the staged data
322
- setAttrInAllVariants(resource.masterData.staged)
323
-
324
- // Also write to published data is isStaged = false
325
- // if isStaged is false we set the attribute on both the staged and
326
- // published data.
327
- if (!onlyStaged) {
328
- setAttrInAllVariants(resource.masterData.current)
329
- }
330
- checkForStagedChanges(resource)
331
-
332
- return resource
333
- },
334
- setDescription: (
335
- context: RepositoryContext,
336
- resource: Writable<Product>,
337
- { description, staged }: ProductSetDescriptionAction
338
- ) => {
339
- const onlyStaged = staged !== undefined ? staged : true
340
-
341
- resource.masterData.staged.description = description
342
- if (!onlyStaged) {
343
- resource.masterData.current.description = description
344
- }
345
- checkForStagedChanges(resource)
346
- return resource
347
- },
348
- setKey: (
349
- context: RepositoryContext,
350
- resource: Writable<Product>,
351
- { key }: ProductSetKeyAction
352
- ) => {
353
- resource.key = key
354
- return resource
355
- },
356
- addExternalImage: (
357
- context: RepositoryContext,
358
- resource: Writable<Product>,
359
- { variantId, sku, image, staged }: ProductAddExternalImageAction
360
- ) => {
361
- const addImg = (data: Writable<ProductData>) => {
362
- const { variant, isMasterVariant, variantIndex } = getVariant(
363
- data,
364
- variantId,
365
- sku
366
- )
367
- if (!variant) {
368
- throw new Error(
369
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
370
- )
371
- }
372
-
373
- if (!variant.images) {
374
- variant.images = []
375
- } else {
376
- const existingImage = variant.images.find((x) => x.url === image.url)
377
- if (existingImage) {
378
- throw new Error(
379
- `Cannot add image '${image.url}' because product '${resource.id}' already has that image.`
380
- )
381
- }
382
- }
383
-
384
- // Add image
385
- variant.images.push(image)
386
-
387
- if (isMasterVariant) {
388
- data.masterVariant = variant
389
- } else {
390
- data.variants[variantIndex] = variant
391
- }
392
- }
393
-
394
- // If true, only the staged Attribute is set. If false, both current and
395
- // staged Attribute is set. Default is true
396
- const onlyStaged = staged !== undefined ? staged : true
397
-
398
- // Write the attribute to the staged data
399
- addImg(resource.masterData.staged)
400
-
401
- // Also write to published data is isStaged = false
402
- // if isStaged is false we set the attribute on both the staged and
403
- // published data.
404
- if (!onlyStaged) {
405
- addImg(resource.masterData.current)
406
- }
407
- checkForStagedChanges(resource)
408
-
409
- return resource
410
- },
411
- removeImage: (
412
- context: RepositoryContext,
413
- resource: Writable<Product>,
414
- { variantId, sku, imageUrl, staged }: ProductRemoveImageAction
415
- ) => {
416
- const removeImg = (data: Writable<ProductData>) => {
417
- const { variant, isMasterVariant, variantIndex } = getVariant(
418
- data,
419
- variantId,
420
- sku
421
- )
422
- if (!variant) {
423
- throw new Error(
424
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
425
- )
426
- }
427
-
428
- const variantImages = variant.images ?? []
429
- const existingImage = variantImages.find((x) => x.url === imageUrl)
430
- if (!existingImage) {
431
- throw new Error(
432
- `Cannot remove image '${imageUrl}' because product '${resource.id}' does not have that image.`
433
- )
434
- }
435
-
436
- // Remove image
437
- variant.images = variantImages.filter((image) => image.url !== imageUrl)
438
-
439
- if (isMasterVariant) {
440
- data.masterVariant = variant
441
- } else {
442
- data.variants[variantIndex] = variant
443
- }
444
- }
445
-
446
- // If true, only the staged Attribute is set. If false, both current and
447
- // staged Attribute is set. Default is true
448
- const onlyStaged = staged !== undefined ? staged : true
449
-
450
- // Write the attribute to the staged data
451
- removeImg(resource.masterData.staged)
452
-
453
- // Also write to published data is isStaged = false
454
- // if isStaged is false we set the attribute on both the staged and
455
- // published data.
456
- if (!onlyStaged) {
457
- removeImg(resource.masterData.current)
458
- }
459
- checkForStagedChanges(resource)
460
-
461
- return resource
462
- },
463
- moveImageToPosition: (
464
- context: RepositoryContext,
465
- resource: Writable<Product>,
466
- {
467
- variantId,
468
- sku,
469
- imageUrl,
470
- position,
471
- staged,
472
- }: ProductMoveImageToPositionAction
473
- ) => {
474
- const moveImg = (data: Writable<ProductData>) => {
475
- const { variant, isMasterVariant, variantIndex } = getVariant(
476
- data,
477
- variantId,
478
- sku
479
- )
480
- if (!variant) {
481
- throw new Error(
482
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
483
- )
484
- }
485
-
486
- const variantImages = variant.images ?? []
487
- const existingImage = variantImages.find((x) => x.url === imageUrl)
488
- if (!existingImage) {
489
- throw new Error(
490
- `Cannot move image '${imageUrl}' because product '${resource.id}' does not have that image.`
491
- )
492
- }
493
-
494
- if (position >= variantImages.length) {
495
- throw new Error(
496
- `Invalid position given. Position in images where the image should be moved. Must be between 0 and the total number of images minus 1.`
497
- )
498
- }
499
-
500
- // Remove image
501
- variant.images = variantImages.filter((image) => image.url !== imageUrl)
502
-
503
- // Re-add image to the correct position
504
- variant.images.splice(position, 0, existingImage)
505
-
506
- if (isMasterVariant) {
507
- data.masterVariant = variant
508
- } else {
509
- data.variants[variantIndex] = variant
510
- }
511
- }
512
-
513
- // If true, only the staged Attribute is set. If false, both current and
514
- // staged Attribute is set. Default is true
515
- const onlyStaged = staged !== undefined ? staged : true
516
-
517
- // Write the attribute to the staged data
518
- moveImg(resource.masterData.staged)
519
-
520
- // Also write to published data is isStaged = false
521
- // if isStaged is false we set the attribute on both the staged and
522
- // published data.
523
- if (!onlyStaged) {
524
- moveImg(resource.masterData.current)
525
- }
526
- checkForStagedChanges(resource)
527
-
528
- return resource
529
- },
530
- addPrice: (
531
- context: RepositoryContext,
532
- resource: Writable<Product>,
533
- { variantId, sku, price, staged }: ProductAddPriceAction
534
- ) => {
535
- const addVariantPrice = (
536
- data: Writable<ProductData>,
537
- priceToAdd: Price
538
- ) => {
539
- const { variant, isMasterVariant, variantIndex } = getVariant(
540
- data,
541
- variantId,
542
- sku
543
- )
544
- if (!variant) {
545
- throw new Error(
546
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
547
- )
548
- }
549
-
550
- if (variant.prices === undefined) {
551
- variant.prices = [priceToAdd]
552
- } else {
553
- variant.prices.push(priceToAdd)
554
- }
555
-
556
- if (isMasterVariant) {
557
- data.masterVariant = variant
558
- } else {
559
- data.variants[variantIndex] = variant
560
- }
561
- }
562
-
563
- // Pre-creating the price object ensures consistency between staged and current versions
564
- const priceToAdd = this.priceFromDraft(context, price)
565
-
566
- // If true, only the staged Attribute is set. If false, both current and
567
- // staged Attribute is set. Default is true
568
- const onlyStaged = staged !== undefined ? staged : true
569
-
570
- // Write the attribute to the staged data
571
- addVariantPrice(resource.masterData.staged, priceToAdd)
572
-
573
- // Also write to published data is isStaged = false
574
- // if isStaged is false we set the attribute on both the staged and
575
- // published data.
576
- if (!onlyStaged) {
577
- addVariantPrice(resource.masterData.current, priceToAdd)
578
- }
579
- checkForStagedChanges(resource)
580
-
581
- return resource
582
- },
583
- changePrice: (
584
- context: RepositoryContext,
585
- resource: Writable<Product>,
586
- { priceId, price, staged }: ProductChangePriceAction
587
- ) => {
588
- const changeVariantPrice = (data: Writable<ProductData>) => {
589
- const allVariants = [data.masterVariant, ...(data.variants ?? [])]
590
- const priceVariant = allVariants.find((variant) =>
591
- variant.prices?.some((x) => x.id === priceId)
592
- )
593
- if (!priceVariant) {
594
- throw new Error(
595
- `Price with id ${priceId} not found on product ${resource.id}`
596
- )
597
- }
598
-
599
- const { variant, isMasterVariant, variantIndex } = getVariant(
600
- data,
601
- priceVariant.id,
602
- priceVariant.sku
603
- )
604
- if (!variant) {
605
- throw new Error(
606
- `Variant with id ${priceVariant.id} or sku ${priceVariant.sku} not found on product ${resource.id}`
607
- )
608
- }
609
-
610
- variant.prices = variant.prices?.map((x) => {
611
- if (x.id === priceId) {
612
- return { ...x, ...price } as Price
613
- }
614
- return x
615
- })
616
-
617
- if (isMasterVariant) {
618
- data.masterVariant = variant
619
- } else {
620
- data.variants[variantIndex] = variant
621
- }
622
- }
623
-
624
- // If true, only the staged Attribute is set. If false, both current and
625
- // staged Attribute is set. Default is true
626
- const onlyStaged = staged !== undefined ? staged : true
627
-
628
- // Write the attribute to the staged data
629
- changeVariantPrice(resource.masterData.staged)
630
-
631
- // Also write to published data is isStaged = false
632
- // if isStaged is false we set the attribute on both the staged and
633
- // published data.
634
- if (!onlyStaged) {
635
- changeVariantPrice(resource.masterData.current)
636
- }
637
- checkForStagedChanges(resource)
638
-
639
- return resource
640
- },
641
- removePrice: (
642
- context: RepositoryContext,
643
- resource: Writable<Product>,
644
- { priceId, staged }: ProductRemovePriceAction
645
- ) => {
646
- const removeVariantPrice = (data: Writable<ProductData>) => {
647
- const allVariants = [data.masterVariant, ...(data.variants ?? [])]
648
- const priceVariant = allVariants.find((variant) =>
649
- variant.prices?.some((x) => x.id === priceId)
650
- )
651
- if (!priceVariant) {
652
- throw new Error(
653
- `Price with id ${priceId} not found on product ${resource.id}`
654
- )
655
- }
656
-
657
- const { variant, isMasterVariant, variantIndex } = getVariant(
658
- data,
659
- priceVariant.id,
660
- priceVariant.sku
661
- )
662
- if (!variant) {
663
- throw new Error(
664
- `Variant with id ${priceVariant.id} or sku ${priceVariant.sku} not found on product ${resource.id}`
665
- )
666
- }
667
-
668
- variant.prices = variant.prices?.filter((x) => x.id !== priceId)
669
-
670
- if (isMasterVariant) {
671
- data.masterVariant = variant
672
- } else {
673
- data.variants[variantIndex] = variant
674
- }
675
- }
676
-
677
- // If true, only the staged Attribute is set. If false, both current and
678
- // staged Attribute is set. Default is true
679
- const onlyStaged = staged !== undefined ? staged : true
680
-
681
- // Write the attribute to the staged data
682
- removeVariantPrice(resource.masterData.staged)
683
-
684
- // Also write to published data is isStaged = false
685
- // if isStaged is false we set the attribute on both the staged and
686
- // published data.
687
- if (!onlyStaged) {
688
- removeVariantPrice(resource.masterData.current)
689
- }
690
- checkForStagedChanges(resource)
691
-
692
- return resource
693
- },
694
- changeName: (
695
- context: RepositoryContext,
696
- resource: Writable<Product>,
697
- { name, staged }: ProductChangeNameAction
698
- ) => {
699
- const onlyStaged = staged !== undefined ? staged : true
700
- resource.masterData.staged.name = name
701
- if (!onlyStaged) {
702
- resource.masterData.current.name = name
703
- }
704
- checkForStagedChanges(resource)
705
- return resource
706
- },
707
- changeSlug: (
708
- context: RepositoryContext,
709
- resource: Writable<Product>,
710
- { slug, staged }: ProductChangeSlugAction
711
- ) => {
712
- const onlyStaged = staged !== undefined ? staged : true
713
- resource.masterData.staged.slug = slug
714
- if (!onlyStaged) {
715
- resource.masterData.current.slug = slug
716
- }
717
- checkForStagedChanges(resource)
718
- return resource
719
- },
720
- setMetaTitle: (
721
- context: RepositoryContext,
722
- resource: Writable<Product>,
723
- { metaTitle, staged }: ProductSetMetaTitleAction
724
- ) => {
725
- const onlyStaged = staged !== undefined ? staged : true
726
- resource.masterData.staged.metaTitle = metaTitle
727
- if (!onlyStaged) {
728
- resource.masterData.current.metaTitle = metaTitle
729
- }
730
- checkForStagedChanges(resource)
731
- return resource
732
- },
733
- setMetaDescription: (
734
- context: RepositoryContext,
735
- resource: Writable<Product>,
736
- { metaDescription, staged }: ProductSetMetaDescriptionAction
737
- ) => {
738
- const onlyStaged = staged !== undefined ? staged : true
739
- resource.masterData.staged.metaDescription = metaDescription
740
- if (!onlyStaged) {
741
- resource.masterData.current.metaDescription = metaDescription
742
- }
743
- checkForStagedChanges(resource)
744
- return resource
745
- },
746
- setMetaKeywords: (
747
- context: RepositoryContext,
748
- resource: Writable<Product>,
749
- { metaKeywords, staged }: ProductSetMetaKeywordsAction
750
- ) => {
751
- const onlyStaged = staged !== undefined ? staged : true
752
- resource.masterData.staged.metaKeywords = metaKeywords
753
- if (!onlyStaged) {
754
- resource.masterData.current.metaKeywords = metaKeywords
755
- }
756
- checkForStagedChanges(resource)
757
- return resource
758
- },
759
- addVariant: (
760
- context: RepositoryContext,
761
- resource: Writable<Product>,
762
- {
763
- sku,
764
- key,
765
- prices,
766
- images,
767
- attributes,
768
- staged,
769
- assets,
770
- }: ProductAddVariantAction
771
- ) => {
772
- const variantDraft: ProductVariantDraft = {
773
- sku: sku,
774
- key: key,
775
- prices: prices,
776
- images: images,
777
- attributes: attributes,
778
- assets: assets,
779
- }
780
-
781
- const dataStaged = resource.masterData.staged
782
- const allVariants = [
783
- dataStaged.masterVariant,
784
- ...(dataStaged.variants ?? []),
785
- ]
786
- const maxId = allVariants.reduce(
787
- (max, element) => (element.id > max ? element.id : max),
788
- 0
789
- )
790
- const variant = this.variantFromDraft(context, maxId + 1, variantDraft)
791
- dataStaged.variants.push(variant)
792
-
793
- const onlyStaged = staged !== undefined ? staged : true
794
-
795
- if (!onlyStaged) {
796
- resource.masterData.current.variants.push(variant)
797
- }
798
- checkForStagedChanges(resource)
799
-
800
- return resource
801
- },
802
- removeVariant: (
803
- context: RepositoryContext,
804
- resource: Writable<Product>,
805
- { id, sku, staged }: ProductRemoveVariantAction
806
- ) => {
807
- const removeVariant = (data: Writable<ProductData>) => {
808
- const { variant, isMasterVariant, variantIndex } = getVariant(
809
- data,
810
- id,
811
- sku
812
- )
813
- if (!variant) {
814
- throw new Error(
815
- `Variant with id ${id} or sku ${sku} not found on product ${resource.id}`
816
- )
817
- }
818
- if (isMasterVariant) {
819
- throw new Error(
820
- `Can not remove the variant [ID:${id}] for [Product:${resource.id}] since it's the master variant`
821
- )
822
- }
823
-
824
- data.variants.splice(variantIndex, 1)
825
- }
826
-
827
- const onlyStaged = staged !== undefined ? staged : true
828
-
829
- removeVariant(resource.masterData.staged)
830
-
831
- if (!onlyStaged) {
832
- removeVariant(resource.masterData.current)
833
- }
834
- checkForStagedChanges(resource)
835
-
836
- return resource
837
- },
838
- changeMasterVariant: (
839
- context: RepositoryContext,
840
- resource: Writable<Product>,
841
- { variantId, sku, staged }: ProductChangeMasterVariantAction
842
- ) => {
843
- const setMaster = (data: Writable<ProductData>) => {
844
- const { variant, isMasterVariant, variantIndex } = getVariant(
845
- data,
846
- variantId,
847
- sku
848
- )
849
- if (!variant) {
850
- throw new Error(
851
- `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
852
- )
853
- }
854
-
855
- if (!isMasterVariant) {
856
- // Save previous master variant
857
- const masterVariantPrev = data.masterVariant
858
- data.masterVariant = variant
859
- // Remove new master from variants
860
- data.variants.splice(variantIndex, 1)
861
- // Add previous master to variants
862
- data.variants.push(masterVariantPrev)
863
- }
864
- }
865
-
866
- const onlyStaged = staged !== undefined ? staged : true
867
-
868
- setMaster(resource.masterData.staged)
869
-
870
- if (!onlyStaged) {
871
- setMaster(resource.masterData.current)
872
- }
873
- checkForStagedChanges(resource)
874
-
875
- return resource
876
- },
877
- setTaxCategory: (
878
- context: RepositoryContext,
879
- resource: Writable<Product>,
880
- { taxCategory }: ProductSetTaxCategoryAction
881
- ) => {
882
- let taxCategoryReference: TaxCategoryReference | undefined = undefined
883
- if (taxCategory) {
884
- taxCategoryReference =
885
- getReferenceFromResourceIdentifier<TaxCategoryReference>(
886
- taxCategory,
887
- context.projectKey,
888
- this._storage
889
- )
890
- } else {
891
- throw new CommercetoolsError<InvalidJsonInputError>(
892
- {
893
- code: 'InvalidJsonInput',
894
- message: 'Request body does not contain valid JSON.',
895
- detailedErrorMessage:
896
- 'actions -> taxCategory: Missing required value',
897
- },
898
- 400
899
- )
900
- }
901
- resource.taxCategory = taxCategoryReference
902
- return resource
903
- },
904
- addToCategory: (
905
- context: RepositoryContext,
906
- resource: Writable<Product>,
907
- { category, staged, orderHint }: ProductAddToCategoryAction
908
- ) => {
909
- const addCategory = (data: Writable<ProductData>) => {
910
- if (category) {
911
- data.categories.push(
912
- getReferenceFromResourceIdentifier<CategoryReference>(
913
- category,
914
- context.projectKey,
915
- this._storage
916
- )
917
- )
918
- } else {
919
- throw new CommercetoolsError<InvalidJsonInputError>(
920
- {
921
- code: 'InvalidJsonInput',
922
- message: 'Request body does not contain valid JSON.',
923
- detailedErrorMessage:
924
- 'actions -> category: Missing required value',
925
- },
926
- 400
927
- )
928
- }
929
- }
930
-
931
- const onlyStaged = staged !== undefined ? staged : true
932
-
933
- addCategory(resource.masterData.staged)
934
-
935
- if (!onlyStaged) {
936
- addCategory(resource.masterData.current)
937
- }
938
- checkForStagedChanges(resource)
939
-
940
- return resource
941
- },
942
- removeFromCategory: (
943
- context: RepositoryContext,
944
- resource: Writable<Product>,
945
- { category, staged }: ProductRemoveFromCategoryAction
946
- ) => {
947
- const removeCategory = (data: Writable<ProductData>) => {
948
- if (category) {
949
- const resolvedCategory =
950
- getReferenceFromResourceIdentifier<CategoryReference>(
951
- category,
952
- context.projectKey,
953
- this._storage
954
- )
955
-
956
- const foundCategory = data.categories.find(
957
- (productCategory: CategoryReference) => {
958
- if (productCategory.id == resolvedCategory.id) {
959
- return productCategory
960
- }
961
- return false
962
- }
963
- )
964
-
965
- if (!foundCategory) {
966
- throw new CommercetoolsError<InvalidOperationError>(
967
- {
968
- code: 'InvalidOperation',
969
- message:
970
- `Cannot remove from category '${resolvedCategory.id}' because product ` +
971
- `'${resource.masterData.current.name}' is not in that category.`,
972
- },
973
- 400
974
- )
975
- }
976
-
977
- data.categories = data.categories.filter(
978
- (productCategory: CategoryReference) => {
979
- if (productCategory.id == resolvedCategory.id) {
980
- return false
981
- }
982
- return true
983
- }
984
- )
985
- } else {
986
- throw new CommercetoolsError<InvalidJsonInputError>(
987
- {
988
- code: 'InvalidJsonInput',
989
- message: 'Request body does not contain valid JSON.',
990
- detailedErrorMessage:
991
- 'actions -> category: Missing required value',
992
- },
993
- 400
994
- )
995
- }
996
- }
997
-
998
- const onlyStaged = staged !== undefined ? staged : true
999
- removeCategory(resource.masterData.staged)
1000
-
1001
- if (!onlyStaged) {
1002
- removeCategory(resource.masterData.current)
1003
- }
1004
- checkForStagedChanges(resource)
1005
-
1006
- return resource
1007
- },
1008
- transitionState: (
1009
- context: RepositoryContext,
1010
- resource: Writable<Product>,
1011
- { state, force }: ProductTransitionStateAction
1012
- ) => {
1013
- let productStateReference: StateReference | undefined = undefined
1014
- if (state) {
1015
- productStateReference =
1016
- getReferenceFromResourceIdentifier<StateReference>(
1017
- state,
1018
- context.projectKey,
1019
- this._storage
1020
- )
1021
- resource.state = productStateReference
1022
- } else {
1023
- throw new CommercetoolsError<InvalidJsonInputError>(
1024
- {
1025
- code: 'InvalidJsonInput',
1026
- message: 'Request body does not contain valid JSON.',
1027
- detailedErrorMessage: 'actions -> state: Missing required value',
1028
- },
1029
- 400
1030
- )
1031
- }
1032
-
1033
- return resource
1034
- },
1035
-
1036
- // 'setPrices': () => {},
1037
- // 'setProductPriceCustomType': () => {},
1038
- // 'setProductPriceCustomField': () => {},
1039
- // 'setDiscountedPrice': () => {},
1040
- // 'setAttributeInAllVariants': () => {},
1041
- // 'setCategoryOrderHint': () => {},
1042
- // 'setSku': () => {},
1043
- // 'setProductVariantKey': () => {},
1044
- // 'setImageLabel': () => {},
1045
- // 'addAsset': () => {},
1046
- // 'removeAsset': () => {},
1047
- // 'setAssetKey': () => {},
1048
- // 'changeAssetOrder': () => {},
1049
- // 'changeAssetName': () => {},
1050
- // 'setAssetDescription': () => {},
1051
- // 'setAssetTags': () => {},
1052
- // 'setAssetSources': () => {},
1053
- // 'setAssetCustomType': () => {},
1054
- // 'setAssetCustomField': () => {},
1055
- // 'setSearchKeywords': () => {},
1056
- // 'revertStagedChanges': () => {},
1057
- // 'revertStagedVariantChanges': () => {},
1058
- }
1059
- }
1060
-
1061
- // Check if the product still has staged data that is different from the
1062
- // current data.
1063
- const checkForStagedChanges = (product: Writable<Product>) => {
1064
- if (!product.masterData.staged) {
1065
- product.masterData.staged = product.masterData.current
1066
- }
1067
-
1068
- if (deepEqual(product.masterData.current, product.masterData.staged)) {
1069
- product.masterData.hasStagedChanges = false
1070
- } else {
1071
- product.masterData.hasStagedChanges = true
1072
- }
1073
- }
1074
-
1075
- interface VariantResult {
1076
- variant: Writable<ProductVariant> | undefined
1077
- isMasterVariant: boolean
1078
- variantIndex: number
1079
- }
1080
-
1081
- const getVariant = (
1082
- productData: ProductData,
1083
- variantId?: number,
1084
- sku?: string
1085
- ): VariantResult => {
1086
- const variants = [productData.masterVariant, ...productData.variants]
1087
- const foundVariant = variants.find((variant: ProductVariant) => {
1088
- if (variantId) {
1089
- return variant.id === variantId
1090
- }
1091
- if (sku) {
1092
- return variant.sku === sku
1093
- }
1094
- return false
1095
- })
1096
-
1097
- const isMasterVariant = foundVariant === productData.masterVariant
1098
- return {
1099
- variant: foundVariant,
1100
- isMasterVariant,
1101
- variantIndex:
1102
- !isMasterVariant && foundVariant
1103
- ? productData.variants.indexOf(foundVariant)
1104
- : -1,
1105
- }
1106
- }