@labdigital/commercetools-mock 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.d.ts +18 -17
- package/dist/index.global.js +1751 -1664
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1773 -1684
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1966 -1877
- package/dist/index.mjs.map +1 -1
- package/package.json +28 -20
- package/src/constants.ts +4 -2
- package/src/ctMock.ts +19 -84
- package/src/helpers.ts +9 -10
- package/src/index.test.ts +1 -1
- package/src/lib/haversine.ts +2 -2
- package/src/lib/masking.ts +3 -1
- package/src/lib/predicateParser.test.ts +16 -0
- package/src/lib/predicateParser.ts +94 -86
- package/src/lib/projectionSearchFilter.test.ts +28 -36
- package/src/lib/projectionSearchFilter.ts +86 -102
- package/src/oauth/store.ts +3 -3
- package/src/priceSelector.test.ts +18 -35
- package/src/priceSelector.ts +6 -9
- package/src/product-projection-search.ts +51 -57
- package/src/repositories/abstract.ts +85 -41
- package/src/repositories/cart-discount.ts +1 -1
- package/src/repositories/cart.ts +36 -31
- package/src/repositories/category.ts +17 -19
- package/src/repositories/channel.ts +1 -1
- package/src/repositories/custom-object.ts +35 -22
- package/src/repositories/customer-group.ts +1 -1
- package/src/repositories/customer.ts +39 -1
- package/src/repositories/discount-code.ts +1 -1
- package/src/repositories/errors.ts +9 -11
- package/src/repositories/extension.ts +13 -11
- package/src/repositories/helpers.ts +8 -13
- package/src/repositories/index.ts +59 -0
- package/src/repositories/inventory-entry.ts +1 -1
- package/src/repositories/order.ts +6 -6
- package/src/repositories/payment.ts +3 -3
- package/src/repositories/product-discount.ts +1 -1
- package/src/repositories/product-projection.ts +1 -0
- package/src/repositories/product-type.ts +29 -34
- package/src/repositories/product.ts +124 -80
- package/src/repositories/project.ts +10 -27
- package/src/repositories/shipping-method.ts +15 -17
- package/src/repositories/shopping-list.ts +2 -2
- package/src/repositories/state.ts +9 -9
- package/src/repositories/store.ts +2 -2
- package/src/repositories/subscription.ts +1 -1
- package/src/repositories/tax-category.ts +4 -4
- package/src/repositories/type.ts +12 -14
- package/src/repositories/zone.ts +5 -6
- package/src/server.ts +5 -0
- package/src/services/abstract.ts +44 -11
- package/src/services/cart-discount.ts +2 -3
- package/src/services/cart.test.ts +8 -10
- package/src/services/cart.ts +8 -11
- package/src/services/category.test.ts +1 -2
- package/src/services/category.ts +2 -3
- package/src/services/channel.ts +2 -3
- package/src/services/custom-object.test.ts +5 -5
- package/src/services/custom-object.ts +2 -3
- package/src/services/customer-group.ts +2 -3
- package/src/services/customer.test.ts +136 -0
- package/src/services/customer.ts +2 -3
- package/src/services/discount-code.ts +2 -3
- package/src/services/extension.ts +2 -3
- package/src/services/index.ts +74 -0
- package/src/services/inventory-entry.test.ts +8 -12
- package/src/services/inventory-entry.ts +2 -3
- package/src/services/my-cart.ts +3 -4
- package/src/services/my-customer.ts +2 -3
- package/src/services/my-order.ts +3 -4
- package/src/services/my-payment.ts +2 -3
- package/src/services/order.test.ts +4 -6
- package/src/services/order.ts +2 -3
- package/src/services/payment.ts +2 -3
- package/src/services/product-discount.ts +2 -3
- package/src/services/product-projection.test.ts +76 -8
- package/src/services/product-projection.ts +2 -3
- package/src/services/product-type.ts +2 -3
- package/src/services/product.test.ts +199 -89
- package/src/services/product.ts +2 -3
- package/src/services/project.ts +3 -3
- package/src/services/shipping-method.ts +2 -3
- package/src/services/shopping-list.ts +2 -3
- package/src/services/state.ts +2 -3
- package/src/services/store.test.ts +11 -2
- package/src/services/store.ts +2 -3
- package/src/services/subscription.ts +2 -3
- package/src/services/tax-category.ts +2 -3
- package/src/services/type.ts +2 -3
- package/src/services/zone.ts +2 -3
- package/src/storage.ts +23 -30
- package/src/types.ts +46 -6
|
@@ -5,14 +5,11 @@ import {
|
|
|
5
5
|
ProductProjection,
|
|
6
6
|
QueryParam,
|
|
7
7
|
FacetResults,
|
|
8
|
-
FacetTerm,
|
|
9
8
|
TermFacetResult,
|
|
10
9
|
RangeFacetResult,
|
|
11
10
|
FilteredFacetResult,
|
|
12
11
|
} from '@commercetools/platform-sdk'
|
|
13
|
-
import { ByProjectKeyProductProjectionsSearchRequestBuilder } from '@commercetools/platform-sdk/dist/declarations/src/generated/client/search/by-project-key-product-projections-search-request-builder'
|
|
14
12
|
import { nestedLookup } from './helpers'
|
|
15
|
-
import { ProductService } from './services/product'
|
|
16
13
|
import { Writable } from './types'
|
|
17
14
|
import { CommercetoolsError } from './exceptions'
|
|
18
15
|
import {
|
|
@@ -60,13 +57,18 @@ export class ProductProjectionSearch {
|
|
|
60
57
|
projectKey: string,
|
|
61
58
|
params: ProductProjectionSearchParams
|
|
62
59
|
): ProductProjectionPagedSearchResponse {
|
|
63
|
-
// Get a copy of all the products in the storage engine. We need a copy
|
|
64
|
-
// since we will be modifying the data.
|
|
65
60
|
let resources = this._storage
|
|
66
61
|
.all(projectKey, 'product')
|
|
67
|
-
.map(r =>
|
|
62
|
+
.map((r) => this.transform(r, params.staged ?? false))
|
|
63
|
+
.filter((p): p is ProductProjection => p !== null)
|
|
64
|
+
.filter((p) => {
|
|
65
|
+
if (!params.staged ?? false) {
|
|
66
|
+
return p.published
|
|
67
|
+
}
|
|
68
|
+
return true
|
|
69
|
+
})
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
const markMatchingVariant = params.markMatchingVariants ?? false
|
|
70
72
|
|
|
71
73
|
// Apply the priceSelector
|
|
72
74
|
applyPriceSelector(resources, {
|
|
@@ -79,15 +81,14 @@ export class ProductProjectionSearch {
|
|
|
79
81
|
// Apply filters pre facetting
|
|
80
82
|
if (params.filter) {
|
|
81
83
|
try {
|
|
82
|
-
const filters = params.filter.map(
|
|
83
|
-
parseFilterExpression(f, params.staged ?? false)
|
|
84
|
-
)
|
|
84
|
+
const filters = params.filter.map(parseFilterExpression)
|
|
85
85
|
|
|
86
86
|
// Filters can modify the output. So clone the resources first.
|
|
87
|
-
resources = resources.filter(resource =>
|
|
88
|
-
filters.every(f => f(resource, markMatchingVariant))
|
|
87
|
+
resources = resources.filter((resource) =>
|
|
88
|
+
filters.every((f) => f(resource, markMatchingVariant))
|
|
89
89
|
)
|
|
90
90
|
} catch (err) {
|
|
91
|
+
console.error(err)
|
|
91
92
|
throw new CommercetoolsError<InvalidInputError>(
|
|
92
93
|
{
|
|
93
94
|
code: 'InvalidInput',
|
|
@@ -104,12 +105,9 @@ export class ProductProjectionSearch {
|
|
|
104
105
|
// Apply filters post facetting
|
|
105
106
|
if (params['filter.query']) {
|
|
106
107
|
try {
|
|
107
|
-
const filters = params['filter.query'].map(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
resources = resources.filter(resource =>
|
|
112
|
-
filters.every(f => f(resource, markMatchingVariant))
|
|
108
|
+
const filters = params['filter.query'].map(parseFilterExpression)
|
|
109
|
+
resources = resources.filter((resource) =>
|
|
110
|
+
filters.every((f) => f(resource, markMatchingVariant))
|
|
113
111
|
)
|
|
114
112
|
} catch (err) {
|
|
115
113
|
throw new CommercetoolsError<InvalidInputError>(
|
|
@@ -122,33 +120,35 @@ export class ProductProjectionSearch {
|
|
|
122
120
|
}
|
|
123
121
|
}
|
|
124
122
|
|
|
125
|
-
// Get the total before slicing the array
|
|
126
|
-
const totalResources = resources.length
|
|
127
|
-
|
|
128
|
-
// Apply offset, limit
|
|
129
|
-
const offset = params.offset || 0
|
|
130
|
-
const limit = params.limit || 20
|
|
131
|
-
resources = resources.slice(offset, offset + limit)
|
|
132
|
-
|
|
133
123
|
// Expand the resources
|
|
134
124
|
if (params.expand !== undefined) {
|
|
135
|
-
resources = resources.map(resource =>
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
resources = resources.map((resource) =>
|
|
126
|
+
this._storage.expand(projectKey, resource, params.expand)
|
|
127
|
+
)
|
|
138
128
|
}
|
|
139
129
|
|
|
130
|
+
// Create a slice for the pagination. If we were working with large datasets
|
|
131
|
+
// then we should have done this before transforming. But that isn't the
|
|
132
|
+
// goal of this library. So lets keep it simple.
|
|
133
|
+
const totalResults = resources.length
|
|
134
|
+
const offset = params.offset || 0
|
|
135
|
+
const limit = params.limit || 20
|
|
136
|
+
const results = resources.slice(offset, offset + limit)
|
|
137
|
+
|
|
140
138
|
return {
|
|
141
|
-
count:
|
|
142
|
-
total:
|
|
139
|
+
count: totalResults,
|
|
140
|
+
total: results.length,
|
|
143
141
|
offset: offset,
|
|
144
142
|
limit: limit,
|
|
145
|
-
results:
|
|
143
|
+
results: results,
|
|
146
144
|
facets: facets,
|
|
147
145
|
}
|
|
148
146
|
}
|
|
149
147
|
|
|
150
|
-
transform(product: Product): ProductProjection {
|
|
151
|
-
const obj = product.masterData.current
|
|
148
|
+
transform(product: Product, staged: boolean): ProductProjection | null {
|
|
149
|
+
const obj = !staged ? product.masterData.current : product.masterData.staged
|
|
150
|
+
if (!obj) return null
|
|
151
|
+
|
|
152
152
|
return {
|
|
153
153
|
id: product.id,
|
|
154
154
|
createdAt: product.createdAt,
|
|
@@ -163,12 +163,14 @@ export class ProductProjectionSearch {
|
|
|
163
163
|
masterVariant: obj.masterVariant,
|
|
164
164
|
variants: obj.variants,
|
|
165
165
|
productType: product.productType,
|
|
166
|
+
hasStagedChanges: product.masterData.hasStagedChanges,
|
|
167
|
+
published: product.masterData.published,
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
getFacets(
|
|
170
172
|
params: ProductProjectionSearchParams,
|
|
171
|
-
products:
|
|
173
|
+
products: ProductProjection[]
|
|
172
174
|
): FacetResults {
|
|
173
175
|
if (!params.facet) return {}
|
|
174
176
|
const staged = false
|
|
@@ -179,7 +181,7 @@ export class ProductProjectionSearch {
|
|
|
179
181
|
|
|
180
182
|
// Term Facet
|
|
181
183
|
if (expression.type === 'TermExpression') {
|
|
182
|
-
result[facet] = this.termFacet(expression.source, products
|
|
184
|
+
result[facet] = this.termFacet(expression.source, products)
|
|
183
185
|
}
|
|
184
186
|
|
|
185
187
|
// Range Facet
|
|
@@ -187,8 +189,7 @@ export class ProductProjectionSearch {
|
|
|
187
189
|
result[expression.source] = this.rangeFacet(
|
|
188
190
|
expression.source,
|
|
189
191
|
expression.children,
|
|
190
|
-
products
|
|
191
|
-
staged
|
|
192
|
+
products
|
|
192
193
|
)
|
|
193
194
|
}
|
|
194
195
|
|
|
@@ -197,8 +198,7 @@ export class ProductProjectionSearch {
|
|
|
197
198
|
result[expression.source] = this.filterFacet(
|
|
198
199
|
expression.source,
|
|
199
200
|
expression.children,
|
|
200
|
-
products
|
|
201
|
-
staged
|
|
201
|
+
products
|
|
202
202
|
)
|
|
203
203
|
}
|
|
204
204
|
}
|
|
@@ -211,11 +211,7 @@ export class ProductProjectionSearch {
|
|
|
211
211
|
* - counting products
|
|
212
212
|
* - correct dataType
|
|
213
213
|
*/
|
|
214
|
-
termFacet(
|
|
215
|
-
facet: string,
|
|
216
|
-
products: Product[],
|
|
217
|
-
staged: boolean
|
|
218
|
-
): TermFacetResult {
|
|
214
|
+
termFacet(facet: string, products: ProductProjection[]): TermFacetResult {
|
|
219
215
|
const result: Writable<TermFacetResult> = {
|
|
220
216
|
type: 'terms',
|
|
221
217
|
dataType: 'text',
|
|
@@ -227,9 +223,9 @@ export class ProductProjectionSearch {
|
|
|
227
223
|
const terms: Record<any, number> = {}
|
|
228
224
|
|
|
229
225
|
if (facet.startsWith('variants.')) {
|
|
230
|
-
products.forEach(p => {
|
|
231
|
-
const variants = getVariants(p
|
|
232
|
-
variants.forEach(v => {
|
|
226
|
+
products.forEach((p) => {
|
|
227
|
+
const variants = getVariants(p)
|
|
228
|
+
variants.forEach((v) => {
|
|
233
229
|
result.total++
|
|
234
230
|
|
|
235
231
|
let value = resolveVariantValue(v, facet)
|
|
@@ -244,7 +240,7 @@ export class ProductProjectionSearch {
|
|
|
244
240
|
})
|
|
245
241
|
})
|
|
246
242
|
} else {
|
|
247
|
-
products.forEach(p => {
|
|
243
|
+
products.forEach((p) => {
|
|
248
244
|
const value = nestedLookup(p, facet)
|
|
249
245
|
result.total++
|
|
250
246
|
if (value === undefined) {
|
|
@@ -266,15 +262,14 @@ export class ProductProjectionSearch {
|
|
|
266
262
|
filterFacet(
|
|
267
263
|
source: string,
|
|
268
264
|
filters: FilterExpression[] | undefined,
|
|
269
|
-
products:
|
|
270
|
-
staged: boolean
|
|
265
|
+
products: ProductProjection[]
|
|
271
266
|
): FilteredFacetResult {
|
|
272
267
|
let count = 0
|
|
273
268
|
if (source.startsWith('variants.')) {
|
|
274
269
|
for (const p of products) {
|
|
275
|
-
for (const v of getVariants(p
|
|
270
|
+
for (const v of getVariants(p)) {
|
|
276
271
|
const val = resolveVariantValue(v, source)
|
|
277
|
-
if (filters?.some(f => f.match(val))) {
|
|
272
|
+
if (filters?.some((f) => f.match(val))) {
|
|
278
273
|
count++
|
|
279
274
|
}
|
|
280
275
|
}
|
|
@@ -292,15 +287,14 @@ export class ProductProjectionSearch {
|
|
|
292
287
|
rangeFacet(
|
|
293
288
|
source: string,
|
|
294
289
|
ranges: RangeExpression[] | undefined,
|
|
295
|
-
products:
|
|
296
|
-
staged: boolean
|
|
290
|
+
products: ProductProjection[]
|
|
297
291
|
): RangeFacetResult {
|
|
298
292
|
const counts =
|
|
299
|
-
ranges?.map(range => {
|
|
293
|
+
ranges?.map((range) => {
|
|
300
294
|
if (source.startsWith('variants.')) {
|
|
301
295
|
const values = []
|
|
302
296
|
for (const p of products) {
|
|
303
|
-
for (const v of getVariants(p
|
|
297
|
+
for (const v of getVariants(p)) {
|
|
304
298
|
const val = resolveVariantValue(v, source)
|
|
305
299
|
if (val === undefined) {
|
|
306
300
|
continue
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { RepositoryTypes } from './../types'
|
|
1
|
+
import { RepositoryTypes, Resource, Writable } from './../types'
|
|
2
2
|
import deepEqual from 'deep-equal'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
BaseResource,
|
|
6
|
-
InvalidOperationError,
|
|
7
6
|
Project,
|
|
7
|
+
ResourceNotFoundError,
|
|
8
8
|
UpdateAction,
|
|
9
9
|
} from '@commercetools/platform-sdk'
|
|
10
10
|
import { AbstractStorage } from '../storage'
|
|
11
11
|
import { checkConcurrentModification } from './errors'
|
|
12
12
|
import { CommercetoolsError } from '../exceptions'
|
|
13
|
+
import { cloneObject } from '../helpers'
|
|
13
14
|
|
|
14
15
|
export type QueryParams = {
|
|
15
16
|
expand?: string[]
|
|
@@ -39,44 +40,68 @@ export abstract class AbstractRepository {
|
|
|
39
40
|
this._storage = storage
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
abstract
|
|
43
|
+
abstract saveNew({ projectKey }: RepositoryContext, resource: Resource): void
|
|
44
|
+
|
|
45
|
+
abstract saveUpdate(
|
|
43
46
|
{ projectKey }: RepositoryContext,
|
|
44
|
-
|
|
47
|
+
version: number,
|
|
48
|
+
resource: Resource
|
|
45
49
|
): void
|
|
46
50
|
|
|
47
|
-
processUpdateActions(
|
|
51
|
+
processUpdateActions<T extends Resource>(
|
|
48
52
|
context: RepositoryContext,
|
|
49
|
-
resource:
|
|
53
|
+
resource: T,
|
|
54
|
+
version: number,
|
|
50
55
|
actions: UpdateAction[]
|
|
51
|
-
):
|
|
56
|
+
): T {
|
|
52
57
|
// Deep-copy
|
|
53
|
-
const
|
|
58
|
+
const updatedResource = cloneObject(resource) as Writable<Resource>
|
|
59
|
+
const identifier = (resource as BaseResource).id
|
|
60
|
+
? (resource as BaseResource).id
|
|
61
|
+
: (resource as Project).key
|
|
54
62
|
|
|
55
|
-
actions.forEach(action => {
|
|
63
|
+
actions.forEach((action) => {
|
|
56
64
|
const updateFunc = this.actions[action.action]
|
|
57
65
|
|
|
58
66
|
if (!updateFunc) {
|
|
59
67
|
console.error(`No mock implemented for update action ${action.action}`)
|
|
60
|
-
|
|
68
|
+
throw new Error(
|
|
69
|
+
`No mock implemented for update action ${action.action}`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const beforeUpdate = cloneObject(resource)
|
|
74
|
+
updateFunc(context, updatedResource, action)
|
|
75
|
+
|
|
76
|
+
// Check if the object is updated. We need to increase the version of
|
|
77
|
+
// an object per action which does an actual modification.
|
|
78
|
+
// This isn't the most performant method to do this (the update action
|
|
79
|
+
// should return a flag) but for now the easiest.
|
|
80
|
+
if (!deepEqual(beforeUpdate, updatedResource)) {
|
|
81
|
+
// We only check the version when there is an actual modification to
|
|
82
|
+
// be stored.
|
|
83
|
+
checkConcurrentModification(resource.version, version, identifier)
|
|
84
|
+
|
|
85
|
+
updatedResource.version += 1
|
|
61
86
|
}
|
|
62
|
-
updateFunc(context, modifiedResource, action)
|
|
63
87
|
})
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
// If all actions succeeded we write the new version
|
|
90
|
+
// to the storage.
|
|
91
|
+
if (resource.version != updatedResource.version) {
|
|
92
|
+
this.saveUpdate(context, version, updatedResource)
|
|
67
93
|
}
|
|
68
94
|
|
|
69
|
-
const result = this.postProcessResource(
|
|
95
|
+
const result = this.postProcessResource(updatedResource)
|
|
70
96
|
if (!result) {
|
|
71
|
-
throw new Error(
|
|
97
|
+
throw new Error('invalid post process action')
|
|
72
98
|
}
|
|
73
|
-
return result
|
|
99
|
+
return result as T
|
|
74
100
|
}
|
|
75
101
|
|
|
76
|
-
postProcessResource(resource:
|
|
102
|
+
postProcessResource<T extends Resource>(resource: T): T {
|
|
77
103
|
return resource
|
|
78
104
|
}
|
|
79
|
-
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
@@ -85,7 +110,6 @@ export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
|
85
110
|
|
|
86
111
|
constructor(storage: AbstractStorage) {
|
|
87
112
|
super(storage)
|
|
88
|
-
this._storage.assertStorage(this.getTypeId())
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
query(context: RepositoryContext, params: QueryParams = {}) {
|
|
@@ -98,7 +122,6 @@ export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
|
98
122
|
|
|
99
123
|
// @ts-ignore
|
|
100
124
|
result.results = result.results.map(this.postProcessResource)
|
|
101
|
-
|
|
102
125
|
return result
|
|
103
126
|
}
|
|
104
127
|
|
|
@@ -107,8 +130,13 @@ export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
|
107
130
|
id: string,
|
|
108
131
|
params: GetParams = {}
|
|
109
132
|
): BaseResource | null {
|
|
110
|
-
const resource = this._storage.get(
|
|
111
|
-
|
|
133
|
+
const resource = this._storage.get(
|
|
134
|
+
context.projectKey,
|
|
135
|
+
this.getTypeId(),
|
|
136
|
+
id,
|
|
137
|
+
params
|
|
138
|
+
)
|
|
139
|
+
return resource ? this.postProcessResource(resource) : null
|
|
112
140
|
}
|
|
113
141
|
|
|
114
142
|
getByKey(
|
|
@@ -122,7 +150,7 @@ export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
|
122
150
|
key,
|
|
123
151
|
params
|
|
124
152
|
)
|
|
125
|
-
return this.postProcessResource(resource)
|
|
153
|
+
return resource ? this.postProcessResource(resource) : null
|
|
126
154
|
}
|
|
127
155
|
|
|
128
156
|
delete(
|
|
@@ -136,28 +164,44 @@ export abstract class AbstractResourceRepository extends AbstractRepository {
|
|
|
136
164
|
id,
|
|
137
165
|
params
|
|
138
166
|
)
|
|
139
|
-
return this.postProcessResource(resource)
|
|
167
|
+
return resource ? this.postProcessResource(resource) : null
|
|
140
168
|
}
|
|
141
169
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
170
|
+
saveNew(context: RepositoryContext, resource: Writable<BaseResource>) {
|
|
171
|
+
resource.version = 1
|
|
172
|
+
this._storage.add(context.projectKey, this.getTypeId(), resource as any)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
saveUpdate(
|
|
176
|
+
context: RepositoryContext,
|
|
177
|
+
version: number,
|
|
178
|
+
resource: Writable<BaseResource>
|
|
179
|
+
) {
|
|
180
|
+
// Check if the resource still exists.
|
|
181
|
+
const current = this._storage.get(
|
|
182
|
+
context.projectKey,
|
|
183
|
+
this.getTypeId(),
|
|
184
|
+
resource.id
|
|
185
|
+
)
|
|
186
|
+
if (!current) {
|
|
187
|
+
throw new CommercetoolsError<ResourceNotFoundError>(
|
|
188
|
+
{
|
|
189
|
+
code: 'ResourceNotFound',
|
|
190
|
+
message: 'Resource not found while updating',
|
|
191
|
+
},
|
|
192
|
+
400
|
|
193
|
+
)
|
|
157
194
|
}
|
|
158
195
|
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
checkConcurrentModification(current.version, version, resource.id)
|
|
197
|
+
|
|
198
|
+
if (current.version === resource.version) {
|
|
199
|
+
throw new Error('Internal error: no changes to save')
|
|
200
|
+
}
|
|
201
|
+
resource.lastModifiedAt = new Date().toISOString()
|
|
202
|
+
|
|
161
203
|
this._storage.add(context.projectKey, this.getTypeId(), resource as any)
|
|
204
|
+
|
|
205
|
+
return resource
|
|
162
206
|
}
|
|
163
207
|
}
|
package/src/repositories/cart.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
34
34
|
|
|
35
35
|
create(context: RepositoryContext, draft: CartDraft): Cart {
|
|
36
36
|
const lineItems =
|
|
37
|
-
draft.lineItems?.map(draftLineItem =>
|
|
37
|
+
draft.lineItems?.map((draftLineItem) =>
|
|
38
38
|
this.draftLineItemtoLineItem(
|
|
39
39
|
context.projectKey,
|
|
40
40
|
draftLineItem,
|
|
@@ -43,7 +43,7 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
43
43
|
)
|
|
44
44
|
) ?? []
|
|
45
45
|
|
|
46
|
-
const resource: Cart = {
|
|
46
|
+
const resource: Writable<Cart> = {
|
|
47
47
|
...getBaseResourceProperties(),
|
|
48
48
|
cartState: 'Active',
|
|
49
49
|
lineItems,
|
|
@@ -67,11 +67,9 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
67
67
|
this._storage
|
|
68
68
|
),
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
// @ts-ignore
|
|
72
70
|
resource.totalPrice.centAmount = calculateCartTotalPrice(resource)
|
|
73
71
|
|
|
74
|
-
this.
|
|
72
|
+
this.saveNew(context, resource)
|
|
75
73
|
return resource
|
|
76
74
|
}
|
|
77
75
|
|
|
@@ -94,7 +92,6 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
94
92
|
{ productId, variantId, sku, quantity = 1 }: CartAddLineItemAction
|
|
95
93
|
) => {
|
|
96
94
|
let product: Product | null = null
|
|
97
|
-
let variant: ProductVariant | undefined
|
|
98
95
|
|
|
99
96
|
if (productId && variantId) {
|
|
100
97
|
// Fetch product and variant by ID
|
|
@@ -128,10 +125,10 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
128
125
|
}
|
|
129
126
|
|
|
130
127
|
// Find matching variant
|
|
131
|
-
variant = [
|
|
128
|
+
const variant: ProductVariant | undefined = [
|
|
132
129
|
product.masterData.current.masterVariant,
|
|
133
130
|
...product.masterData.current.variants,
|
|
134
|
-
].find(x => {
|
|
131
|
+
].find((x) => {
|
|
135
132
|
if (sku) return x.sku === sku
|
|
136
133
|
if (variantId) return x.id === variantId
|
|
137
134
|
return false
|
|
@@ -148,11 +145,11 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
const alreadyAdded = resource.lineItems.some(
|
|
151
|
-
x => x.productId === product?.id && x.variant.id === variant?.id
|
|
148
|
+
(x) => x.productId === product?.id && x.variant.id === variant?.id
|
|
152
149
|
)
|
|
153
150
|
if (alreadyAdded) {
|
|
154
151
|
// increase quantity and update total price
|
|
155
|
-
resource.lineItems.map(x => {
|
|
152
|
+
resource.lineItems.map((x) => {
|
|
156
153
|
if (x.productId === product?.id && x.variant.id === variant?.id) {
|
|
157
154
|
x.quantity += quantity
|
|
158
155
|
x.totalPrice.centAmount = calculateLineItemTotalPrice(x)
|
|
@@ -209,7 +206,7 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
209
206
|
resource: Writable<Cart>,
|
|
210
207
|
{ lineItemId, quantity }: CartRemoveLineItemAction
|
|
211
208
|
) => {
|
|
212
|
-
const lineItem = resource.lineItems.find(x => x.id === lineItemId)
|
|
209
|
+
const lineItem = resource.lineItems.find((x) => x.id === lineItemId)
|
|
213
210
|
if (!lineItem) {
|
|
214
211
|
// Check if product is found
|
|
215
212
|
throw new CommercetoolsError<GeneralError>({
|
|
@@ -221,10 +218,12 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
221
218
|
const shouldDelete = !quantity || quantity >= lineItem.quantity
|
|
222
219
|
if (shouldDelete) {
|
|
223
220
|
// delete line item
|
|
224
|
-
resource.lineItems = resource.lineItems.filter(
|
|
221
|
+
resource.lineItems = resource.lineItems.filter(
|
|
222
|
+
(x) => x.id !== lineItemId
|
|
223
|
+
)
|
|
225
224
|
} else {
|
|
226
225
|
// decrease quantity and update total price
|
|
227
|
-
resource.lineItems.map(x => {
|
|
226
|
+
resource.lineItems.map((x) => {
|
|
228
227
|
if (x.id === lineItemId && quantity) {
|
|
229
228
|
x.quantity -= quantity
|
|
230
229
|
x.totalPrice.centAmount = calculateLineItemTotalPrice(x)
|
|
@@ -248,21 +247,28 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
248
247
|
resource: Writable<Cart>,
|
|
249
248
|
{ shippingMethod }: CartSetShippingMethodAction
|
|
250
249
|
) => {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
250
|
+
if (shippingMethod) {
|
|
251
|
+
const method = this._storage.getByResourceIdentifier<'shipping-method'>(
|
|
252
|
+
context.projectKey,
|
|
253
|
+
shippingMethod
|
|
254
|
+
)
|
|
256
255
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
256
|
+
if (!method) {
|
|
257
|
+
throw new Error(`Type ${shippingMethod} not found`)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Based on the address we should select a shipping zone and
|
|
261
|
+
// use that to define the price.
|
|
262
|
+
// @ts-ignore
|
|
263
|
+
resource.shippingInfo = {
|
|
264
|
+
shippingMethod: {
|
|
265
|
+
typeId: 'shipping-method',
|
|
266
|
+
id: method.id,
|
|
267
|
+
},
|
|
268
|
+
shippingMethodName: method.name,
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
resource.shippingInfo = undefined
|
|
266
272
|
}
|
|
267
273
|
},
|
|
268
274
|
setCountry: (
|
|
@@ -338,7 +344,6 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
338
344
|
const { productId, quantity, variantId, sku } = draftLineItem
|
|
339
345
|
|
|
340
346
|
let product: Product | null = null
|
|
341
|
-
let variant: ProductVariant | undefined
|
|
342
347
|
|
|
343
348
|
if (productId && variantId) {
|
|
344
349
|
// Fetch product and variant by ID
|
|
@@ -367,10 +372,10 @@ export class CartRepository extends AbstractResourceRepository {
|
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
// Find matching variant
|
|
370
|
-
variant = [
|
|
375
|
+
const variant = [
|
|
371
376
|
product.masterData.current.masterVariant,
|
|
372
377
|
...product.masterData.current.variants,
|
|
373
|
-
].find(x => {
|
|
378
|
+
].find((x) => {
|
|
374
379
|
if (sku) return x.sku === sku
|
|
375
380
|
if (variantId) return x.id === variantId
|
|
376
381
|
return false
|
|
@@ -432,7 +437,7 @@ const selectPrice = ({
|
|
|
432
437
|
// Quick-and-dirty way of selecting price based on the given currency and country.
|
|
433
438
|
// Can be improved later to give more priority to exact matches over
|
|
434
439
|
// 'all country' matches, and include customer groups in the mix as well
|
|
435
|
-
return prices.find(price => {
|
|
440
|
+
return prices.find((price) => {
|
|
436
441
|
const countryMatch = !price.country || price.country === country
|
|
437
442
|
const currencyMatch = price.value.currencyCode === currency
|
|
438
443
|
return countryMatch && currencyMatch
|
|
@@ -38,28 +38,26 @@ export class CategoryRepository extends AbstractResourceRepository {
|
|
|
38
38
|
: undefined,
|
|
39
39
|
ancestors: [], // TODO
|
|
40
40
|
assets:
|
|
41
|
-
draft.assets?.map(d => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
custom
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
}) || [],
|
|
41
|
+
draft.assets?.map((d) => ({
|
|
42
|
+
id: uuidv4(),
|
|
43
|
+
name: d.name,
|
|
44
|
+
description: d.description,
|
|
45
|
+
sources: d.sources,
|
|
46
|
+
tags: d.tags,
|
|
47
|
+
key: d.key,
|
|
48
|
+
custom: createCustomFields(
|
|
49
|
+
draft.custom,
|
|
50
|
+
context.projectKey,
|
|
51
|
+
this._storage
|
|
52
|
+
),
|
|
53
|
+
})) || [],
|
|
56
54
|
custom: createCustomFields(
|
|
57
55
|
draft.custom,
|
|
58
56
|
context.projectKey,
|
|
59
57
|
this._storage
|
|
60
58
|
),
|
|
61
59
|
}
|
|
62
|
-
this.
|
|
60
|
+
this.saveNew(context, resource)
|
|
63
61
|
return resource
|
|
64
62
|
}
|
|
65
63
|
|
|
@@ -69,7 +67,7 @@ export class CategoryRepository extends AbstractResourceRepository {
|
|
|
69
67
|
resource: Writable<Category>,
|
|
70
68
|
{ assetId, assetKey, name }: CategoryChangeAssetNameAction
|
|
71
69
|
) => {
|
|
72
|
-
resource.assets?.forEach(asset => {
|
|
70
|
+
resource.assets?.forEach((asset) => {
|
|
73
71
|
if (assetId && assetId === asset.id) {
|
|
74
72
|
asset.name = name
|
|
75
73
|
}
|
|
@@ -97,7 +95,7 @@ export class CategoryRepository extends AbstractResourceRepository {
|
|
|
97
95
|
resource: Writable<Category>,
|
|
98
96
|
{ assetId, assetKey, description }: CategorySetAssetDescriptionAction
|
|
99
97
|
) => {
|
|
100
|
-
resource.assets?.forEach(asset => {
|
|
98
|
+
resource.assets?.forEach((asset) => {
|
|
101
99
|
if (assetId && assetId === asset.id) {
|
|
102
100
|
asset.description = description
|
|
103
101
|
}
|
|
@@ -111,7 +109,7 @@ export class CategoryRepository extends AbstractResourceRepository {
|
|
|
111
109
|
resource: Writable<Category>,
|
|
112
110
|
{ assetId, assetKey, sources }: CategorySetAssetSourcesAction
|
|
113
111
|
) => {
|
|
114
|
-
resource.assets?.forEach(asset => {
|
|
112
|
+
resource.assets?.forEach((asset) => {
|
|
115
113
|
if (assetId && assetId === asset.id) {
|
|
116
114
|
asset.sources = sources
|
|
117
115
|
}
|