@labdigital/commercetools-mock 0.14.1 → 1.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@labdigital/commercetools-mock",
3
3
  "author": "Michael van Tellingen",
4
- "version": "0.14.1",
4
+ "version": "1.1.0",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/commercetools--mock.esm.js",
@@ -17,10 +17,10 @@
17
17
  }
18
18
  },
19
19
  "engines": {
20
- "node": ">=14",
21
- "pnpm": ">=7.13.2"
20
+ "node": ">=16",
21
+ "pnpm": ">=8.1.1"
22
22
  },
23
- "packageManager": "pnpm@7.13.2",
23
+ "packageManager": "pnpm@8.1.1",
24
24
  "publishConfig": {
25
25
  "access": "public"
26
26
  },
@@ -51,6 +51,7 @@
51
51
  "@changesets/cli": "^2.26.0",
52
52
  "@changesets/changelog-github": "^0.4.8",
53
53
  "@commercetools/platform-sdk": "4.0.0",
54
+ "@labdigital/eslint-config-node": "0.0.5",
54
55
  "@types/basic-auth": "^1.1.3",
55
56
  "@types/body-parser": "^1.19.2",
56
57
  "@types/deep-equal": "^1.0.1",
@@ -71,7 +72,6 @@
71
72
  "got": "^11.8.3",
72
73
  "husky": "^7.0.4",
73
74
  "jest": "^28.1.3",
74
- "nodemon": "^2.0.15",
75
75
  "prettier": "^2.7.1",
76
76
  "timekeeper": "^2.2.0",
77
77
  "ts-node": "^10.4.0",
@@ -84,7 +84,7 @@
84
84
  "@commercetools/platform-sdk": "^2.4.1"
85
85
  },
