@labdigital/commercetools-mock 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +5 -4
  2. package/dist/index.cjs +116 -18
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +30 -7
  5. package/dist/index.d.ts +30 -7
  6. package/dist/index.js +116 -18
  7. package/dist/index.js.map +1 -1
  8. package/package.json +4 -3
  9. package/src/constants.ts +2 -2
  10. package/src/ctMock.ts +176 -176
  11. package/src/exceptions.ts +10 -10
  12. package/src/helpers.ts +26 -26
  13. package/src/index.test.ts +173 -173
  14. package/src/index.ts +3 -3
  15. package/src/lib/expandParser.ts +19 -19
  16. package/src/lib/haversine.test.ts +13 -13
  17. package/src/lib/haversine.ts +14 -14
  18. package/src/lib/masking.ts +15 -15
  19. package/src/lib/parser.ts +2 -2
  20. package/src/lib/predicateParser.test.ts +204 -204
  21. package/src/lib/predicateParser.ts +398 -398
  22. package/src/lib/projectionSearchFilter.test.ts +168 -168
  23. package/src/lib/projectionSearchFilter.ts +272 -269
  24. package/src/lib/proxy.ts +8 -8
  25. package/src/oauth/errors.ts +4 -4
  26. package/src/oauth/helpers.ts +6 -6
  27. package/src/oauth/server.ts +103 -101
  28. package/src/oauth/store.ts +27 -27
  29. package/src/priceSelector.test.ts +68 -68
  30. package/src/priceSelector.ts +70 -70
  31. package/src/product-projection-search.ts +296 -296
  32. package/src/projectAPI.test.ts +3 -3
  33. package/src/projectAPI.ts +46 -46
  34. package/src/repositories/abstract.ts +190 -190
  35. package/src/repositories/associate-role.ts +10 -7
  36. package/src/repositories/attribute-group.ts +63 -8
  37. package/src/repositories/business-unit.ts +10 -7
  38. package/src/repositories/cart-discount.ts +134 -134
  39. package/src/repositories/cart.ts +517 -514
  40. package/src/repositories/category.ts +170 -167
  41. package/src/repositories/channel.ts +114 -111
  42. package/src/repositories/custom-object.ts +66 -63
  43. package/src/repositories/customer-group.ts +72 -69
  44. package/src/repositories/customer.ts +93 -79
  45. package/src/repositories/discount-code.ts +171 -168
  46. package/src/repositories/errors.ts +15 -15
  47. package/src/repositories/extension.ts +79 -76
  48. package/src/repositories/helpers.ts +180 -180
  49. package/src/repositories/index.ts +39 -39
  50. package/src/repositories/inventory-entry.ts +98 -95
  51. package/src/repositories/my-order.ts +11 -11
  52. package/src/repositories/order-edit.ts +29 -29
  53. package/src/repositories/order.test.ts +191 -191
  54. package/src/repositories/order.ts +393 -389
  55. package/src/repositories/payment.ts +155 -155
  56. package/src/repositories/product-discount.ts +149 -149
  57. package/src/repositories/product-projection.ts +116 -52
  58. package/src/repositories/product-selection.ts +31 -31
  59. package/src/repositories/product-type.ts +156 -156
  60. package/src/repositories/product.ts +600 -597
  61. package/src/repositories/project.ts +136 -135
  62. package/src/repositories/quote-request.ts +19 -19
  63. package/src/repositories/quote.ts +19 -19
  64. package/src/repositories/review.ts +24 -24
  65. package/src/repositories/shipping-method.ts +217 -217
  66. package/src/repositories/shopping-list.ts +49 -49
  67. package/src/repositories/staged-quote.ts +20 -20
  68. package/src/repositories/standalone-price.ts +72 -61
  69. package/src/repositories/state.ts +84 -84
  70. package/src/repositories/store.ts +114 -114
  71. package/src/repositories/subscription.ts +40 -40
  72. package/src/repositories/tax-category.ts +98 -98
  73. package/src/repositories/type.ts +157 -157
  74. package/src/repositories/zone.ts +71 -71
  75. package/src/server.ts +2 -2
  76. package/src/services/abstract.ts +173 -173
  77. package/src/services/attribute-group.ts +16 -0
  78. package/src/services/cart-discount.ts +8 -8
  79. package/src/services/cart.test.ts +409 -409
  80. package/src/services/cart.ts +50 -50
  81. package/src/services/category.test.ts +25 -25
  82. package/src/services/category.ts +8 -8
  83. package/src/services/channel.ts +8 -8
  84. package/src/services/custom-object.test.ts +184 -184
  85. package/src/services/custom-object.ts +48 -48
  86. package/src/services/customer-group.ts +8 -8
  87. package/src/services/customer.test.ts +151 -129
  88. package/src/services/customer.ts +27 -27
  89. package/src/services/discount-code.ts +8 -8
  90. package/src/services/extension.ts +8 -8
  91. package/src/services/index.ts +52 -44
  92. package/src/services/inventory-entry.test.ts +162 -162
  93. package/src/services/inventory-entry.ts +8 -8
  94. package/src/services/my-cart.test.ts +78 -78
  95. package/src/services/my-cart.ts +28 -28
  96. package/src/services/my-customer.test.ts +44 -44
  97. package/src/services/my-customer.ts +53 -53
  98. package/src/services/my-order.ts +20 -20
  99. package/src/services/my-payment.test.ts +65 -65
  100. package/src/services/my-payment.ts +8 -8
  101. package/src/services/order.test.ts +527 -527
  102. package/src/services/order.ts +31 -31
  103. package/src/services/payment.test.ts +65 -65
  104. package/src/services/payment.ts +8 -8
  105. package/src/services/product-discount.ts +8 -8
  106. package/src/services/product-projection.test.ts +492 -428
  107. package/src/services/product-projection.ts +32 -18
  108. package/src/services/product-type.test.ts +56 -56
  109. package/src/services/product-type.ts +8 -8
  110. package/src/services/product.test.ts +510 -510
  111. package/src/services/product.ts +8 -8
  112. package/src/services/project.ts +34 -34
  113. package/src/services/shipping-method.test.ts +81 -81
  114. package/src/services/shipping-method.ts +12 -12
  115. package/src/services/shopping-list.ts +8 -8
  116. package/src/services/standalone-price.test.ts +256 -256
  117. package/src/services/standalone-price.ts +8 -8
  118. package/src/services/state.test.ts +42 -42
  119. package/src/services/state.ts +8 -8
  120. package/src/services/store.test.ts +57 -57
  121. package/src/services/store.ts +8 -8
  122. package/src/services/subscription.ts +8 -8
  123. package/src/services/tax-category.test.ts +61 -61
  124. package/src/services/tax-category.ts +8 -8
  125. package/src/services/type.ts +8 -8
  126. package/src/services/zone.ts +8 -8
  127. package/src/storage/abstract.ts +58 -58
  128. package/src/storage/in-memory.ts +419 -419
  129. package/src/types.ts +82 -82
