@labdigital/commercetools-mock 0.6.5 → 0.8.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 (118) hide show
  1. package/dist/index.d.ts +409 -3
  2. package/dist/index.global.js +49983 -0
  3. package/dist/index.global.js.map +1 -0
  4. package/dist/index.js +4835 -6
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +4803 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +36 -17
  9. package/src/ctMock.ts +5 -0
  10. package/src/helpers.ts +39 -0
  11. package/src/lib/projectionSearchFilter.test.ts +183 -0
  12. package/src/lib/projectionSearchFilter.ts +347 -0
  13. package/src/priceSelector.test.ts +96 -0
  14. package/src/priceSelector.ts +109 -0
  15. package/src/product-projection-search.ts +345 -0
  16. package/src/projectAPI.ts +19 -20
  17. package/src/repositories/category.ts +36 -0
  18. package/src/repositories/channel.ts +104 -0
  19. package/src/repositories/customer-group.ts +37 -0
  20. package/src/repositories/discount-code.ts +37 -0
  21. package/src/repositories/helpers.ts +46 -4
  22. package/src/repositories/product-discount.ts +181 -0
  23. package/src/repositories/product-projection.ts +30 -59
  24. package/src/repositories/product-type.ts +88 -6
  25. package/src/repositories/product.ts +49 -9
  26. package/src/repositories/shipping-method.ts +31 -0
  27. package/src/repositories/store.ts +43 -3
  28. package/src/repositories/type.ts +19 -0
  29. package/src/services/custom-object.test.ts +2 -2
  30. package/src/services/product-discount.ts +33 -0
  31. package/src/services/product-projection.test.ts +329 -107
  32. package/src/services/product.test.ts +12 -0
  33. package/src/storage.ts +116 -58
  34. package/src/types.ts +9 -2
  35. package/dist/commercetools-mock.cjs.development.js +0 -4382
  36. package/dist/commercetools-mock.cjs.development.js.map +0 -1
  37. package/dist/commercetools-mock.cjs.production.min.js +0 -2
  38. package/dist/commercetools-mock.cjs.production.min.js.map +0 -1
  39. package/dist/commercetools-mock.esm.js +0 -4374
  40. package/dist/commercetools-mock.esm.js.map +0 -1
  41. package/dist/constants.d.ts +0 -2
  42. package/dist/ctMock.d.ts +0 -32
  43. package/dist/exceptions.d.ts +0 -12
  44. package/dist/helpers.d.ts +0 -6
  45. package/dist/lib/expandParser.d.ts +0 -15
  46. package/dist/lib/filterParser.d.ts +0 -1
  47. package/dist/lib/haversine.d.ts +0 -8
  48. package/dist/lib/masking.d.ts +0 -1
  49. package/dist/lib/predicateParser.d.ts +0 -11
  50. package/dist/lib/proxy.d.ts +0 -1
  51. package/dist/oauth/errors.d.ts +0 -8
  52. package/dist/oauth/helpers.d.ts +0 -2
  53. package/dist/oauth/server.d.ts +0 -12
  54. package/dist/oauth/store.d.ts +0 -14
  55. package/dist/projectAPI.d.ts +0 -12
  56. package/dist/repositories/abstract.d.ts +0 -33
  57. package/dist/repositories/cart-discount.d.ts +0 -9
  58. package/dist/repositories/cart.d.ts +0 -21
  59. package/dist/repositories/category.d.ts +0 -18
  60. package/dist/repositories/channel.d.ts +0 -6
  61. package/dist/repositories/custom-object.d.ts +0 -8
  62. package/dist/repositories/customer-group.d.ts +0 -11
  63. package/dist/repositories/customer.d.ts +0 -11
  64. package/dist/repositories/discount-code.d.ts +0 -8
  65. package/dist/repositories/errors.d.ts +0 -2
  66. package/dist/repositories/extension.d.ts +0 -8
  67. package/dist/repositories/helpers.d.ts +0 -10
  68. package/dist/repositories/inventory-entry.d.ts +0 -14
  69. package/dist/repositories/my-order.d.ts +0 -6
  70. package/dist/repositories/order.d.ts +0 -26
  71. package/dist/repositories/payment.d.ts +0 -23
  72. package/dist/repositories/product-projection.d.ts +0 -10
  73. package/dist/repositories/product-type.d.ts +0 -10
  74. package/dist/repositories/product.d.ts +0 -11
  75. package/dist/repositories/project.d.ts +0 -8
  76. package/dist/repositories/shipping-method.d.ts +0 -10
  77. package/dist/repositories/shopping-list.d.ts +0 -6
  78. package/dist/repositories/state.d.ts +0 -8
  79. package/dist/repositories/store.d.ts +0 -10
  80. package/dist/repositories/subscription.d.ts +0 -6
  81. package/dist/repositories/tax-category.d.ts +0 -10
  82. package/dist/repositories/type.d.ts +0 -8
  83. package/dist/repositories/zone.d.ts +0 -8
  84. package/dist/server.d.ts +0 -1
  85. package/dist/services/abstract.d.ts +0 -20
  86. package/dist/services/cart-discount.d.ts +0 -9
  87. package/dist/services/cart.d.ts +0 -12
  88. package/dist/services/category.d.ts +0 -9
  89. package/dist/services/channel.d.ts +0 -9
  90. package/dist/services/custom-object.d.ts +0 -13
  91. package/dist/services/customer-group.d.ts +0 -9
  92. package/dist/services/customer.d.ts +0 -10
  93. package/dist/services/discount-code.d.ts +0 -9
  94. package/dist/services/extension.d.ts +0 -9
  95. package/dist/services/inventory-entry.d.ts +0 -9
  96. package/dist/services/my-cart.d.ts +0 -11
  97. package/dist/services/my-customer.d.ts +0 -13
  98. package/dist/services/my-order.d.ts +0 -10
  99. package/dist/services/my-payment.d.ts +0 -9
  100. package/dist/services/order.d.ts +0 -12
  101. package/dist/services/payment.d.ts +0 -9
  102. package/dist/services/product-projection.d.ts +0 -11
  103. package/dist/services/product-type.d.ts +0 -11
  104. package/dist/services/product.d.ts +0 -9
  105. package/dist/services/project.d.ts +0 -11
  106. package/dist/services/shipping-method.d.ts +0 -10
  107. package/dist/services/shopping-list.d.ts +0 -9
  108. package/dist/services/state.d.ts +0 -9
  109. package/dist/services/store.d.ts +0 -11
  110. package/dist/services/subscription.d.ts +0 -9
  111. package/dist/services/tax-category.d.ts +0 -11
  112. package/dist/services/type.d.ts +0 -9
  113. package/dist/services/zone.d.ts +0 -9
  114. package/dist/storage.d.ts +0 -56
  115. package/dist/types.d.ts +0 -89
  116. package/dist/validate.d.ts +0 -7482
  117. package/src/lib/filterParser.test.ts +0 -15
  118. package/src/lib/filterParser.ts +0 -17
