@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.d.cts
CHANGED
|
@@ -630,7 +630,7 @@ declare class CommercetoolsMock {
|
|
|
630
630
|
options: CommercetoolsMockOptions;
|
|
631
631
|
private _storage;
|
|
632
632
|
private _oauth2;
|
|
633
|
-
private
|
|
633
|
+
private _mswServer;
|
|
634
634
|
private _services;
|
|
635
635
|
private _repositories;
|
|
636
636
|
private _projectService?;
|
|
@@ -641,8 +641,7 @@ declare class CommercetoolsMock {
|
|
|
641
641
|
project(projectKey?: string): ProjectAPI;
|
|
642
642
|
runServer(port?: number, options?: AppOptions): void;
|
|
643
643
|
private createApp;
|
|
644
|
-
private
|
|
645
|
-
private mockAuthHost;
|
|
644
|
+
private startServer;
|
|
646
645
|
}
|
|
647
646
|
|
|
648
647
|
declare const getBaseResourceProperties: () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -630,7 +630,7 @@ declare class CommercetoolsMock {
|
|
|
630
630
|
options: CommercetoolsMockOptions;
|
|
631
631
|
private _storage;
|
|
632
632
|
private _oauth2;
|
|
633
|
-
private
|
|
633
|
+
private _mswServer;
|
|
634
634
|
private _services;
|
|
635
635
|
private _repositories;
|
|
636
636
|
private _projectService?;
|
|
@@ -641,8 +641,7 @@ declare class CommercetoolsMock {
|
|
|
641
641
|
project(projectKey?: string): ProjectAPI;
|
|
642
642
|
runServer(port?: number, options?: AppOptions): void;
|
|
643
643
|
private createApp;
|
|
644
|
-
private
|
|
645
|
-
private mockAuthHost;
|
|
644
|
+
private startServer;
|
|
646
645
|
}
|
|
647
646
|
|
|
648
647
|
declare const getBaseResourceProperties: () => {
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// src/ctMock.ts
|
|
2
|
-
import nock from "nock";
|
|
3
2
|
import express2 from "express";
|
|
4
3
|
import supertest from "supertest";
|
|
5
4
|
import morgan from "morgan";
|
|
5
|
+
import { setupServer } from "msw/node";
|
|
6
|
+
import { http, HttpResponse } from "msw";
|
|
6
7
|
|
|
7
8
|
// src/storage/abstract.ts
|
|
8
9
|
var AbstractStorage = class {
|
|
@@ -15,10 +16,12 @@ import assert from "assert";
|
|
|
15
16
|
var CommercetoolsError = class extends Error {
|
|
16
17
|
info;
|
|
17
18
|
statusCode;
|
|
19
|
+
errors;
|
|
18
20
|
constructor(info, statusCode = 400) {
|
|
19
21
|
super(info.message);
|
|
20
22
|
this.info = info;
|
|
21
23
|
this.statusCode = statusCode || 500;
|
|
24
|
+
this.errors = info.errors ?? [];
|
|
22
25
|
}
|
|
23
26
|
};
|
|
24
27
|
|
|
@@ -1218,6 +1221,16 @@ var OAuth2Store = class {
|
|
|
1218
1221
|
this.tokens.push(token);
|
|
1219
1222
|
return token;
|
|
1220
1223
|
}
|
|
1224
|
+
getCustomerToken(scope, customerId) {
|
|
1225
|
+
const token = {
|
|
1226
|
+
access_token: randomBytes(16).toString("base64"),
|
|
1227
|
+
token_type: "Bearer",
|
|
1228
|
+
expires_in: 172800,
|
|
1229
|
+
scope: scope ? `${scope} custome_id:${customerId}` : `customer_id: ${customerId}`
|
|
1230
|
+
};
|
|
1231
|
+
this.tokens.push(token);
|
|
1232
|
+
return token;
|
|
1233
|
+
}
|
|
1221
1234
|
validateToken(token) {
|
|
1222
1235
|
if (!this.validate)
|
|
1223
1236
|
return true;
|
|
@@ -1239,16 +1252,36 @@ var getBearerToken = (request) => {
|
|
|
1239
1252
|
return void 0;
|
|
1240
1253
|
};
|
|
1241
1254
|
|
|
1255
|
+
// src/lib/password.ts
|
|
1256
|
+
var hashPassword = (clearPassword) => Buffer.from(clearPassword).toString("base64");
|
|
1257
|
+
|
|
1242
1258
|
// src/oauth/server.ts
|
|
1243
1259
|
var OAuth2Server = class {
|
|
1244
1260
|
store;
|
|
1261
|
+
customerRepository;
|
|
1245
1262
|
constructor(options) {
|
|
1246
1263
|
this.store = new OAuth2Store(options.validate);
|
|
1247
1264
|
}
|
|
1265
|
+
setCustomerRepository(repository) {
|
|
1266
|
+
this.customerRepository = repository;
|
|
1267
|
+
}
|
|
1248
1268
|
createRouter() {
|
|
1249
1269
|
const router = express.Router();
|
|
1250
1270
|
router.use(bodyParser.urlencoded({ extended: true }));
|
|
1271
|
+
router.use(this.validateClientCredentials.bind(this));
|
|
1251
1272
|
router.post("/token", this.tokenHandler.bind(this));
|
|
1273
|
+
router.post(
|
|
1274
|
+
"/:projectKey/customers/token",
|
|
1275
|
+
this.customerTokenHandler.bind(this)
|
|
1276
|
+
);
|
|
1277
|
+
router.post(
|
|
1278
|
+
"/:projectKey/in-store/key=:storeKey/customers/token",
|
|
1279
|
+
this.inStoreCustomerTokenHandler.bind(this)
|
|
1280
|
+
);
|
|
1281
|
+
router.post(
|
|
1282
|
+
"/:projectKey/anonymous/token",
|
|
1283
|
+
this.anonymousTokenHandler.bind(this)
|
|
1284
|
+
);
|
|
1252
1285
|
return router;
|
|
1253
1286
|
}
|
|
1254
1287
|
createMiddleware() {
|
|
@@ -1279,7 +1312,7 @@ var OAuth2Server = class {
|
|
|
1279
1312
|
next();
|
|
1280
1313
|
};
|
|
1281
1314
|
}
|
|
1282
|
-
async
|
|
1315
|
+
async validateClientCredentials(request, response, next) {
|
|
1283
1316
|
const authHeader = request.header("Authorization");
|
|
1284
1317
|
if (!authHeader) {
|
|
1285
1318
|
return next(
|
|
@@ -1304,6 +1337,13 @@ var OAuth2Server = class {
|
|
|
1304
1337
|
)
|
|
1305
1338
|
);
|
|
1306
1339
|
}
|
|
1340
|
+
request.credentials = {
|
|
1341
|
+
clientId: credentials.name,
|
|
1342
|
+
clientSecret: credentials.pass
|
|
1343
|
+
};
|
|
1344
|
+
next();
|
|
1345
|
+
}
|
|
1346
|
+
async tokenHandler(request, response, next) {
|
|
1307
1347
|
const grantType = request.query.grant_type || request.body.grant_type;
|
|
1308
1348
|
if (!grantType) {
|
|
1309
1349
|
return next(
|
|
@@ -1318,8 +1358,15 @@ var OAuth2Server = class {
|
|
|
1318
1358
|
}
|
|
1319
1359
|
if (grantType === "client_credentials") {
|
|
1320
1360
|
const token = this.store.getClientToken(
|
|
1321
|
-
credentials.
|
|
1322
|
-
credentials.
|
|
1361
|
+
request.credentials.clientId,
|
|
1362
|
+
request.credentials.clientSecret,
|
|
1363
|
+
request.query.scope?.toString()
|
|
1364
|
+
);
|
|
1365
|
+
return response.status(200).send(token);
|
|
1366
|
+
} else if (grantType === "refresh_token") {
|
|
1367
|
+
const token = this.store.getClientToken(
|
|
1368
|
+
request.credentials.clientId,
|
|
1369
|
+
request.credentials.clientSecret,
|
|
1323
1370
|
request.query.scope?.toString()
|
|
1324
1371
|
);
|
|
1325
1372
|
return response.status(200).send(token);
|
|
@@ -1335,6 +1382,69 @@ var OAuth2Server = class {
|
|
|
1335
1382
|
);
|
|
1336
1383
|
}
|
|
1337
1384
|
}
|
|
1385
|
+
async customerTokenHandler(request, response, next) {
|
|
1386
|
+
const grantType = request.query.grant_type || request.body.grant_type;
|
|
1387
|
+
if (!grantType) {
|
|
1388
|
+
return next(
|
|
1389
|
+
new CommercetoolsError(
|
|
1390
|
+
{
|
|
1391
|
+
code: "invalid_request",
|
|
1392
|
+
message: "Missing required parameter: grant_type."
|
|
1393
|
+
},
|
|
1394
|
+
400
|
|
1395
|
+
)
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
if (grantType === "password") {
|
|
1399
|
+
const username = request.query.username || request.body.username;
|
|
1400
|
+
const password = hashPassword(
|
|
1401
|
+
request.query.password || request.body.password
|
|
1402
|
+
);
|
|
1403
|
+
const scope = request.query.scope?.toString() || request.body.scope?.toString();
|
|
1404
|
+
const result = this.customerRepository.query(
|
|
1405
|
+
{ projectKey: request.params.projectKey },
|
|
1406
|
+
{
|
|
1407
|
+
where: [`email = "${username}"`, `password = "${password}"`]
|
|
1408
|
+
}
|
|
1409
|
+
);
|
|
1410
|
+
if (result.count === 0) {
|
|
1411
|
+
return next(
|
|
1412
|
+
new CommercetoolsError(
|
|
1413
|
+
{
|
|
1414
|
+
code: "invalid_customer_account_credentials",
|
|
1415
|
+
message: "Customer account with the given credentials not found."
|
|
1416
|
+
},
|
|
1417
|
+
400
|
|
1418
|
+
)
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
const customer = result.results[0];
|
|
1422
|
+
const token = this.store.getCustomerToken(scope, customer.id);
|
|
1423
|
+
return response.status(200).send(token);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
async inStoreCustomerTokenHandler(request, response, next) {
|
|
1427
|
+
return next(
|
|
1428
|
+
new CommercetoolsError(
|
|
1429
|
+
{
|
|
1430
|
+
code: "invalid_client",
|
|
1431
|
+
message: "Not implemented yet in commercetools-mock"
|
|
1432
|
+
},
|
|
1433
|
+
401
|
|
1434
|
+
)
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
async anonymousTokenHandler(request, response, next) {
|
|
1438
|
+
return next(
|
|
1439
|
+
new CommercetoolsError(
|
|
1440
|
+
{
|
|
1441
|
+
code: "invalid_client",
|
|
1442
|
+
message: "Not implemented yet in commercetools-mock"
|
|
1443
|
+
},
|
|
1444
|
+
401
|
|
1445
|
+
)
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1338
1448
|
};
|
|
1339
1449
|
|
|
1340
1450
|
// src/projectAPI.ts
|
|
@@ -1378,19 +1488,19 @@ var ProjectAPI = class {
|
|
|
1378
1488
|
|
|
1379
1489
|
// src/lib/proxy.ts
|
|
1380
1490
|
var copyHeaders = (headers) => {
|
|
1381
|
-
const validHeaders = ["accept", "host", "authorization"];
|
|
1491
|
+
const validHeaders = ["accept", "host", "authorization", "content-type"];
|
|
1382
1492
|
const result = {};
|
|
1383
|
-
|
|
1493
|
+
for (const [key, value] of headers.entries()) {
|
|
1384
1494
|
if (validHeaders.includes(key.toLowerCase())) {
|
|
1385
1495
|
result[key] = value;
|
|
1386
1496
|
}
|
|
1387
|
-
}
|
|
1497
|
+
}
|
|
1388
1498
|
return result;
|
|
1389
1499
|
};
|
|
1390
1500
|
|
|
1391
1501
|
// src/constants.ts
|
|
1392
|
-
var DEFAULT_API_HOSTNAME =
|
|
1393
|
-
var DEFAULT_AUTH_HOSTNAME =
|
|
1502
|
+
var DEFAULT_API_HOSTNAME = "https://api.*.commercetools.com";
|
|
1503
|
+
var DEFAULT_AUTH_HOSTNAME = "https://auth.*.commercetools.com";
|
|
1394
1504
|
|
|
1395
1505
|
// src/repositories/helpers.ts
|
|
1396
1506
|
import { v4 as uuidv42 } from "uuid";
|
|
@@ -2811,11 +2921,29 @@ var CustomerRepository = class extends AbstractResourceRepository {
|
|
|
2811
2921
|
return "customer";
|
|
2812
2922
|
}
|
|
2813
2923
|
create(context, draft) {
|
|
2924
|
+
const results = this._storage.query(context.projectKey, this.getTypeId(), {
|
|
2925
|
+
where: [`email="${draft.email.toLocaleLowerCase()}"`]
|
|
2926
|
+
});
|
|
2927
|
+
if (results.count > 0) {
|
|
2928
|
+
throw new CommercetoolsError({
|
|
2929
|
+
code: "CustomerAlreadyExists",
|
|
2930
|
+
statusCode: 400,
|
|
2931
|
+
message: "There is already an existing customer with the provided email.",
|
|
2932
|
+
errors: [
|
|
2933
|
+
{
|
|
2934
|
+
code: "DuplicateField",
|
|
2935
|
+
message: `Customer with email '${draft.email}' already exists.`,
|
|
2936
|
+
duplicateValue: draft.email,
|
|
2937
|
+
field: "email"
|
|
2938
|
+
}
|
|
2939
|
+
]
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2814
2942
|
const resource = {
|
|
2815
2943
|
...getBaseResourceProperties(),
|
|
2816
2944
|
authenticationMode: draft.authenticationMode || "Password",
|
|
2817
|
-
email: draft.email,
|
|
2818
|
-
password: draft.password ?
|
|
2945
|
+
email: draft.email.toLowerCase(),
|
|
2946
|
+
password: draft.password ? hashPassword(draft.password) : void 0,
|
|
2819
2947
|
isEmailVerified: draft.isEmailVerified || false,
|
|
2820
2948
|
addresses: []
|
|
2821
2949
|
};
|
|
@@ -2853,7 +2981,7 @@ var CustomerRepository = class extends AbstractResourceRepository {
|
|
|
2853
2981
|
return;
|
|
2854
2982
|
}
|
|
2855
2983
|
if (authMode === "Password") {
|
|
2856
|
-
resource.password = password ?
|
|
2984
|
+
resource.password = password ? hashPassword(password) : void 0;
|
|
2857
2985
|
return;
|
|
2858
2986
|
}
|
|
2859
2987
|
throw new CommercetoolsError(
|
|
@@ -5983,7 +6111,7 @@ var MyCustomerService = class extends AbstractService {
|
|
|
5983
6111
|
}
|
|
5984
6112
|
signIn(request, response) {
|
|
5985
6113
|
const { email, password } = request.body;
|
|
5986
|
-
const encodedPassword =
|
|
6114
|
+
const encodedPassword = hashPassword(password);
|
|
5987
6115
|
const result = this.repository.query(getRepositoryContext(request), {
|
|
5988
6116
|
where: [`email = "${email}"`, `password = "${encodedPassword}"`]
|
|
5989
6117
|
});
|
|
@@ -6365,12 +6493,13 @@ var DEFAULT_OPTIONS = {
|
|
|
6365
6493
|
authHost: DEFAULT_AUTH_HOSTNAME,
|
|
6366
6494
|
silent: false
|
|
6367
6495
|
};
|
|
6496
|
+
var _globalListeners = [];
|
|
6368
6497
|
var CommercetoolsMock = class {
|
|
6369
6498
|
app;
|
|
6370
6499
|
options;
|
|
6371
6500
|
_storage;
|
|
6372
6501
|
_oauth2;
|
|
6373
|
-
|
|
6502
|
+
_mswServer = void 0;
|
|
6374
6503
|
_services;
|
|
6375
6504
|
_repositories;
|
|
6376
6505
|
_projectService;
|
|
@@ -6387,16 +6516,15 @@ var CommercetoolsMock = class {
|
|
|
6387
6516
|
this.app = this.createApp({ silent: this.options.silent });
|
|
6388
6517
|
}
|
|
6389
6518
|
start() {
|
|
6390
|
-
this.
|
|
6391
|
-
this.
|
|
6519
|
+
this.clear();
|
|
6520
|
+
this.startServer();
|
|
6392
6521
|
}
|
|
6393
6522
|
stop() {
|
|
6394
|
-
this.
|
|
6395
|
-
this.
|
|
6396
|
-
this._nockScopes.api?.persist(false);
|
|
6397
|
-
this._nockScopes.api = void 0;
|
|
6523
|
+
this._mswServer?.close();
|
|
6524
|
+
this._mswServer = void 0;
|
|
6398
6525
|
}
|
|
6399
6526
|
clear() {
|
|
6527
|
+
this._mswServer?.resetHandlers();
|
|
6400
6528
|
this._storage.clear();
|
|
6401
6529
|
}
|
|
6402
6530
|
project(projectKey) {
|
|
@@ -6420,6 +6548,7 @@ var CommercetoolsMock = class {
|
|
|
6420
6548
|
}
|
|
6421
6549
|
createApp(options) {
|
|
6422
6550
|
this._repositories = createRepositories(this._storage);
|
|
6551
|
+
this._oauth2.setCustomerRepository(this._repositories.customer);
|
|
6423
6552
|
const app = express2();
|
|
6424
6553
|
const projectRouter = express2.Router({ mergeParams: true });
|
|
6425
6554
|
projectRouter.use(express2.json());
|
|
@@ -6445,6 +6574,13 @@ var CommercetoolsMock = class {
|
|
|
6445
6574
|
);
|
|
6446
6575
|
app.use((err, req, resp, next) => {
|
|
6447
6576
|
if (err instanceof CommercetoolsError) {
|
|
6577
|
+
if (err.errors?.length > 0) {
|
|
6578
|
+
return resp.status(err.statusCode).send({
|
|
6579
|
+
statusCode: err.statusCode,
|
|
6580
|
+
message: err.message,
|
|
6581
|
+
errors: err.errors
|
|
6582
|
+
});
|
|
6583
|
+
}
|
|
6448
6584
|
return resp.status(err.statusCode).send({
|
|
6449
6585
|
statusCode: err.statusCode,
|
|
6450
6586
|
message: err.message,
|
|
@@ -6459,25 +6595,65 @@ var CommercetoolsMock = class {
|
|
|
6459
6595
|
});
|
|
6460
6596
|
return app;
|
|
6461
6597
|
}
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
}
|
|
6471
|
-
const response = await supertest(app).delete(uri).set(copyHeaders(this.req.headers)).send(body);
|
|
6472
|
-
return [response.status, response.body];
|
|
6473
|
-
});
|
|
6474
|
-
}
|
|
6475
|
-
mockAuthHost() {
|
|
6598
|
+
startServer() {
|
|
6599
|
+
if (_globalListeners.length > 0) {
|
|
6600
|
+
if (this._mswServer !== void 0) {
|
|
6601
|
+
throw new Error("Server already started");
|
|
6602
|
+
} else {
|
|
6603
|
+
console.warn("Server wasn't stopped properly, clearing");
|
|
6604
|
+
_globalListeners.forEach((listener) => listener.close());
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6476
6607
|
const app = this.app;
|
|
6477
|
-
this.
|
|
6478
|
-
|
|
6479
|
-
|
|
6608
|
+
this._mswServer = setupServer(
|
|
6609
|
+
http.post(`${this.options.authHost}/oauth/*`, async ({ request }) => {
|
|
6610
|
+
const text = await request.text();
|
|
6611
|
+
const url = new URL(request.url);
|
|
6612
|
+
const res = await supertest(app).post(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(text);
|
|
6613
|
+
return new HttpResponse(res.text, {
|
|
6614
|
+
status: res.status,
|
|
6615
|
+
headers: res.headers
|
|
6616
|
+
});
|
|
6617
|
+
}),
|
|
6618
|
+
http.get(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6619
|
+
const body = await request.text();
|
|
6620
|
+
const url = new URL(request.url);
|
|
6621
|
+
const res = await supertest(app).get(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6622
|
+
return new HttpResponse(res.text, {
|
|
6623
|
+
status: res.status,
|
|
6624
|
+
headers: res.headers
|
|
6625
|
+
});
|
|
6626
|
+
}),
|
|
6627
|
+
http.post(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6628
|
+
const body = await request.text();
|
|
6629
|
+
const url = new URL(request.url);
|
|
6630
|
+
const res = await supertest(app).post(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6631
|
+
return new HttpResponse(res.text, {
|
|
6632
|
+
status: res.status,
|
|
6633
|
+
headers: res.headers
|
|
6634
|
+
});
|
|
6635
|
+
}),
|
|
6636
|
+
http.delete(`${this.options.apiHost}/*`, async ({ request }) => {
|
|
6637
|
+
const body = await request.text();
|
|
6638
|
+
const url = new URL(request.url);
|
|
6639
|
+
const res = await supertest(app).delete(url.pathname + "?" + url.searchParams.toString()).set(copyHeaders(request.headers)).send(body);
|
|
6640
|
+
return new HttpResponse(res.text, {
|
|
6641
|
+
status: res.status,
|
|
6642
|
+
headers: res.headers
|
|
6643
|
+
});
|
|
6644
|
+
})
|
|
6645
|
+
);
|
|
6646
|
+
this._mswServer.listen({
|
|
6647
|
+
// We need to allow requests done by supertest
|
|
6648
|
+
onUnhandledRequest: (request, print) => {
|
|
6649
|
+
const url = new URL(request.url);
|
|
6650
|
+
if (url.hostname === "127.0.0.1") {
|
|
6651
|
+
return;
|
|
6652
|
+
}
|
|
6653
|
+
print.error();
|
|
6654
|
+
}
|
|
6480
6655
|
});
|
|
6656
|
+
_globalListeners.push(this._mswServer);
|
|
6481
6657
|
}
|
|
6482
6658
|
};
|
|
6483
6659
|
export {
|