@labdigital/commercetools-mock 0.5.13 → 0.5.14
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 +6 -2
- package/dist/commercetools-mock.cjs.development.js +3058 -1645
- package/dist/commercetools-mock.cjs.development.js.map +1 -1
- package/dist/commercetools-mock.cjs.production.min.js +1 -1
- package/dist/commercetools-mock.cjs.production.min.js.map +1 -1
- package/dist/commercetools-mock.esm.js +3058 -1645
- package/dist/commercetools-mock.esm.js.map +1 -1
- package/dist/ctMock.d.ts +1 -0
- package/dist/repositories/abstract.d.ts +9 -7
- package/dist/repositories/cart-discount.d.ts +9 -0
- package/dist/repositories/cart.d.ts +16 -3
- package/dist/repositories/category.d.ts +18 -0
- package/dist/repositories/channel.d.ts +6 -0
- package/dist/repositories/custom-object.d.ts +2 -2
- package/dist/repositories/customer-group.d.ts +11 -0
- package/dist/repositories/customer.d.ts +2 -2
- package/dist/repositories/discount-code.d.ts +8 -0
- package/dist/repositories/errors.d.ts +2 -2
- package/dist/repositories/extension.d.ts +8 -0
- package/dist/repositories/inventory-entry.d.ts +2 -2
- package/dist/repositories/order.d.ts +2 -2
- package/dist/repositories/payment.d.ts +4 -3
- package/dist/repositories/product-projection.d.ts +2 -2
- package/dist/repositories/product-type.d.ts +5 -4
- package/dist/repositories/product.d.ts +2 -2
- package/dist/repositories/project.d.ts +8 -0
- package/dist/repositories/shipping-method.d.ts +7 -5
- package/dist/repositories/shopping-list.d.ts +2 -2
- package/dist/repositories/state.d.ts +5 -4
- package/dist/repositories/store.d.ts +6 -3
- package/dist/repositories/subscription.d.ts +6 -0
- package/dist/repositories/tax-category.d.ts +6 -5
- package/dist/repositories/type.d.ts +5 -3
- package/dist/repositories/zone.d.ts +8 -0
- package/dist/services/abstract.d.ts +4 -3
- package/dist/services/cart-discount.d.ts +9 -0
- package/dist/services/category.d.ts +9 -0
- package/dist/services/channel.d.ts +9 -0
- package/dist/services/customer-group.d.ts +9 -0
- package/dist/services/discount-code.d.ts +9 -0
- package/dist/services/extension.d.ts +9 -0
- package/dist/services/my-cart.d.ts +11 -0
- package/dist/services/project.d.ts +11 -0
- package/dist/services/subscription.d.ts +9 -0
- package/dist/services/zone.d.ts +9 -0
- package/dist/storage.d.ts +10 -1
- package/dist/types.d.ts +2 -1
- package/package.json +20 -20
- package/src/ctMock.ts +44 -17
- package/src/oauth/server.ts +3 -1
- package/src/repositories/abstract.ts +39 -33
- package/src/repositories/cart-discount.ts +140 -0
- package/src/repositories/cart.ts +247 -3
- package/src/repositories/category.ts +140 -0
- package/src/repositories/channel.ts +23 -0
- package/src/repositories/custom-object.ts +2 -2
- package/src/repositories/customer-group.ts +42 -0
- package/src/repositories/customer.ts +2 -2
- package/src/repositories/discount-code.ts +143 -0
- package/src/repositories/errors.ts +7 -2
- package/src/repositories/extension.ts +65 -0
- package/src/repositories/inventory-entry.ts +2 -2
- package/src/repositories/order.ts +2 -2
- package/src/repositories/payment.ts +10 -6
- package/src/repositories/product-projection.ts +2 -2
- package/src/repositories/product-type.ts +57 -4
- package/src/repositories/product.ts +4 -2
- package/src/repositories/project.ts +150 -0
- package/src/repositories/shipping-method.ts +149 -13
- package/src/repositories/shopping-list.ts +2 -2
- package/src/repositories/state.ts +48 -4
- package/src/repositories/store.ts +69 -4
- package/src/repositories/subscription.ts +50 -0
- package/src/repositories/tax-category.ts +80 -6
- package/src/repositories/type.ts +133 -3
- package/src/repositories/zone.ts +77 -0
- package/src/server.ts +6 -1
- package/src/services/abstract.ts +16 -15
- package/src/services/cart-discount.ts +17 -0
- package/src/services/cart.test.ts +314 -3
- package/src/services/category.test.ts +37 -0
- package/src/services/category.ts +17 -0
- package/src/services/channel.ts +17 -0
- package/src/services/custom-object.test.ts +3 -3
- package/src/services/customer-group.ts +17 -0
- package/src/services/discount-code.ts +17 -0
- package/src/services/extension.ts +17 -0
- package/src/services/inventory-entry.test.ts +3 -3
- package/src/services/my-cart.test.ts +93 -0
- package/src/services/my-cart.ts +44 -0
- package/src/services/my-payment.test.ts +2 -2
- package/src/services/order.test.ts +4 -4
- package/src/services/payment.test.ts +2 -2
- package/src/services/product-projection.test.ts +1 -5
- package/src/services/product-type.test.ts +2 -2
- package/src/services/product.test.ts +6 -2
- package/src/services/project.ts +42 -0
- package/src/services/shipping-method.test.ts +3 -3
- package/src/services/state.test.ts +2 -2
- package/src/services/subscription.ts +17 -0
- package/src/services/tax-category.test.ts +3 -3
- package/src/services/zone.ts +17 -0
- package/src/storage.ts +69 -1
- package/src/types.ts +2 -1
package/src/ctMock.ts
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
|
-
import { ShoppingListService } from './services/shopping-list'
|
|
2
|
-
import { ProductProjectionService } from './services/product-projection'
|
|
3
|
-
import { ProductTypeService } from './services/product-type'
|
|
4
|
-
import { ShippingMethodService } from './services/shipping-method'
|
|
5
|
-
import { StateService } from './services/state'
|
|
6
|
-
import { TaxCategoryService } from './services/tax-category'
|
|
7
|
-
import { PaymentService } from './services/payment'
|
|
8
1
|
import nock from 'nock'
|
|
9
2
|
import express, { NextFunction, Request, Response } from 'express'
|
|
10
3
|
import supertest from 'supertest'
|
|
11
4
|
import morgan from 'morgan'
|
|
12
5
|
import { AbstractStorage, InMemoryStorage } from './storage'
|
|
13
|
-
import { TypeService } from './services/type'
|
|
14
|
-
import { CustomObjectService } from './services/custom-object'
|
|
15
|
-
import { CustomerService } from './services/customer'
|
|
16
|
-
import { CartService } from './services/cart'
|
|
17
|
-
import { InventoryEntryService } from './services/inventory-entry'
|
|
18
|
-
import { OrderService } from './services/order'
|
|
19
6
|
import { Services } from './types'
|
|
20
|
-
import { StoreService } from './services/store'
|
|
21
7
|
import { CommercetoolsError } from './exceptions'
|
|
22
8
|
import { OAuth2Server } from './oauth/server'
|
|
23
|
-
import { DEFAULT_API_HOSTNAME, DEFAULT_AUTH_HOSTNAME } from './constants'
|
|
24
9
|
import { ProjectAPI } from './projectAPI'
|
|
25
10
|
import { copyHeaders } from './lib/proxy'
|
|
26
|
-
import {
|
|
11
|
+
import { DEFAULT_API_HOSTNAME, DEFAULT_AUTH_HOSTNAME } from './constants'
|
|
12
|
+
|
|
13
|
+
// Services
|
|
14
|
+
import { CartDiscountService } from './services/cart-discount'
|
|
15
|
+
import { CartService } from './services/cart'
|
|
16
|
+
import { CategoryServices } from './services/category'
|
|
17
|
+
import { ChannelService } from './services/channel'
|
|
18
|
+
import { CustomerGroupService } from './services/customer-group'
|
|
19
|
+
import { CustomerService } from './services/customer'
|
|
20
|
+
import { CustomObjectService } from './services/custom-object'
|
|
21
|
+
import { DiscountCodeService } from './services/discount-code'
|
|
22
|
+
import { ExtensionServices } from './services/extension'
|
|
23
|
+
import { InventoryEntryService } from './services/inventory-entry'
|
|
24
|
+
import { MyCartService } from './services/my-cart'
|
|
27
25
|
import { MyPaymentService } from './services/my-payment'
|
|
26
|
+
import { OrderService } from './services/order'
|
|
27
|
+
import { PaymentService } from './services/payment'
|
|
28
|
+
import { ProductProjectionService } from './services/product-projection'
|
|
29
|
+
import { ProductService } from './services/product'
|
|
30
|
+
import { ProductTypeService } from './services/product-type'
|
|
31
|
+
import { ProjectService } from './services/project'
|
|
32
|
+
import { ShippingMethodService } from './services/shipping-method'
|
|
33
|
+
import { ShoppingListService } from './services/shopping-list'
|
|
34
|
+
import { StateService } from './services/state'
|
|
35
|
+
import { StoreService } from './services/store'
|
|
36
|
+
import { SubscriptionService } from './services/subscription'
|
|
37
|
+
import { TaxCategoryService } from './services/tax-category'
|
|
38
|
+
import { TypeService } from './services/type'
|
|
39
|
+
import { ZoneService } from './services/zone'
|
|
28
40
|
|
|
29
41
|
export type CommercetoolsMockOptions = {
|
|
30
42
|
validateCredentials: boolean
|
|
@@ -57,10 +69,12 @@ export class CommercetoolsMock {
|
|
|
57
69
|
api: nock.Scope | undefined
|
|
58
70
|
} = { auth: undefined, api: undefined }
|
|
59
71
|
private _services: Services
|
|
72
|
+
private _projectService?: ProjectService
|
|
60
73
|
|
|
61
74
|
constructor(options: Partial<CommercetoolsMockOptions> = {}) {
|
|
62
75
|
this.options = { ...DEFAULT_OPTIONS, ...options }
|
|
63
76
|
this._services = {}
|
|
77
|
+
this._projectService = undefined
|
|
64
78
|
|
|
65
79
|
this._storage = new InMemoryStorage()
|
|
66
80
|
this._oauth2 = new OAuth2Server({
|
|
@@ -103,9 +117,10 @@ export class CommercetoolsMock {
|
|
|
103
117
|
|
|
104
118
|
runServer(port: number = 3000, options?: AppOptions) {
|
|
105
119
|
const app = this.createApp(options)
|
|
106
|
-
app.listen(port, () => {
|
|
120
|
+
const server = app.listen(port, () => {
|
|
107
121
|
console.log(`Mock server listening at http://localhost:${port}`)
|
|
108
122
|
})
|
|
123
|
+
server.keepAliveTimeout = 60 * 1000
|
|
109
124
|
}
|
|
110
125
|
|
|
111
126
|
private createApp(options?: AppOptions): express.Express {
|
|
@@ -126,9 +141,17 @@ export class CommercetoolsMock {
|
|
|
126
141
|
app.use('/:projectKey', projectRouter)
|
|
127
142
|
}
|
|
128
143
|
|
|
144
|
+
this._projectService = new ProjectService(projectRouter, this._storage)
|
|
145
|
+
|
|
129
146
|
this._services = {
|
|
147
|
+
category: new CategoryServices(projectRouter, this._storage),
|
|
130
148
|
cart: new CartService(projectRouter, this._storage),
|
|
149
|
+
'cart-discount': new CartDiscountService(projectRouter, this._storage),
|
|
131
150
|
customer: new CustomerService(projectRouter, this._storage),
|
|
151
|
+
channel: new ChannelService(projectRouter, this._storage),
|
|
152
|
+
'customer-group': new CustomerGroupService(projectRouter, this._storage),
|
|
153
|
+
'discount-code': new DiscountCodeService(projectRouter, this._storage),
|
|
154
|
+
extension: new ExtensionServices(projectRouter, this._storage),
|
|
132
155
|
'inventory-entry': new InventoryEntryService(
|
|
133
156
|
projectRouter,
|
|
134
157
|
this._storage
|
|
@@ -139,6 +162,7 @@ export class CommercetoolsMock {
|
|
|
139
162
|
),
|
|
140
163
|
order: new OrderService(projectRouter, this._storage),
|
|
141
164
|
payment: new PaymentService(projectRouter, this._storage),
|
|
165
|
+
'my-cart': new MyCartService(projectRouter, this._storage),
|
|
142
166
|
'my-payment': new MyPaymentService(projectRouter, this._storage),
|
|
143
167
|
'shipping-method': new ShippingMethodService(
|
|
144
168
|
projectRouter,
|
|
@@ -153,8 +177,10 @@ export class CommercetoolsMock {
|
|
|
153
177
|
'shopping-list': new ShoppingListService(projectRouter, this._storage),
|
|
154
178
|
state: new StateService(projectRouter, this._storage),
|
|
155
179
|
store: new StoreService(projectRouter, this._storage),
|
|
180
|
+
subscription: new SubscriptionService(projectRouter, this._storage),
|
|
156
181
|
'tax-category': new TaxCategoryService(projectRouter, this._storage),
|
|
157
182
|
type: new TypeService(projectRouter, this._storage),
|
|
183
|
+
zone: new ZoneService(projectRouter, this._storage),
|
|
158
184
|
}
|
|
159
185
|
|
|
160
186
|
app.use((err: Error, req: Request, resp: Response, next: NextFunction) => {
|
|
@@ -165,6 +191,7 @@ export class CommercetoolsMock {
|
|
|
165
191
|
errors: [err.info],
|
|
166
192
|
})
|
|
167
193
|
} else {
|
|
194
|
+
console.error(err)
|
|
168
195
|
return resp.status(500).send({
|
|
169
196
|
error: err.message,
|
|
170
197
|
})
|
package/src/oauth/server.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import auth from 'basic-auth'
|
|
2
|
+
import bodyParser from 'body-parser'
|
|
2
3
|
import express, { NextFunction, Request, Response } from 'express'
|
|
3
4
|
import {
|
|
4
5
|
AccessDeniedError,
|
|
@@ -18,6 +19,7 @@ export class OAuth2Server {
|
|
|
18
19
|
|
|
19
20
|
createRouter() {
|
|
20
21
|
const router = express.Router()
|
|
22
|
+
router.use(bodyParser.urlencoded({ extended: true }))
|
|
21
23
|
router.post('/token', this.tokenHandler.bind(this))
|
|
22
24
|
return router
|
|
23
25
|
}
|
|
@@ -81,7 +83,7 @@ export class OAuth2Server {
|
|
|
81
83
|
)
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
const grantType = request.query.grant_type
|
|
86
|
+
const grantType = request.query.grant_type || request.body.grant_type
|
|
85
87
|
if (!grantType) {
|
|
86
88
|
return next(
|
|
87
89
|
new CommercetoolsError<InvalidRequestError>(
|
|
@@ -4,6 +4,7 @@ import deepEqual from 'deep-equal'
|
|
|
4
4
|
import {
|
|
5
5
|
BaseResource,
|
|
6
6
|
InvalidOperationError,
|
|
7
|
+
Project,
|
|
7
8
|
UpdateAction,
|
|
8
9
|
} from '@commercetools/platform-sdk'
|
|
9
10
|
import { AbstractStorage } from '../storage'
|
|
@@ -19,20 +20,50 @@ export type GetParams = {
|
|
|
19
20
|
expand?: string[]
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export
|
|
23
|
+
export abstract class AbstractRepository {
|
|
23
24
|
protected _storage: AbstractStorage
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} = {}
|
|
25
|
+
protected actions: Partial<
|
|
26
|
+
Record<any, (projectKey: string, resource: any, action: any) => void>
|
|
27
|
+
> = {}
|
|
28
28
|
|
|
29
29
|
constructor(storage: AbstractStorage) {
|
|
30
30
|
this._storage = storage
|
|
31
|
-
this._storage.assertStorage(this.getTypeId())
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
abstract
|
|
33
|
+
abstract save(projectKey: string, resource: BaseResource | Project): void
|
|
34
|
+
|
|
35
|
+
processUpdateActions(
|
|
36
|
+
projectKey: string,
|
|
37
|
+
resource: BaseResource | Project,
|
|
38
|
+
actions: UpdateAction[]
|
|
39
|
+
): BaseResource {
|
|
40
|
+
// Deep-copy
|
|
41
|
+
const modifiedResource = JSON.parse(JSON.stringify(resource))
|
|
42
|
+
|
|
43
|
+
actions.forEach(action => {
|
|
44
|
+
const updateFunc = this.actions[action.action]
|
|
45
|
+
if (!updateFunc) {
|
|
46
|
+
console.error(`No mock implemented for update action ${action.action}`)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
updateFunc(projectKey, modifiedResource, action)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (!deepEqual(modifiedResource, resource)) {
|
|
53
|
+
this.save(projectKey, modifiedResource)
|
|
54
|
+
}
|
|
55
|
+
return modifiedResource
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
35
60
|
abstract create(projectKey: string, draft: any): BaseResource
|
|
61
|
+
abstract getTypeId(): RepositoryTypes
|
|
62
|
+
|
|
63
|
+
constructor(storage: AbstractStorage) {
|
|
64
|
+
super(storage)
|
|
65
|
+
this._storage.assertStorage(this.getTypeId())
|
|
66
|
+
}
|
|
36
67
|
|
|
37
68
|
query(projectKey: string, params: QueryParams = {}) {
|
|
38
69
|
return this._storage.query(projectKey, this.getTypeId(), {
|
|
@@ -66,8 +97,6 @@ export default abstract class AbstractRepository {
|
|
|
66
97
|
}
|
|
67
98
|
|
|
68
99
|
save(projectKey: string, resource: BaseResource) {
|
|
69
|
-
const typeId = this.getTypeId()
|
|
70
|
-
|
|
71
100
|
const current = this.get(projectKey, resource.id)
|
|
72
101
|
|
|
73
102
|
if (current) {
|
|
@@ -86,29 +115,6 @@ export default abstract class AbstractRepository {
|
|
|
86
115
|
|
|
87
116
|
// @ts-ignore
|
|
88
117
|
resource.version += 1
|
|
89
|
-
this._storage.add(projectKey,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
processUpdateActions(
|
|
93
|
-
projectKey: string,
|
|
94
|
-
resource: BaseResource,
|
|
95
|
-
actions: UpdateAction[]
|
|
96
|
-
): BaseResource {
|
|
97
|
-
// Deep-copy
|
|
98
|
-
const modifiedResource = JSON.parse(JSON.stringify(resource))
|
|
99
|
-
|
|
100
|
-
actions.forEach(action => {
|
|
101
|
-
const updateFunc = this.actions[action.action]
|
|
102
|
-
if (!updateFunc) {
|
|
103
|
-
console.error(`No mock implemented for update action ${action.action}`)
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
updateFunc(projectKey, modifiedResource, action)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
if (!deepEqual(modifiedResource, resource)) {
|
|
110
|
-
this.save(projectKey, modifiedResource)
|
|
111
|
-
}
|
|
112
|
-
return modifiedResource
|
|
118
|
+
this._storage.add(projectKey, this.getTypeId(), resource as any)
|
|
113
119
|
}
|
|
114
120
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CartDiscount,
|
|
3
|
+
CartDiscountChangeIsActiveAction,
|
|
4
|
+
CartDiscountChangeSortOrderAction,
|
|
5
|
+
CartDiscountDraft,
|
|
6
|
+
CartDiscountSetDescriptionAction,
|
|
7
|
+
CartDiscountSetKeyAction,
|
|
8
|
+
CartDiscountSetValidFromAction,
|
|
9
|
+
CartDiscountSetValidFromAndUntilAction,
|
|
10
|
+
CartDiscountSetValidUntilAction,
|
|
11
|
+
CartDiscountUpdateAction,
|
|
12
|
+
CartDiscountValueAbsolute,
|
|
13
|
+
CartDiscountValueDraft,
|
|
14
|
+
CartDiscountValueFixed,
|
|
15
|
+
CartDiscountValueGiftLineItem,
|
|
16
|
+
CartDiscountValueRelative,
|
|
17
|
+
ReferenceTypeId,
|
|
18
|
+
} from '@commercetools/platform-sdk'
|
|
19
|
+
import { Writable } from 'types'
|
|
20
|
+
import { getBaseResourceProperties } from '../helpers'
|
|
21
|
+
import { AbstractResourceRepository } from './abstract'
|
|
22
|
+
import { createTypedMoney } from './helpers'
|
|
23
|
+
|
|
24
|
+
export class CartDiscountRepository extends AbstractResourceRepository {
|
|
25
|
+
getTypeId(): ReferenceTypeId {
|
|
26
|
+
return 'cart-discount'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
create(projectKey: string, draft: CartDiscountDraft): CartDiscount {
|
|
30
|
+
const resource: CartDiscount = {
|
|
31
|
+
...getBaseResourceProperties(),
|
|
32
|
+
key: draft.key,
|
|
33
|
+
description: draft.description,
|
|
34
|
+
cartPredicate: draft.cartPredicate,
|
|
35
|
+
isActive: draft.isActive || false,
|
|
36
|
+
name: draft.name,
|
|
37
|
+
references: [],
|
|
38
|
+
target: draft.target,
|
|
39
|
+
requiresDiscountCode: draft.requiresDiscountCode || false,
|
|
40
|
+
sortOrder: draft.sortOrder,
|
|
41
|
+
stackingMode: draft.stackingMode || 'Stacking',
|
|
42
|
+
validFrom: draft.validFrom,
|
|
43
|
+
validUntil: draft.validUntil,
|
|
44
|
+
value: this.transformValueDraft(draft.value),
|
|
45
|
+
}
|
|
46
|
+
this.save(projectKey, resource)
|
|
47
|
+
return resource
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private transformValueDraft(value: CartDiscountValueDraft) {
|
|
51
|
+
switch (value.type) {
|
|
52
|
+
case 'absolute': {
|
|
53
|
+
return {
|
|
54
|
+
type: 'absolute',
|
|
55
|
+
money: value.money.map(createTypedMoney),
|
|
56
|
+
} as CartDiscountValueAbsolute
|
|
57
|
+
}
|
|
58
|
+
case 'fixed': {
|
|
59
|
+
return {
|
|
60
|
+
type: 'fixed',
|
|
61
|
+
money: value.money.map(createTypedMoney),
|
|
62
|
+
} as CartDiscountValueFixed
|
|
63
|
+
}
|
|
64
|
+
case 'giftLineItem': {
|
|
65
|
+
return {
|
|
66
|
+
...value,
|
|
67
|
+
} as CartDiscountValueGiftLineItem
|
|
68
|
+
}
|
|
69
|
+
case 'relative': {
|
|
70
|
+
return {
|
|
71
|
+
...value,
|
|
72
|
+
} as CartDiscountValueRelative
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
actions: Partial<
|
|
80
|
+
Record<
|
|
81
|
+
CartDiscountUpdateAction['action'],
|
|
82
|
+
(
|
|
83
|
+
projectKey: string,
|
|
84
|
+
resource: Writable<CartDiscount>,
|
|
85
|
+
action: any
|
|
86
|
+
) => void
|
|
87
|
+
>
|
|
88
|
+
> = {
|
|
89
|
+
setKey: (
|
|
90
|
+
projectKey: string,
|
|
91
|
+
resource: Writable<CartDiscount>,
|
|
92
|
+
{ key }: CartDiscountSetKeyAction
|
|
93
|
+
) => {
|
|
94
|
+
resource.key = key
|
|
95
|
+
},
|
|
96
|
+
setDescription: (
|
|
97
|
+
projectKey: string,
|
|
98
|
+
resource: Writable<CartDiscount>,
|
|
99
|
+
{ description }: CartDiscountSetDescriptionAction
|
|
100
|
+
) => {
|
|
101
|
+
resource.description = description
|
|
102
|
+
},
|
|
103
|
+
setValidFrom: (
|
|
104
|
+
projectKey: string,
|
|
105
|
+
resource: Writable<CartDiscount>,
|
|
106
|
+
{ validFrom }: CartDiscountSetValidFromAction
|
|
107
|
+
) => {
|
|
108
|
+
resource.validFrom = validFrom
|
|
109
|
+
},
|
|
110
|
+
setValidUntil: (
|
|
111
|
+
projectKey: string,
|
|
112
|
+
resource: Writable<CartDiscount>,
|
|
113
|
+
{ validUntil }: CartDiscountSetValidUntilAction
|
|
114
|
+
) => {
|
|
115
|
+
resource.validUntil = validUntil
|
|
116
|
+
},
|
|
117
|
+
setValidFromAndUntil: (
|
|
118
|
+
projectKey: string,
|
|
119
|
+
resource: Writable<CartDiscount>,
|
|
120
|
+
{ validFrom, validUntil }: CartDiscountSetValidFromAndUntilAction
|
|
121
|
+
) => {
|
|
122
|
+
resource.validFrom = validFrom
|
|
123
|
+
resource.validUntil = validUntil
|
|
124
|
+
},
|
|
125
|
+
changeSortOrder: (
|
|
126
|
+
projectKey: string,
|
|
127
|
+
resource: Writable<CartDiscount>,
|
|
128
|
+
{ sortOrder }: CartDiscountChangeSortOrderAction
|
|
129
|
+
) => {
|
|
130
|
+
resource.sortOrder = sortOrder
|
|
131
|
+
},
|
|
132
|
+
changeIsActive: (
|
|
133
|
+
projectKey: string,
|
|
134
|
+
resource: Writable<CartDiscount>,
|
|
135
|
+
{ isActive }: CartDiscountChangeIsActiveAction
|
|
136
|
+
) => {
|
|
137
|
+
resource.isActive = isActive
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
}
|
package/src/repositories/cart.ts
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Cart,
|
|
3
|
+
CartAddLineItemAction,
|
|
4
|
+
CartDraft,
|
|
5
|
+
CartRemoveLineItemAction,
|
|
6
|
+
CartSetBillingAddressAction,
|
|
7
|
+
CartSetCountryAction,
|
|
8
|
+
CartSetCustomerEmailAction,
|
|
9
|
+
CartSetCustomFieldAction,
|
|
10
|
+
CartSetCustomTypeAction,
|
|
11
|
+
CartSetLocaleAction,
|
|
12
|
+
CartSetShippingAddressAction,
|
|
13
|
+
GeneralError,
|
|
14
|
+
LineItem,
|
|
15
|
+
Product,
|
|
16
|
+
ProductPagedQueryResponse,
|
|
17
|
+
ProductVariant,
|
|
18
|
+
ReferenceTypeId,
|
|
19
|
+
} from '@commercetools/platform-sdk'
|
|
20
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
2
21
|
import { getBaseResourceProperties } from '../helpers'
|
|
3
|
-
import
|
|
22
|
+
import { AbstractResourceRepository } from './abstract'
|
|
4
23
|
import { createCustomFields } from './helpers'
|
|
24
|
+
import { Writable } from '../types'
|
|
25
|
+
import { CommercetoolsError } from '../exceptions'
|
|
5
26
|
|
|
6
|
-
export class CartRepository extends
|
|
27
|
+
export class CartRepository extends AbstractResourceRepository {
|
|
7
28
|
getTypeId(): ReferenceTypeId {
|
|
8
29
|
return 'cart'
|
|
9
30
|
}
|
|
@@ -30,4 +51,227 @@ export class CartRepository extends AbstractRepository {
|
|
|
30
51
|
this.save(projectKey, resource)
|
|
31
52
|
return resource
|
|
32
53
|
}
|
|
54
|
+
|
|
55
|
+
getActiveCart(projectKey: string): Cart | undefined {
|
|
56
|
+
// Get first active cart
|
|
57
|
+
const results = this._storage.query(projectKey, this.getTypeId(), {
|
|
58
|
+
where: [`cartState="Active"`],
|
|
59
|
+
})
|
|
60
|
+
if (results.count > 0) {
|
|
61
|
+
return results.results[0] as Cart
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
actions = {
|
|
68
|
+
addLineItem: (
|
|
69
|
+
projectKey: string,
|
|
70
|
+
resource: Writable<Cart>,
|
|
71
|
+
{ productId, variantId, sku, quantity = 1 }: CartAddLineItemAction
|
|
72
|
+
) => {
|
|
73
|
+
let product: Product | null = null
|
|
74
|
+
let variant: ProductVariant | undefined
|
|
75
|
+
|
|
76
|
+
if (productId && variantId) {
|
|
77
|
+
// Fetch product and variant by ID
|
|
78
|
+
product = this._storage.get(projectKey, 'product', productId, {})
|
|
79
|
+
} else if (sku) {
|
|
80
|
+
// Fetch product and variant by SKU
|
|
81
|
+
const items = this._storage.query(projectKey, 'product', {
|
|
82
|
+
where: [
|
|
83
|
+
`masterData(current(masterVariant(sku="${sku}"))) or masterData(current(variants(sku="${sku}")))`,
|
|
84
|
+
],
|
|
85
|
+
}) as ProductPagedQueryResponse
|
|
86
|
+
|
|
87
|
+
if (items.count === 1) {
|
|
88
|
+
product = items.results[0]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!product) {
|
|
93
|
+
// Check if product is found
|
|
94
|
+
throw new CommercetoolsError<GeneralError>({
|
|
95
|
+
code: 'General',
|
|
96
|
+
message: sku
|
|
97
|
+
? `A product containing a variant with SKU '${sku}' not found.`
|
|
98
|
+
: `A product with ID '${productId}' not found.`,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Find matching variant
|
|
103
|
+
variant = [
|
|
104
|
+
product.masterData.current.masterVariant,
|
|
105
|
+
...product.masterData.current.variants,
|
|
106
|
+
].find(x => {
|
|
107
|
+
if (sku) return x.sku === sku
|
|
108
|
+
if (variantId) return x.id === variantId
|
|
109
|
+
return false
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
if (!variant) {
|
|
113
|
+
// Check if variant is found
|
|
114
|
+
throw new CommercetoolsError<GeneralError>({
|
|
115
|
+
code: 'General',
|
|
116
|
+
message: sku
|
|
117
|
+
? `A variant with SKU '${sku}' for product '${product.id}' not found.`
|
|
118
|
+
: `A variant with ID '${variantId}' for product '${product.id}' not found.`,
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const alreadyAdded = resource.lineItems.some(
|
|
123
|
+
x => x.productId === product?.id && x.variant.id === variant?.id
|
|
124
|
+
)
|
|
125
|
+
if (alreadyAdded) {
|
|
126
|
+
// increase quantity and update total price
|
|
127
|
+
resource.lineItems.map(x => {
|
|
128
|
+
if (x.productId === product?.id && x.variant.id === variant?.id) {
|
|
129
|
+
x.quantity += quantity
|
|
130
|
+
x.totalPrice.centAmount = calculateLineItemTotalPrice(x)
|
|
131
|
+
}
|
|
132
|
+
return x
|
|
133
|
+
})
|
|
134
|
+
} else {
|
|
135
|
+
// add line item
|
|
136
|
+
if (!variant.prices?.length) {
|
|
137
|
+
throw new CommercetoolsError<GeneralError>({
|
|
138
|
+
code: 'General',
|
|
139
|
+
message: `A product with ID '${productId}' doesn't have any prices.`,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const price = variant.prices[0]
|
|
144
|
+
resource.lineItems.push({
|
|
145
|
+
id: uuidv4(),
|
|
146
|
+
productId: product.id,
|
|
147
|
+
productKey: product.key,
|
|
148
|
+
name: product.masterData.current.name,
|
|
149
|
+
productSlug: product.masterData.current.slug,
|
|
150
|
+
productType: product.productType,
|
|
151
|
+
variant,
|
|
152
|
+
price: price,
|
|
153
|
+
totalPrice: {
|
|
154
|
+
...price.value,
|
|
155
|
+
centAmount: price.value.centAmount * quantity,
|
|
156
|
+
},
|
|
157
|
+
quantity,
|
|
158
|
+
discountedPricePerQuantity: [],
|
|
159
|
+
lineItemMode: 'Standard',
|
|
160
|
+
priceMode: 'Platform',
|
|
161
|
+
state: [],
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Update cart total price
|
|
166
|
+
resource.totalPrice.centAmount = calculateCartTotalPrice(resource)
|
|
167
|
+
},
|
|
168
|
+
removeLineItem: (
|
|
169
|
+
projectKey: string,
|
|
170
|
+
resource: Writable<Cart>,
|
|
171
|
+
{ lineItemId, quantity }: CartRemoveLineItemAction
|
|
172
|
+
) => {
|
|
173
|
+
const lineItem = resource.lineItems.find(x => x.id === lineItemId)
|
|
174
|
+
if (!lineItem) {
|
|
175
|
+
// Check if product is found
|
|
176
|
+
throw new CommercetoolsError<GeneralError>({
|
|
177
|
+
code: 'General',
|
|
178
|
+
message: `A line item with ID '${lineItemId}' not found.`,
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const shouldDelete = !quantity || quantity >= lineItem.quantity
|
|
183
|
+
if (shouldDelete) {
|
|
184
|
+
// delete line item
|
|
185
|
+
resource.lineItems = resource.lineItems.filter(x => x.id !== lineItemId)
|
|
186
|
+
} else {
|
|
187
|
+
// decrease quantity and update total price
|
|
188
|
+
resource.lineItems.map(x => {
|
|
189
|
+
if (x.id === lineItemId && quantity) {
|
|
190
|
+
x.quantity -= quantity
|
|
191
|
+
x.totalPrice.centAmount = calculateLineItemTotalPrice(x)
|
|
192
|
+
}
|
|
193
|
+
return x
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Update cart total price
|
|
198
|
+
resource.totalPrice.centAmount = calculateCartTotalPrice(resource)
|
|
199
|
+
},
|
|
200
|
+
setBillingAddress: (
|
|
201
|
+
projectKey: string,
|
|
202
|
+
resource: Writable<Cart>,
|
|
203
|
+
{ address }: CartSetBillingAddressAction
|
|
204
|
+
) => {
|
|
205
|
+
resource.billingAddress = address
|
|
206
|
+
},
|
|
207
|
+
setCountry: (
|
|
208
|
+
projectKey: string,
|
|
209
|
+
resource: Writable<Cart>,
|
|
210
|
+
{ country }: CartSetCountryAction
|
|
211
|
+
) => {
|
|
212
|
+
resource.country = country
|
|
213
|
+
},
|
|
214
|
+
setCustomerEmail: (
|
|
215
|
+
projectKey: string,
|
|
216
|
+
resource: Writable<Cart>,
|
|
217
|
+
{ email }: CartSetCustomerEmailAction
|
|
218
|
+
) => {
|
|
219
|
+
resource.customerEmail = email
|
|
220
|
+
},
|
|
221
|
+
setCustomField: (
|
|
222
|
+
projectKey: string,
|
|
223
|
+
resource: Cart,
|
|
224
|
+
{ name, value }: CartSetCustomFieldAction
|
|
225
|
+
) => {
|
|
226
|
+
if (!resource.custom) {
|
|
227
|
+
throw new Error('Resource has no custom field')
|
|
228
|
+
}
|
|
229
|
+
resource.custom.fields[name] = value
|
|
230
|
+
},
|
|
231
|
+
setCustomType: (
|
|
232
|
+
projectKey: string,
|
|
233
|
+
resource: Writable<Cart>,
|
|
234
|
+
{ type, fields }: CartSetCustomTypeAction
|
|
235
|
+
) => {
|
|
236
|
+
if (!type) {
|
|
237
|
+
resource.custom = undefined
|
|
238
|
+
} else {
|
|
239
|
+
const resolvedType = this._storage.getByResourceIdentifier(
|
|
240
|
+
projectKey,
|
|
241
|
+
type
|
|
242
|
+
)
|
|
243
|
+
if (!resolvedType) {
|
|
244
|
+
throw new Error(`Type ${type} not found`)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
resource.custom = {
|
|
248
|
+
type: {
|
|
249
|
+
typeId: 'type',
|
|
250
|
+
id: resolvedType.id,
|
|
251
|
+
},
|
|
252
|
+
fields: fields || [],
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
setLocale: (
|
|
257
|
+
projectKey: string,
|
|
258
|
+
resource: Writable<Cart>,
|
|
259
|
+
{ locale }: CartSetLocaleAction
|
|
260
|
+
) => {
|
|
261
|
+
resource.locale = locale
|
|
262
|
+
},
|
|
263
|
+
setShippingAddress: (
|
|
264
|
+
projectKey: string,
|
|
265
|
+
resource: Writable<Cart>,
|
|
266
|
+
{ address }: CartSetShippingAddressAction
|
|
267
|
+
) => {
|
|
268
|
+
resource.shippingAddress = address
|
|
269
|
+
},
|
|
270
|
+
}
|
|
33
271
|
}
|
|
272
|
+
|
|
273
|
+
const calculateLineItemTotalPrice = (lineItem: LineItem): number =>
|
|
274
|
+
lineItem.price!.value.centAmount * lineItem.quantity
|
|
275
|
+
|
|
276
|
+
const calculateCartTotalPrice = (cart: Cart): number =>
|
|
277
|
+
cart.lineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0)
|