@@ -1,4382 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
-
7
- var nock = _interopDefault(require('nock'));
8
- var express = require('express');
9
- var express__default = _interopDefault(express);
10
- var supertest = _interopDefault(require('supertest'));
11
- var morgan = _interopDefault(require('morgan'));
12
- var assert = _interopDefault(require('assert'));
13
- var perplex = _interopDefault(require('perplex'));
14
- var pratt = require('pratt');
15
- var auth = _interopDefault(require('basic-auth'));
16
- var bodyParser = _interopDefault(require('body-parser'));
17
- var crypto = require('crypto');
18
- var uuid = require('uuid');
19
- var deepEqual = _interopDefault(require('deep-equal'));
20
-
21
- const parseExpandClause = clause => {
22
- const result = {
23
- element: clause,
24
- index: undefined,
25
- rest: undefined
26
- };
27
- const pos = clause.indexOf('.');
28
-
29
- if (pos > 0) {
30
- result.element = clause.substring(0, pos);
31
- result.rest = clause.substring(pos + 1);
32
- }
33
-
34
- const match = result.element.match(/\[([^\]+])]/);
35
-
36
- if (match) {
37
- result.index = match[1] === '*' ? '*' : parseInt(match[1], 10);
38
- result.element = result.element.substring(0, match.index);
39
- }
40
-
41
- return result;
42
- };
43
-
44
- /**
45
- * Returns the distance between src and dst as meters
46
- */
47
- const haversineDistance = (src, dst) => {
48
- const RADIUS_OF_EARTH_IN_KM = 6371;
49
-
50
- const toRadian = deg => deg * (Math.PI / 180);
51
-
52
- const dLat = toRadian(dst.latitude - src.latitude);
53
- const dLon = toRadian(dst.longitude - src.longitude);
54
- var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadian(src.latitude)) * Math.cos(toRadian(dst.latitude)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
55
- var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
56
- return RADIUS_OF_EARTH_IN_KM * c * 1000;
57
- };
58
-
59
- /**
60
- * This module implements the commercetools query predicate filter expression.
61
- * Support should be 100% complete.
62
- *
63
- * See https://docs.commercetools.com/api/predicates/query
64
- */
65
- class PredicateError {
66
- constructor(message) {
67
- this.message = message;
68
- }
69
-
70
- }
71
- const parseQueryExpression = predicate => {
72
- if (Array.isArray(predicate)) {
73
- const callbacks = predicate.map(item => generateMatchFunc(item));
74
- return (target, variables) => {
75
- return callbacks.every(callback => callback(target, variables));
76
- };
77
- } else {
78
- return generateMatchFunc(predicate);
79
- }
80
- };
81
-
82
- const validateSymbol = val => {
83
- if (!val.type) {
84
- throw new PredicateError('Internal error');
85
- }
86
-
87
- if (val.type === 'identifier') {
88
- var _val$pos, _val$pos2;
89
-
90
- const char = val.value.charAt(0);
91
- const line = (_val$pos = val.pos) == null ? void 0 : _val$pos.start.line;
92
- const column = (_val$pos2 = val.pos) == null ? void 0 : _val$pos2.start.column;
93
- throw new PredicateError(`Invalid input '${char}', expected input parameter or primitive value (line ${line}, column ${column})`);
94
- }
95
- };
96
-
97
- const resolveSymbol = (val, vars) => {
98
- if (val.type === 'var') {
99
- if (!(val.value in vars)) {
100
- throw new PredicateError(`Missing parameter value for ${val.value}`);
101
- }
102
-
103
- return vars[val.value];
104
- }
105
-
106
- return val.value;
107
- };
108
-
109
- const resolveValue = (obj, val) => {
110
- if (val.type !== 'identifier') {
111
- throw new PredicateError('Internal error');
112
- }
113
-
114
- if (!(val.value in obj)) {
115
- if (Array.isArray(obj)) {
116
- return Object.values(obj).filter(v => val.value in v).map(v => v[val.value]);
117
- }
118
-
119
- throw new PredicateError(`The field '${val.value}' does not exist.`);
120
- }
121
-
122
- return obj[val.value];
123
- };
124
-
125
- const getLexer = value => {
126
- return new perplex(value).token('AND', /and(?![-_a-z0-9]+)/i).token('OR', /or(?![-_a-z0-9]+)/i).token('NOT', /not(?![-_a-z0-9]+)/i).token('WITHIN', /within(?![-_a-z0-9]+)/i).token('IN', /in(?![-_a-z0-9]+)/i).token('MATCHES_IGNORE_CASE', /matches\s+ignore\s+case(?![-_a-z0-9]+)/i).token('CONTAINS', /contains(?![-_a-z0-9]+)/i).token('ALL', /all(?![-_a-z0-9]+)/i).token('ANY', /any(?![-_a-z0-9]+)/i).token('EMPTY', /empty(?![-_a-z0-9]+)/i).token('IS', /is(?![-_a-z0-9]+)/i).token('DEFINED', /defined(?![-_a-z0-9]+)/i).token('FLOAT', /\d+\.\d+/).token('INT', /\d+/).token('VARIABLE', /:([-_A-Za-z0-9]+)/).token('IDENTIFIER', /[-_A-Za-z0-9]+/).token('STRING', /"((?:\\.|[^"\\])*)"/).token('STRING', /'((?:\\.|[^'\\])*)'/).token('COMMA', ',').token('(', '(').token(')', ')').token('>=', '>=').token('<=', '<=').token('>', '>').token('<', '<').token('!=', '!=').token('=', '=').token('"', '"').token('WS', /\s+/, true); // skip
127
- };
128
- /**
129
- * This function converts a query expression in to a callable which returns a
130
- * boolean to indicate if the given object matches or not.
131
- *
132
- * This currently parses the predicate each time it is called, but it should be
133
- * straight-forward to add a query cache (lru-cache)
134
- */
135
-
136
-
137
- const generateMatchFunc = predicate => {
138
- const lexer = getLexer(predicate);
139
- const parser = new pratt.Parser(lexer).builder().nud('IDENTIFIER', 100, t => {
140
- return {
141
- type: 'identifier',
142
- value: t.token.match,
143
- pos: t.token.strpos()
144
- };
145
- }).nud('VARIABLE', 100, t => {
146
- return {
147
- type: 'var',
148
- // @ts-ignore
149
- value: t.token.groups[1],
150
- pos: t.token.strpos()
151
- };
152
- }).nud('STRING', 100, t => {
153
- return {
154
- type: 'string',
155
- // @ts-ignore
156
- value: t.token.groups[1],
157
- pos: t.token.strpos()
158
- };
159
- }).nud('INT', 1, t => {
160
- return {
161
- type: 'int',
162
- value: parseInt(t.token.match, 10),
163
- pos: t.token.strpos()
164
- };
165
- }).nud('FLOAT', 1, t => {
166
- return {
167
- type: 'float',
168
- value: parseFloat(t.token.match),
169
- pos: t.token.strpos()
170
- };
171
- }).nud('NOT', 100, ({
172
- bp
173
- }) => {
174
- const expr = parser.parse({
175
- terminals: [bp - 1]
176
- });
177
- return obj => {
178
- return !expr(obj);
179
- };
180
- }).nud('EMPTY', 10, ({
181
- bp
182
- }) => {
183
- return 'empty';
184
- }).nud('DEFINED', 10, ({
185
- bp
186
- }) => {
187
- return 'defined';
188
- }).led('AND', 5, ({
189
- left,
190
- bp
191
- }) => {
192
- const expr = parser.parse({
193
- terminals: [bp - 1]
194
- });
195
- return obj => {
196
- return left(obj) && expr(obj);
197
- };
198
- }).led('OR', 5, ({
199
- left,
200
- token,
201
- bp
202
- }) => {
203
- const expr = parser.parse({
204
- terminals: [bp - 1]
205
- });
206
- return (obj, vars) => {
207
- return left(obj, vars) || expr(obj, vars);
208
- };
209
- }).led('COMMA', 1, ({
210
- left,
211
- token,
212
- bp
213
- }) => {
214
- const expr = parser.parse({
215
- terminals: [bp - 1]
216
- });
217
-
218
- if (Array.isArray(expr)) {
219
- return [left, ...expr];
220
- } else {
221
- return [left, expr];
222
- }
223
- }).nud('(', 100, t => {
224
- const expr = parser.parse({
225
- terminals: [')']
226
- });
227
- return expr;
228
- }).led('(', 100, ({
229
- left,
230
- bp
231
- }) => {
232
- const expr = parser.parse();
233
- lexer.expect(')');
234
- return (obj, vars) => {
235
- const value = resolveValue(obj, left);
236
-
237
- if (value) {
238
- return expr(value);
239
- }
240
-
241
- return false;
242
- };
243
- }).bp(')', 0).led('=', 20, ({
244
- left,
245
- bp
246
- }) => {
247
- const expr = parser.parse({
248
- terminals: [bp - 1]
249
- });
250
- validateSymbol(expr);
251
- return (obj, vars) => {
252
- const resolvedValue = resolveValue(obj, left);
253
- const resolvedSymbol = resolveSymbol(expr, vars);
254
-
255
- if (Array.isArray(resolvedValue)) {
256
- return !!resolvedValue.some(elem => elem === resolvedSymbol);
257
- }
258
-
259
- return resolvedValue === resolvedSymbol;
260
- };
261
- }).led('!=', 20, ({
262
- left,
263
- bp
264
- }) => {
265
- const expr = parser.parse({
266
- terminals: [bp - 1]
267
- });
268
- validateSymbol(expr);
269
- return (obj, vars) => {
270
- return resolveValue(obj, left) !== resolveSymbol(expr, vars);
271
- };
272
- }).led('>', 20, ({
273
- left,
274
- bp
275
- }) => {
276
- const expr = parser.parse({
277
- terminals: [bp - 1]
278
- });
279
- validateSymbol(expr);
280
- return (obj, vars) => {
281
- return resolveValue(obj, left) > resolveSymbol(expr, vars);
282
- };
283
- }).led('>=', 20, ({
284
- left,
285
- bp
286
- }) => {
287
- const expr = parser.parse({
288
- terminals: [bp - 1]
289
- });
290
- validateSymbol(expr);
291
- return (obj, vars) => {
292
- return resolveValue(obj, left) >= resolveSymbol(expr, vars);
293
- };
294
- }).led('<', 20, ({
295
- left,
296
- bp
297
- }) => {
298
- const expr = parser.parse({
299
- terminals: [bp - 1]
300
- });
301
- validateSymbol(expr);
302
- return (obj, vars) => {
303
- return resolveValue(obj, left) < resolveSymbol(expr, vars);
304
- };
305
- }).led('<=', 20, ({
306
- left,
307
- bp
308
- }) => {
309
- const expr = parser.parse({
310
- terminals: [bp - 1]
311
- });
312
- validateSymbol(expr);
313
- return (obj, vars) => {
314
- return resolveValue(obj, left) <= resolveSymbol(expr, vars);
315
- };
316
- }).led('IS', 20, ({
317
- left,
318
- bp
319
- }) => {
320
- let invert = false; // Peek if this is a `is not` statement
321
-
322
- const next = lexer.peek();
323
-
324
- if (next.type === 'NOT') {
325
- invert = true;
326
- lexer.next();
327
- }
328
-
329
- const expr = parser.parse({
330
- terminals: [bp - 1]
331
- });
332
-
333
- switch (expr) {
334
- case 'empty':
335
- {
336
- if (!invert) {
337
- return (obj, vars) => {
338
- const val = resolveValue(obj, left);
339
- return val.length === 0;
340
- };
341
- } else {
342
- return (obj, vars) => {
343
- const val = resolveValue(obj, left);
344
- return val.length !== 0;
345
- };
346
- }
347
- }
348
-
349
- case 'defined':
350
- {
351
- if (!invert) {
352
- return (obj, vars) => {
353
- const val = resolveValue(obj, left);
354
- return val !== undefined;
355
- };
356
- } else {
357
- return (obj, vars) => {
358
- const val = resolveValue(obj, left);
359
- return val === undefined;
360
- };
361
- }
362
- }
363
-
364
- default:
365
- {
366
- throw new Error('Unexpected');
367
- }
368
- }
369
- }).led('IN', 20, ({
370
- left,
371
- bp
372
- }) => {
373
- const expr = parser.parse({
374
- terminals: [bp - 1]
375
- });
376
- return (obj, vars) => {
377
- let symbols = expr;
378
-
379
- if (!Array.isArray(symbols)) {
380
- symbols = [expr];
381
- }
382
-
383
- const inValues = symbols.map(item => resolveSymbol(item, vars));
384
- return inValues.includes(resolveValue(obj, left));
385
- };
386
- }).led('MATCHES_IGNORE_CASE', 20, ({
387
- left,
388
- bp
389
- }) => {
390
- const expr = parser.parse({
391
- terminals: [bp - 1]
392
- });
393
- validateSymbol(expr);
394
- return (obj, vars) => {
395
- const value = resolveValue(obj, left);
396
- const other = resolveSymbol(expr, vars);
397
-
398
- if (typeof value != 'string') {
399
- throw new PredicateError(`The field '${left.value}' does not support this expression.`);
400
- }
401
-
402
- return value.toLowerCase() === other.toLowerCase();
403
- };
404
- }).led('WITHIN', 20, ({
405
- left,
406
- bp
407
- }) => {
408
- const type = lexer.next();
409
-
410
- if (type.match !== 'circle') {
411
- throw new PredicateError(`Invalid input '${type.match}', expected circle`);
412
- }
413
-
414
- lexer.expect('(');
415
- const expr = parser.parse({
416
- terminals: [')']
417
- });
418
- return (obj, vars) => {
419
- const value = resolveValue(obj, left);
420
- if (!value) return false;
421
- const maxDistance = resolveSymbol(expr[2], vars);
422
- const distance = haversineDistance({
423
- longitude: value[0],
424
- latitude: value[1]
425
- }, {
426
- longitude: resolveSymbol(expr[0], vars),
427
- latitude: resolveSymbol(expr[1], vars)
428
- });
429
- return distance <= maxDistance;
430
- };
431
- }).led('CONTAINS', 20, ({
432
- left,
433
- bp
434
- }) => {
435
- const keyword = lexer.next();
436
- let expr = parser.parse();
437
-
438
- if (!Array.isArray(expr)) {
439
- expr = [expr];
440
- }
441
-
442
- return (obj, vars) => {
443
- const value = resolveValue(obj, left);
444
-
445
- if (!Array.isArray(value)) {
446
- throw new PredicateError(`The field '${left.value}' does not support this expression.`);
447
- }
448
-
449
- const array = expr.map(item => resolveSymbol(item, vars));
450
-
451
- if (keyword.type === 'ALL') {
452
- return array.every(item => value.includes(item));
453
- } else {
454
- return array.some(item => value.includes(item));
455
- }
456
- };
457
- }).build();
458
- const result = parser.parse();
459
-
460
- if (typeof result !== 'function') {
461
- const lines = predicate.split('\n');
462
- const column = lines[lines.length - 1].length;
463
- throw new PredicateError(`Unexpected end of input, expected SphereIdentifierChar, comparison ` + `operator, not, in, contains, is, within or matches` + ` (line ${lines.length}, column ${column})`);
464
- }
465
-
466
- return result;
467
- };
468
-
469
- class CommercetoolsError extends Error {
470
- constructor(info, statusCode = 400) {
471
- super(info.message);
472
- this.info = info;
473
- this.statusCode = statusCode || 500;
474
- }
475
-
476
- }
477
-
478
- class AbstractStorage {}
479
- class InMemoryStorage extends AbstractStorage {
480
- constructor() {
481
- super(...arguments);
482
- this.resources = {};
483
- this.projects = {};
484
-
485
- this.addProject = projectKey => {
486
- if (!this.projects[projectKey]) {
487
- this.projects[projectKey] = {
488
- key: projectKey,
489
- name: '',
490
- countries: [],
491
- currencies: [],
492
- languages: [],
493
- createdAt: '2018-10-04T11:32:12.603Z',
494
- trialUntil: '2018-12',
495
- carts: {
496
- countryTaxRateFallbackEnabled: false,
497
- deleteDaysAfterLastModification: 90
498
- },
499
- messages: {
500
- enabled: false,
501
- deleteDaysAfterCreation: 15
502
- },
503
- shippingRateInputType: undefined,
504
- externalOAuth: undefined,
505
- searchIndexing: {
506
- products: {
507
- status: 'Deactivated'
508
- },
509
- orders: {
510
- status: 'Deactivated'
511
- }
512
- },
513
- version: 1
514
- };
515
- }
516
-
517
- return this.projects[projectKey];
518
- };
519
-
520
- this.saveProject = project => {
521
- this.projects[project.key] = project;
522
- return project;
523
- };
524
-
525
- this.getProject = projectKey => {
526
- return this.addProject(projectKey);
527
- };
528
-
529
- this.expand = (projectKey, obj, clause) => {
530
- if (!clause) return obj;
531
- const newObj = JSON.parse(JSON.stringify(obj));
532
-
533
- if (Array.isArray(clause)) {
534
- clause.forEach(c => {
535
- this._resolveResource(projectKey, newObj, c);
536
- });
537
- } else {
538
- this._resolveResource(projectKey, newObj, clause);
539
- }
540
-
541
- return newObj;
542
- };
543
-
544
- this._resolveResource = (projectKey, obj, expand) => {
545
- const params = parseExpandClause(expand);
546
-
547
- if (!params.index) {
548
- const reference = obj[params.element];
549
-
550
- if (reference === undefined) {
551
- return;
552
- }
553
-
554
- this._resolveReference(projectKey, reference, params.rest);
555
- } else if (params.index === '*') {
556
- const reference = obj[params.element];
557
- if (reference === undefined || !Array.isArray(reference)) return;
558
- reference.forEach(itemRef => {
559
- this._resolveReference(projectKey, itemRef, params.rest);
560
- });
561
- } else {
562
- const reference = obj[params.element][params.index];
563
- if (reference === undefined) return;
564
-
565
- this._resolveReference(projectKey, reference, params.rest);
566
- }
567
- };
568
- }
569
-
570
- forProjectKey(projectKey) {
571
- this.addProject(projectKey);
572
- let projectStorage = this.resources[projectKey];
573
-
574
- if (!projectStorage) {
575
- projectStorage = this.resources[projectKey] = {
576
- cart: new Map(),
577
- 'cart-discount': new Map(),
578
- category: new Map(),
579
- channel: new Map(),
580
- customer: new Map(),
581
- 'customer-group': new Map(),
582
- 'discount-code': new Map(),
583
- extension: new Map(),
584
- 'inventory-entry': new Map(),
585
- 'key-value-document': new Map(),
586
- order: new Map(),
587
- payment: new Map(),
588
- 'product-type': new Map(),
589
- product: new Map(),
590
- 'product-selection': new Map(),
591
- 'product-projection': new Map(),
592
- 'shipping-method': new Map(),
593
- state: new Map(),
594
- store: new Map(),
595
- 'shopping-list': new Map(),
596
- subscription: new Map(),
597
- 'tax-category': new Map(),
598
- type: new Map(),
599
- zone: new Map()
600
- };
601
- }
602
-
603
- return projectStorage;
604
- }
605
-
606
- clear() {
607
- for (const [, projectStorage] of Object.entries(this.resources)) {
608
- for (const [, value] of Object.entries(projectStorage)) {
609
- value == null ? void 0 : value.clear();
610
- }
611
- }
612
- }
613
-
614
- assertStorage(typeId) {}
615
-
616
- all(projectKey, typeId) {
617
- const store = this.forProjectKey(projectKey)[typeId];
618
-
619
- if (store) {
620
- return Array.from(store.values());
621
- }
622
-
623
- return [];
624
- }
625
-
626
- add(projectKey, typeId, obj, params = {}) {
627
- var _this$forProjectKey$t;
628
-
629
- (_this$forProjectKey$t = this.forProjectKey(projectKey)[typeId]) == null ? void 0 : _this$forProjectKey$t.set(obj.id, obj);
630
- const resource = this.get(projectKey, typeId, obj.id, params);
631
- assert(resource, `resource of type ${typeId} with id ${obj.id} not created`);
632
- return resource;
633
- }
634
-
635
- get(projectKey, typeId, id, params = {}) {
636
- var _this$forProjectKey$t2;
637
-
638
- const resource = (_this$forProjectKey$t2 = this.forProjectKey(projectKey)[typeId]) == null ? void 0 : _this$forProjectKey$t2.get(id);
639
-
640
- if (resource) {
641
- return this.expand(projectKey, resource, params.expand);
642
- }
643
-
644
- return null;
645
- }
646
-
647
- getByKey(projectKey, typeId, key, params = {}) {
648
- const store = this.forProjectKey(projectKey)[typeId];
649
-
650
- if (!store) {
651
- throw new Error('No type');
652
- }
653
-
654
- const resources = Array.from(store.values());
655
- const resource = resources.find(e => e.key === key);
656
-
657
- if (resource) {
658
- return this.expand(projectKey, resource, params.expand);
659
- }
660
-
661
- return null;
662
- }
663
-
664
- delete(projectKey, typeId, id, params = {}) {
665
- const resource = this.get(projectKey, typeId, id);
666
-
667
- if (resource) {
668
- var _this$forProjectKey$t3;
669
-
670
- (_this$forProjectKey$t3 = this.forProjectKey(projectKey)[typeId]) == null ? void 0 : _this$forProjectKey$t3.delete(id);
671
- return this.expand(projectKey, resource, params.expand);
672
- }
673
-
674
- return resource;
675
- }
676
-
677
- query(projectKey, typeId, params) {
678
- const store = this.forProjectKey(projectKey)[typeId];
679
-
680
- if (!store) {
681
- throw new Error('No type');
682
- }
683
-
684
- let resources = Array.from(store.values()); // Apply predicates
685
-
686
- if (params.where) {
687
- try {
688
- const filterFunc = parseQueryExpression(params.where);
689
- resources = resources.filter(resource => filterFunc(resource, {}));
690
- } catch (err) {
691
- throw new CommercetoolsError({
692
- code: 'InvalidInput',
693
- message: err.message
694
- }, 400);
695
- }
696
- } // Get the total before slicing the array
697
-
698
-
699
- const totalResources = resources.length; // Apply offset, limit
700
-
701
- const offset = params.offset || 0;
702
- const limit = params.limit || 20;
703
- resources = resources.slice(offset, offset + limit); // Expand the resources
704
-
705
- if (params.expand !== undefined) {
706
- resources = resources.map(resource => {
707
- return this.expand(projectKey, resource, params.expand);
708
- });
709
- }
710
-
711
- return {
712
- count: totalResources,
713
- total: resources.length,
714
- offset: offset,
715
- limit: limit,
716
- results: resources
717
- };
718
- }
719
-
720
- getByResourceIdentifier(projectKey, identifier) {
721
- if (identifier.id) {
722
- const resource = this.get(projectKey, identifier.typeId, identifier.id);
723
-
724
- if (resource) {
725
- return resource;
726
- }
727
-
728
- console.error(`No resource found with typeId=${identifier.typeId}, id=${identifier.id}`);
729
- return undefined;
730
- }
731
-
732
- if (identifier.key) {
733
- const store = this.forProjectKey(projectKey)[identifier.typeId];
734
-
735
- if (store) {
736
- // TODO: BaseResource has no key attribute, but the subclasses should
737
- // have them all.
738
- const resource = Array.from(store.values()).find( // @ts-ignore
739
- r => r.key === identifier.key);
740
-
741
- if (resource) {
742
- return resource;
743
- }
744
- } else {
745
- throw new Error(`No storage found for resource type: ${identifier.typeId}`);
746
- }
747
- }
748
-
749
- return undefined;
750
- }
751
-
752
- _resolveReference(projectKey, reference, expand) {
753
- if (reference === undefined) return;
754
-
755
- if (reference.typeId !== undefined && (reference.id !== undefined || reference.key !== undefined)) {
756
- // @ts-ignore
757
- reference.obj = this.getByResourceIdentifier(projectKey, {
758
- typeId: reference.typeId,
759
- id: reference.id,
760
- key: reference.key
761
- });
762
-
763
- if (expand) {
764
- this._resolveResource(projectKey, reference.obj, expand);
765
- }
766
- } else {
767
- if (expand) {
768
- this._resolveResource(projectKey, reference, expand);
769
- }
770
- }
771
- }
772
-
773
- }
774
-
775
- class OAuth2Store {
776
- constructor(validate = true) {
777
- this.tokens = [];
778
- this.validate = true;
779
- this.validate = validate;
780
- }
781
-
782
- getClientToken(clientId, clientSecret, scope) {
783
- const token = {
784
- access_token: crypto.randomBytes(16).toString('base64'),
785
- token_type: 'Bearer',
786
- expires_in: 172800,
787
- scope: scope || 'todo'
788
- };
789
- this.tokens.push(token);
790
- return token;
791
- }
792
-
793
- validateToken(token) {
794
- if (!this.validate) return true;
795
- const foundToken = this.tokens.find(t => t.access_token === token);
796
-
797
- if (foundToken) {
798
- return true;
799
- }
800
-
801
- return false;
802
- }
803
-
804
- }
805
-
806
- const getBearerToken = request => {
807
- const authHeader = request.header('Authorization');
808
- const match = authHeader == null ? void 0 : authHeader.match(/^Bearer\s(?<token>[^\s]+)$/);
809
-
810
- if (match) {
811
- var _match$groups;
812
-
813
- return (_match$groups = match.groups) == null ? void 0 : _match$groups.token;
814
- }
815
-
816
- return undefined;
817
- };
818
-
819
- class OAuth2Server {
820
- constructor(options) {
821
- this.store = new OAuth2Store(options.validate);
822
- }
823
-
824
- createRouter() {
825
- const router = express__default.Router();
826
- router.use(bodyParser.urlencoded({
827
- extended: true
828
- }));
829
- router.post('/token', this.tokenHandler.bind(this));
830
- return router;
831
- }
832
-
833
- createMiddleware() {
834
- return async (request, response, next) => {
835
- const token = getBearerToken(request);
836
-
837
- if (!token) {
838
- next(new CommercetoolsError({
839
- code: 'access_denied',
840
- message: 'This endpoint requires an access token. You can get one from the authorization server.'
841
- }, 401));
842
- }
843
-
844
- if (!token || !this.store.validateToken(token)) {
845
- next(new CommercetoolsError({
846
- code: 'invalid_token',
847
- message: 'invalid_token'
848
- }, 401));
849
- }
850
-
851
- next();
852
- };
853
- }
854
-
855
- async tokenHandler(request, response, next) {
856
- const authHeader = request.header('Authorization');
857
-
858
- if (!authHeader) {
859
- return next(new CommercetoolsError({
860
- code: 'invalid_client',
861
- message: 'Please provide valid client credentials using HTTP Basic Authentication.'
862
- }, 401));
863
- }
864
-
865
- const credentials = auth.parse(authHeader);
866
-
867
- if (!credentials) {
868
- return next(new CommercetoolsError({
869
- code: 'invalid_client',
870
- message: 'Please provide valid client credentials using HTTP Basic Authentication.'
871
- }, 400));
872
- }
873
-
874
- const grantType = request.query.grant_type || request.body.grant_type;
875
-
876
- if (!grantType) {
877
- return next(new CommercetoolsError({
878
- code: 'invalid_request',
879
- message: 'Missing required parameter: grant_type.'
880
- }, 400));
881
- }
882
-
883
- if (grantType === 'client_credentials') {
884
- var _request$query$scope;
885
-
886
- const token = this.store.getClientToken(credentials.name, credentials.pass, (_request$query$scope = request.query.scope) == null ? void 0 : _request$query$scope.toString());
887
- return response.status(200).send(token);
888
- } else {
889
- return next(new CommercetoolsError({
890
- code: 'unsupported_grant_type',
891
- message: `Invalid parameter: grant_type: Invalid grant type: ${grantType}`
892
- }, 400));
893
- }
894
- }
895
-
896
- }
897
-
898
- const getBaseResourceProperties = () => {
899
- return {
900
- id: uuid.v4(),
901
- createdAt: new Date().toISOString(),
902
- lastModifiedAt: new Date().toISOString(),
903
- version: 0
904
- };
905
- };
906
-
907
- class ProjectAPI {
908
- constructor(projectKey, services, storage) {
909
- this.projectKey = projectKey;
910
- this._storage = storage;
911
- this._services = services;
912
- }
913
-
914
- add(typeId, resource) {
915
- //@ts-ignore
916
- if (typeId === 'custom-object') typeId = 'key-value-document';
917
- const parsedTypeId = typeId;
918
- const service = this._services[parsedTypeId];
919
-
920
- if (service) {
921
- this._storage.add(this.projectKey, parsedTypeId, { ...getBaseResourceProperties(),
922
- ...resource
923
- });
924
- } else {
925
- throw new Error(`Service for ${typeId} not implemented yet`);
926
- }
927
- }
928
-
929
- get(typeId, id, params) {
930
- return this._storage.get(this.projectKey, typeId, id, params);
931
- } // TODO: Not sure if we want to expose this...
932
-
933
-
934
- getRepository(typeId) {
935
- const service = this._services[typeId];
936
-
937
- if (service !== undefined) {
938
- return service.repository;
939
- }
940
-
941
- throw new Error('No such repository');
942
- }
943
-
944
- }
945
-
946
- const copyHeaders = headers => {
947
- const validHeaders = ['accept', 'host', 'authorization'];
948
- const result = {};
949
- Object.entries(headers).forEach(([key, value]) => {
950
- if (validHeaders.includes(key.toLowerCase())) {
951
- result[key] = value;
952
- }
953
- });
954
- return result;
955
- };
956
-
957
- const DEFAULT_API_HOSTNAME = /^https:\/\/api\..*?\.commercetools.com:443$/;
958
- const DEFAULT_AUTH_HOSTNAME = /^https:\/\/auth\..*?\.commercetools.com:443$/;
959
-
960
- const createCustomFields = (draft, projectKey, storage) => {
961
- if (!draft) return undefined;
962
- if (!draft.type) return undefined;
963
- if (!draft.type.typeId) return undefined;
964
- if (!draft.fields) return undefined;
965
- const typeResource = storage.getByResourceIdentifier(projectKey, draft.type);
966
-
967
- if (!typeResource) {
968
- throw new Error(`No type '${draft.type.typeId}' with id=${draft.type.id} or key=${draft.type.key}`);
969
- }
970
-
971
- return {
972
- type: {
973
- typeId: draft.type.typeId,
974
- id: typeResource.id
975
- },
976
- fields: draft.fields
977
- };
978
- };
979
- const createPrice = draft => {
980
- return {
981
- id: uuid.v4(),
982
- value: createTypedMoney(draft.value)
983
- };
984
- };
985
- const createTypedMoney = value => {
986
- return {
987
- type: 'centPrecision',
988
- fractionDigits: 2,
989
- ...value
990
- };
991
- };
992
- const resolveStoreReference = (ref, projectKey, storage) => {
993
- if (!ref) return undefined;
994
- const resource = storage.getByResourceIdentifier(projectKey, ref);
995
-
996
- if (!resource) {
997
- throw new Error('No such store');
998
- }
999
-
1000
- const store = resource;
1001
- return {
1002
- typeId: 'store',
1003
- key: store.key
1004
- };
1005
- };
1006
- const getReferenceFromResourceIdentifier = (resourceIdentifier, projectKey, storage) => {
1007
- const resource = storage.getByResourceIdentifier(projectKey, resourceIdentifier);
1008
- if (!resource) throw new Error(`resource type ${resourceIdentifier.typeId} with id ${resourceIdentifier.id} and key ${resourceIdentifier.key} not found`);
1009
- return {
1010
- typeId: resourceIdentifier.typeId,
1011
- id: resource == null ? void 0 : resource.id
1012
- };
1013
- };
1014
- const getRepositoryContext = request => {
1015
- return {
1016
- projectKey: request.params.projectKey,
1017
- storeKey: request.params.storeKey
1018
- };
1019
- };
1020
-
1021
- class AbstractService {
1022
- constructor(parent) {
1023
- this.createStatusCode = 201;
1024
- this.registerRoutes(parent);
1025
- }
1026
-
1027
- extraRoutes(router) {}
1028
-
1029
- registerRoutes(parent) {
1030
- const basePath = this.getBasePath();
1031
- const router = express.Router({
1032
- mergeParams: true
1033
- }); // Bind this first since the `/:id` routes are currently a bit to greedy
1034
-
1035
- this.extraRoutes(router);
1036
- router.get('/', this.get.bind(this));
1037
- router.get('/key=:key', this.getWithKey.bind(this)); // same thing goes for the key routes
1038
-
1039
- router.get('/:id', this.getWithId.bind(this));
1040
- router.delete('/key=:key', this.deletewithKey.bind(this));
1041
- router.delete('/:id', this.deletewithId.bind(this));
1042
- router.post('/', this.post.bind(this));
1043
- router.post('/key=:key', this.postWithKey.bind(this));
1044
- router.post('/:id', this.postWithId.bind(this));
1045
- parent.use(`/${basePath}`, router);
1046
- }
1047
-
1048
- get(request, response) {
1049
- const limit = this._parseParam(request.query.limit);
1050
-
1051
- const offset = this._parseParam(request.query.offset);
1052
-
1053
- const result = this.repository.query(getRepositoryContext(request), {
1054
- expand: this._parseParam(request.query.expand),
1055
- where: this._parseParam(request.query.where),
1056
- limit: limit !== undefined ? Number(limit) : undefined,
1057
- offset: offset !== undefined ? Number(offset) : undefined
1058
- });
1059
- return response.status(200).send(result);
1060
- }
1061
-
1062
- getWithId(request, response) {
1063
- const result = this._expandWithId(request, request.params['id']);
1064
-
1065
- if (!result) {
1066
- return response.status(404).send();
1067
- }
1068
-
1069
- return response.status(200).send(result);
1070
- }
1071
-
1072
- getWithKey(request, response) {
1073
- const result = this.repository.getByKey(getRepositoryContext(request), request.params['key'], {
1074
- expand: this._parseParam(request.query.expand)
1075
- });
1076
- if (!result) return response.status(404).send();
1077
- return response.status(200).send(result);
1078
- }
1079
-
1080
- deletewithId(request, response) {
1081
- const result = this.repository.delete(getRepositoryContext(request), request.params['id'], {
1082
- expand: this._parseParam(request.query.expand)
1083
- });
1084
-
1085
- if (!result) {
1086
- return response.status(404).send('Not found');
1087
- }
1088
-
1089
- return response.status(200).send(result);
1090
- }
1091
-
1092
- deletewithKey(request, response) {
1093
- return response.status(500).send('Not implemented');
1094
- }
1095
-
1096
- post(request, response) {
1097
- const draft = request.body;
1098
- const resource = this.repository.create(getRepositoryContext(request), draft);
1099
-
1100
- const result = this._expandWithId(request, resource.id);
1101
-
1102
- return response.status(this.createStatusCode).send(result);
1103
- }
1104
-
1105
- postWithId(request, response) {
1106
- const updateRequest = request.body;
1107
- const resource = this.repository.get(getRepositoryContext(request), request.params['id']);
1108
-
1109
- if (!resource) {
1110
- return response.status(404).send('Not found');
1111
- }
1112
-
1113
- if (resource.version !== updateRequest.version) {
1114
- return response.status(409).send('Concurrent modification');
1115
- }
1116
-
1117
- const updatedResource = this.repository.processUpdateActions(getRepositoryContext(request), resource, updateRequest.actions);
1118
-
1119
- const result = this._expandWithId(request, updatedResource.id);
1120
-
1121
- return response.status(200).send(result);
1122
- }
1123
-
1124
- postWithKey(request, response) {
1125
- return response.status(500).send('Not implemented');
1126
- }
1127
-
1128
- _expandWithId(request, resourceId) {
1129
- const result = this.repository.get(getRepositoryContext(request), resourceId, {
1130
- expand: this._parseParam(request.query.expand)
1131
- });
1132
- return result;
1133
- } // No idea what i'm doing
1134
-
1135
-
1136
- _parseParam(value) {
1137
- if (Array.isArray(value)) {
1138
- // @ts-ignore
1139
- return value;
1140
- } else if (value !== undefined) {
1141
- return [`${value}`];
1142
- }
1143
-
1144
- return undefined;
1145
- }
1146
-
1147
- }
1148
-
1149
- const checkConcurrentModification = (resource, expectedVersion) => {
1150
- if (resource.version === expectedVersion) return;
1151
- const identifier = resource.id ? resource.id : resource.key;
1152
- throw new CommercetoolsError({
1153
- message: `Object ${identifier} has a different version than expected. Expected: ${expectedVersion} - Actual: ${resource.version}.`,
1154
- currentVersion: resource.version,
1155
- code: 'ConcurrentModification'
1156
- }, 409);
1157
- };
1158
-
1159
- class AbstractRepository {
1160
- constructor(storage) {
1161
- this.actions = {};
1162
- this._storage = storage;
1163
- }
1164
-
1165
- processUpdateActions(context, resource, actions) {
1166
- // Deep-copy
1167
- const modifiedResource = JSON.parse(JSON.stringify(resource));
1168
- actions.forEach(action => {
1169
- const updateFunc = this.actions[action.action];
1170
-
1171
- if (!updateFunc) {
1172
- console.error(`No mock implemented for update action ${action.action}`);
1173
- return;
1174
- }
1175
-
1176
- updateFunc(context, modifiedResource, action);
1177
- });
1178
-
1179
- if (!deepEqual(modifiedResource, resource)) {
1180
- this.save(context, modifiedResource);
1181
- }
1182
-
1183
- return modifiedResource;
1184
- }
1185
-
1186
- }
1187
- class AbstractResourceRepository extends AbstractRepository {
1188
- constructor(storage) {
1189
- super(storage);
1190
-
1191
- this._storage.assertStorage(this.getTypeId());
1192
- }
1193
-
1194
- query(context, params = {}) {
1195
- return this._storage.query(context.projectKey, this.getTypeId(), {
1196
- expand: params.expand,
1197
- where: params.where,
1198
- offset: params.offset,
1199
- limit: params.limit
1200
- });
1201
- }
1202
-
1203
- get(context, id, params = {}) {
1204
- return this._storage.get(context.projectKey, this.getTypeId(), id, params);
1205
- }
1206
-
1207
- getByKey(context, key, params = {}) {
1208
- return this._storage.getByKey(context.projectKey, this.getTypeId(), key, params);
1209
- }
1210
-
1211
- delete(context, id, params = {}) {
1212
- return this._storage.delete(context.projectKey, this.getTypeId(), id, params);
1213
- }
1214
-
1215
- save(context, resource) {
1216
- const current = this.get(context, resource.id);
1217
-
1218
- if (current) {
1219
- checkConcurrentModification(current, resource.version);
1220
- } else {
1221
- if (resource.version !== 0) {
1222
- throw new CommercetoolsError({
1223
- code: 'InvalidOperation',
1224
- message: 'version on create must be 0'
1225
- }, 400);
1226
- }
1227
- } // @ts-ignore
1228
-
1229
-
1230
- resource.version += 1;
1231
-
1232
- this._storage.add(context.projectKey, this.getTypeId(), resource);
1233
- }
1234
-
1235
- }
1236
-
1237
- class CartDiscountRepository extends AbstractResourceRepository {
1238
- constructor() {
1239
- super(...arguments);
1240
- this.actions = {
1241
- setKey: (context, resource, {
1242
- key
1243
- }) => {
1244
- resource.key = key;
1245
- },
1246
- setDescription: (context, resource, {
1247
- description
1248
- }) => {
1249
- resource.description = description;
1250
- },
1251
- setValidFrom: (context, resource, {
1252
- validFrom
1253
- }) => {
1254
- resource.validFrom = validFrom;
1255
- },
1256
- setValidUntil: (context, resource, {
1257
- validUntil
1258
- }) => {
1259
- resource.validUntil = validUntil;
1260
- },
1261
- setValidFromAndUntil: (context, resource, {
1262
- validFrom,
1263
- validUntil
1264
- }) => {
1265
- resource.validFrom = validFrom;
1266
- resource.validUntil = validUntil;
1267
- },
1268
- changeSortOrder: (context, resource, {
1269
- sortOrder
1270
- }) => {
1271
- resource.sortOrder = sortOrder;
1272
- },
1273
- changeIsActive: (context, resource, {
1274
- isActive
1275
- }) => {
1276
- resource.isActive = isActive;
1277
- }
1278
- };
1279
- }
1280
-
1281
- getTypeId() {
1282
- return 'cart-discount';
1283
- }
1284
-
1285
- create(context, draft) {
1286
- const resource = { ...getBaseResourceProperties(),
1287
- key: draft.key,
1288
- description: draft.description,
1289
- cartPredicate: draft.cartPredicate,
1290
- isActive: draft.isActive || false,
1291
- name: draft.name,
1292
- references: [],
1293
- target: draft.target,
1294
- requiresDiscountCode: draft.requiresDiscountCode || false,
1295
- sortOrder: draft.sortOrder,
1296
- stackingMode: draft.stackingMode || 'Stacking',
1297
- validFrom: draft.validFrom,
1298
- validUntil: draft.validUntil,
1299
- value: this.transformValueDraft(draft.value)
1300
- };
1301
- this.save(context, resource);
1302
- return resource;
1303
- }
1304
-
1305
- transformValueDraft(value) {
1306
- switch (value.type) {
1307
- case 'absolute':
1308
- {
1309
- return {
1310
- type: 'absolute',
1311
- money: value.money.map(createTypedMoney)
1312
- };
1313
- }
1314
-
1315
- case 'fixed':
1316
- {
1317
- return {
1318
- type: 'fixed',
1319
- money: value.money.map(createTypedMoney)
1320
- };
1321
- }
1322
-
1323
- case 'giftLineItem':
1324
- {
1325
- return { ...value
1326
- };
1327
- }
1328
-
1329
- case 'relative':
1330
- {
1331
- return { ...value
1332
- };
1333
- }
1334
- }
1335
-
1336
- return value;
1337
- }
1338
-
1339
- }
1340
-
1341
- class CartDiscountService extends AbstractService {
1342
- constructor(parent, storage) {
1343
- super(parent);
1344
- this.repository = new CartDiscountRepository(storage);
1345
- }
1346
-
1347
- getBasePath() {
1348
- return 'cart-discounts';
1349
- }
1350
-
1351
- }
1352
-
1353
- class CartRepository extends AbstractResourceRepository {
1354
- constructor() {
1355
- super(...arguments);
1356
- this.actions = {
1357
- addLineItem: (context, resource, {
1358
- productId,
1359
- variantId,
1360
- sku,
1361
- quantity = 1
1362
- }) => {
1363
- let product = null;
1364
- let variant;
1365
-
1366
- if (productId && variantId) {
1367
- // Fetch product and variant by ID
1368
- product = this._storage.get(context.projectKey, 'product', productId, {});
1369
- } else if (sku) {
1370
- // Fetch product and variant by SKU
1371
- const items = this._storage.query(context.projectKey, 'product', {
1372
- where: [`masterData(current(masterVariant(sku="${sku}"))) or masterData(current(variants(sku="${sku}")))`]
1373
- });
1374
-
1375
- if (items.count === 1) {
1376
- product = items.results[0];
1377
- }
1378
- }
1379
-
1380
- if (!product) {
1381
- // Check if product is found
1382
- throw new CommercetoolsError({
1383
- code: 'General',
1384
- message: sku ? `A product containing a variant with SKU '${sku}' not found.` : `A product with ID '${productId}' not found.`
1385
- });
1386
- } // Find matching variant
1387
-
1388
-
1389
- variant = [product.masterData.current.masterVariant, ...product.masterData.current.variants].find(x => {
1390
- if (sku) return x.sku === sku;
1391
- if (variantId) return x.id === variantId;
1392
- return false;
1393
- });
1394
-
1395
- if (!variant) {
1396
- // Check if variant is found
1397
- throw new CommercetoolsError({
1398
- code: 'General',
1399
- message: sku ? `A variant with SKU '${sku}' for product '${product.id}' not found.` : `A variant with ID '${variantId}' for product '${product.id}' not found.`
1400
- });
1401
- }
1402
-
1403
- const alreadyAdded = resource.lineItems.some(x => {
1404
- var _product, _variant;
1405
-
1406
- return x.productId === ((_product = product) == null ? void 0 : _product.id) && x.variant.id === ((_variant = variant) == null ? void 0 : _variant.id);
1407
- });
1408
-
1409
- if (alreadyAdded) {
1410
- // increase quantity and update total price
1411
- resource.lineItems.map(x => {
1412
- var _product2, _variant2;
1413
-
1414
- if (x.productId === ((_product2 = product) == null ? void 0 : _product2.id) && x.variant.id === ((_variant2 = variant) == null ? void 0 : _variant2.id)) {
1415
- x.quantity += quantity;
1416
- x.totalPrice.centAmount = calculateLineItemTotalPrice(x);
1417
- }
1418
-
1419
- return x;
1420
- });
1421
- } else {
1422
- var _variant$prices;
1423
-
1424
- // add line item
1425
- if (!((_variant$prices = variant.prices) != null && _variant$prices.length)) {
1426
- throw new CommercetoolsError({
1427
- code: 'General',
1428
- message: `A product with ID '${productId}' doesn't have any prices.`
1429
- });
1430
- }
1431
-
1432
- const currency = resource.totalPrice.currencyCode;
1433
- const price = selectPrice({
1434
- prices: variant.prices,
1435
- currency,
1436
- country: resource.country
1437
- });
1438
-
1439
- if (!price) {
1440
- throw new Error(`No valid price found for ${productId} for country ${resource.country} and currency ${currency}`);
1441
- }
1442
-
1443
- resource.lineItems.push({
1444
- id: uuid.v4(),
1445
- productId: product.id,
1446
- productKey: product.key,
1447
- name: product.masterData.current.name,
1448
- productSlug: product.masterData.current.slug,
1449
- productType: product.productType,
1450
- variant,
1451
- price: price,
1452
- totalPrice: { ...price.value,
1453
- centAmount: price.value.centAmount * quantity
1454
- },
1455
- quantity,
1456
- discountedPricePerQuantity: [],
1457
- lineItemMode: 'Standard',
1458
- priceMode: 'Platform',
1459
- state: []
1460
- });
1461
- } // Update cart total price
1462
-
1463
-
1464
- resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
1465
- },
1466
- removeLineItem: (context, resource, {
1467
- lineItemId,
1468
- quantity
1469
- }) => {
1470
- const lineItem = resource.lineItems.find(x => x.id === lineItemId);
1471
-
1472
- if (!lineItem) {
1473
- // Check if product is found
1474
- throw new CommercetoolsError({
1475
- code: 'General',
1476
- message: `A line item with ID '${lineItemId}' not found.`
1477
- });
1478
- }
1479
-
1480
- const shouldDelete = !quantity || quantity >= lineItem.quantity;
1481
-
1482
- if (shouldDelete) {
1483
- // delete line item
1484
- resource.lineItems = resource.lineItems.filter(x => x.id !== lineItemId);
1485
- } else {
1486
- // decrease quantity and update total price
1487
- resource.lineItems.map(x => {
1488
- if (x.id === lineItemId && quantity) {
1489
- x.quantity -= quantity;
1490
- x.totalPrice.centAmount = calculateLineItemTotalPrice(x);
1491
- }
1492
-
1493
- return x;
1494
- });
1495
- } // Update cart total price
1496
-
1497
-
1498
- resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
1499
- },
1500
- setBillingAddress: (context, resource, {
1501
- address
1502
- }) => {
1503
- resource.billingAddress = address;
1504
- },
1505
- setShippingMethod: (context, resource, {
1506
- shippingMethod
1507
- }) => {
1508
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, //@ts-ignore
1509
- shippingMethod);
1510
-
1511
- if (!resolvedType) {
1512
- throw new Error(`Type ${shippingMethod} not found`);
1513
- } //@ts-ignore
1514
-
1515
-
1516
- resource.shippingInfo = {
1517
- shippingMethod: {
1518
- typeId: 'shipping-method',
1519
- id: resolvedType.id
1520
- }
1521
- };
1522
- },
1523
- setCountry: (context, resource, {
1524
- country
1525
- }) => {
1526
- resource.country = country;
1527
- },
1528
- setCustomerEmail: (context, resource, {
1529
- email
1530
- }) => {
1531
- resource.customerEmail = email;
1532
- },
1533
- setCustomField: (context, resource, {
1534
- name,
1535
- value
1536
- }) => {
1537
- if (!resource.custom) {
1538
- throw new Error('Resource has no custom field');
1539
- }
1540
-
1541
- resource.custom.fields[name] = value;
1542
- },
1543
- setCustomType: (context, resource, {
1544
- type,
1545
- fields
1546
- }) => {
1547
- if (!type) {
1548
- resource.custom = undefined;
1549
- } else {
1550
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, type);
1551
-
1552
- if (!resolvedType) {
1553
- throw new Error(`Type ${type} not found`);
1554
- }
1555
-
1556
- resource.custom = {
1557
- type: {
1558
- typeId: 'type',
1559
- id: resolvedType.id
1560
- },
1561
- fields: fields || []
1562
- };
1563
- }
1564
- },
1565
- setLocale: (context, resource, {
1566
- locale
1567
- }) => {
1568
- resource.locale = locale;
1569
- },
1570
- setShippingAddress: (context, resource, {
1571
- address
1572
- }) => {
1573
- resource.shippingAddress = address;
1574
- }
1575
- };
1576
-
1577
- this.draftLineItemtoLineItem = (projectKey, draftLineItem, currency, country) => {
1578
- const {
1579
- productId,
1580
- quantity,
1581
- variantId,
1582
- sku
1583
- } = draftLineItem;
1584
- let product = null;
1585
- let variant;
1586
-
1587
- if (productId && variantId) {
1588
- // Fetch product and variant by ID
1589
- product = this._storage.get(projectKey, 'product', productId, {});
1590
- } else if (sku) {
1591
- // Fetch product and variant by SKU
1592
- const items = this._storage.query(projectKey, 'product', {
1593
- where: [`masterData(current(masterVariant(sku="${sku}"))) or masterData(current(variants(sku="${sku}")))`]
1594
- });
1595
-
1596
- if (items.count === 1) {
1597
- product = items.results[0];
1598
- }
1599
- }
1600
-
1601
- if (!product) {
1602
- // Check if product is found
1603
- throw new CommercetoolsError({
1604
- code: 'General',
1605
- message: sku ? `A product containing a variant with SKU '${sku}' not found.` : `A product with ID '${productId}' not found.`
1606
- });
1607
- } // Find matching variant
1608
-
1609
-
1610
- variant = [product.masterData.current.masterVariant, ...product.masterData.current.variants].find(x => {
1611
- if (sku) return x.sku === sku;
1612
- if (variantId) return x.id === variantId;
1613
- return false;
1614
- });
1615
-
1616
- if (!variant) {
1617
- // Check if variant is found
1618
- throw new Error(sku ? `A variant with SKU '${sku}' for product '${product.id}' not found.` : `A variant with ID '${variantId}' for product '${product.id}' not found.`);
1619
- }
1620
-
1621
- const quant = quantity != null ? quantity : 1;
1622
- const price = selectPrice({
1623
- prices: variant.prices,
1624
- currency,
1625
- country
1626
- });
1627
-
1628
- if (!price) {
1629
- throw new Error(`No valid price found for ${productId} for country ${country} and currency ${currency}`);
1630
- }
1631
-
1632
- return {
1633
- id: uuid.v4(),
1634
- productId: product.id,
1635
- productKey: product.key,
1636
- name: product.masterData.current.name,
1637
- productSlug: product.masterData.current.slug,
1638
- productType: product.productType,
1639
- variant,
1640
- price: price,
1641
- totalPrice: { ...price.value,
1642
- centAmount: price.value.centAmount * quant
1643
- },
1644
- quantity: quant,
1645
- discountedPricePerQuantity: [],
1646
- lineItemMode: 'Standard',
1647
- priceMode: 'Platform',
1648
- state: []
1649
- };
1650
- };
1651
- }
1652
-
1653
- getTypeId() {
1654
- return 'cart';
1655
- }
1656
-
1657
- create(context, draft) {
1658
- var _draft$lineItems$map, _draft$lineItems, _draft$taxMode, _draft$taxRoundingMod, _draft$taxCalculation, _draft$origin;
1659
-
1660
- const lineItems = (_draft$lineItems$map = (_draft$lineItems = draft.lineItems) == null ? void 0 : _draft$lineItems.map(draftLineItem => this.draftLineItemtoLineItem(context.projectKey, draftLineItem, draft.currency, draft.country))) != null ? _draft$lineItems$map : [];
1661
- const resource = { ...getBaseResourceProperties(),
1662
- cartState: 'Active',
1663
- lineItems,
1664
- customLineItems: [],
1665
- totalPrice: {
1666
- type: 'centPrecision',
1667
- centAmount: 0,
1668
- currencyCode: draft.currency,
1669
- fractionDigits: 0
1670
- },
1671
- taxMode: (_draft$taxMode = draft.taxMode) != null ? _draft$taxMode : 'Platform',
1672
- taxRoundingMode: (_draft$taxRoundingMod = draft.taxRoundingMode) != null ? _draft$taxRoundingMod : 'HalfEven',
1673
- taxCalculationMode: (_draft$taxCalculation = draft.taxCalculationMode) != null ? _draft$taxCalculation : 'LineItemLevel',
1674
- refusedGifts: [],
1675
- locale: draft.locale,
1676
- country: draft.country,
1677
- origin: (_draft$origin = draft.origin) != null ? _draft$origin : 'Customer',
1678
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
1679
- }; // @ts-ignore
1680
-
1681
- resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
1682
- this.save(context, resource);
1683
- return resource;
1684
- }
1685
-
1686
- getActiveCart(projectKey) {
1687
- // Get first active cart
1688
- const results = this._storage.query(projectKey, this.getTypeId(), {
1689
- where: [`cartState="Active"`]
1690
- });
1691
-
1692
- if (results.count > 0) {
1693
- return results.results[0];
1694
- }
1695
-
1696
- return;
1697
- }
1698
-
1699
- }
1700
-
1701
- const selectPrice = ({
1702
- prices,
1703
- currency,
1704
- country
1705
- }) => {
1706
- if (!prices) {
1707
- return undefined;
1708
- } // Quick-and-dirty way of selecting price based on the given currency and country.
1709
- // Can be improved later to give more priority to exact matches over
1710
- // 'all country' matches, and include customer groups in the mix as well
1711
-
1712
-
1713
- return prices.find(price => {
1714
- const countryMatch = !price.country || price.country === country;
1715
- const currencyMatch = price.value.currencyCode === currency;
1716
- return countryMatch && currencyMatch;
1717
- });
1718
- };
1719
-
1720
- const calculateLineItemTotalPrice = lineItem => lineItem.price.value.centAmount * lineItem.quantity;
1721
-
1722
- const calculateCartTotalPrice = cart => cart.lineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
1723
-
1724
- class OrderRepository extends AbstractResourceRepository {
1725
- constructor() {
1726
- super(...arguments);
1727
- this.actions = {
1728
- addPayment: (context, resource, {
1729
- payment
1730
- }) => {
1731
- const resolvedPayment = this._storage.getByResourceIdentifier(context.projectKey, payment);
1732
-
1733
- if (!resolvedPayment) {
1734
- throw new Error(`Payment ${payment.id} not found`);
1735
- }
1736
-
1737
- if (!resource.paymentInfo) {
1738
- resource.paymentInfo = {
1739
- payments: []
1740
- };
1741
- }
1742
-
1743
- resource.paymentInfo.payments.push({
1744
- typeId: 'payment',
1745
- id: payment.id
1746
- });
1747
- },
1748
- changeOrderState: (context, resource, {
1749
- orderState
1750
- }) => {
1751
- resource.orderState = orderState;
1752
- },
1753
- changePaymentState: (context, resource, {
1754
- paymentState
1755
- }) => {
1756
- resource.paymentState = paymentState;
1757
- },
1758
- transitionState: (context, resource, {
1759
- state
1760
- }) => {
1761
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, state);
1762
-
1763
- if (!resolvedType) {
1764
- throw new Error(`No state found with key=${state.key} or id=${state.key}`);
1765
- }
1766
-
1767
- resource.state = {
1768
- typeId: 'state',
1769
- id: resolvedType.id
1770
- };
1771
- },
1772
- setBillingAddress: (context, resource, {
1773
- address
1774
- }) => {
1775
- resource.billingAddress = address;
1776
- },
1777
- setCustomerEmail: (context, resource, {
1778
- email
1779
- }) => {
1780
- resource.customerEmail = email;
1781
- },
1782
- setCustomField: (context, resource, {
1783
- name,
1784
- value
1785
- }) => {
1786
- if (!resource.custom) {
1787
- throw new Error('Resource has no custom field');
1788
- }
1789
-
1790
- resource.custom.fields[name] = value;
1791
- },
1792
- setCustomType: (context, resource, {
1793
- type,
1794
- fields
1795
- }) => {
1796
- if (!type) {
1797
- resource.custom = undefined;
1798
- } else {
1799
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, type);
1800
-
1801
- if (!resolvedType) {
1802
- throw new Error(`Type ${type} not found`);
1803
- }
1804
-
1805
- resource.custom = {
1806
- type: {
1807
- typeId: 'type',
1808
- id: resolvedType.id
1809
- },
1810
- fields: fields || []
1811
- };
1812
- }
1813
- },
1814
- setLocale: (context, resource, {
1815
- locale
1816
- }) => {
1817
- resource.locale = locale;
1818
- },
1819
- setOrderNumber: (context, resource, {
1820
- orderNumber
1821
- }) => {
1822
- resource.orderNumber = orderNumber;
1823
- },
1824
- setShippingAddress: (context, resource, {
1825
- address
1826
- }) => {
1827
- resource.shippingAddress = address;
1828
- },
1829
- setStore: (context, resource, {
1830
- store
1831
- }) => {
1832
- if (!store) return;
1833
-
1834
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, store);
1835
-
1836
- if (!resolvedType) {
1837
- throw new Error(`No store found with key=${store.key}`);
1838
- }
1839
-
1840
- const storeReference = resolvedType;
1841
- resource.store = {
1842
- typeId: 'store',
1843
- key: storeReference.key
1844
- };
1845
- }
1846
- };
1847
- }
1848
-
1849
- getTypeId() {
1850
- return 'order';
1851
- }
1852
-
1853
- create(context, draft) {
1854
- assert(draft.cart, 'draft.cart is missing');
1855
- return this.createFromCart(context, {
1856
- id: draft.cart.id,
1857
- typeId: 'cart'
1858
- }, draft.orderNumber);
1859
- }
1860
-
1861
- createFromCart(context, cartReference, orderNumber) {
1862
- const cart = this._storage.getByResourceIdentifier(context.projectKey, cartReference);
1863
-
1864
- if (!cart) {
1865
- throw new Error('Cannot find cart');
1866
- }
1867
-
1868
- const resource = { ...getBaseResourceProperties(),
1869
- orderNumber,
1870
- cart: cartReference,
1871
- orderState: 'Open',
1872
- lineItems: [],
1873
- customLineItems: [],
1874
- totalPrice: cart.totalPrice,
1875
- refusedGifts: [],
1876
- origin: 'Customer',
1877
- syncInfo: [],
1878
- store: context.storeKey ? {
1879
- key: context.storeKey,
1880
- typeId: 'store'
1881
- } : undefined,
1882
- lastMessageSequenceNumber: 0
1883
- };
1884
- this.save(context, resource);
1885
- return resource;
1886
- }
1887
-
1888
- import(context, draft) {
1889
- var _draft$lineItems, _draft$customLineItem;
1890
-
1891
- // TODO: Check if order with given orderNumber already exists
1892
- assert(this, 'OrderRepository not valid');
1893
- const resource = { ...getBaseResourceProperties(),
1894
- billingAddress: draft.billingAddress,
1895
- shippingAddress: draft.shippingAddress,
1896
- custom: createCustomFields(draft.custom, context.projectKey, this._storage),
1897
- customerEmail: draft.customerEmail,
1898
- lastMessageSequenceNumber: 0,
1899
- orderNumber: draft.orderNumber,
1900
- orderState: draft.orderState || 'Open',
1901
- origin: draft.origin || 'Customer',
1902
- paymentState: draft.paymentState,
1903
- refusedGifts: [],
1904
- store: resolveStoreReference(draft.store, context.projectKey, this._storage),
1905
- syncInfo: [],
1906
- lineItems: ((_draft$lineItems = draft.lineItems) == null ? void 0 : _draft$lineItems.map(item => this.lineItemFromImportDraft.bind(this)(context, item))) || [],
1907
- customLineItems: ((_draft$customLineItem = draft.customLineItems) == null ? void 0 : _draft$customLineItem.map(item => this.customLineItemFromImportDraft.bind(this)(context, item))) || [],
1908
- totalPrice: {
1909
- type: 'centPrecision',
1910
- ...draft.totalPrice,
1911
- fractionDigits: 2
1912
- }
1913
- };
1914
- this.save(context, resource);
1915
- return resource;
1916
- }
1917
-
1918
- lineItemFromImportDraft(context, draft) {
1919
- let product;
1920
- let variant;
1921
-
1922
- if (draft.variant.sku) {
1923
- variant = {
1924
- id: 0,
1925
- sku: draft.variant.sku
1926
- };
1927
-
1928
- var items = this._storage.query(context.projectKey, 'product', {
1929
- where: [`masterData(current(masterVariant(sku="${draft.variant.sku}"))) or masterData(current(variants(sku="${draft.variant.sku}")))`]
1930
- });
1931
-
1932
- if (items.count !== 1) {
1933
- throw new CommercetoolsError({
1934
- code: 'General',
1935
- message: `A product containing a variant with SKU '${draft.variant.sku}' not found.`
1936
- });
1937
- }
1938
-
1939
- product = items.results[0];
1940
-
1941
- if (product.masterData.current.masterVariant.sku === draft.variant.sku) {
1942
- variant = product.masterData.current.masterVariant;
1943
- } else {
1944
- variant = product.masterData.current.variants.find(v => v.sku === draft.variant.sku);
1945
- }
1946
-
1947
- if (!variant) {
1948
- throw new Error('Internal state error');
1949
- }
1950
- } else {
1951
- throw new Error('No product found');
1952
- }
1953
-
1954
- const lineItem = { ...getBaseResourceProperties(),
1955
- custom: createCustomFields(draft.custom, context.projectKey, this._storage),
1956
- discountedPricePerQuantity: [],
1957
- lineItemMode: 'Standard',
1958
- name: draft.name,
1959
- price: createPrice(draft.price),
1960
- priceMode: 'Platform',
1961
- productId: product.id,
1962
- productType: product.productType,
1963
- quantity: draft.quantity,
1964
- state: draft.state || [],
1965
- taxRate: draft.taxRate,
1966
- totalPrice: createTypedMoney(draft.price.value),
1967
- variant: {
1968
- id: variant.id,
1969
- sku: variant.sku,
1970
- price: createPrice(draft.price)
1971
- }
1972
- };
1973
- return lineItem;
1974
- }
1975
-
1976
- customLineItemFromImportDraft(context, draft) {
1977
- const lineItem = { ...getBaseResourceProperties(),
1978
- custom: createCustomFields(draft.custom, context.projectKey, this._storage),
1979
- discountedPricePerQuantity: [],
1980
- money: createTypedMoney(draft.money),
1981
- name: draft.name,
1982
- quantity: draft.quantity,
1983
- slug: draft.slug,
1984
- state: [],
1985
- totalPrice: createTypedMoney(draft.money)
1986
- };
1987
- return lineItem;
1988
- }
1989
-
1990
- getWithOrderNumber(context, orderNumber, params = {}) {
1991
- const result = this._storage.query(context.projectKey, this.getTypeId(), { ...params,
1992
- where: [`orderNumber="${orderNumber}"`]
1993
- });
1994
-
1995
- if (result.count === 1) {
1996
- return result.results[0];
1997
- } // Catch this for now, should be checked when creating/updating
1998
-
1999
-
2000
- if (result.count > 1) {
2001
- throw new Error('Duplicate order numbers');
2002
- }
2003
-
2004
- return;
2005
- }
2006
-
2007
- }
2008
-
2009
- class CartService extends AbstractService {
2010
- constructor(parent, storage) {
2011
- super(parent);
2012
- this.repository = new CartRepository(storage);
2013
- this.orderRepository = new OrderRepository(storage);
2014
- }
2015
-
2016
- getBasePath() {
2017
- return 'carts';
2018
- }
2019
-
2020
- extraRoutes(parent) {
2021
- parent.post('/replicate', (request, response) => {
2022
- const context = getRepositoryContext(request); // @ts-ignore
2023
-
2024
- const cartOrOrder = request.body.reference.typeId === 'order' ? this.orderRepository.get(context, request.body.reference.id) : this.repository.get(context, request.body.reference.id);
2025
-
2026
- if (!cartOrOrder) {
2027
- return response.status(400).send();
2028
- }
2029
-
2030
- const cartDraft = { ...cartOrOrder,
2031
- currency: cartOrOrder.totalPrice.currencyCode,
2032
- discountCodes: [],
2033
- lineItems: cartOrOrder.lineItems.map(lineItem => {
2034
- return { ...lineItem,
2035
- variantId: lineItem.variant.id,
2036
- sku: lineItem.variant.sku
2037
- };
2038
- })
2039
- };
2040
- const newCart = this.repository.create(context, cartDraft);
2041
- return response.status(200).send(newCart);
2042
- });
2043
- }
2044
-
2045
- }
2046
-
2047
- class CategoryRepository extends AbstractResourceRepository {
2048
- constructor() {
2049
- super(...arguments);
2050
- this.actions = {
2051
- changeAssetName: (context, resource, {
2052
- assetId,
2053
- assetKey,
2054
- name
2055
- }) => {
2056
- var _resource$assets;
2057
-
2058
- (_resource$assets = resource.assets) == null ? void 0 : _resource$assets.forEach(asset => {
2059
- if (assetId && assetId === asset.id) {
2060
- asset.name = name;
2061
- }
2062
-
2063
- if (assetKey && assetKey === asset.key) {
2064
- asset.name = name;
2065
- }
2066
- });
2067
- },
2068
- changeSlug: (context, resource, {
2069
- slug
2070
- }) => {
2071
- resource.slug = slug;
2072
- },
2073
- setKey: (context, resource, {
2074
- key
2075
- }) => {
2076
- resource.key = key;
2077
- },
2078
- setAssetDescription: (context, resource, {
2079
- assetId,
2080
- assetKey,
2081
- description
2082
- }) => {
2083
- var _resource$assets2;
2084
-
2085
- (_resource$assets2 = resource.assets) == null ? void 0 : _resource$assets2.forEach(asset => {
2086
- if (assetId && assetId === asset.id) {
2087
- asset.description = description;
2088
- }
2089
-
2090
- if (assetKey && assetKey === asset.key) {
2091
- asset.description = description;
2092
- }
2093
- });
2094
- },
2095
- setAssetSources: (context, resource, {
2096
- assetId,
2097
- assetKey,
2098
- sources
2099
- }) => {
2100
- var _resource$assets3;
2101
-
2102
- (_resource$assets3 = resource.assets) == null ? void 0 : _resource$assets3.forEach(asset => {
2103
- if (assetId && assetId === asset.id) {
2104
- asset.sources = sources;
2105
- }
2106
-
2107
- if (assetKey && assetKey === asset.key) {
2108
- asset.sources = sources;
2109
- }
2110
- });
2111
- },
2112
- setDescription: (context, resource, {
2113
- description
2114
- }) => {
2115
- resource.description = description;
2116
- },
2117
- setMetaDescription: (context, resource, {
2118
- metaDescription
2119
- }) => {
2120
- resource.metaDescription = metaDescription;
2121
- },
2122
- setMetaKeywords: (context, resource, {
2123
- metaKeywords
2124
- }) => {
2125
- resource.metaKeywords = metaKeywords;
2126
- },
2127
- setMetaTitle: (context, resource, {
2128
- metaTitle
2129
- }) => {
2130
- resource.metaTitle = metaTitle;
2131
- }
2132
- };
2133
- }
2134
-
2135
- getTypeId() {
2136
- return 'category';
2137
- }
2138
-
2139
- create(context, draft) {
2140
- var _draft$assets;
2141
-
2142
- const resource = { ...getBaseResourceProperties(),
2143
- key: draft.key,
2144
- name: draft.name,
2145
- slug: draft.slug,
2146
- orderHint: draft.orderHint || '',
2147
- externalId: draft.externalId || '',
2148
- parent: draft.parent ? {
2149
- typeId: 'category',
2150
- id: draft.parent.id
2151
- } : undefined,
2152
- ancestors: [],
2153
- assets: ((_draft$assets = draft.assets) == null ? void 0 : _draft$assets.map(d => {
2154
- return {
2155
- id: uuid.v4(),
2156
- name: d.name,
2157
- description: d.description,
2158
- sources: d.sources,
2159
- tags: d.tags,
2160
- key: d.key,
2161
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
2162
- };
2163
- })) || []
2164
- };
2165
- this.save(context, resource);
2166
- return resource;
2167
- }
2168
-
2169
- }
2170
-
2171
- class CategoryServices extends AbstractService {
2172
- constructor(parent, storage) {
2173
- super(parent);
2174
- this.repository = new CategoryRepository(storage);
2175
- }
2176
-
2177
- getBasePath() {
2178
- return 'categories';
2179
- }
2180
-
2181
- }
2182
-
2183
- class ChannelRepository extends AbstractResourceRepository {
2184
- getTypeId() {
2185
- return 'channel';
2186
- }
2187
-
2188
- create(context, draft) {
2189
- const resource = { ...getBaseResourceProperties(),
2190
- key: draft.key,
2191
- roles: draft.roles || []
2192
- };
2193
- this.save(context, resource);
2194
- return resource;
2195
- }
2196
-
2197
- }
2198
-
2199
- class ChannelService extends AbstractService {
2200
- constructor(parent, storage) {
2201
- super(parent);
2202
- this.repository = new ChannelRepository(storage);
2203
- }
2204
-
2205
- getBasePath() {
2206
- return 'channels';
2207
- }
2208
-
2209
- }
2210
-
2211
- class CustomerGroupRepository extends AbstractResourceRepository {
2212
- constructor() {
2213
- super(...arguments);
2214
- this.actions = {
2215
- setKey: (context, resource, {
2216
- key
2217
- }) => {
2218
- resource.key = key;
2219
- },
2220
- changeName: (context, resource, {
2221
- name
2222
- }) => {
2223
- resource.name = name;
2224
- }
2225
- };
2226
- }
2227
-
2228
- getTypeId() {
2229
- return 'customer';
2230
- }
2231
-
2232
- create(context, draft) {
2233
- const resource = { ...getBaseResourceProperties(),
2234
- key: draft.key,
2235
- name: draft.groupName
2236
- };
2237
- this.save(context, resource);
2238
- return resource;
2239
- }
2240
-
2241
- }
2242
-
2243
- class CustomerGroupService extends AbstractService {
2244
- constructor(parent, storage) {
2245
- super(parent);
2246
- this.repository = new CustomerGroupRepository(storage);
2247
- }
2248
-
2249
- getBasePath() {
2250
- return 'customer-groups';
2251
- }
2252
-
2253
- }
2254
-
2255
- class CustomerRepository extends AbstractResourceRepository {
2256
- constructor() {
2257
- super(...arguments);
2258
- this.actions = {
2259
- changeEmail: (_context, resource, {
2260
- email
2261
- }) => {
2262
- resource.email = email;
2263
- }
2264
- };
2265
- }
2266
-
2267
- getTypeId() {
2268
- return 'customer';
2269
- }
2270
-
2271
- create(context, draft) {
2272
- const resource = { ...getBaseResourceProperties(),
2273
- email: draft.email,
2274
- password: draft.password ? Buffer.from(draft.password).toString('base64') : undefined,
2275
- isEmailVerified: draft.isEmailVerified || false,
2276
- addresses: []
2277
- };
2278
- this.save(context, resource);
2279
- return resource;
2280
- }
2281
-
2282
- getMe(context) {
2283
- const results = this._storage.query(context.projectKey, this.getTypeId(), {}); // grab the first customer you can find
2284
-
2285
-
2286
- if (results.count > 0) {
2287
- return results.results[0];
2288
- }
2289
-
2290
- return;
2291
- }
2292
-
2293
- }
2294
-
2295
- class CustomerService extends AbstractService {
2296
- constructor(parent, storage) {
2297
- super(parent);
2298
- this.repository = new CustomerRepository(storage);
2299
- }
2300
-
2301
- getBasePath() {
2302
- return 'customers';
2303
- }
2304
-
2305
- extraRoutes(parent) {
2306
- parent.post('/password-token', (request, response) => {
2307
- const customer = this.repository.query(getRepositoryContext(request), {
2308
- where: [`email="${request.body.email}"`]
2309
- }); // @ts-ignore
2310
-
2311
- const ttlMinutes = request.params.ttlMinutes ? // @ts-ignore
2312
- +request.params.ttlMinutes : 34560;
2313
- const {
2314
- version,
2315
- ...rest
2316
- } = getBaseResourceProperties();
2317
- return response.status(200).send({ ...rest,
2318
- customerId: customer.results[0].id,
2319
- expiresAt: new Date(Date.now() + ttlMinutes * 60).toISOString(),
2320
- value: uuid.v4()
2321
- });
2322
- });
2323
- }
2324
-
2325
- }
2326
-
2327
- class CustomObjectRepository extends AbstractResourceRepository {
2328
- getTypeId() {
2329
- return 'key-value-document';
2330
- }
2331
-
2332
- create(context, draft) {
2333
- const current = this.getWithContainerAndKey(context, draft.container, draft.key);
2334
- const baseProperties = getBaseResourceProperties();
2335
-
2336
- if (current) {
2337
- baseProperties.id = current.id;
2338
-
2339
- if (!draft.version) {
2340
- draft.version = current.version;
2341
- }
2342
-
2343
- checkConcurrentModification(current, draft.version);
2344
-
2345
- if (draft.value === current.value) {
2346
- return current;
2347
- }
2348
-
2349
- baseProperties.version = current.version;
2350
- } else {
2351
- if (draft.version) {
2352
- baseProperties.version = draft.version;
2353
- }
2354
- }
2355
-
2356
- const resource = { ...baseProperties,
2357
- container: draft.container,
2358
- key: draft.key,
2359
- value: draft.value
2360
- };
2361
- this.save(context, resource);
2362
- return resource;
2363
- }
2364
-
2365
- getWithContainerAndKey(context, container, key) {
2366
- const items = this._storage.all(context.projectKey, this.getTypeId());
2367
-
2368
- return items.find(item => item.container === container && item.key === key);
2369
- }
2370
-
2371
- }
2372
-
2373
- class CustomObjectService extends AbstractService {
2374
- constructor(parent, storage) {
2375
- super(parent);
2376
- this.repository = new CustomObjectRepository(storage);
2377
- }
2378
-
2379
- getBasePath() {
2380
- return 'custom-objects';
2381
- }
2382
-
2383
- extraRoutes(router) {
2384
- router.get('/:container/:key', this.getWithContainerAndKey.bind(this));
2385
- router.post('/:container/:key', this.createWithContainerAndKey.bind(this));
2386
- router.delete('/:container/:key', this.deleteWithContainerAndKey.bind(this));
2387
- }
2388
-
2389
- getWithContainerAndKey(request, response) {
2390
- const result = this.repository.getWithContainerAndKey(getRepositoryContext(request), request.params.container, request.params.key);
2391
-
2392
- if (!result) {
2393
- return response.status(404).send('Not Found');
2394
- }
2395
-
2396
- return response.status(200).send(result);
2397
- }
2398
-
2399
- createWithContainerAndKey(request, response) {
2400
- const draft = { ...request.body,
2401
- key: request.params.key,
2402
- container: request.params.container
2403
- };
2404
- const result = this.repository.create(getRepositoryContext(request), draft);
2405
- return response.status(200).send(result);
2406
- }
2407
-
2408
- deleteWithContainerAndKey(request, response) {
2409
- const current = this.repository.getWithContainerAndKey(getRepositoryContext(request), request.params.container, request.params.key);
2410
-
2411
- if (!current) {
2412
- return response.status(404).send('Not Found');
2413
- }
2414
-
2415
- const result = this.repository.delete(getRepositoryContext(request), current.id);
2416
- return response.status(200).send(result);
2417
- }
2418
-
2419
- }
2420
-
2421
- class DiscountCodeRepository extends AbstractResourceRepository {
2422
- constructor() {
2423
- super(...arguments);
2424
- this.actions = {
2425
- changeIsActive: (context, resource, {
2426
- isActive
2427
- }) => {
2428
- resource.isActive = isActive;
2429
- },
2430
- changeCartDiscounts: (context, resource, {
2431
- cartDiscounts
2432
- }) => {
2433
- resource.cartDiscounts = cartDiscounts.map(obj => ({
2434
- typeId: 'cart-discount',
2435
- id: obj.id
2436
- }));
2437
- },
2438
- setDescription: (context, resource, {
2439
- description
2440
- }) => {
2441
- resource.description = description;
2442
- },
2443
- setCartPredicate: (context, resource, {
2444
- cartPredicate
2445
- }) => {
2446
- resource.cartPredicate = cartPredicate;
2447
- },
2448
- setName: (context, resource, {
2449
- name
2450
- }) => {
2451
- resource.name = name;
2452
- },
2453
- setMaxApplications: (context, resource, {
2454
- maxApplications
2455
- }) => {
2456
- resource.maxApplications = maxApplications;
2457
- },
2458
- setMaxApplicationsPerCustomer: (context, resource, {
2459
- maxApplicationsPerCustomer
2460
- }) => {
2461
- resource.maxApplicationsPerCustomer = maxApplicationsPerCustomer;
2462
- },
2463
- setValidFrom: (context, resource, {
2464
- validFrom
2465
- }) => {
2466
- resource.validFrom = validFrom;
2467
- },
2468
- setValidUntil: (context, resource, {
2469
- validUntil
2470
- }) => {
2471
- resource.validUntil = validUntil;
2472
- },
2473
- setValidFromAndUntil: (context, resource, {
2474
- validFrom,
2475
- validUntil
2476
- }) => {
2477
- resource.validFrom = validFrom;
2478
- resource.validUntil = validUntil;
2479
- }
2480
- };
2481
- }
2482
-
2483
- getTypeId() {
2484
- return 'cart-discount';
2485
- }
2486
-
2487
- create(context, draft) {
2488
- const resource = { ...getBaseResourceProperties(),
2489
- applicationVersion: 1,
2490
- cartDiscounts: draft.cartDiscounts.map(obj => ({
2491
- typeId: 'cart-discount',
2492
- id: obj.id
2493
- })),
2494
- cartPredicate: draft.cartPredicate,
2495
- code: draft.code,
2496
- description: draft.description,
2497
- groups: draft.groups || [],
2498
- isActive: draft.isActive || true,
2499
- name: draft.name,
2500
- references: [],
2501
- validFrom: draft.validFrom,
2502
- validUntil: draft.validUntil,
2503
- maxApplications: draft.maxApplications,
2504
- maxApplicationsPerCustomer: draft.maxApplicationsPerCustomer
2505
- };
2506
- this.save(context, resource);
2507
- return resource;
2508
- }
2509
-
2510
- }
2511
-
2512
- class DiscountCodeService extends AbstractService {
2513
- constructor(parent, storage) {
2514
- super(parent);
2515
- this.repository = new DiscountCodeRepository(storage);
2516
- }
2517
-
2518
- getBasePath() {
2519
- return 'discount-codes';
2520
- }
2521
-
2522
- }
2523
-
2524
- class ExtensionRepository extends AbstractResourceRepository {
2525
- constructor() {
2526
- super(...arguments);
2527
- this.actions = {
2528
- setKey: (context, resource, {
2529
- key
2530
- }) => {
2531
- resource.key = key;
2532
- },
2533
- setTimeoutInMs: (context, resource, {
2534
- timeoutInMs
2535
- }) => {
2536
- resource.timeoutInMs = timeoutInMs;
2537
- },
2538
- changeTriggers: (context, resource, {
2539
- triggers
2540
- }) => {
2541
- resource.triggers = triggers;
2542
- },
2543
- changeDestination: (context, resource, {
2544
- destination
2545
- }) => {
2546
- resource.destination = destination;
2547
- }
2548
- };
2549
- }
2550
-
2551
- getTypeId() {
2552
- return 'extension';
2553
- }
2554
-
2555
- create(context, draft) {
2556
- const resource = { ...getBaseResourceProperties(),
2557
- key: draft.key,
2558
- timeoutInMs: draft.timeoutInMs,
2559
- destination: draft.destination,
2560
- triggers: draft.triggers
2561
- };
2562
- this.save(context, resource);
2563
- return resource;
2564
- }
2565
-
2566
- }
2567
-
2568
- class ExtensionServices extends AbstractService {
2569
- constructor(parent, storage) {
2570
- super(parent);
2571
- this.repository = new ExtensionRepository(storage);
2572
- }
2573
-
2574
- getBasePath() {
2575
- return 'extensions';
2576
- }
2577
-
2578
- }
2579
-
2580
- class InventoryEntryRepository extends AbstractResourceRepository {
2581
- constructor() {
2582
- super(...arguments);
2583
- this.actions = {
2584
- changeQuantity: (context, resource, {
2585
- quantity
2586
- }) => {
2587
- resource.quantityOnStock = quantity; // don't know active reservations so just set to same value
2588
-
2589
- resource.availableQuantity = quantity;
2590
- },
2591
- setExpectedDelivery: (context, resource, {
2592
- expectedDelivery
2593
- }) => {
2594
- resource.expectedDelivery = new Date(expectedDelivery).toISOString();
2595
- },
2596
- setCustomField: (context, resource, {
2597
- name,
2598
- value
2599
- }) => {
2600
- if (!resource.custom) {
2601
- throw new Error('Resource has no custom field');
2602
- }
2603
-
2604
- resource.custom.fields[name] = value;
2605
- },
2606
- setCustomType: (context, resource, {
2607
- type,
2608
- fields
2609
- }) => {
2610
- if (!type) {
2611
- resource.custom = undefined;
2612
- } else {
2613
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, type);
2614
-
2615
- if (!resolvedType) {
2616
- throw new Error(`Type ${type} not found`);
2617
- }
2618
-
2619
- resource.custom = {
2620
- type: {
2621
- typeId: 'type',
2622
- id: resolvedType.id
2623
- },
2624
- fields: fields || []
2625
- };
2626
- }
2627
- },
2628
- setRestockableInDays: (context, resource, {
2629
- restockableInDays
2630
- }) => {
2631
- resource.restockableInDays = restockableInDays;
2632
- }
2633
- };
2634
- }
2635
-
2636
- getTypeId() {
2637
- return 'inventory-entry';
2638
- }
2639
-
2640
- create(context, draft) {
2641
- var _draft$supplyChannel$, _draft$supplyChannel;
2642
-
2643
- const resource = { ...getBaseResourceProperties(),
2644
- sku: draft.sku,
2645
- quantityOnStock: draft.quantityOnStock,
2646
- availableQuantity: draft.quantityOnStock,
2647
- expectedDelivery: draft.expectedDelivery,
2648
- restockableInDays: draft.restockableInDays,
2649
- supplyChannel: { ...draft.supplyChannel,
2650
- typeId: 'channel',
2651
- id: (_draft$supplyChannel$ = (_draft$supplyChannel = draft.supplyChannel) == null ? void 0 : _draft$supplyChannel.id) != null ? _draft$supplyChannel$ : ''
2652
- },
2653
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
2654
- };
2655
- this.save(context, resource);
2656
- return resource;
2657
- }
2658
-
2659
- }
2660
-
2661
- class InventoryEntryService extends AbstractService {
2662
- constructor(parent, storage) {
2663
- super(parent);
2664
- this.repository = new InventoryEntryRepository(storage);
2665
- }
2666
-
2667
- getBasePath() {
2668
- return 'inventory';
2669
- }
2670
-
2671
- }
2672
-
2673
- class MyCartService extends AbstractService {
2674
- constructor(parent, storage) {
2675
- super(parent);
2676
- this.repository = new CartRepository(storage);
2677
- }
2678
-
2679
- getBasePath() {
2680
- return 'me';
2681
- }
2682
-
2683
- registerRoutes(parent) {
2684
- // Overwrite this function to be able to handle /me/active-cart path.
2685
- const basePath = this.getBasePath();
2686
- const router = express.Router({
2687
- mergeParams: true
2688
- });
2689
- this.extraRoutes(router);
2690
- router.get('/active-cart', this.activeCart.bind(this));
2691
- router.get('/carts/', this.get.bind(this));
2692
- router.get('/carts/:id', this.getWithId.bind(this));
2693
- router.delete('/carts/:id', this.deletewithId.bind(this));
2694
- router.post('/carts/', this.post.bind(this));
2695
- router.post('/carts/:id', this.postWithId.bind(this));
2696
- parent.use(`/${basePath}`, router);
2697
- }
2698
-
2699
- activeCart(request, response) {
2700
- const resource = this.repository.getActiveCart(request.params.projectKey);
2701
-
2702
- if (!resource) {
2703
- return response.status(404).send('Not found');
2704
- }
2705
-
2706
- return response.status(200).send(resource);
2707
- }
2708
-
2709
- }
2710
-
2711
- class PaymentRepository extends AbstractResourceRepository {
2712
- constructor() {
2713
- super(...arguments);
2714
-
2715
- this.transactionFromTransactionDraft = (draft, context) => ({ ...draft,
2716
- id: uuid.v4(),
2717
- amount: createTypedMoney(draft.amount),
2718
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
2719
- });
2720
-
2721
- this.actions = {
2722
- setCustomField: (context, resource, {
2723
- name,
2724
- value
2725
- }) => {
2726
- if (!resource.custom) {
2727
- throw new Error('Resource has no custom field');
2728
- }
2729
-
2730
- resource.custom.fields[name] = value;
2731
- },
2732
- setCustomType: (context, resource, {
2733
- type,
2734
- fields
2735
- }) => {
2736
- if (!type) {
2737
- resource.custom = undefined;
2738
- } else {
2739
- const resolvedType = this._storage.getByResourceIdentifier(context.projectKey, type);
2740
-
2741
- if (!resolvedType) {
2742
- throw new Error(`Type ${type} not found`);
2743
- }
2744
-
2745
- resource.custom = {
2746
- type: {
2747
- typeId: 'type',
2748
- id: resolvedType.id
2749
- },
2750
- fields: fields || []
2751
- };
2752
- }
2753
- },
2754
- addTransaction: (context, resource, {
2755
- transaction
2756
- }) => {
2757
- resource.transactions = [...resource.transactions, this.transactionFromTransactionDraft(transaction, context)];
2758
- },
2759
- changeTransactionState: (_context, resource, {
2760
- transactionId,
2761
- state
2762
- }) => {
2763
- const index = resource.transactions.findIndex(e => e.id === transactionId);
2764
- const updatedTransaction = { ...resource.transactions[index],
2765
- state
2766
- };
2767
- resource.transactions[index] = updatedTransaction;
2768
- },
2769
- transitionState: (context, resource, {
2770
- state
2771
- }) => {
2772
- const stateObj = this._storage.getByResourceIdentifier(context.projectKey, state);
2773
-
2774
- if (!stateObj) {
2775
- throw new Error(`State ${state} not found`);
2776
- }
2777
-
2778
- resource.paymentStatus.state = {
2779
- typeId: 'state',
2780
- id: stateObj.id,
2781
- obj: stateObj
2782
- };
2783
- }
2784
- };
2785
- }
2786
-
2787
- getTypeId() {
2788
- return 'payment';
2789
- }
2790
-
2791
- create(context, draft) {
2792
- const resource = { ...getBaseResourceProperties(),
2793
- amountPlanned: createTypedMoney(draft.amountPlanned),
2794
- paymentMethodInfo: draft.paymentMethodInfo,
2795
- paymentStatus: draft.paymentStatus ? { ...draft.paymentStatus,
2796
- state: draft.paymentStatus.state ? getReferenceFromResourceIdentifier(draft.paymentStatus.state, context.projectKey, this._storage) : undefined
2797
- } : {},
2798
- transactions: (draft.transactions || []).map(t => this.transactionFromTransactionDraft(t, context)),
2799
- interfaceInteractions: (draft.interfaceInteractions || []).map(interaction => createCustomFields(interaction, context.projectKey, this._storage)),
2800
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
2801
- };
2802
- this.save(context, resource);
2803
- return resource;
2804
- }
2805
-
2806
- }
2807
-
2808
- class MyPaymentService extends AbstractService {
2809
- constructor(parent, storage) {
2810
- super(parent);
2811
- this.repository = new PaymentRepository(storage);
2812
- }
2813
-
2814
- getBasePath() {
2815
- return 'me/payments';
2816
- }
2817
-
2818
- }
2819
-
2820
- class OrderService extends AbstractService {
2821
- constructor(parent, storage) {
2822
- super(parent);
2823
- this.repository = new OrderRepository(storage);
2824
- }
2825
-
2826
- getBasePath() {
2827
- return 'orders';
2828
- }
2829
-
2830
- extraRoutes(router) {
2831
- router.post('/import', this.import.bind(this));
2832
- router.get('/order-number=:orderNumber', this.getWithOrderNumber.bind(this));
2833
- }
2834
-
2835
- import(request, response) {
2836
- const importDraft = request.body;
2837
- const resource = this.repository.import(getRepositoryContext(request), importDraft);
2838
- return response.status(200).send(resource);
2839
- }
2840
-
2841
- getWithOrderNumber(request, response) {
2842
- const resource = this.repository.getWithOrderNumber(getRepositoryContext(request), request.params.orderNumber, request.query);
2843
-
2844
- if (resource) {
2845
- return response.status(200).send(resource);
2846
- }
2847
-
2848
- return response.status(404).send('Not found');
2849
- }
2850
-
2851
- }
2852
-
2853
- class PaymentService extends AbstractService {
2854
- constructor(parent, storage) {
2855
- super(parent);
2856
- this.repository = new PaymentRepository(storage);
2857
- }
2858
-
2859
- getBasePath() {
2860
- return 'payments';
2861
- }
2862
-
2863
- }
2864
-
2865
- const parseFilterExpression = filter => {
2866
- if (typeof filter === 'object') {
2867
- const filters = filter.map(parse);
2868
- return filters.join(' and ');
2869
- }
2870
-
2871
- return parse(filter);
2872
- };
2873
-
2874
- const parse = filter => {
2875
- let parsed = filter.replace(/:/g, '=');
2876
-
2877
- do {
2878
- parsed = parsed.replace(/(.*)\.(.*)=(.*)$/g, '$1($2=$3)');
2879
- } while (parsed.includes('.'));
2880
-
2881
- return parsed;
2882
- };
2883
-
2884
- class ProductProjectionRepository extends AbstractResourceRepository {
2885
- constructor() {
2886
- super(...arguments);
2887
- this.actions = {};
2888
- }
2889
-
2890
- getTypeId() {
2891
- return 'product-projection';
2892
- }
2893
-
2894
- create(context, draft) {
2895
- var _draft$variants$map, _draft$variants;
2896
-
2897
- if (!draft.masterVariant) {
2898
- throw new Error(`must provider mastervariant for product projection with key ${draft.key}`);
2899
- }
2900
-
2901
- if (!draft.productType.id) {
2902
- throw new Error(`must provider product type id for product projection with key ${draft.key}`);
2903
- }
2904
-
2905
- const resource = { ...getBaseResourceProperties(),
2906
- name: draft.name,
2907
- slug: draft.slug,
2908
- categories: [],
2909
- productType: { ...draft.productType,
2910
- id: draft.productType.id
2911
- },
2912
- masterVariant: variantFromDraft(0, draft.masterVariant),
2913
- variants: (_draft$variants$map = (_draft$variants = draft.variants) == null ? void 0 : _draft$variants.map((variant, index) => {
2914
- return variantFromDraft(index + 1, variant);
2915
- })) != null ? _draft$variants$map : [],
2916
- // @ts-ignore
2917
- searchKeywords: draft.searchKeywords
2918
- };
2919
- this.save(context, resource);
2920
- return resource;
2921
- }
2922
-
2923
- search(context, query) {
2924
- var _query$filterQuery;
2925
-
2926
- const expand = query.expand ? query.expand : undefined;
2927
- const filter = (_query$filterQuery = query['filter.query']) != null ? _query$filterQuery : query.filter;
2928
- const wherePredicate = filter ? parseFilterExpression(filter) : undefined;
2929
-
2930
- const results = this._storage.query(context.projectKey, this.getTypeId(), {
2931
- where: wherePredicate,
2932
- offset: query.offset ? Number(query.offset) : undefined,
2933
- limit: query.limit ? Number(query.limit) : undefined,
2934
- expand
2935
- }); //TODO: this is a partial implementation, but I don't really have the time to implement an actual search API right now
2936
-
2937
-
2938
- return results;
2939
- }
2940
-
2941
- }
2942
-
2943
- const variantFromDraft = (variantId, variant) => {
2944
- return {
2945
- id: variantId,
2946
- sku: variant == null ? void 0 : variant.sku,
2947
- attributes: variant == null ? void 0 : variant.attributes
2948
- };
2949
- };
2950
-
2951
- class ProductProjectionService extends AbstractService {
2952
- constructor(parent, storage) {
2953
- super(parent);
2954
- this.repository = new ProductProjectionRepository(storage);
2955
- }
2956
-
2957
- getBasePath() {
2958
- return 'product-projections';
2959
- }
2960
-
2961
- extraRoutes(router) {
2962
- router.get('/search', this.search.bind(this));
2963
- }
2964
-
2965
- search(request, response) {
2966
- const resource = this.repository.search(getRepositoryContext(request), request.query);
2967
- return response.status(200).send(resource);
2968
- }
2969
-
2970
- }
2971
-
2972
- class ProductRepository extends AbstractResourceRepository {
2973
- constructor() {
2974
- super(...arguments);
2975
- this.actions = {
2976
- publish: (context, resource, {
2977
- scope
2978
- }) => {
2979
- if (resource.masterData.staged) {
2980
- resource.masterData.current = resource.masterData.staged; // @ts-ignore
2981
-
2982
- resource.masterData.staged = undefined;
2983
- }
2984
-
2985
- resource.masterData.hasStagedChanges = false;
2986
- resource.masterData.published = true;
2987
- },
2988
- setAttribute: (context, resource, {
2989
- variantId,
2990
- sku,
2991
- name,
2992
- value,
2993
- staged
2994
- }) => {
2995
- const isStaged = staged !== undefined ? staged : false;
2996
- const productData = getProductData(resource, isStaged);
2997
- const {
2998
- variant,
2999
- isMasterVariant,
3000
- variantIndex
3001
- } = getVariant(productData, variantId, sku);
3002
-
3003
- if (!variant) {
3004
- throw new Error(`Variant with id ${variantId} or sku ${sku} not found on product ${resource.id}`);
3005
- }
3006
-
3007
- if (!variant.attributes) {
3008
- variant.attributes = [];
3009
- }
3010
-
3011
- const existingAttr = variant.attributes.find(attr => attr.name === name);
3012
-
3013
- if (existingAttr) {
3014
- existingAttr.value = value;
3015
- } else {
3016
- variant.attributes.push({
3017
- name,
3018
- value
3019
- });
3020
- }
3021
-
3022
- if (isStaged) {
3023
- resource.masterData.staged = productData;
3024
-
3025
- if (isMasterVariant) {
3026
- resource.masterData.staged.masterVariant = variant;
3027
- } else {
3028
- resource.masterData.staged.variants[variantIndex] = variant;
3029
- }
3030
-
3031
- resource.masterData.hasStagedChanges = true;
3032
- } else {
3033
- resource.masterData.current = productData;
3034
-
3035
- if (isMasterVariant) {
3036
- resource.masterData.current.masterVariant = variant;
3037
- } else {
3038
- resource.masterData.current.variants[variantIndex] = variant;
3039
- }
3040
- }
3041
- }
3042
- };
3043
- }
3044
-
3045
- getTypeId() {
3046
- return 'product';
3047
- }
3048
-
3049
- create(context, draft) {
3050
- var _draft$publish, _draft$publish2;
3051
-
3052
- const productData = {
3053
- name: draft.name,
3054
- slug: draft.slug,
3055
- categories: [],
3056
- masterVariant: draft.masterVariant && variantFromDraft$1(0, draft.masterVariant),
3057
- variants: draft.variants && draft.variants.map((variant, index) => {
3058
- return variantFromDraft$1(index + 1, variant);
3059
- }),
3060
- // @ts-ignore
3061
- searchKeywords: draft.searchKeywords
3062
- };
3063
- const resource = { ...getBaseResourceProperties(),
3064
- masterData: {
3065
- // @ts-ignore
3066
- current: draft.publish ? productData : undefined,
3067
- // @ts-ignore
3068
- staged: draft.publish ? undefined : productData,
3069
- hasStagedChanges: (_draft$publish = draft.publish) != null ? _draft$publish : true,
3070
- published: (_draft$publish2 = draft.publish) != null ? _draft$publish2 : false
3071
- }
3072
- };
3073
- this.save(context, resource);
3074
- return resource;
3075
- }
3076
-
3077
- }
3078
-
3079
- const getProductData = (product, staged) => {
3080
- if (!staged && product.masterData.current) {
3081
- return product.masterData.current;
3082
- }
3083
-
3084
- return product.masterData.staged;
3085
- };
3086
-
3087
- const getVariant = (productData, variantId, sku) => {
3088
- const variants = [productData.masterVariant, ...productData.variants];
3089
- const foundVariant = variants.find(variant => {
3090
- if (variantId) {
3091
- return variant.id === variantId;
3092
- }
3093
-
3094
- if (sku) {
3095
- return variant.sku === sku;
3096
- }
3097
-
3098
- return false;
3099
- });
3100
- const isMasterVariant = foundVariant === productData.masterVariant;
3101
- return {
3102
- variant: foundVariant,
3103
- isMasterVariant,
3104
- variantIndex: !isMasterVariant && foundVariant ? productData.variants.indexOf(foundVariant) : -1
3105
- };
3106
- };
3107
-
3108
- const variantFromDraft$1 = (variantId, variant) => {
3109
- return {
3110
- id: variantId,
3111
- sku: variant == null ? void 0 : variant.sku,
3112
- attributes: variant == null ? void 0 : variant.attributes,
3113
- prices: variant == null ? void 0 : variant.prices
3114
- };
3115
- };
3116
-
3117
- class ProductService extends AbstractService {
3118
- constructor(parent, storage) {
3119
- super(parent);
3120
- this.repository = new ProductRepository(storage);
3121
- }
3122
-
3123
- getBasePath() {
3124
- return 'products';
3125
- }
3126
-
3127
- }
3128
-
3129
- class ProductTypeRepository extends AbstractResourceRepository {
3130
- constructor() {
3131
- super(...arguments);
3132
-
3133
- this.attributeDefinitionFromAttributeDefinitionDraft = (_context, draft) => {
3134
- var _draft$attributeConst, _draft$inputHint, _draft$isSearchable;
3135
-
3136
- return { ...draft,
3137
- attributeConstraint: (_draft$attributeConst = draft.attributeConstraint) != null ? _draft$attributeConst : 'None',
3138
- inputHint: (_draft$inputHint = draft.inputHint) != null ? _draft$inputHint : 'SingleLine',
3139
- isSearchable: (_draft$isSearchable = draft.isSearchable) != null ? _draft$isSearchable : true
3140
- };
3141
- };
3142
-
3143
- this.actions = {
3144
- changeLocalizedEnumValueLabel: (context, resource, {
3145
- attributeName,
3146
- newValue
3147
- }) => {
3148
- var _resource$attributes;
3149
-
3150
- const updateAttributeType = type => {
3151
- switch (type.name) {
3152
- case 'lenum':
3153
- type.values.forEach(v => {
3154
- if (v.key === newValue.key) {
3155
- v.label = newValue.label;
3156
- }
3157
- });
3158
- return;
3159
-
3160
- case 'set':
3161
- updateAttributeType(type.elementType);
3162
- return;
3163
- }
3164
- };
3165
-
3166
- (_resource$attributes = resource.attributes) == null ? void 0 : _resource$attributes.forEach(value => {
3167
- if (value.name === attributeName) {
3168
- updateAttributeType(value.type);
3169
- }
3170
- });
3171
- },
3172
- changeLabel: (context, resource, {
3173
- attributeName,
3174
- label
3175
- }) => {
3176
- var _resource$attributes2;
3177
-
3178
- (_resource$attributes2 = resource.attributes) == null ? void 0 : _resource$attributes2.forEach(value => {
3179
- if (value.name === attributeName) {
3180
- value.label = label;
3181
- }
3182
- });
3183
- }
3184
- };
3185
- }
3186
-
3187
- getTypeId() {
3188
- return 'product-type';
3189
- }
3190
-
3191
- create(context, draft) {
3192
- var _draft$attributes;
3193
-
3194
- const resource = { ...getBaseResourceProperties(),
3195
- key: draft.key,
3196
- name: draft.name,
3197
- description: draft.description,
3198
- attributes: ((_draft$attributes = draft.attributes) != null ? _draft$attributes : []).map(a => this.attributeDefinitionFromAttributeDefinitionDraft(context, a))
3199
- };
3200
- this.save(context, resource);
3201
- return resource;
3202
- }
3203
-
3204
- getWithKey(context, key) {
3205
- const result = this._storage.query(context.projectKey, this.getTypeId(), {
3206
- where: [`key="${key}"`]
3207
- });
3208
-
3209
- if (result.count === 1) {
3210
- return result.results[0];
3211
- } // Catch this for now, should be checked when creating/updating
3212
-
3213
-
3214
- if (result.count > 1) {
3215
- throw new Error('Duplicate product type key');
3216
- }
3217
-
3218
- return;
3219
- }
3220
-
3221
- }
3222
-
3223
- class ProductTypeService extends AbstractService {
3224
- constructor(parent, storage) {
3225
- super(parent);
3226
- this.repository = new ProductTypeRepository(storage);
3227
- }
3228
-
3229
- getBasePath() {
3230
- return 'product-types';
3231
- }
3232
-
3233
- extraRoutes(router) {
3234
- router.get('/key=:key', this.getWithKey.bind(this));
3235
- }
3236
-
3237
- getWithKey(request, response) {
3238
- const resource = this.repository.getWithKey(getRepositoryContext(request), request.params.key);
3239
-
3240
- if (resource) {
3241
- return response.status(200).send(resource);
3242
- }
3243
-
3244
- return response.status(404).send('Not found');
3245
- }
3246
-
3247
- }
3248
-
3249
- const maskSecretValue = (resource, path) => {
3250
- const parts = path.split('.');
3251
- const clone = JSON.parse(JSON.stringify(resource));
3252
- let val = clone;
3253
- const target = parts.pop();
3254
-
3255
- for (let i = 0; i < parts.length; i++) {
3256
- const part = parts[i];
3257
- val = val[part];
3258
-
3259
- if (val === undefined) {
3260
- return resource;
3261
- }
3262
- }
3263
-
3264
- if (val && target && val[target]) {
3265
- val[target] = '****';
3266
- }
3267
-
3268
- return clone;
3269
- };
3270
-
3271
- class ProjectRepository extends AbstractRepository {
3272
- constructor() {
3273
- super(...arguments);
3274
- this.actions = {
3275
- changeName: (context, resource, {
3276
- name
3277
- }) => {
3278
- resource.name = name;
3279
- },
3280
- changeCurrencies: (context, resource, {
3281
- currencies
3282
- }) => {
3283
- resource.currencies = currencies;
3284
- },
3285
- changeCountries: (context, resource, {
3286
- countries
3287
- }) => {
3288
- resource.countries = countries;
3289
- },
3290
- changeLanguages: (context, resource, {
3291
- languages
3292
- }) => {
3293
- resource.languages = languages;
3294
- },
3295
- changeMessagesEnabled: (context, resource, {
3296
- messagesEnabled
3297
- }) => {
3298
- resource.messages.enabled = messagesEnabled;
3299
- },
3300
- changeProductSearchIndexingEnabled: (context, resource, {
3301
- enabled
3302
- }) => {
3303
- var _resource$searchIndex;
3304
-
3305
- if (!((_resource$searchIndex = resource.searchIndexing) != null && _resource$searchIndex.products)) {
3306
- throw new Error('Invalid project state');
3307
- }
3308
-
3309
- resource.searchIndexing.products.status = enabled ? 'Activated' : 'Deactivated';
3310
- resource.searchIndexing.products.lastModifiedAt = new Date().toISOString();
3311
- },
3312
- changeOrderSearchStatus: (context, resource, {
3313
- status
3314
- }) => {
3315
- var _resource$searchIndex2;
3316
-
3317
- if (!((_resource$searchIndex2 = resource.searchIndexing) != null && _resource$searchIndex2.orders)) {
3318
- throw new Error('Invalid project state');
3319
- }
3320
-
3321
- resource.searchIndexing.orders.status = status;
3322
- resource.searchIndexing.orders.lastModifiedAt = new Date().toISOString();
3323
- },
3324
- setShippingRateInputType: (context, resource, {
3325
- shippingRateInputType
3326
- }) => {
3327
- resource.shippingRateInputType = shippingRateInputType;
3328
- },
3329
- setExternalOAuth: (context, resource, {
3330
- externalOAuth
3331
- }) => {
3332
- resource.externalOAuth = externalOAuth;
3333
- },
3334
- changeCountryTaxRateFallbackEnabled: (context, resource, {
3335
- countryTaxRateFallbackEnabled
3336
- }) => {
3337
- resource.carts.countryTaxRateFallbackEnabled = countryTaxRateFallbackEnabled;
3338
- },
3339
- changeCartsConfiguration: (context, resource, {
3340
- cartsConfiguration
3341
- }) => {
3342
- resource.carts = cartsConfiguration || {
3343
- countryTaxRateFallbackEnabled: false,
3344
- deleteDaysAfterLastModification: 90
3345
- };
3346
- }
3347
- };
3348
- }
3349
-
3350
- get(context) {
3351
- const resource = this._storage.getProject(context.projectKey);
3352
-
3353
- const masked = maskSecretValue(resource, 'externalOAuth.authorizationHeader');
3354
- return masked;
3355
- }
3356
-
3357
- save(context, resource) {
3358
- const current = this.get(context);
3359
-
3360
- if (current) {
3361
- checkConcurrentModification(current, resource.version);
3362
- } else {
3363
- if (resource.version !== 0) {
3364
- throw new CommercetoolsError({
3365
- code: 'InvalidOperation',
3366
- message: 'version on create must be 0'
3367
- }, 400);
3368
- }
3369
- } // @ts-ignore
3370
-
3371
-
3372
- resource.version += 1;
3373
-
3374
- this._storage.saveProject(resource);
3375
- }
3376
-
3377
- }
3378
-
3379
- class ProjectService {
3380
- constructor(parent, storage) {
3381
- this.repository = new ProjectRepository(storage);
3382
- this.registerRoutes(parent);
3383
- }
3384
-
3385
- registerRoutes(parent) {
3386
- parent.get('', this.get.bind(this));
3387
- parent.post('', this.post.bind(this));
3388
- }
3389
-
3390
- get(request, response) {
3391
- const project = this.repository.get(getRepositoryContext(request));
3392
- return response.status(200).send(project);
3393
- }
3394
-
3395
- post(request, response) {
3396
- const updateRequest = request.body;
3397
- const project = this.repository.get(getRepositoryContext(request));
3398
-
3399
- if (!project) {
3400
- return response.status(404).send({});
3401
- }
3402
-
3403
- this.repository.processUpdateActions(getRepositoryContext(request), project, updateRequest.actions);
3404
- return response.status(200).send({});
3405
- }
3406
-
3407
- }
3408
-
3409
- class ShippingMethodRepository extends AbstractResourceRepository {
3410
- constructor() {
3411
- super(...arguments);
3412
-
3413
- this._transformZoneRateDraft = (context, draft) => {
3414
- var _draft$shippingRates;
3415
-
3416
- return { ...draft,
3417
- zone: getReferenceFromResourceIdentifier(draft.zone, context.projectKey, this._storage),
3418
- shippingRates: (_draft$shippingRates = draft.shippingRates) == null ? void 0 : _draft$shippingRates.map(this._transformShippingRate)
3419
- };
3420
- };
3421
-
3422
- this._transformShippingRate = rate => {
3423
- return {
3424
- price: createTypedMoney(rate.price),
3425
- freeAbove: rate.freeAbove && createTypedMoney(rate.freeAbove),
3426
- tiers: rate.tiers || []
3427
- };
3428
- };
3429
-
3430
- this.actions = {
3431
- addShippingRate: (_context, resource, {
3432
- shippingRate,
3433
- zone
3434
- }) => {
3435
- const rate = this._transformShippingRate(shippingRate);
3436
-
3437
- resource.zoneRates.forEach(zoneRate => {
3438
- if (zoneRate.zone.id === zone.id) {
3439
- zoneRate.shippingRates.push(rate);
3440
- return;
3441
- }
3442
- });
3443
- resource.zoneRates.push({
3444
- zone: {
3445
- typeId: 'zone',
3446
- id: zone.id
3447
- },
3448
- shippingRates: [rate]
3449
- });
3450
- },
3451
- removeShippingRate: (_context, resource, {
3452
- shippingRate,
3453
- zone
3454
- }) => {
3455
- const rate = this._transformShippingRate(shippingRate);
3456
-
3457
- resource.zoneRates.forEach(zoneRate => {
3458
- if (zoneRate.zone.id === zone.id) {
3459
- zoneRate.shippingRates = zoneRate.shippingRates.filter(otherRate => {
3460
- return !deepEqual(rate, otherRate);
3461
- });
3462
- }
3463
- });
3464
- },
3465
- addZone: (context, resource, {
3466
- zone
3467
- }) => {
3468
- const zoneReference = getReferenceFromResourceIdentifier(zone, context.projectKey, this._storage);
3469
-
3470
- if (resource.zoneRates === undefined) {
3471
- resource.zoneRates = [];
3472
- }
3473
-
3474
- resource.zoneRates.push({
3475
- zone: zoneReference,
3476
- shippingRates: []
3477
- });
3478
- },
3479
- removeZone: (_context, resource, {
3480
- zone
3481
- }) => {
3482
- resource.zoneRates = resource.zoneRates.filter(zoneRate => {
3483
- return zoneRate.zone.id !== zone.id;
3484
- });
3485
- },
3486
- setKey: (_context, resource, {
3487
- key
3488
- }) => {
3489
- resource.key = key;
3490
- },
3491
- setDescription: (_context, resource, {
3492
- description
3493
- }) => {
3494
- resource.description = description;
3495
- },
3496
- setLocalizedDescription: (_context, resource, {
3497
- localizedDescription
3498
- }) => {
3499
- resource.localizedDescription = localizedDescription;
3500
- },
3501
- setPredicate: (_context, resource, {
3502
- predicate
3503
- }) => {
3504
- resource.predicate = predicate;
3505
- },
3506
- changeIsDefault: (_context, resource, {
3507
- isDefault
3508
- }) => {
3509
- resource.isDefault = isDefault;
3510
- },
3511
- changeName: (_context, resource, {
3512
- name
3513
- }) => {
3514
- resource.name = name;
3515
- }
3516
- };
3517
- }
3518
-
3519
- getTypeId() {
3520
- return 'shipping-method';
3521
- }
3522
-
3523
- create(context, draft) {
3524
- var _draft$zoneRates;
3525
-
3526
- const resource = { ...getBaseResourceProperties(),
3527
- ...draft,
3528
- taxCategory: getReferenceFromResourceIdentifier(draft.taxCategory, context.projectKey, this._storage),
3529
- zoneRates: (_draft$zoneRates = draft.zoneRates) == null ? void 0 : _draft$zoneRates.map(z => this._transformZoneRateDraft(context, z)),
3530
- custom: createCustomFields(draft.custom, context.projectKey, this._storage)
3531
- };
3532
- this.save(context, resource);
3533
- return resource;
3534
- }
3535
-
3536
- }
3537
-
3538
- class ShippingMethodService extends AbstractService {
3539
- constructor(parent, storage) {
3540
- super(parent);
3541
- this.repository = new ShippingMethodRepository(storage);
3542
- this.registerRoutes(parent);
3543
- }
3544
-
3545
- getBasePath() {
3546
- return 'shipping-methods';
3547
- }
3548
-
3549
- extraRoutes(parent) {
3550
- parent.get('/matching-cart', this.get.bind(this));
3551
- }
3552
-
3553
- }
3554
-
3555
- class ShoppingListRepository extends AbstractResourceRepository {
3556
- getTypeId() {
3557
- return 'shopping-list';
3558
- }
3559
-
3560
- create(context, draft) {
3561
- var _draft$lineItems, _draft$store;
3562
-
3563
- // const product =
3564
- const resource = { ...getBaseResourceProperties(),
3565
- ...draft,
3566
- custom: createCustomFields(draft.custom, context.projectKey, this._storage),
3567
- textLineItems: [],
3568
- lineItems: (_draft$lineItems = draft.lineItems) == null ? void 0 : _draft$lineItems.map(e => {
3569
- var _e$addedAt, _e$productId, _e$quantity;
3570
-
3571
- return { ...getBaseResourceProperties(),
3572
- ...e,
3573
- addedAt: (_e$addedAt = e.addedAt) != null ? _e$addedAt : '',
3574
- productId: (_e$productId = e.productId) != null ? _e$productId : '',
3575
- name: {},
3576
- quantity: (_e$quantity = e.quantity) != null ? _e$quantity : 1,
3577
- productType: {
3578
- typeId: 'product-type',
3579
- id: ''
3580
- },
3581
- custom: createCustomFields(e.custom, context.projectKey, this._storage)
3582
- };
3583
- }),
3584
- customer: draft.customer ? getReferenceFromResourceIdentifier(draft.customer, context.projectKey, this._storage) : undefined,
3585
- store: (_draft$store = draft.store) != null && _draft$store.key ? {
3586
- typeId: 'store',
3587
- key: draft.store.key
3588
- } : undefined
3589
- };
3590
- this.save(context, resource);
3591
- return resource;
3592
- }
3593
-
3594
- }
3595
-
3596
- class ShoppingListService extends AbstractService {
3597
- constructor(parent, storage) {
3598
- super(parent);
3599
- this.repository = new ShoppingListRepository(storage);
3600
- }
3601
-
3602
- getBasePath() {
3603
- return 'shopping-lists';
3604
- }
3605
-
3606
- }
3607
-
3608
- class StateRepository extends AbstractResourceRepository {
3609
- constructor() {
3610
- super(...arguments);
3611
- this.actions = {
3612
- changeKey: (context, resource, {
3613
- key
3614
- }) => {
3615
- resource.key = key;
3616
- },
3617
- setDescription: (context, resource, {
3618
- description
3619
- }) => {
3620
- resource.description = description;
3621
- },
3622
- setName: (context, resource, {
3623
- name
3624
- }) => {
3625
- resource.name = name;
3626
- },
3627
- setRoles: (context, resource, {
3628
- roles
3629
- }) => {
3630
- resource.roles = roles;
3631
- }
3632
- };
3633
- }
3634
-
3635
- getTypeId() {
3636
- return 'state';
3637
- }
3638
-
3639
- create(context, draft) {
3640
- const resource = { ...getBaseResourceProperties(),
3641
- ...draft,
3642
- builtIn: false,
3643
- initial: draft.initial || false,
3644
- transitions: (draft.transitions || []).map(t => getReferenceFromResourceIdentifier(t, context.projectKey, this._storage))
3645
- };
3646
- this.save(context, resource);
3647
- return resource;
3648
- }
3649
-
3650
- }
3651
-
3652
- class StateService extends AbstractService {
3653
- constructor(parent, storage) {
3654
- super(parent);
3655
- this.repository = new StateRepository(storage);
3656
- }
3657
-
3658
- getBasePath() {
3659
- return 'states';
3660
- }
3661
-
3662
- }
3663
-
3664
- class StoreRepository extends AbstractResourceRepository {
3665
- constructor() {
3666
- super(...arguments);
3667
- this.actions = {
3668
- setName: (context, resource, {
3669
- name
3670
- }) => {
3671
- resource.name = name;
3672
- },
3673
- setDistributionChannels: (context, resource, {
3674
- distributionChannels
3675
- }) => {
3676
- resource.distributionChannels = this.transformChannels(context, distributionChannels);
3677
- },
3678
- setLanguages: (context, resource, {
3679
- languages
3680
- }) => {
3681
- resource.languages = languages;
3682
- }
3683
- };
3684
- }
3685
-
3686
- getTypeId() {
3687
- return 'store';
3688
- }
3689
-
3690
- create(context, draft) {
3691
- const resource = { ...getBaseResourceProperties(),
3692
- key: draft.key,
3693
- name: draft.name,
3694
- languages: draft.languages,
3695
- distributionChannels: this.transformChannels(context, draft.distributionChannels),
3696
- supplyChannels: this.transformChannels(context, draft.supplyChannels)
3697
- };
3698
- this.save(context, resource);
3699
- return resource;
3700
- }
3701
-
3702
- transformChannels(context, channels) {
3703
- if (!channels) return [];
3704
- return channels.map(ref => getReferenceFromResourceIdentifier(ref, context.projectKey, this._storage));
3705
- }
3706
-
3707
- getWithKey(context, key) {
3708
- const result = this._storage.query(context.projectKey, this.getTypeId(), {
3709
- where: [`key="${key}"`]
3710
- });
3711
-
3712
- if (result.count === 1) {
3713
- return result.results[0];
3714
- }
3715
-
3716
- if (result.count > 1) {
3717
- throw new Error('Duplicate store key');
3718
- }
3719
-
3720
- return;
3721
- }
3722
-
3723
- }
3724
-
3725
- class StoreService extends AbstractService {
3726
- constructor(parent, storage) {
3727
- super(parent);
3728
- this.repository = new StoreRepository(storage);
3729
- }
3730
-
3731
- getBasePath() {
3732
- return 'stores';
3733
- }
3734
-
3735
- extraRoutes(router) {
3736
- router.get('/key=:key', this.getWithKey.bind(this));
3737
- }
3738
-
3739
- getWithKey(request, response) {
3740
- const resource = this.repository.getWithKey(getRepositoryContext(request), request.params.key);
3741
-
3742
- if (resource) {
3743
- return response.status(200).send(resource);
3744
- }
3745
-
3746
- return response.status(404).send('Not found');
3747
- }
3748
-
3749
- }
3750
-
3751
- class SubscriptionRepository extends AbstractResourceRepository {
3752
- getTypeId() {
3753
- return 'subscription';
3754
- }
3755
-
3756
- create(context, draft) {
3757
- // TODO: We could actually test this here by using the aws sdk. For now
3758
- // hardcode a failed check when account id is 0000000000
3759
- if (draft.destination.type === 'SQS') {
3760
- const queueURL = new URL(draft.destination.queueUrl);
3761
- const accountId = queueURL.pathname.split('/')[1];
3762
-
3763
- if (accountId === '0000000000') {
3764
- const dest = draft.destination;
3765
- throw new CommercetoolsError({
3766
- code: 'InvalidInput',
3767
- message: 'A test message could not be delivered to this destination: ' + `SQS ${dest.queueUrl} in ${dest.region} for ${dest.accessKey}. ` + 'Please make sure your destination is correctly configured.'
3768
- }, 400);
3769
- }
3770
- }
3771
-
3772
- const resource = { ...getBaseResourceProperties(),
3773
- changes: draft.changes || [],
3774
- destination: draft.destination,
3775
- format: draft.format || {
3776
- type: 'Platform'
3777
- },
3778
- key: draft.key,
3779
- messages: draft.messages || [],
3780
- status: 'Healthy'
3781
- };
3782
- this.save(context, resource);
3783
- return resource;
3784
- }
3785
-
3786
- }
3787
-
3788
- class SubscriptionService extends AbstractService {
3789
- constructor(parent, storage) {
3790
- super(parent);
3791
- this.repository = new SubscriptionRepository(storage);
3792
- }
3793
-
3794
- getBasePath() {
3795
- return 'subscriptions';
3796
- }
3797
-
3798
- }
3799
-
3800
- class TaxCategoryRepository extends AbstractResourceRepository {
3801
- constructor() {
3802
- super(...arguments);
3803
-
3804
- this.taxRateFromTaxRateDraft = draft => ({ ...draft,
3805
- id: uuid.v4(),
3806
- amount: draft.amount || 0
3807
- });
3808
-
3809
- this.actions = {
3810
- addTaxRate: (context, resource, {
3811
- taxRate
3812
- }) => {
3813
- if (resource.rates === undefined) {
3814
- resource.rates = [];
3815
- }
3816
-
3817
- resource.rates.push(this.taxRateFromTaxRateDraft(taxRate));
3818
- },
3819
- removeTaxRate: (context, resource, {
3820
- taxRateId
3821
- }) => {
3822
- if (resource.rates === undefined) {
3823
- resource.rates = [];
3824
- }
3825
-
3826
- resource.rates = resource.rates.filter(taxRate => {
3827
- return taxRate.id !== taxRateId;
3828
- });
3829
- },
3830
- replaceTaxRate: (context, resource, {
3831
- taxRateId,
3832
- taxRate
3833
- }) => {
3834
- if (resource.rates === undefined) {
3835
- resource.rates = [];
3836
- }
3837
-
3838
- const taxRateObj = this.taxRateFromTaxRateDraft(taxRate);
3839
-
3840
- for (let i = 0; i < resource.rates.length; i++) {
3841
- const rate = resource.rates[i];
3842
-
3843
- if (rate.id === taxRateId) {
3844
- resource.rates[i] = taxRateObj;
3845
- }
3846
- }
3847
- },
3848
- setDescription: (context, resource, {
3849
- description
3850
- }) => {
3851
- resource.description = description;
3852
- },
3853
- setKey: (context, resource, {
3854
- key
3855
- }) => {
3856
- resource.key = key;
3857
- },
3858
- changeName: (context, resource, {
3859
- name
3860
- }) => {
3861
- resource.name = name;
3862
- }
3863
- };
3864
- }
3865
-
3866
- getTypeId() {
3867
- return 'tax-category';
3868
- }
3869
-
3870
- create(context, draft) {
3871
- var _draft$rates;
3872
-
3873
- const resource = { ...getBaseResourceProperties(),
3874
- ...draft,
3875
- rates: ((_draft$rates = draft.rates) == null ? void 0 : _draft$rates.map(this.taxRateFromTaxRateDraft)) || []
3876
- };
3877
- this.save(context, resource);
3878
- return resource;
3879
- }
3880
-
3881
- getWithKey(context, key) {
3882
- const result = this._storage.query(context.projectKey, this.getTypeId(), {
3883
- where: [`key="${key}"`]
3884
- });
3885
-
3886
- if (result.count === 1) {
3887
- return result.results[0];
3888
- } // Catch this for now, should be checked when creating/updating
3889
-
3890
-
3891
- if (result.count > 1) {
3892
- throw new Error('Duplicate tax category key');
3893
- }
3894
-
3895
- return;
3896
- }
3897
-
3898
- }
3899
-
3900
- class TaxCategoryService extends AbstractService {
3901
- constructor(parent, storage) {
3902
- super(parent);
3903
- this.repository = new TaxCategoryRepository(storage);
3904
- }
3905
-
3906
- getBasePath() {
3907
- return 'tax-categories';
3908
- }
3909
-
3910
- extraRoutes(router) {
3911
- router.get('/key=:key', this.getWithKey.bind(this));
3912
- }
3913
-
3914
- getWithKey(request, response) {
3915
- const resource = this.repository.getWithKey(getRepositoryContext(request), request.params.key);
3916
-
3917
- if (resource) {
3918
- return response.status(200).send(resource);
3919
- }
3920
-
3921
- return response.status(404).send('Not found');
3922
- }
3923
-
3924
- }
3925
-
3926
- class TypeRepository extends AbstractResourceRepository {
3927
- constructor() {
3928
- super(...arguments);
3929
- this.actions = {
3930
- addFieldDefinition: (context, resource, {
3931
- fieldDefinition
3932
- }) => {
3933
- resource.fieldDefinitions.push(fieldDefinition);
3934
- },
3935
- removeFieldDefinition: (context, resource, {
3936
- fieldName
3937
- }) => {
3938
- resource.fieldDefinitions = resource.fieldDefinitions.filter(f => {
3939
- return f.name !== fieldName;
3940
- });
3941
- },
3942
- setDescription: (context, resource, {
3943
- description
3944
- }) => {
3945
- resource.description = description;
3946
- },
3947
- changeName: (context, resource, {
3948
- name
3949
- }) => {
3950
- resource.name = name;
3951
- },
3952
- changeFieldDefinitionOrder: (context, resource, {
3953
- fieldNames
3954
- }) => {
3955
- const fields = new Map(resource.fieldDefinitions.map(item => [item.name, item]));
3956
- const result = [];
3957
- let current = resource.fieldDefinitions;
3958
- fieldNames.forEach(fieldName => {
3959
- const field = fields.get(fieldName);
3960
-
3961
- if (field === undefined) {
3962
- throw new Error('New field');
3963
- }
3964
-
3965
- result.push(field); // Remove from current items
3966
-
3967
- current = current.filter(f => {
3968
- return f.name !== fieldName;
3969
- });
3970
- });
3971
- resource.fieldDefinitions = result; // Add fields which were not specified in the order as last items. Not
3972
- // sure if this follows commercetools
3973
-
3974
- resource.fieldDefinitions.push(...current);
3975
- },
3976
- addEnumValue: (context, resource, {
3977
- fieldName,
3978
- value
3979
- }) => {
3980
- resource.fieldDefinitions.forEach(field => {
3981
- if (field.name === fieldName) {
3982
- // TODO, should be done better i suppose
3983
- if (field.type.name === 'Enum') {
3984
- field.type.values.push(value);
3985
- } else if (field.type.name === 'Set' && field.type.elementType.name === 'Enum') {
3986
- field.type.elementType.values.push(value);
3987
- } else {
3988
- throw new Error('Type is not a Enum (or Set of Enum)');
3989
- }
3990
- }
3991
- });
3992
- },
3993
- changeEnumValueLabel: (context, resource, {
3994
- fieldName,
3995
- value
3996
- }) => {
3997
- resource.fieldDefinitions.forEach(field => {
3998
- if (field.name === fieldName) {
3999
- // TODO, should be done better i suppose
4000
- if (field.type.name === 'Enum') {
4001
- field.type.values.forEach(v => {
4002
- if (v.key === value.key) {
4003
- v.label = value.label;
4004
- }
4005
- });
4006
- } else if (field.type.name === 'Set' && field.type.elementType.name === 'Enum') {
4007
- field.type.elementType.values.forEach(v => {
4008
- if (v.key === value.key) {
4009
- v.label = value.label;
4010
- }
4011
- });
4012
- } else {
4013
- throw new Error('Type is not a Enum (or Set of Enum)');
4014
- }
4015
- }
4016
- });
4017
- }
4018
- };
4019
- }
4020
-
4021
- getTypeId() {
4022
- return 'type';
4023
- }
4024
-
4025
- create(context, draft) {
4026
- const resource = { ...getBaseResourceProperties(),
4027
- key: draft.key,
4028
- name: draft.name,
4029
- resourceTypeIds: draft.resourceTypeIds,
4030
- fieldDefinitions: draft.fieldDefinitions || [],
4031
- description: draft.description
4032
- };
4033
- this.save(context, resource);
4034
- return resource;
4035
- }
4036
-
4037
- }
4038
-
4039
- class TypeService extends AbstractService {
4040
- constructor(parent, storage) {
4041
- super(parent);
4042
- this.repository = new TypeRepository(storage);
4043
- }
4044
-
4045
- getBasePath() {
4046
- return 'types';
4047
- }
4048
-
4049
- }
4050
-
4051
- class ZoneRepository extends AbstractResourceRepository {
4052
- constructor() {
4053
- super(...arguments);
4054
- this.actions = {
4055
- addLocation: (context, resource, {
4056
- location
4057
- }) => {
4058
- resource.locations.push(location);
4059
- },
4060
- removeLocation: (context, resource, {
4061
- location
4062
- }) => {
4063
- resource.locations = resource.locations.filter(loc => {
4064
- return !(loc.country === location.country && loc.state === location.state);
4065
- });
4066
- },
4067
- changeName: (context, resource, {
4068
- name
4069
- }) => {
4070
- resource.name = name;
4071
- },
4072
- setDescription: (context, resource, {
4073
- description
4074
- }) => {
4075
- resource.description = description;
4076
- },
4077
- setKey: (context, resource, {
4078
- key
4079
- }) => {
4080
- resource.key = key;
4081
- }
4082
- };
4083
- }
4084
-
4085
- getTypeId() {
4086
- return 'zone';
4087
- }
4088
-
4089
- create(context, draft) {
4090
- const resource = { ...getBaseResourceProperties(),
4091
- key: draft.key,
4092
- locations: draft.locations || [],
4093
- name: draft.name,
4094
- description: draft.description
4095
- };
4096
- this.save(context, resource);
4097
- return resource;
4098
- }
4099
-
4100
- }
4101
-
4102
- class ZoneService extends AbstractService {
4103
- constructor(parent, storage) {
4104
- super(parent);
4105
- this.repository = new ZoneRepository(storage);
4106
- }
4107
-
4108
- getBasePath() {
4109
- return 'zones';
4110
- }
4111
-
4112
- }
4113
-
4114
- class MyCustomerService extends AbstractService {
4115
- constructor(parent, storage) {
4116
- super(parent);
4117
- this.repository = new CustomerRepository(storage);
4118
- }
4119
-
4120
- getBasePath() {
4121
- return 'me';
4122
- }
4123
-
4124
- registerRoutes(parent) {
4125
- // Overwrite this function to be able to handle /me path.
4126
- const basePath = this.getBasePath();
4127
- const router = express.Router({
4128
- mergeParams: true
4129
- });
4130
- this.extraRoutes(router);
4131
- router.get('', this.getMe.bind(this));
4132
- router.post('/signup', this.signUp.bind(this));
4133
- router.post('/login', this.signIn.bind(this));
4134
- parent.use(`/${basePath}`, router);
4135
- }
4136
-
4137
- getMe(request, response) {
4138
- const resource = this.repository.getMe(getRepositoryContext(request));
4139
-
4140
- if (!resource) {
4141
- return response.status(404).send('Not found');
4142
- }
4143
-
4144
- return response.status(200).send(resource);
4145
- }
4146
-
4147
- signUp(request, response) {
4148
- const draft = request.body;
4149
- const resource = this.repository.create(getRepositoryContext(request), draft);
4150
-
4151
- const result = this._expandWithId(request, resource.id);
4152
-
4153
- return response.status(this.createStatusCode).send({
4154
- customer: result
4155
- });
4156
- }
4157
-
4158
- signIn(request, response) {
4159
- const {
4160
- email,
4161
- password
4162
- } = request.body;
4163
- const encodedPassword = Buffer.from(password).toString('base64');
4164
- const result = this.repository.query(getRepositoryContext(request), {
4165
- where: [`email = "${email}"`, `password = "${encodedPassword}"`]
4166
- });
4167
-
4168
- if (result.count === 0) {
4169
- return response.status(400).send({
4170
- message: 'Account with the given credentials not found.',
4171
- errors: [{
4172
- code: 'InvalidCredentials',
4173
- message: 'Account with the given credentials not found.'
4174
- }]
4175
- });
4176
- }
4177
-
4178
- return response.status(200).send({
4179
- customer: result.results[0]
4180
- });
4181
- }
4182
-
4183
- }
4184
-
4185
- class MyOrderRepository extends OrderRepository {
4186
- create(context, draft) {
4187
- assert(draft.id, 'draft.id is missing');
4188
- const cartIdentifier = {
4189
- id: draft.id,
4190
- typeId: 'cart'
4191
- };
4192
- return this.createFromCart(context, cartIdentifier);
4193
- }
4194
-
4195
- }
4196
-
4197
- class MyOrderService extends AbstractService {
4198
- constructor(parent, storage) {
4199
- super(parent);
4200
- this.repository = new MyOrderRepository(storage);
4201
- }
4202
-
4203
- getBasePath() {
4204
- return 'me';
4205
- }
4206
-
4207
- registerRoutes(parent) {
4208
- // Overwrite this function to be able to handle /me/active-cart path.
4209
- const basePath = this.getBasePath();
4210
- const router = express.Router({
4211
- mergeParams: true
4212
- });
4213
- this.extraRoutes(router);
4214
- router.get('/orders/', this.get.bind(this));
4215
- router.get('/orders/:id', this.getWithId.bind(this));
4216
- router.delete('/orders/:id', this.deletewithId.bind(this));
4217
- router.post('/orders/', this.post.bind(this));
4218
- router.post('/orders/:id', this.postWithId.bind(this));
4219
- parent.use(`/${basePath}`, router);
4220
- }
4221
-
4222
- }
4223
-
4224
- const DEFAULT_OPTIONS = {
4225
- enableAuthentication: false,
4226
- validateCredentials: false,
4227
- defaultProjectKey: undefined,
4228
- apiHost: DEFAULT_API_HOSTNAME,
4229
- authHost: DEFAULT_AUTH_HOSTNAME,
4230
- silent: false
4231
- };
4232
- class CommercetoolsMock {
4233
- constructor(options = {}) {
4234
- this._nockScopes = {
4235
- auth: undefined,
4236
- api: undefined
4237
- };
4238
- this.options = { ...DEFAULT_OPTIONS,
4239
- ...options
4240
- };
4241
- this._services = {};
4242
- this._projectService = undefined;
4243
- this._storage = new InMemoryStorage();
4244
- this._oauth2 = new OAuth2Server({
4245
- enabled: this.options.enableAuthentication,
4246
- validate: this.options.validateCredentials
4247
- });
4248
- this.app = this.createApp({
4249
- silent: this.options.silent
4250
- });
4251
- }
4252
-
4253
- start() {
4254
- // Order is important here when the hostnames match
4255
- this.mockAuthHost();
4256
- this.mockApiHost();
4257
- }
4258
-
4259
- stop() {
4260
- var _this$_nockScopes$aut, _this$_nockScopes$api;
4261
-
4262
- (_this$_nockScopes$aut = this._nockScopes.auth) == null ? void 0 : _this$_nockScopes$aut.persist(false);
4263
- this._nockScopes.auth = undefined;
4264
- (_this$_nockScopes$api = this._nockScopes.api) == null ? void 0 : _this$_nockScopes$api.persist(false);
4265
- this._nockScopes.api = undefined;
4266
- }
4267
-
4268
- clear() {
4269
- this._storage.clear();
4270
- }
4271
-
4272
- project(projectKey) {
4273
- if (!projectKey && !this.options.defaultProjectKey) {
4274
- throw new Error('No projectKey passed and no default set');
4275
- }
4276
-
4277
- return new ProjectAPI(projectKey || this.options.defaultProjectKey, this._services, this._storage);
4278
- }
4279
-
4280
- runServer(port = 3000, options) {
4281
- const app = this.createApp(options);
4282
- const server = app.listen(port, () => {
4283
- console.log(`Mock server listening at http://localhost:${port}`);
4284
- });
4285
- server.keepAliveTimeout = 60 * 1000;
4286
- }
4287
-
4288
- createApp(options) {
4289
- const app = express__default();
4290
- const projectRouter = express__default.Router({
4291
- mergeParams: true
4292
- });
4293
- projectRouter.use(express__default.json());
4294
-
4295
- if (!(options != null && options.silent)) {
4296
- app.use(morgan('tiny'));
4297
- }
4298
-
4299
- app.use('/oauth', this._oauth2.createRouter()); // Only enable auth middleware if we have enabled this
4300
-
4301
- if (this.options.enableAuthentication) {
4302
- app.use('/:projectKey', this._oauth2.createMiddleware(), projectRouter);
4303
- app.use('/:projectKey/in-store/key=:storeKey', this._oauth2.createMiddleware(), projectRouter);
4304
- } else {
4305
- app.use('/:projectKey', projectRouter);
4306
- app.use('/:projectKey/in-store/key=:storeKey', projectRouter);
4307
- }
4308
-
4309
- this._projectService = new ProjectService(projectRouter, this._storage);
4310
- this._services = {
4311
- category: new CategoryServices(projectRouter, this._storage),
4312
- cart: new CartService(projectRouter, this._storage),
4313
- 'cart-discount': new CartDiscountService(projectRouter, this._storage),
4314
- customer: new CustomerService(projectRouter, this._storage),
4315
- channel: new ChannelService(projectRouter, this._storage),
4316
- 'customer-group': new CustomerGroupService(projectRouter, this._storage),
4317
- 'discount-code': new DiscountCodeService(projectRouter, this._storage),
4318
- extension: new ExtensionServices(projectRouter, this._storage),
4319
- 'inventory-entry': new InventoryEntryService(projectRouter, this._storage),
4320
- 'key-value-document': new CustomObjectService(projectRouter, this._storage),
4321
- order: new OrderService(projectRouter, this._storage),
4322
- payment: new PaymentService(projectRouter, this._storage),
4323
- 'my-cart': new MyCartService(projectRouter, this._storage),
4324
- 'my-order': new MyOrderService(projectRouter, this._storage),
4325
- 'my-customer': new MyCustomerService(projectRouter, this._storage),
4326
- 'my-payment': new MyPaymentService(projectRouter, this._storage),
4327
- 'shipping-method': new ShippingMethodService(projectRouter, this._storage),
4328
- 'product-type': new ProductTypeService(projectRouter, this._storage),
4329
- product: new ProductService(projectRouter, this._storage),
4330
- 'product-projection': new ProductProjectionService(projectRouter, this._storage),
4331
- 'shopping-list': new ShoppingListService(projectRouter, this._storage),
4332
- state: new StateService(projectRouter, this._storage),
4333
- store: new StoreService(projectRouter, this._storage),
4334
- subscription: new SubscriptionService(projectRouter, this._storage),
4335
- 'tax-category': new TaxCategoryService(projectRouter, this._storage),
4336
- type: new TypeService(projectRouter, this._storage),
4337
- zone: new ZoneService(projectRouter, this._storage)
4338
- };
4339
- app.use((err, req, resp, next) => {
4340
- if (err instanceof CommercetoolsError) {
4341
- return resp.status(err.statusCode).send({
4342
- statusCode: err.statusCode,
4343
- message: err.message,
4344
- errors: [err.info]
4345
- });
4346
- } else {
4347
- console.error(err);
4348
- return resp.status(500).send({
4349
- error: err.message
4350
- });
4351
- }
4352
- });
4353
- return app;
4354
- }
4355
-
4356
- mockApiHost() {
4357
- const app = this.app;
4358
- this._nockScopes.api = nock(this.options.apiHost).persist().get(/.*/).reply(async function (uri) {
4359
- const response = await supertest(app).get(uri).set(copyHeaders(this.req.headers));
4360
- return [response.status, response.body];
4361
- }).post(/.*/).reply(async function (uri, body) {
4362
- const response = await supertest(app).post(uri).set(copyHeaders(this.req.headers)).send(body);
4363
- return [response.status, response.body];
4364
- }).delete(/.*/).reply(async function (uri, body) {
4365
- const response = await supertest(app).delete(uri).set(copyHeaders(this.req.headers)).send(body);
4366
- return [response.status, response.body];
4367
- });
4368
- }
4369
-
4370
- mockAuthHost() {
4371
- const app = this.app;
4372
- this._nockScopes.auth = nock(this.options.authHost).persist().post(/^\/oauth\/.*/).reply(async function (uri, body) {
4373
- const response = await supertest(app).post(uri + '?' + body).set(copyHeaders(this.req.headers)).send();
4374
- return [response.status, response.body];
4375
- });
4376
- }
4377
-
4378
- }
4379
-
4380
- exports.CommercetoolsMock = CommercetoolsMock;
4381
- exports.getBaseResourceProperties = getBaseResourceProperties;
4382
- //# sourceMappingURL=commercetools-mock.cjs.development.js.map