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