Skip to content

Commit

Permalink
Merge pull request #16432 from Prince-Mendiratta/dynamic-api
Browse files Browse the repository at this point in the history
[Web Proxy] Use staging server for Web, Desktop when the "Use staging server" toggle is ON.
  • Loading branch information
neil-marcellini authored Mar 28, 2023
2 parents 9545850 + 02b5da9 commit 3f66c68
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 22 deletions.
9 changes: 9 additions & 0 deletions config/proxyConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* These are the base API roots used to send requests to the proxy.
* We only specify for staging URLs as API requests are sent to the production
* servers by default.
*/
module.exports = {
STAGING: '/staging/',
STAGING_SECURE: '/staging-secure/',
};
1 change: 1 addition & 0 deletions config/webpack/webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = (env = {}) => portfinder.getPortPromise({port: BASE_PORT})
: {
proxy: {
'/api': 'http://[::1]:9000',
'/staging': 'http://[::1]:9000',
'/chat-attachments': 'http://[::1]:9000',
},
};
Expand Down
1 change: 1 addition & 0 deletions src/CONFIG.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ export default {
DEV_PORT: process.env.PORT || 8080,
E2E_TESTING: lodashGet(Config, 'E2E_TESTING', 'false') === 'true',
SEND_CRASH_REPORTS: lodashGet(Config, 'SEND_CRASH_REPORTS', 'false') === 'true',
IS_USING_WEB_PROXY: getPlatform() === 'web' && useWebProxy,
};
22 changes: 13 additions & 9 deletions src/components/TestToolMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import networkPropTypes from './networkPropTypes';
import compose from '../libs/compose';
import {withNetwork} from './OnyxProvider';
import * as ApiUtils from '../libs/ApiUtils';
import CONFIG from '../CONFIG';

const propTypes = {
/** User object in Onyx */
Expand Down Expand Up @@ -42,15 +43,18 @@ const TestToolMenu = props => (
</Text>

{/* Option to switch between staging and default api endpoints.
This enables QA and internal testers to take advantage of sandbox environments for 3rd party services like Plaid and Onfido. */}
<TestToolRow title="Use Staging Server">
<Switch
isOn={lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi())}
onToggle={() => User.setShouldUseStagingServer(
!lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi()),
)}
/>
</TestToolRow>
This enables QA, internal testers and external devs to take advantage of sandbox environments for 3rd party services like Plaid and Onfido.
This toggle is not rendered for internal devs as they make environment changes directly to the .env file. */}
{!CONFIG.IS_USING_LOCAL_WEB && (
<TestToolRow title="Use Staging Server">
<Switch
isOn={lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi())}
onToggle={() => User.setShouldUseStagingServer(
!lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi()),
)}
/>
</TestToolRow>
)}

{/* When toggled the app will be forced offline. */}
<TestToolRow title="Force offline">
Expand Down
10 changes: 8 additions & 2 deletions src/libs/ApiUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ONYXKEYS from '../ONYXKEYS';
import CONFIG from '../CONFIG';
import CONST from '../CONST';
import * as Environment from './Environment/Environment';
import proxyConfig from '../../config/proxyConfig';

// To avoid rebuilding native apps, native apps use production config for both staging and prod
// We use the async environment check because it works on all platforms
Expand All @@ -17,8 +18,8 @@ Environment.getEnvironment()
Onyx.connect({
key: ONYXKEYS.USER,
callback: (val) => {
// Toggling between APIs is not allowed on production
if (ENV_NAME === CONST.ENVIRONMENT.PRODUCTION) {
// Toggling between APIs is not allowed on production and internal dev environment
if (ENV_NAME === CONST.ENVIRONMENT.PRODUCTION || CONFIG.IS_USING_LOCAL_WEB) {
shouldUseStagingServer = false;
return;
}
Expand All @@ -41,6 +42,11 @@ function getApiRoot(request) {
const shouldUseSecure = lodashGet(request, 'shouldUseSecure', false);

if (shouldUseStagingServer) {
if (CONFIG.IS_USING_WEB_PROXY) {
return shouldUseSecure
? proxyConfig.STAGING_SECURE
: proxyConfig.STAGING;
}
return shouldUseSecure
? CONFIG.EXPENSIFY.STAGING_SECURE_API_ROOT
: CONFIG.EXPENSIFY.STAGING_API_ROOT;
Expand Down
42 changes: 31 additions & 11 deletions web/proxy.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
const http = require('http');
const https = require('https');
const proxyConfig = require('../config/proxyConfig');
require('dotenv').config();

if (process.env.USE_WEB_PROXY === 'false') {
process.stdout.write('Skipping proxy as USE_WEB_PROXY was set to false.\n');
process.exit();
}

let host = 'www.expensify.com';

// If we are testing against the staging API then we must use the correct host here or nothing with work.
if (/staging/.test(process.env.EXPENSIFY_URL)) {
host = 'staging.expensify.com';
}
const host = 'www.expensify.com';
const stagingHost = 'staging.expensify.com';
const stagingSecureHost = 'staging-secure.expensify.com';

// eslint-disable-next-line no-console
console.log(`Creating proxy with host: ${host}`);
console.log(`Creating proxy with host: ${host} for production API and ${stagingHost} for staging API`);

/**
* Local proxy server that hits the production endpoint
Expand All @@ -24,13 +21,36 @@ console.log(`Creating proxy with host: ${host}`);
* environment that has no local API.
*/
const server = http.createServer((request, response) => {
let hostname = host;
let requestPath = request.url;

/**
* When a request is matching a proxy config path we might direct it to a different host (e.g. staging)
* For requests matching proxy config patterns we replace the mapping url (prefix) with the actual path.
* This is done because the staging api root is only intended for the proxy,
* the actual server request must use the /api path.
* For example,
* /api?command=OpenReport => request sent to production server
* /staging/api?command=OpenReport => request sent to staging server
* /staging-secure/api?command=OpenReport => request sent to secure staging server
* /chat-attachments/46545... => request sent to production server
* /staging/chat-attachments/46545... => request sent to staging server
*/
if (request.url.startsWith(proxyConfig.STAGING_SECURE)) {
hostname = stagingSecureHost;
requestPath = request.url.replace(proxyConfig.STAGING_SECURE, '/');
} else if (request.url.startsWith(proxyConfig.STAGING)) {
hostname = stagingHost;
requestPath = request.url.replace(proxyConfig.STAGING, '/');
}

const proxyRequest = https.request({
hostname: host,
hostname,
method: 'POST',
path: request.url,
path: requestPath,
headers: {
...request.headers,
host,
host: hostname,
'user-agent': request.headers['user-agent'].concat(' Development-NewDot/1.0'),
},
port: 443,
Expand Down

0 comments on commit 3f66c68

Please sign in to comment.