@@ -1,39 +1,39 @@
1
1
  import type {
2
- AssociateRole,
3
- AttributeGroup,
4
- BusinessUnit,
5
- Cart,
6
- CartDiscount,
7
- Category,
8
- Channel,
9
- Customer,
10
- CustomerGroup,
11
- CustomObject,
12
- DiscountCode,
13
- Extension,
14
- InvalidInputError,
15
- InventoryEntry,
16
- Order,
17
- PagedQueryResponse,
18
- Payment,
19
- Product,
20
- ProductDiscount,
21
- ProductProjection,
22
- ProductType,
23
- Project,
24
- Quote,
25
- QuoteRequest,
26
- Reference,
27
- ResourceIdentifier,
28
- ShippingMethod,
29
- ShoppingList,
30
- StagedQuote,
31
- State,
32
- Store,
33
- Subscription,
34
- TaxCategory,
35
- Type,
36
- Zone,
2
+ AssociateRole,
3
+ AttributeGroup,
4
+ BusinessUnit,
5
+ Cart,
6
+ CartDiscount,
7
+ Category,
8
+ Channel,
9
+ Customer,
10
+ CustomerGroup,
11
+ CustomObject,
12
+ DiscountCode,
13
+ Extension,
14
+ InvalidInputError,
15
+ InventoryEntry,
16
+ Order,
17
+ PagedQueryResponse,
18
+ Payment,
19
+ Product,
20
+ ProductDiscount,
21
+ ProductProjection,
22
+ ProductType,
23
+ Project,
24
+ Quote,
25
+ QuoteRequest,
26
+ Reference,
27
+ ResourceIdentifier,
28
+ ShippingMethod,
29
+ ShoppingList,
30
+ StagedQuote,
31
+ State,
32
+ Store,
33
+ Subscription,
34
+ TaxCategory,
35
+ Type,
36
+ Zone,
37
37
  } from '@commercetools/platform-sdk'
38
38
  import assert from 'assert'
