@salesforce/pwa-kit-runtime 3.5.0-alpha.0 → 3.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/pwa-kit-runtime",
|
|
3
|
-
"version": "3.5.0-alpha.
|
|
3
|
+
"version": "3.5.0-alpha.1",
|
|
4
4
|
"description": "The PWAKit Runtime",
|
|
5
5
|
"homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/pwa-kit-runtime#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"cross-env": "^5.2.1",
|
|
38
38
|
"express": "^4.19.2",
|
|
39
39
|
"header-case": "1.0.1",
|
|
40
|
-
"http-proxy-middleware": "0.
|
|
40
|
+
"http-proxy-middleware": "^2.0.6",
|
|
41
41
|
"merge-descriptors": "^1.0.1",
|
|
42
42
|
"morgan": "^1.10.0",
|
|
43
43
|
"semver": "^7.5.2",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@loadable/component": "^5.15.3",
|
|
49
|
-
"@salesforce/pwa-kit-dev": "3.5.0-alpha.
|
|
49
|
+
"@salesforce/pwa-kit-dev": "3.5.0-alpha.1",
|
|
50
50
|
"@serverless/event-mocks": "^1.1.1",
|
|
51
51
|
"aws-lambda-mock-context": "^3.2.1",
|
|
52
52
|
"fs-extra": "^11.1.1",
|
|
53
|
-
"internal-lib-build": "3.5.0-alpha.
|
|
53
|
+
"internal-lib-build": "3.5.0-alpha.1",
|
|
54
54
|
"nock": "^13.3.0",
|
|
55
55
|
"nodemon": "^2.0.22",
|
|
56
56
|
"sinon": "^13.0.2",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"supertest": "^4.0.2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@salesforce/pwa-kit-dev": "3.5.0-alpha.
|
|
61
|
+
"@salesforce/pwa-kit-dev": "3.5.0-alpha.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"@salesforce/pwa-kit-dev": {
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"directory": "dist"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "2df9f520e6de6585926743fb72bf8e77d515dcf1"
|
|
76
76
|
}
|
|
@@ -21,9 +21,11 @@ var _express2 = require("./express");
|
|
|
21
21
|
var _http = _interopRequireDefault(require("http"));
|
|
22
22
|
var _https = _interopRequireDefault(require("https"));
|
|
23
23
|
var _ssrShared = require("../../utils/ssr-shared");
|
|
24
|
+
var _configureProxy = require("../../utils/ssr-server/configure-proxy");
|
|
24
25
|
var _awsServerlessExpress = _interopRequireDefault(require("aws-serverless-express"));
|
|
25
26
|
var _morgan = _interopRequireDefault(require("morgan"));
|
|
26
27
|
var _morganStream = require("../../utils/morgan-stream");
|
|
28
|
+
var _httpProxyMiddleware = require("http-proxy-middleware");
|
|
27
29
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
30
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
29
31
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -94,7 +96,14 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
94
96
|
strictSSL: true,
|
|
95
97
|
mobify: undefined,
|
|
96
98
|
// Toggle cookies being passed and set
|
|
97
|
-
localAllowCookies: false
|
|
99
|
+
localAllowCookies: false,
|
|
100
|
+
// Toggle for setting up the custom SLAS private client secret handler
|
|
101
|
+
useSLASPrivateClient: false,
|
|
102
|
+
// A regex for identifying which SLAS endpoints the custom SLAS private
|
|
103
|
+
// client secret handler will inject an Authorization header.
|
|
104
|
+
// Do not modify unless a project wants to customize additional SLAS
|
|
105
|
+
// endpoints that we currently do not support (ie. /oauth2/passwordless/token)
|
|
106
|
+
applySLASPrivateClientToEndpoints: /\/oauth2\/token/
|
|
98
107
|
};
|
|
99
108
|
options = _extends({}, defaults, options);
|
|
100
109
|
(0, _ssrServer.setQuiet)(options.quiet || process.env.SSR_QUIET);
|
|
@@ -121,6 +130,10 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
121
130
|
// Toggle cookies being passed and set. Can be overridden locally,
|
|
122
131
|
// always uses MRT_ALLOW_COOKIES env remotely
|
|
123
132
|
options.allowCookies = this._getAllowCookies(options);
|
|
133
|
+
|
|
134
|
+
// For test only – configure the SLAS private client secret proxy endpoint
|
|
135
|
+
options.slasHostName = this._getSlasEndpoint(options);
|
|
136
|
+
options.slasTarget = options.slasTarget || `https://${options.slasHostName}`;
|
|
124
137
|
return options;
|
|
125
138
|
},
|
|
126
139
|
/**
|
|
@@ -157,6 +170,15 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
157
170
|
_strictSSL(options) {
|
|
158
171
|
return true;
|
|
159
172
|
},
|
|
173
|
+
/**
|
|
174
|
+
* @private
|
|
175
|
+
*/
|
|
176
|
+
_getSlasEndpoint(options) {
|
|
177
|
+
var _options$mobify, _options$mobify$app, _options$mobify$app$c, _options$mobify$app$c2;
|
|
178
|
+
if (!options.useSLASPrivateClient) return undefined;
|
|
179
|
+
const shortCode = (_options$mobify = options.mobify) === null || _options$mobify === void 0 ? void 0 : (_options$mobify$app = _options$mobify.app) === null || _options$mobify$app === void 0 ? void 0 : (_options$mobify$app$c = _options$mobify$app.commerceAPI) === null || _options$mobify$app$c === void 0 ? void 0 : (_options$mobify$app$c2 = _options$mobify$app$c.parameters) === null || _options$mobify$app$c2 === void 0 ? void 0 : _options$mobify$app$c2.shortCode;
|
|
180
|
+
return `${shortCode}.api.commercecloud.salesforce.com`;
|
|
181
|
+
},
|
|
160
182
|
/**
|
|
161
183
|
* @private
|
|
162
184
|
*/
|
|
@@ -245,6 +267,7 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
245
267
|
this._setupMetricsFlushing(app);
|
|
246
268
|
this._setupHealthcheck(app);
|
|
247
269
|
this._setupProxying(app, options);
|
|
270
|
+
this._setupSlasPrivateClientProxy(app, options);
|
|
248
271
|
|
|
249
272
|
// Beyond this point, we know that this is not a proxy request
|
|
250
273
|
// and not a bundle request, so we can apply specific
|
|
@@ -508,6 +531,61 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
508
531
|
});
|
|
509
532
|
});
|
|
510
533
|
},
|
|
534
|
+
/**
|
|
535
|
+
* @private
|
|
536
|
+
*/
|
|
537
|
+
_handleMissingSlasPrivateEnvVar(app) {
|
|
538
|
+
app.use(_constants.SLAS_CUSTOM_PROXY_PATH, (_, res) => {
|
|
539
|
+
return res.status(501).json({
|
|
540
|
+
message: 'Environment variable PWA_KIT_SLAS_CLIENT_SECRET not set: Please set this environment variable to proceed.'
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
},
|
|
544
|
+
/**
|
|
545
|
+
* @private
|
|
546
|
+
*/
|
|
547
|
+
_setupSlasPrivateClientProxy(app, options) {
|
|
548
|
+
var _options$mobify2, _options$mobify2$app, _options$mobify2$app$, _options$mobify2$app$2;
|
|
549
|
+
if (!options.useSLASPrivateClient) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
(0, _ssrServer.localDevLog)(`Proxying ${_constants.SLAS_CUSTOM_PROXY_PATH} to ${options.slasTarget}`);
|
|
553
|
+
const clientId = (_options$mobify2 = options.mobify) === null || _options$mobify2 === void 0 ? void 0 : (_options$mobify2$app = _options$mobify2.app) === null || _options$mobify2$app === void 0 ? void 0 : (_options$mobify2$app$ = _options$mobify2$app.commerceAPI) === null || _options$mobify2$app$ === void 0 ? void 0 : (_options$mobify2$app$2 = _options$mobify2$app$.parameters) === null || _options$mobify2$app$2 === void 0 ? void 0 : _options$mobify2$app$2.clientId;
|
|
554
|
+
const clientSecret = process.env.PWA_KIT_SLAS_CLIENT_SECRET;
|
|
555
|
+
if (!clientSecret) {
|
|
556
|
+
this._handleMissingSlasPrivateEnvVar(app);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const encodedSlasCredentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
560
|
+
app.use(_constants.SLAS_CUSTOM_PROXY_PATH, (0, _httpProxyMiddleware.createProxyMiddleware)({
|
|
561
|
+
target: options.slasTarget,
|
|
562
|
+
changeOrigin: true,
|
|
563
|
+
pathRewrite: {
|
|
564
|
+
[_constants.SLAS_CUSTOM_PROXY_PATH]: ''
|
|
565
|
+
},
|
|
566
|
+
onProxyReq: (proxyRequest, incomingRequest) => {
|
|
567
|
+
var _incomingRequest$path;
|
|
568
|
+
(0, _configureProxy.applyProxyRequestHeaders)({
|
|
569
|
+
proxyRequest,
|
|
570
|
+
incomingRequest,
|
|
571
|
+
proxyPath: _constants.SLAS_CUSTOM_PROXY_PATH,
|
|
572
|
+
targetHost: options.slasHostName,
|
|
573
|
+
targetProtocol: 'https'
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// We pattern match and add client secrets only to endpoints that
|
|
577
|
+
// match the regex specified by options.applySLASPrivateClientToEndpoints.
|
|
578
|
+
// By default, this regex matches only calls to SLAS /oauth2/token
|
|
579
|
+
// (see option defaults at the top of this file).
|
|
580
|
+
// Other SLAS endpoints, ie. SLAS authenticate (/oauth2/login) and
|
|
581
|
+
// SLAS logout (/oauth2/logout), use the Authorization header for a different
|
|
582
|
+
// purpose so we don't want to overwrite the header for those calls.
|
|
583
|
+
if ((_incomingRequest$path = incomingRequest.path) !== null && _incomingRequest$path !== void 0 && _incomingRequest$path.match(options.applySLASPrivateClientToEndpoints)) {
|
|
584
|
+
proxyRequest.setHeader('Authorization', `Basic ${encodedSlasCredentials}`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}));
|
|
588
|
+
},
|
|
511
589
|
/**
|
|
512
590
|
* @private
|
|
513
591
|
*/
|
|
@@ -21,4 +21,16 @@ describe('the once function', () => {
|
|
|
21
21
|
expect(fn.mock.calls).toHaveLength(1);
|
|
22
22
|
expect(v1).toBe(v2); // The exact same instance
|
|
23
23
|
});
|
|
24
|
+
});
|
|
25
|
+
describe('remote server factory test coverage', () => {
|
|
26
|
+
test('getSlasEndpoint returns undefined if useSLASPrivateClient is false', () => {
|
|
27
|
+
const endpoint = _buildRemoteServer.RemoteServerFactory._getSlasEndpoint({});
|
|
28
|
+
expect(endpoint).toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
test('getSlasEndpoint returns endpoint if useSLASPrivateClient is true', () => {
|
|
31
|
+
const endpoint = _buildRemoteServer.RemoteServerFactory._getSlasEndpoint({
|
|
32
|
+
useSLASPrivateClient: true
|
|
33
|
+
});
|
|
34
|
+
expect(endpoint).toBeDefined();
|
|
35
|
+
});
|
|
24
36
|
});
|
package/ssr/server/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
|
|
6
|
+
exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SLAS_CUSTOM_PROXY_PATH = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
|
|
7
7
|
/*
|
|
8
8
|
* Copyright (c) 2021, salesforce.com, inc.
|
|
9
9
|
* All rights reserved.
|
|
@@ -24,4 +24,5 @@ const SET_COOKIE = exports.SET_COOKIE = 'set-cookie';
|
|
|
24
24
|
const CACHE_CONTROL = exports.CACHE_CONTROL = 'cache-control';
|
|
25
25
|
const NO_CACHE = exports.NO_CACHE = 'max-age=0, nocache, nostore, must-revalidate';
|
|
26
26
|
const CONTENT_SECURITY_POLICY = exports.CONTENT_SECURITY_POLICY = 'content-security-policy';
|
|
27
|
-
const STRICT_TRANSPORT_SECURITY = exports.STRICT_TRANSPORT_SECURITY = 'strict-transport-security';
|
|
27
|
+
const STRICT_TRANSPORT_SECURITY = exports.STRICT_TRANSPORT_SECURITY = 'strict-transport-security';
|
|
28
|
+
const SLAS_CUSTOM_PROXY_PATH = exports.SLAS_CUSTOM_PROXY_PATH = '/mobify/scapi/shopper/auth';
|
|
@@ -8,16 +8,19 @@ var _path = _interopRequireDefault(require("path"));
|
|
|
8
8
|
var _sinon = _interopRequireDefault(require("sinon"));
|
|
9
9
|
var _superagent = _interopRequireDefault(require("superagent"));
|
|
10
10
|
var _supertest = _interopRequireDefault(require("supertest"));
|
|
11
|
+
var _express = _interopRequireDefault(require("express"));
|
|
11
12
|
var _ssrCache = require("../../utils/ssr-cache");
|
|
12
13
|
var _ssrServer = require("../../utils/ssr-server");
|
|
13
14
|
var ssrServerUtils = _interopRequireWildcard(require("../../utils/ssr-server/utils"));
|
|
14
15
|
var _buildRemoteServer = require("./build-remote-server");
|
|
15
16
|
var _constants = require("./constants");
|
|
16
|
-
var
|
|
17
|
+
var _express2 = require("./express");
|
|
17
18
|
var _crypto = require("crypto");
|
|
18
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
19
20
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
20
21
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
22
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
23
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
21
24
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
22
25
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
23
26
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -78,7 +81,8 @@ const opts = (overrides = {}) => {
|
|
|
78
81
|
https: httpsAgent
|
|
79
82
|
},
|
|
80
83
|
defaultCacheTimeSeconds: 123,
|
|
81
|
-
enableLegacyRemoteProxying: false
|
|
84
|
+
enableLegacyRemoteProxying: false,
|
|
85
|
+
useSLASPrivateClient: false
|
|
82
86
|
};
|
|
83
87
|
return _objectSpread(_objectSpread({}, defaults), overrides);
|
|
84
88
|
};
|
|
@@ -203,11 +207,11 @@ describe('SSRServer operation', () => {
|
|
|
203
207
|
app._requestMonitor._responseFinished(response1);
|
|
204
208
|
expect(app._requestMonitor._pendingResponses.ids).toEqual([]);
|
|
205
209
|
const promise1 = app._requestMonitor._waitForResponses();
|
|
206
|
-
expect(promise1).toBe(
|
|
210
|
+
expect(promise1).toBe(_express2.RESOLVED_PROMISE);
|
|
207
211
|
app._requestMonitor._responseStarted(response1);
|
|
208
212
|
expect(app._requestMonitor._pendingResponses.ids).toEqual([1]);
|
|
209
213
|
const promise2 = app._requestMonitor._waitForResponses();
|
|
210
|
-
expect(promise2).not.toBe(
|
|
214
|
+
expect(promise2).not.toBe(_express2.RESOLVED_PROMISE);
|
|
211
215
|
app._requestMonitor._responseStarted(response2);
|
|
212
216
|
expect(app._requestMonitor._pendingResponses.ids).toEqual([1, 2]);
|
|
213
217
|
const promise3 = app._requestMonitor._waitForResponses();
|
|
@@ -389,7 +393,7 @@ describe('SSRServer operation', () => {
|
|
|
389
393
|
test('should support redirects to bundle assets', () => {
|
|
390
394
|
const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
|
|
391
395
|
const route = (req, res) => {
|
|
392
|
-
(0,
|
|
396
|
+
(0, _express2.respondFromBundle)({
|
|
393
397
|
req,
|
|
394
398
|
res
|
|
395
399
|
});
|
|
@@ -517,12 +521,12 @@ describe('SSRServer persistent caching', () => {
|
|
|
517
521
|
key: keyFromURL(req.url)
|
|
518
522
|
};
|
|
519
523
|
const shouldCacheResponse = (req, res) => res.statusCode >= 200 && res.statusCode < 300;
|
|
520
|
-
return Promise.resolve().then(() => (0,
|
|
524
|
+
return Promise.resolve().then(() => (0, _express2.getResponseFromCache)(cacheArgs)).then(entry => {
|
|
521
525
|
if (entry.found) {
|
|
522
|
-
(0,
|
|
526
|
+
(0, _express2.sendCachedResponse)(entry);
|
|
523
527
|
} else {
|
|
524
528
|
if (shouldCache) {
|
|
525
|
-
(0,
|
|
529
|
+
(0, _express2.cacheResponseWhenDone)(_objectSpread({
|
|
526
530
|
shouldCacheResponse
|
|
527
531
|
}, cacheArgs));
|
|
528
532
|
}
|
|
@@ -727,7 +731,7 @@ describe('SSRServer persistent caching', () => {
|
|
|
727
731
|
});
|
|
728
732
|
});
|
|
729
733
|
test('Try to send non-cached response', () => {
|
|
730
|
-
expect(() => (0,
|
|
734
|
+
expect(() => (0, _express2.sendCachedResponse)(new _ssrServer.CachedResponse({}))).toThrow('non-cached');
|
|
731
735
|
});
|
|
732
736
|
});
|
|
733
737
|
describe('generateCacheKey', () => {
|
|
@@ -742,41 +746,41 @@ describe('generateCacheKey', () => {
|
|
|
742
746
|
}, overrides);
|
|
743
747
|
};
|
|
744
748
|
test('returns expected results', () => {
|
|
745
|
-
expect((0,
|
|
749
|
+
expect((0, _express2.generateCacheKey)(mockRequest({
|
|
746
750
|
url: '/test/1?id=abc'
|
|
747
751
|
})).indexOf('/test/1')).toBe(0);
|
|
748
752
|
});
|
|
749
753
|
test('path affects key', () => {
|
|
750
|
-
const result1 = (0,
|
|
754
|
+
const result1 = (0, _express2.generateCacheKey)(mockRequest({
|
|
751
755
|
url: '/test2a/'
|
|
752
756
|
}));
|
|
753
|
-
expect((0,
|
|
757
|
+
expect((0, _express2.generateCacheKey)(mockRequest({
|
|
754
758
|
url: '/testab/'
|
|
755
759
|
}))).not.toEqual(result1);
|
|
756
760
|
});
|
|
757
761
|
test('query affects key', () => {
|
|
758
|
-
const result1 = (0,
|
|
762
|
+
const result1 = (0, _express2.generateCacheKey)(mockRequest({
|
|
759
763
|
url: '/test3?a=1'
|
|
760
764
|
}));
|
|
761
|
-
expect((0,
|
|
765
|
+
expect((0, _express2.generateCacheKey)(mockRequest({
|
|
762
766
|
url: '/test3?a=2'
|
|
763
767
|
}))).not.toEqual(result1);
|
|
764
768
|
});
|
|
765
769
|
test('request class affects key', () => {
|
|
766
|
-
const result1 = (0,
|
|
770
|
+
const result1 = (0, _express2.generateCacheKey)(mockRequest());
|
|
767
771
|
const request2 = mockRequest({
|
|
768
772
|
headers: {
|
|
769
773
|
'x-mobify-request-class': 'bot'
|
|
770
774
|
}
|
|
771
775
|
});
|
|
772
|
-
expect((0,
|
|
773
|
-
expect((0,
|
|
776
|
+
expect((0, _express2.generateCacheKey)(request2)).not.toEqual(result1);
|
|
777
|
+
expect((0, _express2.generateCacheKey)(request2, {
|
|
774
778
|
ignoreRequestClass: true
|
|
775
779
|
})).toEqual(result1);
|
|
776
780
|
});
|
|
777
781
|
test('extras affect key', () => {
|
|
778
|
-
const result1 = (0,
|
|
779
|
-
expect((0,
|
|
782
|
+
const result1 = (0, _express2.generateCacheKey)(mockRequest());
|
|
783
|
+
expect((0, _express2.generateCacheKey)(mockRequest(), {
|
|
780
784
|
extras: ['123']
|
|
781
785
|
})).not.toEqual(result1);
|
|
782
786
|
});
|
|
@@ -836,10 +840,10 @@ describe('getRuntime', () => {
|
|
|
836
840
|
expectedRuntime
|
|
837
841
|
}) => {
|
|
838
842
|
process.env = _objectSpread(_objectSpread({}, process.env), env);
|
|
839
|
-
expect((0,
|
|
843
|
+
expect((0, _express2.getRuntime)()).toMatchObject(matchExceptFunctionValues(expectedRuntime));
|
|
840
844
|
});
|
|
841
845
|
test('should return a remote/development runtime bound to the correct context', () => {
|
|
842
|
-
const mockDevRuntime = (0,
|
|
846
|
+
const mockDevRuntime = (0, _express2.getRuntime)();
|
|
843
847
|
const func = mockDevRuntime.returnMyName;
|
|
844
848
|
expect(func()).toBe(MockDevServerFactory.name);
|
|
845
849
|
});
|
|
@@ -873,4 +877,81 @@ describe('DevServer middleware', () => {
|
|
|
873
877
|
}));
|
|
874
878
|
expect(warn.mock.calls).toEqual([['The SSR Server has _strictSSL turned off for https requests']]);
|
|
875
879
|
});
|
|
880
|
+
});
|
|
881
|
+
describe('SLAS private client proxy', () => {
|
|
882
|
+
const savedEnvironment = _extends({}, process.env);
|
|
883
|
+
let proxyApp;
|
|
884
|
+
const proxyPort = 12345;
|
|
885
|
+
const proxyPath = '/responseHeaders';
|
|
886
|
+
const slasTarget = `http://localhost:${proxyPort}${proxyPath}`;
|
|
887
|
+
beforeAll(() => {
|
|
888
|
+
// by setting slasTarget, rather than forwarding the request to SLAS,
|
|
889
|
+
// we send the proxy request here so we can return the request headers
|
|
890
|
+
proxyApp = (0, _express.default)();
|
|
891
|
+
proxyApp.use(proxyPath, (req, res) => {
|
|
892
|
+
res.send(req.headers);
|
|
893
|
+
});
|
|
894
|
+
proxyApp.listen(proxyPort);
|
|
895
|
+
});
|
|
896
|
+
afterEach(() => {
|
|
897
|
+
process.env = savedEnvironment;
|
|
898
|
+
});
|
|
899
|
+
afterAll(() => {
|
|
900
|
+
proxyApp.close();
|
|
901
|
+
});
|
|
902
|
+
test('should not create proxy by default', () => {
|
|
903
|
+
const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
|
|
904
|
+
return (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth').expect(404);
|
|
905
|
+
});
|
|
906
|
+
test('should return HTTP 501 if PWA_KIT_SLAS_CLIENT_SECRET env var not set', () => {
|
|
907
|
+
const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
|
|
908
|
+
useSLASPrivateClient: true
|
|
909
|
+
}));
|
|
910
|
+
return (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth').expect(501);
|
|
911
|
+
});
|
|
912
|
+
test('does not insert client secret if request not for /oauth2/token', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
913
|
+
process.env.PWA_KIT_SLAS_CLIENT_SECRET = 'a secret';
|
|
914
|
+
const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
|
|
915
|
+
mobify: {
|
|
916
|
+
app: {
|
|
917
|
+
commerceAPI: {
|
|
918
|
+
parameters: {
|
|
919
|
+
clientId: 'clientId',
|
|
920
|
+
shortCode: 'shortCode'
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
useSLASPrivateClient: true,
|
|
926
|
+
slasTarget: slasTarget
|
|
927
|
+
}));
|
|
928
|
+
return yield (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth/somePath').then(response => {
|
|
929
|
+
expect(response.body.authorization).toBeUndefined();
|
|
930
|
+
expect(response.body.host).toBe('shortCode.api.commercecloud.salesforce.com');
|
|
931
|
+
expect(response.body['x-mobify']).toBe('true');
|
|
932
|
+
});
|
|
933
|
+
}), 15000);
|
|
934
|
+
test('inserts client secret if request is for /oauth2/token', /*#__PURE__*/_asyncToGenerator(function* () {
|
|
935
|
+
process.env.PWA_KIT_SLAS_CLIENT_SECRET = 'a secret';
|
|
936
|
+
const encodedCredentials = Buffer.from('clientId:a secret').toString('base64');
|
|
937
|
+
const app = _buildRemoteServer.RemoteServerFactory._createApp(opts({
|
|
938
|
+
mobify: {
|
|
939
|
+
app: {
|
|
940
|
+
commerceAPI: {
|
|
941
|
+
parameters: {
|
|
942
|
+
clientId: 'clientId',
|
|
943
|
+
shortCode: 'shortCode'
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
useSLASPrivateClient: true,
|
|
949
|
+
slasTarget: slasTarget
|
|
950
|
+
}));
|
|
951
|
+
return yield (0, _supertest.default)(app).get('/mobify/scapi/shopper/auth/oauth2/token').then(response => {
|
|
952
|
+
expect(response.body.authorization).toBe(`Basic ${encodedCredentials}`);
|
|
953
|
+
expect(response.body.host).toBe('shortCode.api.commercecloud.salesforce.com');
|
|
954
|
+
expect(response.body['x-mobify']).toBe('true');
|
|
955
|
+
});
|
|
956
|
+
}), 15000);
|
|
876
957
|
});
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.configureProxyConfigs = exports.configureProxy = exports.ALLOWED_CACHING_PROXY_REQUEST_METHODS = void 0;
|
|
7
|
-
var _httpProxyMiddleware =
|
|
6
|
+
exports.configureProxyConfigs = exports.configureProxy = exports.applyProxyRequestHeaders = exports.ALLOWED_CACHING_PROXY_REQUEST_METHODS = void 0;
|
|
7
|
+
var _httpProxyMiddleware = require("http-proxy-middleware");
|
|
8
8
|
var _ssrProxying = require("../ssr-proxying");
|
|
9
9
|
var _ssrShared = require("../ssr-shared");
|
|
10
10
|
var _processExpressResponse = require("./process-express-response");
|
|
11
11
|
var _utils = require("./utils");
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
12
|
/*
|
|
14
13
|
* Copyright (c) 2022, Salesforce, Inc.
|
|
15
14
|
* All rights reserved.
|
|
@@ -29,6 +28,73 @@ const ALLOWED_CACHING_PROXY_REQUEST_METHODS = exports.ALLOWED_CACHING_PROXY_REQU
|
|
|
29
28
|
*/
|
|
30
29
|
const generalProxyPathRE = /^\/mobify\/proxy\/([^/]+)(\/.*)$/;
|
|
31
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Apply proxy headers to a request that is being proxied.
|
|
33
|
+
*
|
|
34
|
+
* This function is intended to be called from within a proxy's
|
|
35
|
+
* onProxyReq method.
|
|
36
|
+
*
|
|
37
|
+
* For more details on the headers being applied,
|
|
38
|
+
* see ssr-proxying.js rewriteProxyRequestHeaders method
|
|
39
|
+
* @private
|
|
40
|
+
* @function
|
|
41
|
+
* @param proxyRequest {http.ClientRequest} the request that will be
|
|
42
|
+
* sent to the target host
|
|
43
|
+
* @param incomingRequest {http.IncomingMessage} the request made to
|
|
44
|
+
* this Express app that prompted the proxying
|
|
45
|
+
* @param caching {Boolean} true for a caching proxy, false for a standard proxy
|
|
46
|
+
* @param logging {Boolean} true to log operations
|
|
47
|
+
* @param proxyPath {String} the path being proxied (e.g. /mobify/proxy/base/
|
|
48
|
+
* or /mobify/caching/base/)
|
|
49
|
+
* @param targetHost {String} the target hostname (host+port)
|
|
50
|
+
* @param targetProtocol {String} the protocol to use to make requests to
|
|
51
|
+
* the target ('http' or 'https')
|
|
52
|
+
*/
|
|
53
|
+
const applyProxyRequestHeaders = ({
|
|
54
|
+
proxyRequest,
|
|
55
|
+
incomingRequest,
|
|
56
|
+
caching = false,
|
|
57
|
+
logging = !(0, _utils.isRemote)() && _utils.verboseProxyLogging,
|
|
58
|
+
proxyPath,
|
|
59
|
+
targetHost,
|
|
60
|
+
targetProtocol
|
|
61
|
+
}) => {
|
|
62
|
+
const url = incomingRequest.url;
|
|
63
|
+
const headers = incomingRequest.headers;
|
|
64
|
+
/* istanbul ignore next */
|
|
65
|
+
if (logging) {
|
|
66
|
+
console.log(`Proxy: request for ${proxyPath}${url} => ${targetProtocol}://${targetHost}/${url}`);
|
|
67
|
+
}
|
|
68
|
+
const newHeaders = (0, _ssrProxying.rewriteProxyRequestHeaders)({
|
|
69
|
+
caching,
|
|
70
|
+
headers,
|
|
71
|
+
headerFormat: 'http',
|
|
72
|
+
logging,
|
|
73
|
+
proxyPath,
|
|
74
|
+
targetHost,
|
|
75
|
+
targetProtocol
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Copy any new and updated headers to the proxyRequest
|
|
79
|
+
// using setHeader.
|
|
80
|
+
Object.entries(newHeaders).forEach(
|
|
81
|
+
// setHeader always replaces any current value.
|
|
82
|
+
([key, value]) => proxyRequest.setHeader(key, value));
|
|
83
|
+
|
|
84
|
+
// Handle deletion of headers.
|
|
85
|
+
// Iterate over the keys of incomingRequest.headers - for every
|
|
86
|
+
// key, if the value is not present in newHeaders, we remove
|
|
87
|
+
// that value from proxyRequest's headers.
|
|
88
|
+
Object.keys(headers).forEach(key => {
|
|
89
|
+
// We delete the header on any falsy value, since
|
|
90
|
+
// there's no use case where we supply an empty header
|
|
91
|
+
// value.
|
|
92
|
+
if (!newHeaders[key]) {
|
|
93
|
+
proxyRequest.removeHeader(key);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
32
98
|
/**
|
|
33
99
|
* Configure proxying for a path.
|
|
34
100
|
* @private
|
|
@@ -46,6 +112,7 @@ const generalProxyPathRE = /^\/mobify\/proxy\/([^/]+)(\/.*)$/;
|
|
|
46
112
|
* standard proxy.
|
|
47
113
|
* @returns {middleware} function to pass to expressApp.use()
|
|
48
114
|
*/
|
|
115
|
+
exports.applyProxyRequestHeaders = applyProxyRequestHeaders;
|
|
49
116
|
const configureProxy = ({
|
|
50
117
|
appHostname,
|
|
51
118
|
proxyPath,
|
|
@@ -105,41 +172,14 @@ const configureProxy = ({
|
|
|
105
172
|
* this Express app that prompted the proxying
|
|
106
173
|
*/
|
|
107
174
|
onProxyReq: (proxyRequest, incomingRequest) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
console.log(`Proxy: request for ${proxyPath}${url} => ${targetOrigin}/${url}`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Rewrite key headers.
|
|
115
|
-
const newHeaders = (0, _ssrProxying.rewriteProxyRequestHeaders)({
|
|
175
|
+
applyProxyRequestHeaders({
|
|
176
|
+
proxyRequest,
|
|
177
|
+
incomingRequest,
|
|
116
178
|
caching,
|
|
117
|
-
headers: incomingRequest.headers,
|
|
118
|
-
headerFormat: 'http',
|
|
119
|
-
logging: !(0, _utils.isRemote)() && _utils.verboseProxyLogging,
|
|
120
179
|
proxyPath,
|
|
121
180
|
targetHost,
|
|
122
181
|
targetProtocol
|
|
123
182
|
});
|
|
124
|
-
|
|
125
|
-
// Copy any new and updated headers to the proxyRequest
|
|
126
|
-
// using setHeader.
|
|
127
|
-
Object.entries(newHeaders).forEach(
|
|
128
|
-
// setHeader always replaces any current value.
|
|
129
|
-
([key, value]) => proxyRequest.setHeader(key, value));
|
|
130
|
-
|
|
131
|
-
// Handle deletion of headers.
|
|
132
|
-
// Iterate over the keys of incomingRequest.headers - for every
|
|
133
|
-
// key, if the value is not present in newHeaders, we remove
|
|
134
|
-
// that value from proxyRequest's headers.
|
|
135
|
-
Object.keys(incomingRequest.headers).forEach(key => {
|
|
136
|
-
// We delete the header on any falsy value, since
|
|
137
|
-
// there's no use case where we supply an empty header
|
|
138
|
-
// value.
|
|
139
|
-
if (!newHeaders[key]) {
|
|
140
|
-
proxyRequest.removeHeader(key);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
183
|
},
|
|
144
184
|
onProxyRes: (proxyResponse, req) => {
|
|
145
185
|
/* istanbul ignore next */
|
|
@@ -183,7 +223,7 @@ const configureProxy = ({
|
|
|
183
223
|
// The origin (protocol + host) to which we proxy
|
|
184
224
|
target: targetOrigin
|
|
185
225
|
};
|
|
186
|
-
const proxyFunc = (0, _httpProxyMiddleware.
|
|
226
|
+
const proxyFunc = (0, _httpProxyMiddleware.createProxyMiddleware)(config);
|
|
187
227
|
|
|
188
228
|
// For a standard proxy, we're done
|
|
189
229
|
if (!caching) {
|