86
86
  "scripts": {
87
- "start": "nodemon --watch src --exec 'node -r esbuild-register' src/server.ts",
87
+ "start": "tsup src --watch src/server.ts",
88
88
  "build": "tsup",
89
89
  "build:server": "esbuild src/server.ts --bundle --outfile=dist/server.js --platform=node",
90
90
  "publish:ci": "pnpm build && pnpm changeset publish",
@@ -15,6 +15,9 @@ import {
15
15
  ProductVariant,
16
16
  ProductVariantDraft,
17
17
  ProductMoveImageToPositionAction,
18
+ ProductChangePriceAction,
19
+ ProductAddPriceAction,
20
+ ProductRemovePriceAction,
18
21
  } from '@commercetools/platform-sdk'
19
22
  import { v4 as uuidv4 } from 'uuid'
20
23
  import { Writable } from '../types'
@@ -361,15 +364,171 @@ export class ProductRepository extends AbstractResourceRepository<'product'> {
361
364
  return resource
362
365
  },
363
366
 
367
+ addPrice: (
368
+ context: RepositoryContext,
369
+ resource: Writable<Product>,
370
+ { variantId, sku, price, staged }: ProductAddPriceAction
371
+ ) => {
372
+ const addVariantPrice = (data: Writable<ProductData>) => {
373
+ const { variant, isMasterVariant, variantIndex } = getVariant(
374
+ data,
375
+ variantId,
376
+ sku
377
+ )
378
+ if (!variant) {
379
+ throw new Error(
380
+ `Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`
381
+ )
382
+ }
383
+
384
+ if (variant.prices === undefined) {
385
+ variant.prices = [priceFromDraft(price)]
386
+ } else {
387
+ variant.prices.push(priceFromDraft(price))
388
+ }
389
+
390
+ if (isMasterVariant) {
391
+ data.masterVariant = variant
392
+ } else {
393
+ data.variants[variantIndex] = variant
394
+ }
395
+ }
396
+
397
+ // If true, only the staged Attribute is set. If false, both current and
398
+ // staged Attribute is set. Default is true
399
+ const onlyStaged = staged !== undefined ? staged : true
400
+
401
+ // Write the attribute to the staged data
402
+ addVariantPrice(resource.masterData.staged)
403
+
404
+ // Also write to published data is isStaged = false
405
+ // if isStaged is false we set the attribute on both the staged and
406
+ // published data.
407
+ if (!onlyStaged) {
408
+ addVariantPrice(resource.masterData.current)
409
+ }
410
+ checkForStagedChanges(resource)
411
+
412
+ return resource
413
+ },
414
+ changePrice: (
415
+ context: RepositoryContext,
416
+ resource: Writable<Product>,
417
+ { priceId, price, staged }: ProductChangePriceAction
418
+ ) => {
419
+ const changeVariantPrice = (data: Writable<ProductData>) => {
420
+ const allVariants = [data.masterVariant, ...(data.variants ?? [])]
421
+ const priceVariant = allVariants.find((variant) =>
422
+ variant.prices?.some((x) => x.id === priceId)
423
+ )
424
+ if (!priceVariant) {
425
+ throw new Error(
426
+ `Price with id ${priceId} not found on product ${resource.id}`
427
+ )
428
+ }
429
+
430
+ const { variant, isMasterVariant, variantIndex } = getVariant(
431
+ data,
432
+ priceVariant.id,
433
+ priceVariant.sku
434
+ )
435
+ if (!variant) {
436
+ throw new Error(
437
+ `Variant with id ${priceVariant.id} or sku ${priceVariant.sku} not found on product ${resource.id}`
438
+ )
439
+ }
440
+
441
+ variant.prices = variant.prices?.map((x) => {
442
+ if (x.id === priceId) {
443
+ return { ...x, ...price } as Price
444
+ }
445
+ return x
446
+ })
447
+
448
+ if (isMasterVariant) {
449
+ data.masterVariant = variant
450
+ } else {
451
+ data.variants[variantIndex] = variant
452
+ }
453
+ }
454
+
455
+ // If true, only the staged Attribute is set. If false, both current and
456
+ // staged Attribute is set. Default is true
457
+ const onlyStaged = staged !== undefined ? staged : true
458
+
459
+ // Write the attribute to the staged data
460
+ changeVariantPrice(resource.masterData.staged)
461
+
462
+ // Also write to published data is isStaged = false
463
+ // if isStaged is false we set the attribute on both the staged and
464
+ // published data.
465
+ if (!onlyStaged) {
466
+ changeVariantPrice(resource.masterData.current)
467
+ }
468
+ checkForStagedChanges(resource)
469
+
470
+ return resource
471
+ },
472
+ removePrice: (
473
+ context: RepositoryContext,
474
+ resource: Writable<Product>,
475
+ { priceId, staged }: ProductRemovePriceAction
476
+ ) => {
477
+ const removeVariantPrice = (data: Writable<ProductData>) => {
478
+ const allVariants = [data.masterVariant, ...(data.variants ?? [])]
479
+ const priceVariant = allVariants.find((variant) =>
480
+ variant.prices?.some((x) => x.id === priceId)
481
+ )
482
+ if (!priceVariant) {
483
+ throw new Error(
484
+ `Price with id ${priceId} not found on product ${resource.id}`
485
+ )
486
+ }
487
+
488
+ const { variant, isMasterVariant, variantIndex } = getVariant(
489
+ data,
490
+ priceVariant.id,
491
+ priceVariant.sku
492
+ )
493
+ if (!variant) {
494
+ throw new Error(
495
+ `Variant with id ${priceVariant.id} or sku ${priceVariant.sku} not found on product ${resource.id}`
496
+ )
497
+ }
498
+
499
+ variant.prices = variant.prices?.filter((x) => x.id !== priceId)
500
+
501
+ if (isMasterVariant) {
502
+ data.masterVariant = variant
503
+ } else {
504
+ data.variants[variantIndex] = variant
505
+ }
506
+ }
507
+
508
+ // If true, only the staged Attribute is set. If false, both current and
509
+ // staged Attribute is set. Default is true
510
+ const onlyStaged = staged !== undefined ? staged : true
511
+
512
+ // Write the attribute to the staged data
513
+ removeVariantPrice(resource.masterData.staged)
514
+
515
+ // Also write to published data is isStaged = false
516
+ // if isStaged is false we set the attribute on both the staged and
517
+ // published data.
518
+ if (!onlyStaged) {
519
+ removeVariantPrice(resource.masterData.current)
520
+ }
521
+ checkForStagedChanges(resource)
522
+
523
+ return resource
524
+ },
525
+
364
526
  // 'changeName': () => {},
365
527
  // 'changeSlug': () => {},
366
528
  // 'addVariant': () => {},
367
529
  // 'removeVariant': () => {},
368
530
  // 'changeMasterVariant': () => {},
369
- // 'addPrice': () => {},
370
531
  // 'setPrices': () => {},
371
- // 'changePrice': () => {},
372
- // 'removePrice': () => {},
373
532
  // 'setProductPriceCustomType': () => {},
374
533
  // 'setProductPriceCustomField': () => {},
375
534
  // 'setDiscountedPrice': () => {},
@@ -462,6 +621,7 @@ const variantFromDraft = (
462
621
 
463
622
  const priceFromDraft = (draft: PriceDraft): Price => ({
464
623
  id: uuidv4(),
624
+ country: draft.country,
465
625
  value: {
466
626
  currencyCode: draft.value.currencyCode,
467
627
  centAmount: draft.value.centAmount,
@@ -1,36 +1,74 @@
1
1
  import {
2
- Review,
2
+ ChannelReference,
3
+ ChannelResourceIdentifier,
4
+ DiscountedPriceDraft,
3
5
  StandalonePrice,
4
- StandalonePriceUpdateAction,
6
+ StandalonePriceChangeActiveAction,
7
+ StandalonePriceChangeValueAction,
8
+ StandalonePriceDraft,
9
+ StandalonePriceSetDiscountedPriceAction,
5
10
  } from '@commercetools/platform-sdk'
6
11
  import { getBaseResourceProperties } from '../helpers'
7
12
  import { Writable } from '../types'
8
13
  import { AbstractResourceRepository, RepositoryContext } from './abstract'
14
+ import { createTypedMoney } from './helpers'
9
15
 
10
16
  export class StandAlonePriceRepository extends AbstractResourceRepository<'standalone-price'> {
11
17
  getTypeId() {
12
18
  return 'standalone-price' as const
13
19
  }
14
20
 
15
- create(context: RepositoryContext, draft: StandalonePrice): StandalonePrice {
21
+ create(context: RepositoryContext, draft: StandalonePriceDraft): StandalonePrice {
16
22
  const resource: StandalonePrice = {
17
23
  ...getBaseResourceProperties(),
18
- active: draft.active,
24
+ active: draft.active? draft.active : false,
19
25
  sku: draft.sku,
20
- value: draft.value,
26
+ value: createTypedMoney(draft.value),
27
+ country: draft.country,
28
+ discounted: draft.discounted ? this.transformDiscountDraft(draft.discounted) : undefined,
29
+ channel: draft.channel?.id ? this.transformChannelReferenceDraft(draft.channel) : undefined,
30
+ validFrom: draft.validFrom,
31
+ validUntil: draft.validUntil,
21
32
  }
22
33
  this.saveNew(context, resource)
23
34
  return resource
24
35
  }
25
36
 
26
- actions: Partial<
27
- Record<
28
- StandalonePriceUpdateAction['action'],
29
- (
30
- context: RepositoryContext,
31
- resource: Writable<Review>,
32
- action: any
33
- ) => void
34
- >
35
- > = {}
37
+ transformChannelReferenceDraft(channel: ChannelResourceIdentifier) : ChannelReference {
38
+ return {
39
+ typeId: channel.typeId,
40
+ id: channel.id as string,
41
+ }
42
+ }
43
+
44
+ transformDiscountDraft(discounted: DiscountedPriceDraft) {
45
+ return {
46
+ value: createTypedMoney(discounted.value),
47
+ discount: discounted.discount,
48
+ }
49
+ }
50
+
51
+ actions = {
52
+ setActive: (
53
+ context: RepositoryContext,
54
+ resource: Writable<StandalonePrice>,
55
+ action: StandalonePriceChangeActiveAction
56
+ ) => {
57
+ resource.active = action.active
58
+ },
59
+ changeValue: (
60
+ context: RepositoryContext,
61
+ resource: Writable<StandalonePrice>,
62
+ action: StandalonePriceChangeValueAction
63
+ ) => {
64
+ resource.value = createTypedMoney(action.value)
65
+ },
66
+ setDiscountedPrice: (
67
+ context: RepositoryContext,
68
+ resource: Writable<StandalonePrice>,
69
+ action: StandalonePriceSetDiscountedPriceAction
70
+ ) => {
71
+ resource.discounted = action.discounted ? this.transformDiscountDraft(action.discounted) : undefined
72
+ }
73
+ }
36
74
  }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  Image,
3
+ PriceDraft,
3
4
  Product,
4
5
  ProductData,
5
6
  ProductDraft,
@@ -26,6 +27,15 @@ const publishedProductDraft: ProductDraft = {
26
27
  value: 'test',
27
28
  },
28
29
  ],
30
+ prices: [
31
+ {
32
+ country: 'NL',
33
+ value: {
34
+ currencyCode: 'EUR',
35
+ centAmount: 1000,
36
+ },
37
+ },
38
+ ],
29
39
  },
30
40
  variants: [
31
41
  {
@@ -36,6 +46,15 @@ const publishedProductDraft: ProductDraft = {
36
46
  value: 'test2',
37
47
  },
38
48
  ],
49
+ prices: [
50
+ {
51
+ country: 'NL',
52
+ value: {
53
+ currencyCode: 'EUR',
54
+ centAmount: 2000,
55
+ },
56
+ },
57
+ ],
39
58
  },
40
59
  ],
41
60
  slug: {
@@ -402,4 +421,109 @@ describe('Product update actions', () => {
402
421
  { url: 'http://example.com/image1', dimensions: { w: 100, h: 100 } },
403
422
  ])
404
423
  })
424
+
425
+ test('addPrice variant', async () => {
426
+ assert(productPublished, 'product not created')
427
+
428
+ const priceDraft: PriceDraft = {
429
+ country: 'BE',
430
+ value: {
431
+ currencyCode: 'EUR',
432
+ centAmount: 3000,
433
+ },
434
+ }
435
+
436
+ const response = await supertest(ctMock.app)
437
+ .post(`/dummy/products/${productPublished.id}`)
438
+ .send({
439
+ version: 1,
440
+ actions: [
441
+ {
442
+ action: 'addPrice',
443
+ price: priceDraft,
444
+ variantId: 1,
445
+ },
446
+ ],
447
+ })
448
+ expect(response.status).toBe(200)
449
+ expect(response.body.version).toBe(2)
450
+ expect(response.body.masterData.staged.masterVariant.prices).toMatchObject([
451
+ {
452
+ country: 'NL',
453
+ value: {
454
+ currencyCode: 'EUR',
455
+ centAmount: 1000,
456
+ },
457
+ },
458
+ {
459
+ country: 'BE',
460
+ value: {
461
+ currencyCode: 'EUR',
462
+ centAmount: 3000,
463
+ },
464
+ },
465
+ ])
466
+ })
467
+
468
+ test('changePrice variant', async () => {
469
+ assert(productPublished, 'product not created')
470
+ const priceId =
471
+ productPublished?.masterData.current.masterVariant.prices?.[0].id
472
+ assert(priceId)
473
+
474
+ const priceDraft: PriceDraft = {
475
+ country: 'BE',
476
+ value: {
477
+ currencyCode: 'EUR',
478
+ centAmount: 3000,
479
+ },
480
+ }
481
+
482
+ const response = await supertest(ctMock.app)
483
+ .post(`/dummy/products/${productPublished.id}`)
484
+ .send({
485
+ version: 1,
486
+ actions: [
487
+ {
488
+ action: 'changePrice',
489
+ priceId,
490
+ price: priceDraft,
491
+ },
492
+ ],
493
+ })
494
+ expect(response.status).toBe(200)
495
+ expect(response.body.version).toBe(2)
496
+ expect(response.body.masterData.staged.masterVariant.prices).toMatchObject([
497
+ {
498
+ id: priceId,
499
+ country: 'BE',
500
+ value: {
501
+ currencyCode: 'EUR',
502
+ centAmount: 3000,
503
+ },
504
+ },
505
+ ])
506
+ })
507
+
508
+ test('removePrice variant', async () => {
509
+ assert(productPublished, 'product not created')
510
+ const priceId =
511
+ productPublished?.masterData.current.masterVariant.prices?.[0].id
512
+ assert(priceId)
513
+
514
+ const response = await supertest(ctMock.app)
515
+ .post(`/dummy/products/${productPublished.id}`)
516
+ .send({
517
+ version: 1,
518
+ actions: [
519
+ {
520
+ action: 'removePrice',
521
+ priceId,
522
+ },
523
+ ],
524
+ })
525
+ expect(response.status).toBe(200)
526
+ expect(response.body.version).toBe(2)
527
+ expect(response.body.masterData.staged.masterVariant.prices).toHaveLength(0)
528
+ })
405
529
  })