From 5ced530ccdd5d6c096a29cbef3d6e3df0467a3ee Mon Sep 17 00:00:00 2001 From: Rishichandra Wawhal Date: Tue, 8 Oct 2019 15:53:01 +0530 Subject: [PATCH] separate server and cli env variables in console local dev (#2937) --- console/README.md | 136 +++++++++++------- console/src/Globals.js | 27 ++-- console/src/components/App/App.js | 5 +- console/src/components/Main/Main.js | 2 +- .../components/Services/Settings/Actions.js | 4 +- console/src/helpers/localDev.js | 61 ++++---- console/src/telemetry/Actions.js | 3 +- console/src/utils/validateLogin.js | 2 +- 8 files changed, 139 insertions(+), 101 deletions(-) diff --git a/console/README.md b/console/README.md index 0782e80186d..cb4783d9eb6 100644 --- a/console/README.md +++ b/console/README.md @@ -28,7 +28,7 @@ Feel free to open pull requests to address these issues or to add/fix console f - [Hasura GraphQL Engine](https://docs.hasura.io/1.0/graphql/manual/getting-started/index.html) - [Hasura CLI](https://docs.hasura.io/1.0/graphql/manual/hasura-cli/install-hasura-cli.html) (for working with migrations) -### Setup and Install Dependencies +### Set up and install dependencies - Fork the repo on GitHub. - Clone your forked repo: `git clone https://github.com//graphql-engine` @@ -40,88 +40,114 @@ cd console npm install ``` -Hasura console can be developed in two modes (`server` or `cli` mode). Both modes require a running instance of GraphQL Engine. The easiest way to get Hasura GraphQL engine instance is by Heroku. You can get it by following the steps given in [this](https://docs.hasura.io/1.0/graphql/manual/getting-started/heroku-simple.html) link. Other methods to install Hasura GraphQL engine are documented [here](https://docs.hasura.io/1.0/graphql/manual/getting-started/index.html). +### Run console development server -### Development with Hasura GraphQL Engine (`server` mode) +Hasura console can be developed in two modes, `server` or `cli` mode. If you are looking to add/tweak functionality related to migrations, check out [Development with Hasura CLI](#development-with-hasura-cli-cli-mode), otherwise check out [Development with Hasura GraphQL engine](#development-with-hasura-graphql-engine-server-mode). -Hasura GraphQL engine should be running to develop console in this mode. If you have set it up on Heroku, your url will look like `.herokuapp.com`, if it's on your local machine, it's probably `http://localhost:8080`. +Both modes require a running instance of GraphQL Engine. The easiest way to get Hasura GraphQL engine instance is by Heroku. You can get it by following the steps given in [this](https://docs.hasura.io/1.0/graphql/manual/getting-started/heroku-simple.html) link. Other methods to install Hasura GraphQL engine are documented [here](https://docs.hasura.io/1.0/graphql/manual/getting-started/index.html). -[Dotenv](https://github.com/motdotla/dotenv) is used for setting environment variables for development. Create a `.env` file in the root directory for console (wherever package.json is). Here's a `.env` file with some environment variable examples : +[Dotenv](https://github.com/motdotla/dotenv) is used for setting environment variables for development. In production, these environment variables are templated by the server or CLI. -```bash -PORT=3000 -NODE_ENV=development -DATA_API_URL=http://localhost:8080 -ADMIN_SECRET=xyz -IS_ADMIN_SECRET_SET=true -CONSOLE_MODE=server -URL_PREFIX=/ -ASSETS_PATH=https://graphql-engine-cdn.hasura.io/console/assets -ASSETS_VERSION=channel/beta/v1.0 -CDN_ASSETS=true -``` +#### Develop with Hasura GraphQL engine (`server` mode) -Note that `CONSOLE_MODE` is set to `server`. In this mode, **migrations** will be disabled and the corresponding functionality on the console will be hidden. If you are looking to add/tweak functionality related to migrations, check out [Development with Hasura CLI](#development-with-hasura-cli-cli-mode). +In server mode, **migrations** will be disabled and the corresponding functionality on the console will be hidden. +##### Set up `.env` file Environment variables accepted in `server` mode: -1. `PORT`: Configure the port where Hasura console will run locally. -2. `NODE_ENV`: `development` -3. `DATA_API_URL`: Configure it with the Hasura GraphQL Engine url. If you are running it on Heroku, your url will look like `.herokuapp.com` -4. `ADMIN_SECRET`: Set admin secret if Hasura GraphQL engine is configured to run with ADMIN_SECRET. -5. `CONSOLE_MODE`: `server` -6. `URL_PREFIX`: `/` (forward slash) +- `NODE_ENV`: Console build environment (`development`/`production`) +- `PORT`: The port where Hasura console will run locally +- `CDN_ASSETS`: Should assets be loaded from CDN (`true`/`false`) +- `ASSETS_PATH`: Path to console assets +- `ASSETS_VERSION`: Version of console assets being served +- `ENABLE_TELEMETRY`: Whether to enable telemetry (`true`/`false`) +- `URL_PREFIX`: Path at which the console is running +- `DATA_API_URL`: The Hasura GraphQL engine url. (If you are running it on Heroku, it will look like .herokuapp.com, if you are running locally, it will look like http://localhost:) +- `SERVER_VERSION`: Hasura GraphQL Engine server version +- `CONSOLE_MODE`: In server mode, it should be `server` +- `IS_ADMIN_SECRET_SET`: Is GraphQl engine configured with an admin secret (`true`/`false`) -> The server also templates `consolePath` in `window.__env` which is the relative path of the current page (something like `/console/data/schema/public`). Using this path, the console determines the DATA_API_URL. You do not need to worry about this in development since you are hardcoding the value of DATA_API_URL in `.env`. +Here's an example `.env` file for `server` mode: -#### Run Development Server: +```bash +NODE_ENV=development +PORT=3000 +CDN_ASSETS=true +ASSETS_PATH=https://graphql-engine-cdn.hasura.io/console/assets +ASSETS_VERSION=channel/beta/v1.0 +ENABLE_TELEMETRY=true +URL_PREFIX=/ +DATA_API_URL=http://localhost:8080 +SERVER_VERSION=v1.0.0-beta.6 +CONSOLE_MODE=server +IS_ADMIN_SECRET_SET=true +``` + +> The server also templates `consolePath` in `window.__env` which is the relative path of the current page (something like `/console/data/schema/public`). Using this path, the console determines the DATA_API_URL in production. You do not need to worry about this in development since you are hardcoding the value of DATA_API_URL in `.env`. + +##### Run console development server: ```bash npm run dev ``` -### Development with Hasura CLI (`cli` mode) +#### Develop with Hasura CLI (`cli` mode) -Configure .env file with appropriate values for the required environment variables, such as the examples below: +##### Set up `.env` file -```bash -PORT=3000 -NODE_ENV=development -DATA_API_URL=http://localhost:8080 -API_HOST=http://localhost -API_PORT=9693 -ADMIN_SECRET=xyz -CONSOLE_MODE=cli -URL_PREFIX=/ -``` Environment variables accepted in `cli` mode: -1. `PORT`: Configure the port where Hasura console will run locally. -2. `NODE_ENV`: `development` -3. `DATA_API_URL`: Configure it with the Hasura GraphQL Engine url. If you are running it on Heroku. Your url will look like .herokuapp.com -4. `API_HOST`: Hasura CLI host. Hasura CLI runs on `http://localhost` by default. -5. `API_PORT`: Hasura CLI port. Hasura CLI exposes the API at `9693` by default -6. `ADMIN_SECRET`: Set admin secret if Hasura GraphQL engine is configured to run with ADMIN_SECRET -7. `CONSOLE_MODE`: `cli` -8. `URL_PREFIX`: ‘/’ (forward slash) +- `NODE_ENV`: Console build environment (`development`/`production`) +- `PORT`: The port where Hasura console will run locally +- `API_HOST`: Hasura CLI host. Hasura CLI runs on `http://localhost` by default. +- `API_PORT`: Hasura CLI port. Hasura CLI exposes the API at `9693` by default +- `CDN_ASSETS`: Should assets be loaded from CDN (`true`/`false`) +- `ASSETS_PATH`: Path to console assets +- `ASSETS_VERSION`: Version of console assets being served +- `ENABLE_TELEMETRY`: Whether to enable telemetry (`true`/`false`) +- `URL_PREFIX`: Path at which the console is running +- `DATA_API_URL`: The Hasura GraphQL engine url. (If you are running it on Heroku, it will look like .herokuapp.com, if you are running locally, it will look like http://localhost:) +- `SERVER_VERSION`: Hasura GraphQL Engine server version +- `CONSOLE_MODE`: In cli mode, it should be `cli` +- `ADMIN_SECRET`: the admin secret passed via the CLI -#### Run Development Server: - -This setup requires hasura cli to be running in a different window. Start hasura cli with the same Hasura GraphQL engine url as configured for `DATA_API_URL`. - -##### Start Hasura CLI server +Here's an example `.env` file for `cli` mode: ```bash -hasura console +NODE_ENV=development +PORT=3000 +API_HOST=http://localhost +API_PORT=9693 +CDN_ASSETS=true +ASSETS_PATH=https://graphql-engine-cdn.hasura.io/console/assets +ASSETS_VERSION=channel/beta/v1.0 +ENABLE_TELEMETRY=true +URL_PREFIX=/ +DATA_API_URL=http://localhost:8080 +SERVER_VERSION=v1.0.0-beta.6 +CONSOLE_MODE=cli +ADMIN_SECRET=my-admin-secret ``` -##### Start development server +##### Run console development server: + +This setup requires a Hasura CLI console server to be running. + +###### Start Hasura CLI console server + +Start Hasura CLI console with the same Hasura GraphQL engine url as configured for `DATA_API_URL`. + +```bash +hasura console --endpoint --admin-secret (optional) +``` + +###### Start development console server ```bash npm run dev ``` -### Checkout the console +### Check out the console Visit [http://localhost:3000](http://localhost:3000) to confirm the setup. @@ -140,7 +166,7 @@ It should automatically connect to the Redux store when started in development m By default [redux-logger](https://www.npmjs.com/package/redux-logger) is enabled to assist in development. You can disable it if you wish by commenting out the `createLogger` line in `src/client.js` -### Run Tests +### Run tests - Run tests: `npm run cypress` - Write your tests in the `cypress` directory, integration. diff --git a/console/src/Globals.js b/console/src/Globals.js index 34406685066..e2d6efea92c 100644 --- a/console/src/Globals.js +++ b/console/src/Globals.js @@ -2,6 +2,7 @@ import { SERVER_CONSOLE_MODE } from './constants'; import { getFeaturesCompatibility } from './helpers/versionUtils'; import { stripTrailingSlash } from './components/Common/utils/urlUtils'; +// TODO: move this section to a more appropriate location /* set helper tools into window */ import sqlFormatter from './helpers/sql-formatter.min'; @@ -19,19 +20,19 @@ if ( /* initialize globals */ +const isProduction = window.__env.nodeEnv === 'production'; + const globals = { apiHost: window.__env.apiHost, apiPort: window.__env.apiPort, - dataApiUrl: stripTrailingSlash(window.__env.dataApiUrl), - devDataApiUrl: window.__env.devDataApiUrl, - nodeEnv: window.__env.nodeEnv, - adminSecret: window.__env.adminSecret || null, // will be updated after login/logout - isAdminSecretSet: window.__env.isAdminSecretSet || false, + dataApiUrl: stripTrailingSlash(window.__env.dataApiUrl), // overridden below if server mode + urlPrefix: stripTrailingSlash(window.__env.urlPrefix || '/'), // overridden below if server mode in production + adminSecret: window.__env.adminSecret || null, // gets updated after login/logout in server mode + isAdminSecretSet: + window.__env.isAdminSecretSet || window.__env.adminSecret || false, consoleMode: window.__env.consoleMode || SERVER_CONSOLE_MODE, - urlPrefix: stripTrailingSlash(window.__env.urlPrefix) || '', enableTelemetry: window.__env.enableTelemetry, - telemetryTopic: - window.__env.nodeEnv !== 'development' ? 'console' : 'console_test', + telemetryTopic: isProduction ? 'console' : 'console_test', assetsPath: window.__env.assetsPath, serverVersion: window.__env.serverVersion, consoleAssetVersion: CONSOLE_ASSET_VERSION, // set during console build @@ -41,22 +42,24 @@ const globals = { }; if (globals.consoleMode === SERVER_CONSOLE_MODE) { - if (globals.nodeEnv !== 'development') { + if (isProduction) { const consolePath = window.__env.consolePath; if (consolePath) { const currentUrl = stripTrailingSlash(window.location.href); + const currentPath = stripTrailingSlash(window.location.pathname); + globals.dataApiUrl = currentUrl.slice( 0, currentUrl.lastIndexOf(consolePath) ); - const currentPath = stripTrailingSlash(window.location.pathname); globals.urlPrefix = currentPath.slice(0, currentPath.lastIndexOf(consolePath)) + '/console'; } else { - const windowUrl = window.location.protocol + '//' + window.location.host; + const windowHostUrl = + window.location.protocol + '//' + window.location.host; - globals.dataApiUrl = windowUrl; + globals.dataApiUrl = windowHostUrl; } } } diff --git a/console/src/components/App/App.js b/console/src/components/App/App.js index f5b568a82ec..c7d23acd37b 100644 --- a/console/src/components/App/App.js +++ b/console/src/components/App/App.js @@ -53,7 +53,10 @@ class App extends Component { ); } - if (telemetry.console_opts && !telemetry.console_opts.telemetryNotificationShown) { + if ( + telemetry.console_opts && + !telemetry.console_opts.telemetryNotificationShown + ) { dispatch(showTelemetryNotification()); dispatch(telemetryNotificationShown()); } diff --git a/console/src/components/Main/Main.js b/console/src/components/Main/Main.js index 05bafd6e5d5..886a11407eb 100644 --- a/console/src/components/Main/Main.js +++ b/console/src/components/Main/Main.js @@ -226,7 +226,7 @@ class Main extends React.Component { const getAdminSecretSection = () => { let adminSecretHtml = null; - if (!(globals.isAdminSecretSet || globals.adminSecret)) { + if (!globals.isAdminSecretSet) { adminSecretHtml = (
{ }; export const isMetadataStatusPage = () => { - return window.location.pathname.includes('/setting/metadata-status'); + return window.location.pathname.includes('/settings/metadata-status'); }; export const redirectToMetadataStatus = () => { @@ -535,7 +535,7 @@ export const metadataReducer = (state = defaultState, action) => { ...state, allowedQueries: [ ...state.allowedQueries.map(q => - (q.name === action.data.queryName ? action.data.newQuery : q) + q.name === action.data.queryName ? action.data.newQuery : q ), ], }; diff --git a/console/src/helpers/localDev.js b/console/src/helpers/localDev.js index beb98de1fc3..ff2f55d5673 100644 --- a/console/src/helpers/localDev.js +++ b/console/src/helpers/localDev.js @@ -1,31 +1,36 @@ -let envObj = ` - apiHost: '${process.env.API_HOST}', - apiPort: '${process.env.API_PORT}', - dataApiUrl: '${process.env.DATA_API_URL}', - consoleMode: '${process.env.CONSOLE_MODE}', - nodeEnv: '${process.env.NODE_ENV}', - urlPrefix: '${process.env.URL_PREFIX}', - enableTelemetry: ${process.env.ENABLE_TELEMETRY}, - assetsPath: '${process.env.ASSETS_PATH}', - assetsVersion: '${process.env.ASSETS_VERSION}', - serverVersion: '${process.env.SERVER_VERSION}', - cdnAssets: ${process.env.CDN_ASSETS},`; +import { CLI_CONSOLE_MODE } from '../constants'; -if (process.env.ADMIN_SECRET !== undefined) { - envObj += ` - adminSecret: '${process.env.ADMIN_SECRET}',`; -} else { - // ADMIN_SECRET is undefined - if (process.env.IS_ADMIN_SECRET_SET !== undefined) { - envObj += ` - isAdminSecretSet: ${process.env.IS_ADMIN_SECRET_SET},`; - } -} - -const env = ` - window.__env={ - ${envObj} - }; +const serverEnvVars = ` + dataApiUrl: '${process.env.DATA_API_URL}', + isAdminSecretSet: '${process.env.IS_ADMIN_SECRET_SET}', + consoleMode: '${process.env.CONSOLE_MODE}', + nodeEnv: '${process.env.NODE_ENV}', + serverVersion: '${process.env.SERVER_VERSION}', + urlPrefix: '${process.env.URL_PREFIX}', + consolePath: '${process.env.CONSOLE_PATH}', + enableTelemetry: ${process.env.ENABLE_TELEMETRY}, + assetsPath: '${process.env.ASSETS_PATH}', + assetsVersion: '${process.env.ASSETS_VERSION}', + cdnAssets: ${process.env.CDN_ASSETS} `; -export { env }; +const cliEnvVars = ` + apiPort: '${process.env.API_PORT}', + apiHost: '${process.env.API_HOST}', + dataApiUrl: '${process.env.DATA_API_URL}', + adminSecret: '${process.env.ADMIN_SECRET}', + consoleMode: '${process.env.CONSOLE_MODE}', + nodeEnv: '${process.env.NODE_ENV}', + enableTelemetry: ${process.env.ENABLE_TELEMETRY}, + assetsPath: '${process.env.ASSETS_PATH}', + assetsVersion: '${process.env.ASSETS_VERSION}', + serverVersion: '${process.env.SERVER_VERSION}', + cdnAssets: ${process.env.CDN_ASSETS}, +`; + +const envVars = + process.env.CONSOLE_MODE === CLI_CONSOLE_MODE ? cliEnvVars : serverEnvVars; + +export const env = ` + window.__env = {${envVars}}; +`; diff --git a/console/src/telemetry/Actions.js b/console/src/telemetry/Actions.js index f6367a18b3e..007b4a7897e 100644 --- a/console/src/telemetry/Actions.js +++ b/console/src/telemetry/Actions.js @@ -2,6 +2,7 @@ import Endpoints, { globalCookiePolicy } from '../Endpoints'; import requestAction from '../utils/requestAction'; import dataHeaders from '../components/Services/Data/Common/Headers'; import defaultTelemetryState from './State'; +import globals from '../Globals'; const SET_CONSOLE_OPTS = 'Telemetry/SET_CONSOLE_OPTS'; const SET_NOTIFICATION_SHOWN = 'Telemetry/SET_NOTIFICATION_SHOWN'; @@ -43,7 +44,7 @@ const setNotificationShownInDB = () => (dispatch, getState) => { const loadConsoleTelemetryOpts = () => { return (dispatch, getState) => { - if (window.__env.enableTelemetry === undefined) { + if (globals.enableTelemetry === undefined) { return dispatch({ type: SET_TELEMETRY_DISABLED }); } diff --git a/console/src/utils/validateLogin.js b/console/src/utils/validateLogin.js index 60a04b543a5..549cc7fc0f3 100644 --- a/console/src/utils/validateLogin.js +++ b/console/src/utils/validateLogin.js @@ -9,7 +9,7 @@ import { CLI_CONSOLE_MODE } from '../constants'; const validateLogin = ({ dispatch }) => { return (nextState, replaceState, cb) => { // care about admin secret only if it is set - if (globals.isAdminSecretSet || globals.adminSecret) { + if (globals.isAdminSecretSet) { const validationSuccessCallback = () => { if (nextState.location.pathname === '/login') { replaceState('/');