@labdigital/commercetools-mock 1.11.0 → 2.1.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.cjs +213 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +213 -37
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/constants.ts +2 -4
- package/src/ctMock.ts +90 -50
- package/src/exceptions.ts +4 -0
- package/src/index.test.ts +54 -20
- package/src/lib/password.ts +7 -0
- package/src/lib/proxy.ts +4 -4
- package/src/oauth/server.ts +137 -3
- package/src/oauth/store.ts +13 -0
- package/src/repositories/customer.ts +30 -8
- package/src/services/my-customer.ts +2 -1
package/dist/index.cjs
CHANGED
|
@@ -36,10 +36,11 @@ __export(src_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(src_exports);
|
|
37
37
|
|
|
38
38
|
// src/ctMock.ts
|
|
39
|
-
var import_nock = __toESM(require("nock"), 1);
|
|
40
39
|
var import_express6 = __toESM(require("express"), 1);
|
|
41
40
|
var import_supertest = __toESM(require("supertest"), 1);
|
|
42
41
|
var import_morgan = __toESM(require("morgan"), 1);
|
|
42
|
+
var import_node = require("msw/node");
|
|
43
|
+
var import_msw = require("msw");
|
|
43
44
|
|
|
44
45
|
// src/storage/abstract.ts
|
|
45
46
|
var AbstractStorage = class {
|
|
@@ -52,10 +53,12 @@ var import_assert = __toESM(require("assert"), 1);
|
|
|
52
53
|
var CommercetoolsError = class extends Error {
|
|
53
54
|
info;
|
|
54
55
|
statusCode;
|
|
56
|
+
errors;
|
|
55
57
|
constructor(info, statusCode = 400) {
|
|
56
58
|
super(info.message);
|
|
57
59
|
this.info = info;
|
|
58
60
|
this.statusCode = statusCode || 500;
|
|
61
|
+
this.errors = info.errors ?? [];
|
|
59
62
|
}
|
|
60
63
|
};
|
|
61
64
|
|
|
@@ -1255,6 +1258,16 @@ var OAuth2Store = class {
|
|
|
1255
1258
|
this.tokens.push(token);
|
|
1256
1259
|
return token;
|
|
1257
1260
|
}
|
|
1261
|
+
getCustomerToken(scope, customerId) {
|
|
1262
|
+
const token = {
|
|
1263
|
+
access_token: (0, import_crypto.randomBytes)(16).toString("base64"),
|
|
1264
|
+
token_type: "Bearer",
|
|
1265
|
+
expires_in: 172800,
|
|
1266
|
+
scope: scope ? `${scope} custome_id:${customerId}` : `customer_id: ${customerId}`
|
|
1267
|
+
};
|
|
1268
|
+
this.tokens.push(token);
|
|
1269
|
+
return token;
|
|
1270
|
+
}
|
|
1258
1271
|
validateToken(token) {
|
|
1259
1272
|
if (!this.validate)
|
|
1260
1273
|
return true;
|
|
@@ -1276,16 +1289,36 @@ var getBearerToken = (request) => {
|
|
|
1276
1289
|
return void 0;
|
|
1277
1290
|
};
|
|
1278
1291
|
|
|
1292
|
+
// src/lib/password.ts
|
|
1293
|
+
var hashPassword = (clearPassword) => Buffer.from(clearPassword).toString("base64");
|
|
1294
|
+
|
|
1279
1295
|
// src/oauth/server.ts
|
|
1280
1296
|
var OAuth2Server = class {
|
|
1281
1297
|
store;
|
|
1298
|
+
customerRepository;
|
|
1282
1299
|
constructor(options) {
|
|
1283
1300
|
this.store = new OAuth2Store(options.validate);
|
|
1284
1301
|
}
|
|
1302
|
+
setCustomerRepository(repository) {
|
|
1303
|
+
this.customerRepository = repository;
|
|
1304
|
+
}
|
|
1285
1305
|
createRouter() {
|
|
1286
1306
|
const router = import_express.default.Router();
|
|
1287
1307
|
router.use(import_body_parser.default.urlencoded({ extended: true }));
|
|
1308
|
+
router.use(this.validateClientCredentials.bind(this));
|
|
1288
1309
|
router.post("/token", this.tokenHandler.bind(this));
|
|
1310
|
+
router.post(
|
|
1311
|
+
"/:projectKey/customers/token",
|
|
1312
|
+
this.customerTokenHandler.bind(this)
|
|
1313
|
+
);
|
|
1314
|
+
router.post(
|
|
1315
|
+
"/:projectKey/in-store/key=:storeKey/customers/token",
|
|
1316
|
+
this.inStoreCustomerTokenHandler.bind(this)
|
|
1317
|
+
);
|
|
1318
|
+
router.post(
|
|
1319
|
+
"/:projectKey/anonymous/token",
|
|
1320
|
+
this.anonymousTokenHandler.bind(this)
|
|
1321
|
+
);
|
|
1289
1322
|
return router;
|
|
1290
1323
|
}
|
|
1291
1324
|
createMiddleware() {
|
|
@@ -1316,7 +1349,7 @@ var OAuth2Server = class {
|
|
|
1316
1349
|
next();
|
|
1317
1350
|
};
|
|
1318
1351
|
}
|
|
1319
|
-
async
|
|
1352
|
+
async validateClientCredentials(request, response, next) {
|
|
1320
1353
|
const authHeader = request.header("Authorization");
|
|
1321
1354
|
if (!authHeader) {
|
|
1322
1355
|
return next(
|
|
@@ -1341,6 +1374,13 @@ var OAuth2Server = class {
|
|
|
1341
1374
|
)
|
|
1342
1375
|
);
|
|
1343
1376
|
}
|
|
1377
|
+
request.credentials = {
|
|
1378
|
+
clientId: credentials.name,
|
|
1379
|
+
clientSecret: credentials.pass
|
|
1380
|
+
};
|
|
1381
|
+
next();
|
|
1382
|
+
}
|
|
1383
|
+
async tokenHandler(request, response, next) {
|
|
1344
1384
|
const grantType = request.query.grant_type || request.body.grant_type;
|
|
1345
1385
|
if (!grantType) {
|
|
1346
1386
|
return next(
|
|
@@ -1355,8 +1395,15 @@ var OAuth2Server = class {
|
|
|
1355
1395
|
}
|
|
1356
1396
|
if (grantType === "client_credentials") {
|
|
1357
1397
|
const token = this.store.getClientToken(
|
|
1358
|
-
credentials.
|
|
1359
|
-
credentials.
|
|
1398
|
+
request.credentials.clientId,
|
|
1399
|
+
request.credentials.clientSecret,
|
|
1400
|
+
request.query.scope?.toString()
|
|
1401
|
+
);
|
|
1402
|
+
return response.status(200).send(token);
|
|
1403
|
+
} else if (grantType === "refresh_token") {
|
|
1404
|
+
const token = this.store.getClientToken(
|
|
1405
|
+
request.credentials.clientId,
|
|
1406
|
+
request.credentials.clientSecret,
|
|
1360
1407
|
request.query.scope?.toString()
|
|
1361
1408
|
);
|
|
1362
1409
|
return response.status(200).send(token);
|
|
@@ -1372,6 +1419,69 @@ var OAuth2Server = class {
|
|
|
1372
1419
|
);
|
|
1373
1420
|
}
|
|
1374
1421
|
}
|
|
1422
|
+
async customerTokenHandler(request, response, next) {
|
|
1423
|
+
const grantType = request.query.grant_type || request.body.grant_type;
|
|
1424
|
+
if (!grantType) {
|
|
1425
|
+
return next(
|
|
1426
|
+
new CommercetoolsError(
|
|
1427
|
+
{
|
|
1428
|
+
code: "invalid_request",
|
|
1429
|
+
message: "Missing required parameter: grant_type."
|
|
1430
|
+
},
|
|
1431
|
+
400
|
|
1432
|
+
)
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
if (grantType === "password") {
|
|
1436
|
+
const username = request.query.username || request.body.username;
|
|
1437
|
+
const password = hashPassword(
|
|
1438
|
+
request.query.password || request.body.password
|
|
1439
|
+
);
|
|
1440
|
+
const scope = request.query.scope?.toString() || request.body.scope?.toString();
|
|
1441
|
+
const result = this.customerRepository.query(
|
|
1442
|
+
{ projectKey: request.params.projectKey },
|
|
1443
|
+
{
|
|
1444
|
+
where: [`email = "${username}"`, `password = "${password}"`]
|
|
1445
|
+
}
|
|
1446
|
+
);
|
|
1447
|
+
if (result.count === 0) {
|
|
1448
|
+
return next(
|
|
1449
|
+
new CommercetoolsError(
|
|
1450
|
+
{
|
|
1451
|
+
code: "invalid_customer_account_credentials",
|
|
1452
|
+
message: "Customer account with the given credentials not found."
|
|
1453
|
+
},
|
|
1454
|
+
400
|
|
1455
|
+
)
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
const customer = result.results[0];
|
|
1459
|
+
const token = this.store.getCustomerToken(scope, customer.id);
|
|
1460
|
+
return response.status(200).send(token);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
async inStoreCustomerTokenHandler(request, response, next) {
|
|
1464
|
+
return next(
|
|
1465
|
+
new CommercetoolsError(
|
|
1466
|
+
{
|
|
1467
|
+
code: "invalid_client",
|
|
1468
|
+
message: "Not implemented yet in commercetools-mock"
|
|
1469
|
+
},
|
|
1470
|
+
401
|
|
1471
|
+
)
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
async anonymousTokenHandler(request, response, next) {
|
|
1475
|
+
return next(
|
|
1476
|
+
new CommercetoolsError(
|
|
1477
|
+
{
|
|
1478
|
+
code: "invalid_client",
|
|
1479
|
+
message: "Not implemented yet in commercetools-mock"
|
|
1480
|
+
},
|
|
1481
|
+
401
|
|
1482
|
+
)
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1375
1485
|
};
|
|
1376
1486
|
|
|
1377
1487
|
// src/projectAPI.ts
|
|
@@ -1415,19 +1525,19 @@ var ProjectAPI = class {
|
|
|
1415
1525
|
|
|
1416
1526
|
// src/lib/proxy.ts
|
|
1417
1527
|
var copyHeaders = (headers) => {
|
|
1418
|
-
const validHeaders = ["accept", "host", "authorization"];
|
|
1528
|
+
const validHeaders = ["accept", "host", "authorization", "content-type"];
|
|
1419
1529
|
const result = {};
|
|
1420
|
-
|
|
1530
|
+
for (const [key, value] of headers.entries()) {
|
|
1421
1531
|
if (validHeaders.includes(key.toLowerCase())) {
|
|
1422
1532
|
result[key] = value;
|
|
1423
1533
|
}
|
|
1424
|
-
}
|
|
1534
|
+
}
|
|
1425
1535
|
return result;
|
|
1426
1536
|
};
|
|
1427
1537
|
|
|
1428
1538
|
// src/constants.ts
|
|
1429
|
-
var DEFAULT_API_HOSTNAME =
|
|
1430
|
-
var DEFAULT_AUTH_HOSTNAME =
|
|
1539
|
+
var DEFAULT_API_HOSTNAME = "https://api.*.commercetools.com";
|
|
1540
|
+
var DEFAULT_AUTH_HOSTNAME = "https://auth.*.commercetools.com";
|
|
1431
1541
|
|
|
1432
1542
|
// src/repositories/helpers.ts
|
|
1433
1543
|
var import_uuid2 = require("uuid");
|
|
@@ -2848,11 +2958,29 @@ var CustomerRepository = class extends AbstractResourceRepository {
|
|
|
2848
2958
|
return "customer";
|
|
2849
2959
|
}
|
|
2850
2960
|
create(context, draft) {
|
|
2961
|
+
const results = this._storage.query(context.projectKey, this.getTypeId(), {
|
|
2962
|
+
where: [`email="${draft.email.toLocaleLowerCase()}"`]
|
|
2963
|
+
});
|
|
2964
|
+
if (results.count > 0) {
|
|
2965
|
+
throw new CommercetoolsError({
|
|
2966
|
+
code: "CustomerAlreadyExists",
|
|
2967
|
+
statusCode: 400,
|
|
2968
|
+
message: "There is already an existing customer with the provided email.",
|
|
2969
|
+
errors: [
|
|
2970
|
+
{
|
|
2971
|
+
code: "DuplicateField",
|
|
2972
|
+
message: `Customer with email '${draft.email}' already exists.`,
|
|
2973
|
+
duplicateValue: draft.email,
|
|
2974
|
+
field: "email"
|
|
2975
|
+
}
|
|
2976
|
+
]
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2851
2979
|
const resource = {
|
|
2852
2980
|
...getBaseResourceProperties(),
|
|
2853
2981
|
authenticationMode: draft.authenticationMode || "Password",
|
|
2854
|
-
email: draft.email,
|
|
2855
|
-
password: draft.password ?
|
|
2982
|
+
email: draft.email.toLowerCase(),
|
|
2983
|
+
password: draft.password ? hashPassword(draft.password) : void 0,
|
|
2856
2984
|
isEmailVerified: draft.isEmailVerified || false,
|
|
2857
2985
|
addresses: []
|
|
2858
2986
|
};
|
|
@@ -2890,7 +3018,7 @@ var CustomerRepository = class extends AbstractResourceRepository {
|
|
|
2890
3018
|
return;
|
|
2891
3019
|
}
|
|
2892
3020
|
if (authMode === "Password") {
|
|
2893
|
-
resource.password = password ?
|
|
3021
|
+
resource.password = password ? hashPassword(password) : void 0;
|
|
2894
3022
|
return;
|
|
2895
3023
|
}
|
|
2896
3024
|
throw new CommercetoolsError(
|
|
@@ -6020,7 +6148,7 @@ var MyCustomerService = class extends AbstractService {
|
|
|
6020
6148
|
}
|
|
6021
6149
|
signIn(request, response) {
|
|
6022
6150
|
const { email, password } = request.body;
|
|
6023
|
-
const encodedPassword =
|
|
6151
|
+
const encodedPassword = hashPassword(password);
|
|
6024
6152
|
const result = this.repository.query(getRepositoryContext(request), {
|
|
6025
6153
|
where: [`email = "${email}"`, `password = "${encodedPassword}"`]
|
|
6026
6154
|
});
|
|
@@ -6402,12 +6530,13 @@ var DEFAULT_OPTIONS = {
|
|
|
6402
6530
|
authHost: DEFAULT_AUTH_HOSTNAME,
|
|
6403
6531
|
silent: false
|
|
6404
6532
|
};
|
|
6533
|
+
var _globalListeners = [];
|
|
6405
6534
|
var CommercetoolsMock = class {
|
|
6406
6535
|
app;
|
|
6407
6536
|
options;
|
|
6408
6537
|
_storage;
|
|
6409
6538
|
_oauth2;
|
|
6410
|
-
|
|
6539
|
+
_mswServer = void 0;
|
|
6411
6540
|
_services;
|
|
6412
6541
|
_repositories;
|
|
6413
6542
|
_projectService;
|
|
@@ -6424,16 +6553,15 @@ var CommercetoolsMock = class {
|
|
|
6424
6553
|
this.app = this.createApp({ silent: this.options.silent });
|
|
6425
6554
|
}
|
|
6426
6555
|
start() {
|
|
6427
|
-
this.
|
|
6428
|
-
this.
|
|
6556
|
+
this.clear();
|
|
6557
|
+
this.startServer();
|
|
6429
6558
|
}
|
|
6430
6559
|
stop() {
|
|
6431
|
-
this.
|
|
6432
|
-
this.
|
|
6433
|
-
this._nockScopes.api?.persist(false);
|
|
6434
|
-
this._nockScopes.api = void 0;
|
|
6560
|
+
this._mswServer?.close();
|
|
6561
|
+
this._mswServer = void 0;
|
|
6435
6562
|
}
|
|
6436
6563
|
clear() {
|
|
6564
|
+
this._mswServer?.resetHandlers();
|
|
6437
6565
|
this._storage.clear();
|
|
6438
6566
|
}
|
|
6439
6567
|
project(projectKey) {
|
|
@@ -6457,6 +6585,7 @@ var CommercetoolsMock = class {
|
|
|
6457
6585
|
}
|
|
6458
6586
|
createApp(options) {
|
|
6459
6587
|
this._repositories = createRepositories(this._storage);
|
|
6588
|
+
this._oauth2.setCustomerRepository(this._repositories.customer);
|
|
6460
6589
|
const app = (0, import_express6.default)();
|
|
6461
6590
|
const projectRouter = import_express6.default.Router({ mergeParams: true });
|
|
6462
6591
|
projectRouter.use(import_express6.default.json());
|
|
@@ -6482,6 +6611,13 @@ var CommercetoolsMock = class {
|
|
|
6482
6611
|
);
|
|
6483
6612
|
app.use((err, req, resp, next) => {
|
|
6484
6613
|
if (err instanceof CommercetoolsError) {
|
|
6614
|
+
if (err.errors?.length > 0) {
|
|
6615
|
+
return resp.status(err.statusCode).send({
|
|
6616
|
+
statusCode: err.statusCode,
|
|
6617
|
+
message: err.message,
|
|
6618
|
+
errors: err.errors
|
|
6619
|
+
});
|
|
6620
|
+
}
|
|
6485
6621
|
return resp.status(err.statusCode).send({
|
|
6486
6622
|
statusCode: err.statusCode,
|
|
6487
6623
|
message: err.message,
|
|
@@ -6496,25 +6632,65 @@ var CommercetoolsMock = class {
|
|
|
6496
6632
|
});
|
|
6497
6633
|
return app;
|
|
6498
6634
|
}
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
}
|
|
6508
|
-
const response = await (0, import_supertest.default)(app).delete(uri).set(copyHeaders(this.req.headers)).send(body);
|
|
6509
|
-
return [response.status, response.body];
|
|
6510
|
-
});
|
|
6511
|
-
}
|
|
6512
|
-
mockAuthHost() {
|
|
6635
|
+
startServer() {
|
|
6636
|
+
if (_globalListeners.length > 0) {
|
|
6637
|
+
if (this._mswServer !== void 0) {
|
|
6638
|
+
throw new Error("Server already started");
|
|
6639
|
+
} else {
|
|
6640
|
+
console.warn("Server wasn't stopped properly, clearing");
|
|
6641
|
+
_globalListeners.forEach((listener) => listener.close());
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6513
6644
|
const app = this.app;
|
|
6514
|
-
this.
|
|
6515
|
-
|
|
6516
|
-
|
|
6645
|
+
this._mswServer = (0, import_node.setupServer)(
|
|
6646
|
+
import_msw.http.post(`${this.options.authHost}/oauth/*`, async ({ request }) => {
|
|
6647
|
+
const text = await request.text();
|
|
6648
|
+
const url = new URL(request.url);
|
|
6649
|
+
const res = await (0, import_supertest.default)(app).post(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(text);
|
|
6650
|
+
return new import_msw.HttpResponse(res.text, {
|
|
6651
|
+
status: res.status,
|
|
6652
|
+
headers: res.headers
|
|
6653
|
+
});
|
|
6654
|
+
}),
|
|
6655
|
+
import_msw.http.get(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6656
|
+
const body = await request.text();
|
|
6657
|
+
const url = new URL(request.url);
|
|
6658
|
+
const res = await (0, import_supertest.default)(app).get(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6659
|
+
return new import_msw.HttpResponse(res.text, {
|
|
6660
|
+
status: res.status,
|
|
6661
|
+
headers: res.headers
|
|
6662
|
+
});
|
|
6663
|
+
}),
|
|
6664
|
+
import_msw.http.post(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6665
|
+
const body = await request.text();
|
|
6666
|
+
const url = new URL(request.url);
|
|
6667
|
+
const res = await (0, import_supertest.default)(app).post(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6668
|
+
return new import_msw.HttpResponse(res.text, {
|
|
6669
|
+
status: res.status,
|
|
6670
|
+
headers: res.headers
|
|
6671
|
+
});
|
|
6672
|
+
}),
|
|
6673
|
+
import_msw.http.delete(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6674
|
+
const body = await request.text();
|
|
6675
|
+
const url = new URL(request.url);
|
|
6676
|
+
const res = await (0, import_supertest.default)(app).delete(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6677
|
+
return new import_msw.HttpResponse(res.text, {
|
|
6678
|
+
status: res.status,
|
|
6679
|
+
headers: res.headers
|
|
6680
|
+
});
|
|
6681
|
+
})
|
|
6682
|
+
);
|
|
6683
|
+
this._mswServer.listen({
|
|
6684
|
+
// We need to allow requests done by supertest
|
|
6685
|
+
onUnhandledRequest: (request, print) => {
|
|
6686
|
+
const url = new URL(request.url);
|
|
6687
|
+
if (url.hostname === "127.0.0.1") {
|
|
6688
|
+
return;
|
|
6689
|
+
}
|
|
6690
|
+
print.error();
|
|
6691
|
+
}
|
|
6517
6692
|
});
|
|
6693
|
+
_globalListeners.push(this._mswServer);
|
|
6518
6694
|
}
|
|
6519
6695
|
};
|
|
6520
6696
|
// Annotate the CommonJS export names for ESM import in node:
|