-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CP Stg] Fix CORS errors in desktop application #7665
Conversation
|
Also see this SO regarding the |
👀 |
desktop/main.js
Outdated
@@ -147,6 +150,27 @@ const mainWindow = (() => { | |||
titleBarStyle: 'hidden', | |||
}); | |||
|
|||
if (!isDev) { | |||
const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like it would be cleaner to have a single variable called env
that would be either dev, staging, or production. Is there anyway we can use the ENVIRONMENT variable from the .env files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree one env variable would be cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like it would be cleaner to have a single variable called env that would be either dev, staging, or production.
Let me look into this.
Is there anyway we can use the ENVIRONMENT variable from the .env files?
Maybe, but the .env
files are consumed using react-native-config
and/or react-web-config
. This is the main process, running in an Electron context, more akin to a Node.js environment than a web environment. It might be possible to use dotenv
separately to consume the .env
files, but we'd still need a way to tell Electron which environment it's running in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anyway we can use the ENVIRONMENT variable from the .env files?
Expanding on this some more, we'd have no way of knowing which .env
file to use. For the main web app, we have a separate webpack config that specifies this for us. However, the code in desktop/main.js
isn't touched by webpack. Still thinking of how this can be cleaned up.
// Modify the request origin for requests sent to our API | ||
const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; | ||
browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => { | ||
// eslint-disable-next-line no-param-reassign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could get around this by using const newDetails = {...details}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think const newRequestHeaders = {...details.requestHeaders}
would be better since that is all that is being modified and const newDetails = {...details}
would create a shallow copy AFAIK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually took one out of your playbook from this PR ... this code will be executed before and after every api request, so performance matters. Using the spread operator would cause us to perform an unnecessary shallow copy of all the headers for every request twice per request, which seems less good than just overwriting a couple properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's probably a good call. I had also thought of that, and the fact that this is done on every request is a good reason to keep it very performant. The only caveat I will say is that in my PR, I think the spread operator is not performant because of how Babel chooses to transpile it into _spreadOperator()
. Since this is in Node.js and nothing is transpiled, I would think it's much more performant.
Something else to consider. In my PR, since there was a reducer used along with the spread operator, it could have been compounding the problem. This is mentioned specifically in this SO: https://stackoverflow.com/questions/55843097/does-spread-operator-affect-performance
beware of using spread with an array.reduce(). I suspect it leads to O(n^2) behavior or worse.
So, I'm not really sure that using a spread operator here is such a bad thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I wasn't aware of that context, especially regarding Babel. Very good to know about watching out for a spread operator in reducers! (maybe we could create a lint rule for that?)
So, I'm not really sure that using a spread operator here is such a bad thing.
That makes sense – I agree it might not create a performance problem to use the spread operator here. But still I would lean towards leaving this as-is because I think it will be "technically" more performant in a very performance-critical place, which seems worth the couple eslint-disable
s to me. I don't feel as strongly anymore though, so I'll change it if you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The local production and staging builds test well for me. After some tweaks it should be good.
desktop/main.js
Outdated
@@ -147,6 +150,27 @@ const mainWindow = (() => { | |||
titleBarStyle: 'hidden', | |||
}); | |||
|
|||
if (!isDev) { | |||
const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree one env variable would be cleaner.
// Modify the request origin for requests sent to our API | ||
const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; | ||
browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => { | ||
// eslint-disable-next-line no-param-reassign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think const newRequestHeaders = {...details.requestHeaders}
would be better since that is all that is being modified and const newDetails = {...details}
would create a shallow copy AFAIK.
I cleaned up the environment stuff a bit by creating a separate module for |
@@ -1,6 +1,7 @@ | |||
import lodashGet from 'lodash/get'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did the name of this file change? I don't think it should be changed. The only time we should ever have an index.js
is when it's in a directory used to load platform specific files (eg. index.js
, index.website.js
, index.ios.js
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This got renamed because of CJS and ESM modules not playing nice together. I wanted to use CONST.ENVIRONMENT
from the ELECTRON_ENVIRONMENT
CJS module, but can't require
an ESM module from Node.js. So we end up with a setup like this:
src/CONST/
---------| ENVIRONMENT.js // CJS module with just CONST.ENVIRONMENT
---------| index.js // ESM module with all our existing CONST stuff. This imports ENVIRONMENT.js as a CJS module and exports it as an ESM module
desktop/ELECTRON_ENVIRONMENT.js // This imports src/CONST/ENVIRONMENT.js directly.
The only time we should ever have an index.js is when it's in a directory used to load platform specific files (eg. index.js, index.website.js, index.ios.js)
By moving CONST.js
to CONST/index.js
, we don't have to change any imports. We can still import it like:
import CONST from '../CONST';
And JS will automatically grab index.js
from the CONST
directory.
If you want me to revert this, I could just create a separate constant directly in the ELECTRON_ENVIRONMENT
file and not worry about importing CONST.ENVIRONMENT
into the CJS module.
// Modify the request origin for requests sent to our API | ||
const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; | ||
browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => { | ||
// eslint-disable-next-line no-param-reassign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's probably a good call. I had also thought of that, and the fact that this is done on every request is a good reason to keep it very performant. The only caveat I will say is that in my PR, I think the spread operator is not performant because of how Babel chooses to transpile it into _spreadOperator()
. Since this is in Node.js and nothing is transpiled, I would think it's much more performant.
Something else to consider. In my PR, since there was a reducer used along with the spread operator, it could have been compounding the problem. This is mentioned specifically in this SO: https://stackoverflow.com/questions/55843097/does-spread-operator-affect-performance
beware of using spread with an array.reduce(). I suspect it leads to O(n^2) behavior or worse.
So, I'm not really sure that using a spread operator here is such a bad thing.
desktop/main.js
Outdated
if (!ELECTRON_ENVIRONMENT.isDev()) { | ||
const newDotURL = ELECTRON_ENVIRONMENT.isProd() ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; | ||
|
||
// Modify the request origin for requests sent to our API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add more to this comment to explain "why?". Currently, it is only explaining "what".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment above this block. Give it a read and let me know if you think it's clear enough, or if it leaves you with more questions.
module.exports = { | ||
appId: 'com.expensifyreactnative.chat', | ||
productName: 'New Expensify', | ||
extraMetadata: { | ||
main: './desktop/main.js', | ||
electronEnvironment: process.env.SHOULD_DEPLOY_PRODUCTION ? ENVIRONMENT.PRODUCTION : ENVIRONMENT.STAGING, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where it still remains a bit weird for me. The electronEnvironment
variable will only contain PROD or STAG. The development environment comes from process.env.NODE_ENV
. Can't they all come from the same place? Like, can this code here be something like...
let env = process.env.NODE_ENV;
if (env !== 'development') {
env = process.env.SHOULD_DEPLOY_PRODUCTION ? ENVIRONMENT.PRODUCTION : ENVIRONMENT.STAGING;
} else {
env = ENVIRONMENT.DEVELOPMENT;
}
// ...
electronEnvironment: env,
However, even in that code... if process.env.NODE_ENV
is not development
, then what is it? Maybe this logic can be cleaned up a little more.
The goal here would be that getEnvironment()
(below) would only be looking at a single place to determine the environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a good idea. As I understand it process.env.NODE_ENV
would be undefined outside of development because:
function getEnvironment() {
// If we are on dev, then the NODE_ENV environment variable will be present (set by the executing shell in start.js)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't they all come from the same place? ... The goal here would be that getEnvironment() (below) would only be looking at a single place to determine the environment.
I would love to achieve this goal, but I don't see how. You see, electron.config.js
would actually be better-named electron-builder.config.js
, because it's not actually used by Electron itself – only electron-builder
. So there would never be a time where process.env.NODE_ENV === 'development'
in electron.config.js
.
When we run the development app here, we can easily set an environment variable such as NODE_ENV
in the runtime, and that will be available to the Electron main process. However, when we bundle an app using electron-builder
(or really, create any executable file), we can't "bake" an environment variable in AFAIK. If I were to do something like this:
export NODE_ENV=production && electron-builder --config config/electron.config.js
Then when I install and run the bundled Electron app, and try to access process.env.NODE_ENV
, it will be undefined. You have to set the environment variable from the shell or environment that runs the executable.
However, electron-builder
does provide a weird workaround. You can set data in the extraMetadata
field in the configuration file, and then electron-builder
will inject those variables into your package.json
, which is available to the application at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from some small details I think this looks good.
module.exports = { | ||
appId: 'com.expensifyreactnative.chat', | ||
productName: 'New Expensify', | ||
extraMetadata: { | ||
main: './desktop/main.js', | ||
electronEnvironment: process.env.SHOULD_DEPLOY_PRODUCTION ? ENVIRONMENT.PRODUCTION : ENVIRONMENT.STAGING, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like a good idea. As I understand it process.env.NODE_ENV
would be undefined outside of development because:
function getEnvironment() {
// If we are on dev, then the NODE_ENV environment variable will be present (set by the executing shell in start.js)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes here are OK. There might still be some cleanup stuff for this, but let's get that deploy blocker fixed for now.
[CP Stg] Fix CORS errors in desktop application (cherry picked from commit a344c05)
🚀 Cherry-picked to staging by @tgolen in version: 1.1.38-2 🚀
@Expensify/applauseleads please QA this PR and check it off on the deploy checklist if it passes. |
🚀 Deployed to production by @Julesssss in version: 1.1.38-3 🚀
|
Details
Fixes CORS errors which were introduced by enabling web-security in this PR
Fixed Issues
$ #7659
Tests
export SHOULD_DEPLOY_PRODUCTION=true && npm run desktop-build
.OPT+CMD+i
), verify that there are no errors.export SHOULD_DEPLOY_PRODUCTION=false && npm run desktop-build-staging
QA Steps
Verify that you can log out and back in.
Tested On
Screenshots
Desktop