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