39
39
  import { CommercetoolsError } from '../exceptions.js'
@@ -41,393 +41,393 @@ import { cloneObject } from '../helpers.js'
41
41
  import { parseExpandClause } from '../lib/expandParser.js'
42
42
  import { parseQueryExpression } from '../lib/predicateParser.js'
43
43
  import {
44
- PagedQueryResponseMap,
45
- ResourceMap,
46
- ResourceType,
47
- Writable,
44
+ PagedQueryResponseMap,
45
+ ResourceMap,
46
+ ResourceType,
47
+ Writable,
48
48
  } from '../types.js'
49
49
  import {
50
- AbstractStorage,
51
- GetParams,
52
- ProjectStorage,
53
- QueryParams,
50
+ AbstractStorage,
51
+ GetParams,
52
+ ProjectStorage,
53
+ QueryParams,
54
54
  } from './abstract.js'
55
55
 
56
56
  export class InMemoryStorage extends AbstractStorage {
57
- protected resources: {
58
- [projectKey: string]: ProjectStorage
59
- } = {}
60
-
61
- protected projects: {
62
- [projectKey: string]: Project
63
- } = {}
64
-
65
- private forProjectKey(projectKey: string): ProjectStorage {
66
- this.addProject(projectKey)
67
-
68
- let projectStorage = this.resources[projectKey]
69
- if (!projectStorage) {
70
- projectStorage = this.resources[projectKey] = {
71
- 'associate-role': new Map<string, AssociateRole>(),
72
- 'attribute-group': new Map<string, AttributeGroup>(),
73
- 'business-unit': new Map<string, BusinessUnit>(),
74
- cart: new Map<string, Cart>(),
75
- 'cart-discount': new Map<string, CartDiscount>(),
76
- category: new Map<string, Category>(),
77
- channel: new Map<string, Channel>(),
78
- customer: new Map<string, Customer>(),
79
- 'customer-group': new Map<string, CustomerGroup>(),
80
- 'discount-code': new Map<string, DiscountCode>(),
81
- extension: new Map<string, Extension>(),
82
- 'inventory-entry': new Map<string, InventoryEntry>(),
83
- 'key-value-document': new Map<string, CustomObject>(),
84
- order: new Map<string, Order>(),
85
- 'order-edit': new Map<string, any>(),
86
- payment: new Map<string, Payment>(),
87
- product: new Map<string, Product>(),
88
- quote: new Map<string, Quote>(),
89
- 'quote-request': new Map<string, QuoteRequest>(),
90
- 'product-discount': new Map<string, ProductDiscount>(),
91
- 'product-selection': new Map<string, any>(),
92
- 'product-type': new Map<string, ProductType>(),
93
- 'product-projection': new Map<string, ProductProjection>(),
94
- review: new Map<string, any>(),
95
- 'shipping-method': new Map<string, ShippingMethod>(),
96
- 'staged-quote': new Map<string, StagedQuote>(),
97
- state: new Map<string, State>(),
98
- store: new Map<string, Store>(),
99
- 'shopping-list': new Map<string, ShoppingList>(),
100
- 'standalone-price': new Map<string, any>(),
101
- subscription: new Map<string, Subscription>(),
102
- 'tax-category': new Map<string, TaxCategory>(),
103
- type: new Map<string, Type>(),
104
- zone: new Map<string, Zone>(),
105
- }
106
- }
107
- return projectStorage
108
- }
109
-
110
- clear() {
111
- for (const [, projectStorage] of Object.entries(this.resources)) {
112
- for (const [, value] of Object.entries(projectStorage)) {
113
- value?.clear()
114
- }
115
- }
116
- }
117
-
118
- all<RT extends ResourceType>(
119
- projectKey: string,
120
- typeId: RT
121
- ): ResourceMap[RT][] {
122
- const store = this.forProjectKey(projectKey)[typeId]
123
- if (store) {
124
- return Array.from(store.values()).map(cloneObject) as ResourceMap[RT][]
125
- }
126
- return []
127
- }
128
-
129
- add<RT extends ResourceType>(
130
- projectKey: string,
131
- typeId: RT,
132
- obj: ResourceMap[RT],
133
- params: GetParams = {}
134
- ): ResourceMap[RT] {
135
- const store = this.forProjectKey(projectKey)
136
- store[typeId]?.set(obj.id, obj)
137
-
138
- const resource = this.get(projectKey, typeId, obj.id, params)
139
- assert(resource, `resource of type ${typeId} with id ${obj.id} not created`)
140
- return cloneObject(resource)
141
- }
142
-
143
- get<RT extends ResourceType>(
144
- projectKey: string,
145
- typeId: RT,
146
- id: string,
147
- params: GetParams = {}
148
- ): ResourceMap[RT] | null {
149
- const resource = this.forProjectKey(projectKey)[typeId]?.get(id)
150
- if (resource) {
151
- const clone = cloneObject(resource)
152
- return this.expand(projectKey, clone, params.expand) as ResourceMap[RT]
153
- }
154
- return null
155
- }
156
-
157
- getByKey<RT extends ResourceType>(
158
- projectKey: string,
159
- typeId: RT,
160
- key: string,
161
- params: GetParams = {}
162
- ): ResourceMap[RT] | null {
163
- const store = this.forProjectKey(projectKey)
164
- const resourceStore = store[typeId]
165
- if (!store) {
166
- throw new Error('No type')
167
- }
168
-
169
- const resources: any[] = Array.from(resourceStore.values())
170
- const resource = resources.find((e) => e.key === key)
171
- if (resource) {
172
- const clone = cloneObject(resource)
173
- return this.expand(projectKey, clone, params.expand) as ResourceMap[RT]
174
- }
175
- return null
176
- }
177
-
178
- delete<RT extends ResourceType>(
179
- projectKey: string,
180
- typeId: RT,
181
- id: string,
182
- params: GetParams = {}
183
- ): ResourceMap[RT] | null {
184
- const resource = this.get(projectKey, typeId, id)
185
-
186
- if (resource) {
187
- this.forProjectKey(projectKey)[typeId]?.delete(id)
188
- return this.expand(projectKey, resource, params.expand)
189
- }
190
- return resource
191
- }
192
-
193
- query<RT extends ResourceType>(
194
- projectKey: string,
195
- typeId: RT,
196
- params: QueryParams
197
- ): PagedQueryResponseMap[RT] {
198
- const store = this.forProjectKey(projectKey)[typeId]
199
- if (!store) {
200
- throw new Error('No type')
201
- }
202
-
203
- let resources = this.all<RT>(projectKey, typeId)
204
-
205
- // Apply predicates
206
- if (params.where) {
207
- try {
208
- const filterFunc = parseQueryExpression(params.where)
209
- resources = resources.filter((resource) => filterFunc(resource, {}))
210
- } catch (err) {
211
- throw new CommercetoolsError<InvalidInputError>(
212
- {
213
- code: 'InvalidInput',
214
- message: (err as any).message,
215
- },
216
- 400
217
- )
218
- }
219
- }
220
-
221
- // Get the total before slicing the array
222
- const totalResources = resources.length
223
-
224
- // Apply offset, limit
225
- const offset = params.offset || 0
226
- const limit = params.limit || 20
227
- resources = resources.slice(offset, offset + limit)
228
-
229
- // Expand the resources
230
- if (params.expand !== undefined) {
231
- resources = resources.map((resource) =>
232
- this.expand(projectKey, resource, params.expand)
233
- )
234
- }
235
-
236
- return {
237
- count: totalResources,
238
- total: resources.length,
239
- offset: offset,
240
- limit: limit,
241
- results: resources.map(cloneObject),
242
- } as PagedQueryResponseMap[RT]
243
- }
244
-
245
- search(
246
- projectKey: string,
247
- typeId: ResourceType,
248
- params: QueryParams
249
- ): PagedQueryResponse {
250
- let resources = this.all(projectKey, typeId)
251
-
252
- // Apply predicates
253
- if (params.where) {
254
- try {
255
- const filterFunc = parseQueryExpression(params.where)
256
- resources = resources.filter((resource) => filterFunc(resource, {}))
257
- } catch (err) {
258
- throw new CommercetoolsError<InvalidInputError>(
259
- {
260
- code: 'InvalidInput',
261
- message: (err as any).message,
262
- },
263
- 400
264
- )
265
- }
266
- }
267
-
268
- // Get the total before slicing the array
269
- const totalResources = resources.length
270
-
271
- // Apply offset, limit
272
- const offset = params.offset || 0
273
- const limit = params.limit || 20
274
- resources = resources.slice(offset, offset + limit)
275
-
276
- // Expand the resources
277
- if (params.expand !== undefined) {
278
- resources = resources.map((resource) =>
279
- this.expand(projectKey, resource, params.expand)
280
- )
281
- }
282
-
283
- return {
284
- count: totalResources,
285
- total: resources.length,
286
- offset: offset,
287
- limit: limit,
288
- results: resources,
289
- }
290
- }
291
-
292
- getByResourceIdentifier<RT extends ResourceType>(
293
- projectKey: string,
294
- identifier: ResourceIdentifier
295
- ): ResourceMap[RT] | null {
296
- if (identifier.id) {
297
- const resource = this.get(projectKey, identifier.typeId, identifier.id)
298
- if (resource) {
299
- return resource as ResourceMap[RT]
300
- }
301
- console.error(
302
- `No resource found with typeId=${identifier.typeId}, id=${identifier.id}`
303
- )
304
- return null
305
- }
306
-
307
- if (identifier.key) {
308
- const store = this.forProjectKey(projectKey)[identifier.typeId]
309
-
310
- if (store) {
311
- // TODO: BaseResource has no key attribute, but the subclasses should
312
- // have them all.
313
- const resource = Array.from(store.values()).find(
314
- // @ts-ignore
315
- (r) => r.key === identifier.key
316
- )
317
- if (resource) {
318
- return resource as ResourceMap[RT]
319
- }
320
- } else {
321
- throw new Error(
322
- `No storage found for resource type: ${identifier.typeId}`
323
- )
324
- }
325
- }
326
- return null
327
- }
328
-
329
- addProject = (projectKey: string): Project => {
330
- if (!this.projects[projectKey]) {
331
- this.projects[projectKey] = {
332
- key: projectKey,
333
- name: '',
334
- countries: [],
335
- currencies: [],
336
- languages: [],
337
- createdAt: '2018-10-04T11:32:12.603Z',
338
- trialUntil: '2018-12',
339
- carts: {
340
- countryTaxRateFallbackEnabled: false,
341
- deleteDaysAfterLastModification: 90,
342
- },
343
- messages: { enabled: false, deleteDaysAfterCreation: 15 },
344
- shippingRateInputType: undefined,
345
- externalOAuth: undefined,
346
- searchIndexing: {
347
- products: {
348
- status: 'Deactivated',
349
- },
350
- orders: {
351
- status: 'Deactivated',
352
- },
353
- },
354
- version: 1,
355
- }
356
- }
357
- return this.projects[projectKey]
358
- }
359
-
360
- saveProject = (project: Project): Project => {
361
- this.projects[project.key] = project
362
- return project
363
- }
364
-
365
- getProject = (projectKey: string): Project => this.addProject(projectKey)
366
-
367
- // Expand resolves a nested reference and injects the object in the given obj
368
- public expand = <T>(
369
- projectKey: string,
370
- obj: T,
371
- clause: undefined | string | string[]
372
- ): T => {
373
- if (!clause) return obj
374
- const newObj = cloneObject(obj)
375
- if (Array.isArray(clause)) {
376
- clause.forEach((c) => {
377
- this._resolveResource(projectKey, newObj, c)
378
- })
379
- } else {
380
- this._resolveResource(projectKey, newObj, clause)
381
- }
382
- return newObj
383
- }
384
-
385
- private _resolveResource = (projectKey: string, obj: any, expand: string) => {
386
- const params = parseExpandClause(expand)
387
-
388
- if (!params.index) {
389
- const reference = obj[params.element]
390
- if (reference === undefined) {
391
- return
392
- }
393
- this._resolveReference(projectKey, reference, params.rest)
394
- } else if (params.index === '*') {
395
- const reference = obj[params.element]
396
- if (reference === undefined || !Array.isArray(reference)) return
397
- reference.forEach((itemRef: Writable<Reference>) => {
398
- this._resolveReference(projectKey, itemRef, params.rest)
399
- })
400
- } else {
401
- const reference = obj[params.element][params.index]
402
- if (reference === undefined) return
403
- this._resolveReference(projectKey, reference, params.rest)
404
- }
405
- }
406
-
407
- private _resolveReference(
408
- projectKey: string,
409
- reference: any,
410
- expand: string | undefined
411
- ) {
412
- if (reference === undefined) return
413
-
414
- if (
415
- reference.typeId !== undefined &&
416
- (reference.id !== undefined || reference.key !== undefined)
417
- ) {
418
- // @ts-ignore
419
- reference.obj = this.getByResourceIdentifier(projectKey, {
420
- typeId: reference.typeId,
421
- id: reference.id,
422
- key: reference.key,
423
- } as ResourceIdentifier)
424
- if (expand) {
425
- this._resolveResource(projectKey, reference.obj, expand)
426
- }
427
- } else {
428
- if (expand) {
429
- this._resolveResource(projectKey, reference, expand)
430
- }
431
- }
432
- }
57
+ protected resources: {
58
+ [projectKey: string]: ProjectStorage
59
+ } = {}
60
+
61
+ protected projects: {
62
+ [projectKey: string]: Project
63
+ } = {}
64
+
65
+ private forProjectKey(projectKey: string): ProjectStorage {
66
+ this.addProject(projectKey)
67
+
68
+ let projectStorage = this.resources[projectKey]
69
+ if (!projectStorage) {
70
+ projectStorage = this.resources[projectKey] = {
71
+ 'associate-role': new Map<string, AssociateRole>(),
72
+ 'attribute-group': new Map<string, AttributeGroup>(),
73
+ 'business-unit': new Map<string, BusinessUnit>(),
74
+ cart: new Map<string, Cart>(),
75
+ 'cart-discount': new Map<string, CartDiscount>(),
76
+ category: new Map<string, Category>(),
77
+ channel: new Map<string, Channel>(),
78
+ customer: new Map<string, Customer>(),
79
+ 'customer-group': new Map<string, CustomerGroup>(),
80
+ 'discount-code': new Map<string, DiscountCode>(),
81
+ extension: new Map<string, Extension>(),
82
+ 'inventory-entry': new Map<string, InventoryEntry>(),
83
+ 'key-value-document': new Map<string, CustomObject>(),
84
+ order: new Map<string, Order>(),
85
+ 'order-edit': new Map<string, any>(),
86
+ payment: new Map<string, Payment>(),
87
+ product: new Map<string, Product>(),
88
+ quote: new Map<string, Quote>(),
89
+ 'quote-request': new Map<string, QuoteRequest>(),
90
+ 'product-discount': new Map<string, ProductDiscount>(),
91
+ 'product-selection': new Map<string, any>(),
92
+ 'product-type': new Map<string, ProductType>(),
93
+ 'product-projection': new Map<string, ProductProjection>(),
94
+ review: new Map<string, any>(),
95
+ 'shipping-method': new Map<string, ShippingMethod>(),
96
+ 'staged-quote': new Map<string, StagedQuote>(),
97
+ state: new Map<string, State>(),
98
+ store: new Map<string, Store>(),
99
+ 'shopping-list': new Map<string, ShoppingList>(),
100
+ 'standalone-price': new Map<string, any>(),
101
+ subscription: new Map<string, Subscription>(),
102
+ 'tax-category': new Map<string, TaxCategory>(),
103
+ type: new Map<string, Type>(),
104
+ zone: new Map<string, Zone>(),
105
+ }
106
+ }
107
+ return projectStorage
108
+ }
109
+
110
+ clear() {
111
+ for (const [, projectStorage] of Object.entries(this.resources)) {
112
+ for (const [, value] of Object.entries(projectStorage)) {
113
+ value?.clear()
114
+ }
115
+ }
116
+ }
117
+
118
+ all<RT extends ResourceType>(
119
+ projectKey: string,
120
+ typeId: RT
121
+ ): ResourceMap[RT][] {
122
+ const store = this.forProjectKey(projectKey)[typeId]
123
+ if (store) {
124
+ return Array.from(store.values()).map(cloneObject) as ResourceMap[RT][]
125
+ }
126
+ return []
127
+ }
128
+
129
+ add<RT extends ResourceType>(
130
+ projectKey: string,
131
+ typeId: RT,
132
+ obj: ResourceMap[RT],
133
+ params: GetParams = {}
134
+ ): ResourceMap[RT] {
135
+ const store = this.forProjectKey(projectKey)
136
+ store[typeId]?.set(obj.id, obj)
137
+
138
+ const resource = this.get(projectKey, typeId, obj.id, params)
139
+ assert(resource, `resource of type ${typeId} with id ${obj.id} not created`)
140
+ return cloneObject(resource)
141
+ }
142
+
143
+ get<RT extends ResourceType>(
144
+ projectKey: string,
145
+ typeId: RT,
146
+ id: string,
147
+ params: GetParams = {}
148
+ ): ResourceMap[RT] | null {
149
+ const resource = this.forProjectKey(projectKey)[typeId]?.get(id)
150
+ if (resource) {
151
+ const clone = cloneObject(resource)
152
+ return this.expand(projectKey, clone, params.expand) as ResourceMap[RT]
153
+ }
154
+ return null
155
+ }
156
+
157
+ getByKey<RT extends ResourceType>(
158
+ projectKey: string,
159
+ typeId: RT,
160
+ key: string,
161
+ params: GetParams = {}
162
+ ): ResourceMap[RT] | null {
163
+ const store = this.forProjectKey(projectKey)
164
+ const resourceStore = store[typeId]
165
+ if (!store) {
166
+ throw new Error('No type')
167
+ }
168
+
169
+ const resources: any[] = Array.from(resourceStore.values())
170
+ const resource = resources.find((e) => e.key === key)
171
+ if (resource) {
172
+ const clone = cloneObject(resource)
173
+ return this.expand(projectKey, clone, params.expand) as ResourceMap[RT]
174
+ }
175
+ return null
176
+ }
177
+
178
+ delete<RT extends ResourceType>(
179
+ projectKey: string,
180
+ typeId: RT,
181
+ id: string,
182
+ params: GetParams = {}
183
+ ): ResourceMap[RT] | null {
184
+ const resource = this.get(projectKey, typeId, id)
185
+
186
+ if (resource) {
187
+ this.forProjectKey(projectKey)[typeId]?.delete(id)
188
+ return this.expand(projectKey, resource, params.expand)
189
+ }
190
+ return resource
191
+ }
192
+
193
+ query<RT extends ResourceType>(
194
+ projectKey: string,
195
+ typeId: RT,
196
+ params: QueryParams
197
+ ): PagedQueryResponseMap[RT] {
198
+ const store = this.forProjectKey(projectKey)[typeId]
199
+ if (!store) {
200
+ throw new Error('No type')
201
+ }
202
+
203
+ let resources = this.all<RT>(projectKey, typeId)
204
+
205
+ // Apply predicates
206
+ if (params.where) {
207
+ try {
208
+ const filterFunc = parseQueryExpression(params.where)
209
+ resources = resources.filter((resource) => filterFunc(resource, {}))
210
+ } catch (err) {
211
+ throw new CommercetoolsError<InvalidInputError>(
212
+ {
213
+ code: 'InvalidInput',
214
+ message: (err as any).message,
215
+ },
216
+ 400
217
+ )
218
+ }
219
+ }
220
+
221
+ // Get the total before slicing the array
222
+ const totalResources = resources.length
223
+
224
+ // Apply offset, limit
225
+ const offset = params.offset || 0
226
+ const limit = params.limit || 20
227
+ resources = resources.slice(offset, offset + limit)
228
+
229
+ // Expand the resources
230
+ if (params.expand !== undefined) {
231
+ resources = resources.map((resource) =>
232
+ this.expand(projectKey, resource, params.expand)
233
+ )
234
+ }
235
+
236
+ return {
237
+ count: totalResources,
238
+ total: resources.length,
239
+ offset: offset,
240
+ limit: limit,
241
+ results: resources.map(cloneObject),
242
+ } as PagedQueryResponseMap[RT]
243
+ }
244
+
245
+ search(
246
+ projectKey: string,
247
+ typeId: ResourceType,
248
+ params: QueryParams
249
+ ): PagedQueryResponse {
250
+ let resources = this.all(projectKey, typeId)
251
+
252
+ // Apply predicates
253
+ if (params.where) {
254
+ try {
255
+ const filterFunc = parseQueryExpression(params.where)
256
+ resources = resources.filter((resource) => filterFunc(resource, {}))
257
+ } catch (err) {
258
+ throw new CommercetoolsError<InvalidInputError>(
259
+ {
260
+ code: 'InvalidInput',
261
+ message: (err as any).message,
262
+ },
263
+ 400
264
+ )
265
+ }
266
+ }
267
+
268
+ // Get the total before slicing the array
269
+ const totalResources = resources.length
270
+
271
+ // Apply offset, limit
272
+ const offset = params.offset || 0
273
+ const limit = params.limit || 20
274
+ resources = resources.slice(offset, offset + limit)
275
+
276
+ // Expand the resources
277
+ if (params.expand !== undefined) {
278
+ resources = resources.map((resource) =>
279
+ this.expand(projectKey, resource, params.expand)
280
+ )
281
+ }
282
+
283
+ return {
284
+ count: totalResources,
285
+ total: resources.length,
286
+ offset: offset,
287
+ limit: limit,
288
+ results: resources,
289
+ }
290
+ }
291
+
292
+ getByResourceIdentifier<RT extends ResourceType>(
293
+ projectKey: string,
294
+ identifier: ResourceIdentifier
295
+ ): ResourceMap[RT] | null {
296
+ if (identifier.id) {
297
+ const resource = this.get(projectKey, identifier.typeId, identifier.id)
298
+ if (resource) {
299
+ return resource as ResourceMap[RT]
300
+ }
301
+ console.error(
302
+ `No resource found with typeId=${identifier.typeId}, id=${identifier.id}`
303
+ )
304
+ return null
305
+ }
306
+
307
+ if (identifier.key) {
308
+ const store = this.forProjectKey(projectKey)[identifier.typeId]
309
+
310
+ if (store) {
311
+ // TODO: BaseResource has no key attribute, but the subclasses should
312
+ // have them all.
313
+ const resource = Array.from(store.values()).find(
314
+ // @ts-ignore
315
+ (r) => r.key === identifier.key
316
+ )
317
+ if (resource) {
318
+ return resource as ResourceMap[RT]
319
+ }
320
+ } else {
321
+ throw new Error(
322
+ `No storage found for resource type: ${identifier.typeId}`
323
+ )
324
+ }
325
+ }
326
+ return null
327
+ }
328
+
329
+ addProject = (projectKey: string): Project => {
330
+ if (!this.projects[projectKey]) {
331
+ this.projects[projectKey] = {
332
+ key: projectKey,
333
+ name: '',
334
+ countries: [],
335
+ currencies: [],
336
+ languages: [],
337
+ createdAt: '2018-10-04T11:32:12.603Z',
338
+ trialUntil: '2018-12',
339
+ carts: {
340
+ countryTaxRateFallbackEnabled: false,
341
+ deleteDaysAfterLastModification: 90,
342
+ },
343
+ messages: { enabled: false, deleteDaysAfterCreation: 15 },
344
+ shippingRateInputType: undefined,
345
+ externalOAuth: undefined,
346
+ searchIndexing: {
347
+ products: {
348
+ status: 'Deactivated',
349
+ },
350
+ orders: {
351
+ status: 'Deactivated',
352
+ },
353
+ },
354
+ version: 1,
355
+ }
356
+ }
357
+ return this.projects[projectKey]
358
+ }
359
+
360
+ saveProject = (project: Project): Project => {
361
+ this.projects[project.key] = project
362
+ return project
363
+ }
364
+
365
+ getProject = (projectKey: string): Project => this.addProject(projectKey)
366
+
367
+ // Expand resolves a nested reference and injects the object in the given obj
368
+ public expand = <T>(
369
+ projectKey: string,
370
+ obj: T,
371
+ clause: undefined | string | string[]
372
+ ): T => {
373
+ if (!clause) return obj
374
+ const newObj = cloneObject(obj)
375
+ if (Array.isArray(clause)) {
376
+ clause.forEach((c) => {
377
+ this._resolveResource(projectKey, newObj, c)
378
+ })
379
+ } else {
380
+ this._resolveResource(projectKey, newObj, clause)
381
+ }
382
+ return newObj
383
+ }
384
+
385
+ private _resolveResource = (projectKey: string, obj: any, expand: string) => {
386
+ const params = parseExpandClause(expand)
387
+
388
+ if (!params.index) {
389
+ const reference = obj[params.element]
390
+ if (reference === undefined) {
391
+ return
392
+ }
393
+ this._resolveReference(projectKey, reference, params.rest)
394
+ } else if (params.index === '*') {
395
+ const reference = obj[params.element]
396
+ if (reference === undefined || !Array.isArray(reference)) return
397
+ reference.forEach((itemRef: Writable<Reference>) => {
398
+ this._resolveReference(projectKey, itemRef, params.rest)
399
+ })
400
+ } else {
401
+ const reference = obj[params.element][params.index]
402
+ if (reference === undefined) return
403
+ this._resolveReference(projectKey, reference, params.rest)
404
+ }
405
+ }
406
+
407
+ private _resolveReference(
408
+ projectKey: string,
409
+ reference: any,
410
+ expand: string | undefined
411
+ ) {
412
+ if (reference === undefined) return
413
+
414
+ if (
415
+ reference.typeId !== undefined &&
416
+ (reference.id !== undefined || reference.key !== undefined)
417
+ ) {
418
+ // @ts-ignore
419
+ reference.obj = this.getByResourceIdentifier(projectKey, {
420
+ typeId: reference.typeId,
421
+ id: reference.id,
422
+ key: reference.key,
423
+ } as ResourceIdentifier)
424
+ if (expand) {
425
+ this._resolveResource(projectKey, reference.obj, expand)
426
+ }
427
+ } else {
428
+ if (expand) {
429
+ this._resolveResource(projectKey, reference, expand)
430
+ }
431
+ }
432
+ }
433
433
  }