@labdigital/commercetools-mock 2.8.0 → 2.9.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/dist/index.cjs +235 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +235 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/predicateParser.test.ts +96 -11
- package/src/lib/predicateParser.ts +43 -12
- package/src/oauth/server.ts +11 -1
- package/src/repositories/abstract.ts +5 -4
- package/src/repositories/cart.ts +77 -33
- package/src/repositories/custom-object.ts +18 -0
- package/src/repositories/shipping-method.ts +98 -22
- package/src/services/custom-object.ts +19 -0
- package/src/services/shipping-method.test.ts +120 -21
- package/src/services/shipping-method.ts +19 -2
- package/src/shippingCalculator.test.ts +280 -0
- package/src/shippingCalculator.ts +74 -0
- package/src/storage/abstract.ts +1 -1
- package/src/storage/in-memory.ts +82 -59
- package/src/types.ts +2 -0
package/package.json
CHANGED
|
@@ -16,7 +16,67 @@ describe('Predicate filter', () => {
|
|
|
16
16
|
stringProperty: 'foobar',
|
|
17
17
|
booleanProperty: true,
|
|
18
18
|
},
|
|
19
|
+
array: [
|
|
20
|
+
{
|
|
21
|
+
numberProperty: 1234,
|
|
22
|
+
stringProperty: 'foo',
|
|
23
|
+
objectProperty: {
|
|
24
|
+
stringProperty: 'foo',
|
|
25
|
+
booleanProperty: true,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
numberProperty: 2345,
|
|
30
|
+
stringProperty: 'bar',
|
|
31
|
+
objectProperty: {
|
|
32
|
+
stringProperty: 'bar',
|
|
33
|
+
booleanProperty: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
19
37
|
},
|
|
38
|
+
array: [
|
|
39
|
+
{
|
|
40
|
+
nestedArray: [
|
|
41
|
+
{
|
|
42
|
+
stringProperty: 'foo',
|
|
43
|
+
nested: [
|
|
44
|
+
{
|
|
45
|
+
stringProperty: 'foo',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
stringProperty: 'bar',
|
|
51
|
+
nested: [
|
|
52
|
+
{
|
|
53
|
+
stringProperty: 'bar',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
nestedArray: [
|
|
61
|
+
{
|
|
62
|
+
stringProperty: 'foo-2',
|
|
63
|
+
nested: [
|
|
64
|
+
{
|
|
65
|
+
stringProperty: 'foo-2',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
stringProperty: 'bar-2',
|
|
71
|
+
nested: [
|
|
72
|
+
{
|
|
73
|
+
stringProperty: 'bar-2',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
20
80
|
|
|
21
81
|
// Longitude, latitude
|
|
22
82
|
geoLocation: [5.110230209615395, 52.06969591642097],
|
|
@@ -115,6 +175,30 @@ describe('Predicate filter', () => {
|
|
|
115
175
|
).toBeTruthy()
|
|
116
176
|
})
|
|
117
177
|
|
|
178
|
+
test('nestedArray filters on property', async () => {
|
|
179
|
+
expect(match(`nested(array(stringProperty="foo"))`)).toBeTruthy()
|
|
180
|
+
expect(match(`nested(array(stringProperty="bar"))`)).toBeTruthy()
|
|
181
|
+
expect(match(`nested(array(stringProperty="foobar"))`)).toBeFalsy()
|
|
182
|
+
|
|
183
|
+
// One level deeper
|
|
184
|
+
expect(
|
|
185
|
+
match(`nested(array(objectProperty(stringProperty="foo")))`)
|
|
186
|
+
).toBeTruthy()
|
|
187
|
+
expect(
|
|
188
|
+
match(`nested(array(objectProperty(stringProperty="bar")))`)
|
|
189
|
+
).toBeTruthy()
|
|
190
|
+
expect(
|
|
191
|
+
match(`nested(array(objectProperty(stringProperty="foobar")))`)
|
|
192
|
+
).toBeFalsy()
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('array filters on property', async () => {
|
|
196
|
+
expect(match(`array(nestedArray(stringProperty="foo")))`)).toBeTruthy()
|
|
197
|
+
expect(
|
|
198
|
+
match(`array(nestedArray(nested(stringProperty="foo"))))`)
|
|
199
|
+
).toBeTruthy()
|
|
200
|
+
})
|
|
201
|
+
|
|
118
202
|
test('geolocation within circle (...)', async () => {
|
|
119
203
|
expect(
|
|
120
204
|
match(
|
|
@@ -185,17 +269,18 @@ describe('Predicate filter', () => {
|
|
|
185
269
|
).toBeTruthy()
|
|
186
270
|
})
|
|
187
271
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
272
|
+
// TODO: disabled for now, see remark in predicateParser.ts in resolveValue
|
|
273
|
+
// test('lexer confusion', async () => {
|
|
274
|
+
// expect(() => match(`orSomething="foobar"`)).toThrow(PredicateError)
|
|
275
|
+
// expect(() => match(`orSomething="foobar"`)).toThrow(
|
|
276
|
+
// "The field 'orSomething' does not exist."
|
|
277
|
+
// )
|
|
278
|
+
|
|
279
|
+
// expect(() => match(`andSomething="foobar"`)).toThrow(PredicateError)
|
|
280
|
+
// expect(() => match(`andSomething="foobar"`)).toThrow(
|
|
281
|
+
// "The field 'andSomething' does not exist."
|
|
282
|
+
// )
|
|
283
|
+
// })
|
|
199
284
|
|
|
200
285
|
test('invalid predicate', async () => {
|
|
201
286
|
expect(() => match(`stringProperty=nomatch`)).toThrow(PredicateError)
|
|
@@ -96,7 +96,12 @@ const resolveValue = (obj: any, val: TypeSymbol): any => {
|
|
|
96
96
|
.filter((v) => val.value in v)
|
|
97
97
|
.map((v) => v[val.value])
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
|
|
100
|
+
// TODO: We don't really validate the shape of the object here. To actually
|
|
101
|
+
// match commercetools behaviour we should throw an error if the requested
|
|
102
|
+
// field doesn't exist (unless it's a map)
|
|
103
|
+
// throw new PredicateError(`The field '${val.value}' does not exist.`)
|
|
104
|
+
return undefined
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
return obj[val.value]
|
|
@@ -243,11 +248,21 @@ const generateMatchFunc = (predicate: string): MatchFunc => {
|
|
|
243
248
|
const expr = parser.parse()
|
|
244
249
|
lexer.expect(')')
|
|
245
250
|
return (obj: any, vars: object) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
if (Array.isArray(obj)) {
|
|
252
|
+
return obj.some((item) => {
|
|
253
|
+
const value = resolveValue(item, left)
|
|
254
|
+
if (value) {
|
|
255
|
+
return expr(value, vars)
|
|
256
|
+
}
|
|
257
|
+
return false
|
|
258
|
+
})
|
|
259
|
+
} else {
|
|
260
|
+
const value = resolveValue(obj, left)
|
|
261
|
+
if (value) {
|
|
262
|
+
return expr(value, vars)
|
|
263
|
+
}
|
|
264
|
+
return false
|
|
249
265
|
}
|
|
250
|
-
return false
|
|
251
266
|
}
|
|
252
267
|
})
|
|
253
268
|
.bp(')', 0)
|
|
@@ -256,12 +271,23 @@ const generateMatchFunc = (predicate: string): MatchFunc => {
|
|
|
256
271
|
validateSymbol(expr)
|
|
257
272
|
|
|
258
273
|
return (obj: any, vars: VariableMap) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
if (Array.isArray(obj)) {
|
|
275
|
+
return obj.some((item) => {
|
|
276
|
+
const value = resolveValue(item, left)
|
|
277
|
+
const other = resolveSymbol(expr, vars)
|
|
278
|
+
if (Array.isArray(value)) {
|
|
279
|
+
return !!value.some((elem) => elem === other)
|
|
280
|
+
}
|
|
281
|
+
return value === other
|
|
282
|
+
})
|
|
283
|
+
} else {
|
|
284
|
+
const resolvedValue = resolveValue(obj, left)
|
|
285
|
+
const resolvedSymbol = resolveSymbol(expr, vars)
|
|
286
|
+
if (Array.isArray(resolvedValue)) {
|
|
287
|
+
return !!resolvedValue.some((elem) => elem === resolvedSymbol)
|
|
288
|
+
}
|
|
289
|
+
return resolvedValue === resolvedSymbol
|
|
263
290
|
}
|
|
264
|
-
return resolvedValue === resolvedSymbol
|
|
265
291
|
}
|
|
266
292
|
})
|
|
267
293
|
.led('!=', 20, ({ left, bp }) => {
|
|
@@ -350,10 +376,15 @@ const generateMatchFunc = (predicate: string): MatchFunc => {
|
|
|
350
376
|
symbols = [expr]
|
|
351
377
|
}
|
|
352
378
|
|
|
353
|
-
|
|
379
|
+
// The expression can be a list of variables, like
|
|
380
|
+
// :value_1, :value_2, but it can also be one variable
|
|
381
|
+
// containing a list, like :values.
|
|
382
|
+
// So to support both we just flatten the list.
|
|
383
|
+
const inValues = symbols.flatMap((item: TypeSymbol) =>
|
|
354
384
|
resolveSymbol(item, vars)
|
|
355
385
|
)
|
|
356
|
-
|
|
386
|
+
const value = resolveValue(obj, left)
|
|
387
|
+
return inValues.includes(value)
|
|
357
388
|
}
|
|
358
389
|
})
|
|
359
390
|
.led('MATCHES_IGNORE_CASE', 20, ({ left, bp }) => {
|
package/src/oauth/server.ts
CHANGED
|
@@ -24,7 +24,7 @@ export class OAuth2Server {
|
|
|
24
24
|
store: OAuth2Store
|
|
25
25
|
private customerRepository: CustomerRepository
|
|
26
26
|
|
|
27
|
-
constructor(options: { enabled: boolean; validate: boolean }) {
|
|
27
|
+
constructor(private options: { enabled: boolean; validate: boolean }) {
|
|
28
28
|
this.store = new OAuth2Store(options.validate)
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -53,6 +53,16 @@ export class OAuth2Server {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
createMiddleware() {
|
|
56
|
+
if (!this.options.validate) {
|
|
57
|
+
return async (
|
|
58
|
+
request: Request,
|
|
59
|
+
response: Response,
|
|
60
|
+
next: NextFunction
|
|
61
|
+
) => {
|
|
62
|
+
next()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
return async (request: Request, response: Response, next: NextFunction) => {
|
|
57
67
|
const token = getBearerToken(request)
|
|
58
68
|
if (!token) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
BaseResource,
|
|
3
3
|
Project,
|
|
4
|
+
QueryParam,
|
|
4
5
|
ResourceNotFoundError,
|
|
5
6
|
UpdateAction,
|
|
6
7
|
} from '@commercetools/platform-sdk'
|
|
@@ -16,6 +17,9 @@ export type QueryParams = {
|
|
|
16
17
|
where?: string[]
|
|
17
18
|
offset?: number
|
|
18
19
|
limit?: number
|
|
20
|
+
|
|
21
|
+
// Predicate var values. Should always start with `var.`
|
|
22
|
+
[key: string]: QueryParam
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
export type GetParams = {
|
|
@@ -118,10 +122,7 @@ export abstract class AbstractResourceRepository<
|
|
|
118
122
|
|
|
119
123
|
query(context: RepositoryContext, params: QueryParams = {}) {
|
|
120
124
|
const result = this._storage.query(context.projectKey, this.getTypeId(), {
|
|
121
|
-
|
|
122
|
-
where: params.where,
|
|
123
|
-
offset: params.offset,
|
|
124
|
-
limit: params.limit,
|
|
125
|
+
...params,
|
|
125
126
|
})
|
|
126
127
|
|
|
127
128
|
// @ts-ignore
|
package/src/repositories/cart.ts
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
Address,
|
|
3
|
-
AddressDraft,
|
|
4
|
-
Cart,
|
|
5
|
-
CartAddLineItemAction,
|
|
6
|
-
CartChangeLineItemQuantityAction,
|
|
7
|
-
CartAddItemShippingAddressAction,
|
|
8
|
-
CartSetLineItemShippingDetailsAction,
|
|
9
|
-
CartDraft,
|
|
10
|
-
CartRemoveLineItemAction,
|
|
11
|
-
CartSetBillingAddressAction,
|
|
12
|
-
CartSetCountryAction,
|
|
13
|
-
CartSetCustomerEmailAction,
|
|
14
|
-
CartSetCustomFieldAction,
|
|
15
|
-
CartSetCustomTypeAction,
|
|
16
|
-
CartSetLocaleAction,
|
|
17
|
-
CartSetShippingAddressAction,
|
|
18
|
-
CartSetShippingMethodAction,
|
|
19
|
-
CustomFields,
|
|
20
|
-
GeneralError,
|
|
21
|
-
LineItem,
|
|
22
|
-
LineItemDraft,
|
|
23
|
-
ItemShippingDetails,
|
|
24
|
-
Price,
|
|
25
|
-
Product,
|
|
26
|
-
ProductPagedQueryResponse,
|
|
27
|
-
CartRemoveDiscountCodeAction,
|
|
28
|
-
ProductVariant,
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
type AddressDraft,
|
|
4
|
+
type Cart,
|
|
5
|
+
type CartAddLineItemAction,
|
|
6
|
+
type CartChangeLineItemQuantityAction,
|
|
7
|
+
type CartAddItemShippingAddressAction,
|
|
8
|
+
type CartSetLineItemShippingDetailsAction,
|
|
9
|
+
type CartDraft,
|
|
10
|
+
type CartRemoveLineItemAction,
|
|
11
|
+
type CartSetBillingAddressAction,
|
|
12
|
+
type CartSetCountryAction,
|
|
13
|
+
type CartSetCustomerEmailAction,
|
|
14
|
+
type CartSetCustomFieldAction,
|
|
15
|
+
type CartSetCustomTypeAction,
|
|
16
|
+
type CartSetLocaleAction,
|
|
17
|
+
type CartSetShippingAddressAction,
|
|
18
|
+
type CartSetShippingMethodAction,
|
|
19
|
+
type CustomFields,
|
|
20
|
+
type GeneralError,
|
|
21
|
+
type LineItem,
|
|
22
|
+
type LineItemDraft,
|
|
23
|
+
type ItemShippingDetails,
|
|
24
|
+
type Price,
|
|
25
|
+
type Product,
|
|
26
|
+
type ProductPagedQueryResponse,
|
|
27
|
+
type CartRemoveDiscountCodeAction,
|
|
28
|
+
type ProductVariant,
|
|
29
|
+
type CartSetCustomShippingMethodAction,
|
|
29
30
|
} from '@commercetools/platform-sdk'
|
|
30
31
|
import { v4 as uuidv4 } from 'uuid'
|
|
31
32
|
import { CommercetoolsError } from '../exceptions.js'
|
|
@@ -35,7 +36,12 @@ import {
|
|
|
35
36
|
AbstractResourceRepository,
|
|
36
37
|
type RepositoryContext,
|
|
37
38
|
} from './abstract.js'
|
|
38
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
createAddress,
|
|
41
|
+
createCentPrecisionMoney,
|
|
42
|
+
createCustomFields,
|
|
43
|
+
createTypedMoney,
|
|
44
|
+
} from './helpers.js'
|
|
39
45
|
|
|
40
46
|
export class CartRepository extends AbstractResourceRepository<'cart'> {
|
|
41
47
|
getTypeId() {
|
|
@@ -74,6 +80,11 @@ export class CartRepository extends AbstractResourceRepository<'cart'> {
|
|
|
74
80
|
fractionDigits: 0,
|
|
75
81
|
},
|
|
76
82
|
shippingMode: 'Single',
|
|
83
|
+
shippingAddress: createAddress(
|
|
84
|
+
draft.shippingAddress,
|
|
85
|
+
context.projectKey,
|
|
86
|
+
this._storage
|
|
87
|
+
),
|
|
77
88
|
shipping: [],
|
|
78
89
|
origin: draft.origin ?? 'Customer',
|
|
79
90
|
refusedGifts: [],
|
|
@@ -335,10 +346,6 @@ export class CartRepository extends AbstractResourceRepository<'cart'> {
|
|
|
335
346
|
shippingMethod
|
|
336
347
|
)
|
|
337
348
|
|
|
338
|
-
if (!method) {
|
|
339
|
-
throw new Error(`Type ${shippingMethod} not found`)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
349
|
// Based on the address we should select a shipping zone and
|
|
343
350
|
// use that to define the price.
|
|
344
351
|
// @ts-ignore
|
|
@@ -377,6 +384,43 @@ export class CartRepository extends AbstractResourceRepository<'cart'> {
|
|
|
377
384
|
}
|
|
378
385
|
resource.custom.fields[name] = value
|
|
379
386
|
},
|
|
387
|
+
setCustomShippingMethod: (
|
|
388
|
+
context: RepositoryContext,
|
|
389
|
+
resource: Writable<Cart>,
|
|
390
|
+
{
|
|
391
|
+
shippingMethodName,
|
|
392
|
+
shippingRate,
|
|
393
|
+
taxCategory,
|
|
394
|
+
externalTaxRate,
|
|
395
|
+
}: CartSetCustomShippingMethodAction
|
|
396
|
+
) => {
|
|
397
|
+
if (externalTaxRate) {
|
|
398
|
+
throw new Error('External tax rate is not supported')
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const tax = taxCategory
|
|
402
|
+
? this._storage.getByResourceIdentifier<'tax-category'>(
|
|
403
|
+
context.projectKey,
|
|
404
|
+
taxCategory
|
|
405
|
+
)
|
|
406
|
+
: undefined
|
|
407
|
+
|
|
408
|
+
resource.shippingInfo = {
|
|
409
|
+
shippingMethodName,
|
|
410
|
+
price: createCentPrecisionMoney(shippingRate.price),
|
|
411
|
+
shippingRate: {
|
|
412
|
+
price: createTypedMoney(shippingRate.price),
|
|
413
|
+
tiers: [],
|
|
414
|
+
},
|
|
415
|
+
taxCategory: tax
|
|
416
|
+
? {
|
|
417
|
+
typeId: 'tax-category',
|
|
418
|
+
id: tax?.id,
|
|
419
|
+
}
|
|
420
|
+
: undefined,
|
|
421
|
+
shippingMethodState: 'MatchesCart',
|
|
422
|
+
}
|
|
423
|
+
},
|
|
380
424
|
setCustomType: (
|
|
381
425
|
context: RepositoryContext,
|
|
382
426
|
resource: Writable<Cart>,
|
|
@@ -8,6 +8,7 @@ import { cloneObject, getBaseResourceProperties } from '../helpers.js'
|
|
|
8
8
|
import type { Writable } from '../types.js'
|
|
9
9
|
import {
|
|
10
10
|
AbstractResourceRepository,
|
|
11
|
+
QueryParams,
|
|
11
12
|
type RepositoryContext,
|
|
12
13
|
} from './abstract.js'
|
|
13
14
|
import { checkConcurrentModification } from './errors.js'
|
|
@@ -67,6 +68,23 @@ export class CustomObjectRepository extends AbstractResourceRepository<'key-valu
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
queryWithContainer(
|
|
72
|
+
context: RepositoryContext,
|
|
73
|
+
container: string,
|
|
74
|
+
params: QueryParams = {}
|
|
75
|
+
) {
|
|
76
|
+
const whereClause = params.where || []
|
|
77
|
+
whereClause.push(`container="${container}"`)
|
|
78
|
+
const result = this._storage.query(context.projectKey, this.getTypeId(), {
|
|
79
|
+
...params,
|
|
80
|
+
where: whereClause,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
result.results = result.results.map(this.postProcessResource)
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
getWithContainerAndKey(
|
|
71
89
|
context: RepositoryContext,
|
|
72
90
|
container: string,
|
|
@@ -1,34 +1,41 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import {
|
|
2
|
+
InvalidOperationError,
|
|
3
|
+
type ShippingMethod,
|
|
4
|
+
type ShippingMethodAddShippingRateAction,
|
|
5
|
+
type ShippingMethodAddZoneAction,
|
|
6
|
+
type ShippingMethodChangeIsDefaultAction,
|
|
7
|
+
type ShippingMethodChangeNameAction,
|
|
8
|
+
type ShippingMethodDraft,
|
|
9
|
+
type ShippingMethodRemoveZoneAction,
|
|
10
|
+
type ShippingMethodSetCustomFieldAction,
|
|
11
|
+
type ShippingMethodSetCustomTypeAction,
|
|
12
|
+
type ShippingMethodSetDescriptionAction,
|
|
13
|
+
type ShippingMethodSetKeyAction,
|
|
14
|
+
type ShippingMethodSetLocalizedDescriptionAction,
|
|
15
|
+
type ShippingMethodSetLocalizedNameAction,
|
|
16
|
+
type ShippingMethodSetPredicateAction,
|
|
17
|
+
type ShippingMethodUpdateAction,
|
|
18
|
+
type ShippingRate,
|
|
19
|
+
type ShippingRateDraft,
|
|
20
|
+
type ZoneRate,
|
|
21
|
+
type ZoneRateDraft,
|
|
22
|
+
type ZoneReference,
|
|
22
23
|
} from '@commercetools/platform-sdk'
|
|
23
24
|
import deepEqual from 'deep-equal'
|
|
24
25
|
import { getBaseResourceProperties } from '../helpers.js'
|
|
25
26
|
import type { Writable } from '../types.js'
|
|
26
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
AbstractResourceRepository,
|
|
29
|
+
GetParams,
|
|
30
|
+
RepositoryContext,
|
|
31
|
+
} from './abstract.js'
|
|
27
32
|
import {
|
|
28
33
|
createCustomFields,
|
|
29
34
|
createTypedMoney,
|
|
30
35
|
getReferenceFromResourceIdentifier,
|
|
31
36
|
} from './helpers.js'
|
|
37
|
+
import { CommercetoolsError } from '../exceptions.js'
|
|
38
|
+
import { markMatchingShippingRate } from '../shippingCalculator.js'
|
|
32
39
|
|
|
33
40
|
export class ShippingMethodRepository extends AbstractResourceRepository<'shipping-method'> {
|
|
34
41
|
getTypeId() {
|
|
@@ -79,6 +86,75 @@ export class ShippingMethodRepository extends AbstractResourceRepository<'shippi
|
|
|
79
86
|
tiers: rate.tiers || [],
|
|
80
87
|
})
|
|
81
88
|
|
|
89
|
+
/*
|
|
90
|
+
* Retrieves all the ShippingMethods that can ship to the shipping address of
|
|
91
|
+
* the given Cart. Each ShippingMethod contains exactly one ShippingRate with
|
|
92
|
+
* the flag isMatching set to true. This ShippingRate is used when the
|
|
93
|
+
* ShippingMethod is added to the Cart.
|
|
94
|
+
*/
|
|
95
|
+
public matchingCart(
|
|
96
|
+
context: RepositoryContext,
|
|
97
|
+
cartId: string,
|
|
98
|
+
params: GetParams = {}
|
|
99
|
+
) {
|
|
100
|
+
const cart = this._storage.get(context.projectKey, 'cart', cartId)
|
|
101
|
+
if (!cart) {
|
|
102
|
+
return undefined
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!cart.shippingAddress?.country) {
|
|
106
|
+
throw new CommercetoolsError<InvalidOperationError>({
|
|
107
|
+
code: 'InvalidOperation',
|
|
108
|
+
message: `The cart with ID '${cart.id}' does not have a shipping address set.`,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Get all shipping methods that have a zone that matches the shipping address
|
|
113
|
+
const zones = this._storage.query<'zone'>(context.projectKey, 'zone', {
|
|
114
|
+
where: [`locations(country="${cart.shippingAddress.country}"))`],
|
|
115
|
+
limit: 100,
|
|
116
|
+
})
|
|
117
|
+
const zoneIds = zones.results.map((zone) => zone.id)
|
|
118
|
+
const shippingMethods = this.query(context, {
|
|
119
|
+
where: [
|
|
120
|
+
`zoneRates(zone(id in (:zoneIds)))`,
|
|
121
|
+
`zoneRates(shippingRates(price(currencyCode="${cart.totalPrice.currencyCode}")))`,
|
|
122
|
+
],
|
|
123
|
+
'var.zoneIds': zoneIds,
|
|
124
|
+
expand: params.expand,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Make sure that each shipping method has exactly one shipping rate and
|
|
128
|
+
// that the shipping rate is marked as matching
|
|
129
|
+
const results = shippingMethods.results
|
|
130
|
+
.map((shippingMethod) => {
|
|
131
|
+
// Iterate through the zoneRates, process the shipping rates and filter
|
|
132
|
+
// out all zoneRates which have no matching shipping rates left
|
|
133
|
+
const rates = shippingMethod.zoneRates
|
|
134
|
+
.map((zoneRate) => ({
|
|
135
|
+
zone: zoneRate.zone,
|
|
136
|
+
|
|
137
|
+
// Iterate through the shippingRates and mark the matching ones
|
|
138
|
+
// then we filter out the non-matching ones
|
|
139
|
+
shippingRates: zoneRate.shippingRates
|
|
140
|
+
.map((rate) => markMatchingShippingRate(cart, rate))
|
|
141
|
+
.filter((rate) => rate.isMatching),
|
|
142
|
+
}))
|
|
143
|
+
.filter((zoneRate) => zoneRate.shippingRates.length > 0)
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
...shippingMethod,
|
|
147
|
+
zoneRates: rates,
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
.filter((shippingMethod) => shippingMethod.zoneRates.length > 0)
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
...shippingMethods,
|
|
154
|
+
results: results,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
82
158
|
actions: Partial<
|
|
83
159
|
Record<
|
|
84
160
|
ShippingMethodUpdateAction['action'],
|
|
@@ -17,11 +17,30 @@ export class CustomObjectService extends AbstractService {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
extraRoutes(router: Router) {
|
|
20
|
+
router.get('/:container', this.getWithContainer.bind(this))
|
|
20
21
|
router.get('/:container/:key', this.getWithContainerAndKey.bind(this))
|
|
21
22
|
router.post('/:container/:key', this.createWithContainerAndKey.bind(this))
|
|
22
23
|
router.delete('/:container/:key', this.deleteWithContainerAndKey.bind(this))
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
getWithContainer(request: Request, response: Response) {
|
|
27
|
+
const limit = this._parseParam(request.query.limit)
|
|
28
|
+
const offset = this._parseParam(request.query.offset)
|
|
29
|
+
|
|
30
|
+
const result = this.repository.queryWithContainer(
|
|
31
|
+
getRepositoryContext(request),
|
|
32
|
+
request.params.container,
|
|
33
|
+
{
|
|
34
|
+
expand: this._parseParam(request.query.expand),
|
|
35
|
+
where: this._parseParam(request.query.where),
|
|
36
|
+
limit: limit !== undefined ? Number(limit) : undefined,
|
|
37
|
+
offset: offset !== undefined ? Number(offset) : undefined,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return response.status(200).send(result)
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
getWithContainerAndKey(request: Request, response: Response) {
|
|
26
45
|
const result = this.repository.getWithContainerAndKey(
|
|
27
46
|
getRepositoryContext(request),
|