separate server and cli env variables in console local dev (#2937)

This commit is contained in:
Rishichandra Wawhal 2019-10-08 15:53:01 +05:30 committed by Rikin Kachhia
parent 453a544d83
commit 5ced530ccd
8 changed files with 139 additions and 101 deletions

View File

@ -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 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) - [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. - Fork the repo on GitHub.
- Clone your forked repo: `git clone https://github.com/<your-username>/graphql-engine` - Clone your forked repo: `git clone https://github.com/<your-username>/graphql-engine`
@ -40,88 +40,114 @@ cd console
npm install 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 `<app-name>.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 #### Develop with Hasura GraphQL engine (`server` mode)
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
```
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: Environment variables accepted in `server` mode:
1. `PORT`: Configure the port where Hasura console will run locally. - `NODE_ENV`: Console build environment (`development`/`production`)
2. `NODE_ENV`: `development` - `PORT`: The port where Hasura console will run locally
3. `DATA_API_URL`: Configure it with the Hasura GraphQL Engine url. If you are running it on Heroku, your url will look like `<app-name>.herokuapp.com` - `CDN_ASSETS`: Should assets be loaded from CDN (`true`/`false`)
4. `ADMIN_SECRET`: Set admin secret if Hasura GraphQL engine is configured to run with ADMIN_SECRET. - `ASSETS_PATH`: Path to console assets
5. `CONSOLE_MODE`: `server` - `ASSETS_VERSION`: Version of console assets being served
6. `URL_PREFIX`: `/` (forward slash) - `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 <app-name>.herokuapp.com, if you are running locally, it will look like http://localhost:<port>)
- `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 ```bash
npm run dev 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: Environment variables accepted in `cli` mode:
1. `PORT`: Configure the port where Hasura console will run locally. - `NODE_ENV`: Console build environment (`development`/`production`)
2. `NODE_ENV`: `development` - `PORT`: The port where Hasura console will run locally
3. `DATA_API_URL`: Configure it with the Hasura GraphQL Engine url. If you are running it on Heroku. Your url will look like <app-name>.herokuapp.com - `API_HOST`: Hasura CLI host. Hasura CLI runs on `http://localhost` by default.
4. `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
5. `API_PORT`: Hasura CLI port. Hasura CLI exposes the API at `9693` by default - `CDN_ASSETS`: Should assets be loaded from CDN (`true`/`false`)
6. `ADMIN_SECRET`: Set admin secret if Hasura GraphQL engine is configured to run with ADMIN_SECRET - `ASSETS_PATH`: Path to console assets
7. `CONSOLE_MODE`: `cli` - `ASSETS_VERSION`: Version of console assets being served
8. `URL_PREFIX`: / (forward slash) - `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 <app-name>.herokuapp.com, if you are running locally, it will look like http://localhost:<port>)
- `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: Here's an example `.env` file for `cli` mode:
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
```bash ```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 <DATA_API_URL> --admin-secret <your-admin-secret> (optional)
```
###### Start development console server
```bash ```bash
npm run dev npm run dev
``` ```
### Checkout the console ### Check out the console
Visit [http://localhost:3000](http://localhost:3000) to confirm the setup. 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. 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` 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` - Run tests: `npm run cypress`
- Write your tests in the `cypress` directory, integration. - Write your tests in the `cypress` directory, integration.

View File

@ -2,6 +2,7 @@ import { SERVER_CONSOLE_MODE } from './constants';
import { getFeaturesCompatibility } from './helpers/versionUtils'; import { getFeaturesCompatibility } from './helpers/versionUtils';
import { stripTrailingSlash } from './components/Common/utils/urlUtils'; import { stripTrailingSlash } from './components/Common/utils/urlUtils';
// TODO: move this section to a more appropriate location
/* set helper tools into window */ /* set helper tools into window */
import sqlFormatter from './helpers/sql-formatter.min'; import sqlFormatter from './helpers/sql-formatter.min';
@ -19,19 +20,19 @@ if (
/* initialize globals */ /* initialize globals */
const isProduction = window.__env.nodeEnv === 'production';
const globals = { const globals = {
apiHost: window.__env.apiHost, apiHost: window.__env.apiHost,
apiPort: window.__env.apiPort, apiPort: window.__env.apiPort,
dataApiUrl: stripTrailingSlash(window.__env.dataApiUrl), dataApiUrl: stripTrailingSlash(window.__env.dataApiUrl), // overridden below if server mode
devDataApiUrl: window.__env.devDataApiUrl, urlPrefix: stripTrailingSlash(window.__env.urlPrefix || '/'), // overridden below if server mode in production
nodeEnv: window.__env.nodeEnv, adminSecret: window.__env.adminSecret || null, // gets updated after login/logout in server mode
adminSecret: window.__env.adminSecret || null, // will be updated after login/logout isAdminSecretSet:
isAdminSecretSet: window.__env.isAdminSecretSet || false, window.__env.isAdminSecretSet || window.__env.adminSecret || false,
consoleMode: window.__env.consoleMode || SERVER_CONSOLE_MODE, consoleMode: window.__env.consoleMode || SERVER_CONSOLE_MODE,
urlPrefix: stripTrailingSlash(window.__env.urlPrefix) || '',
enableTelemetry: window.__env.enableTelemetry, enableTelemetry: window.__env.enableTelemetry,
telemetryTopic: telemetryTopic: isProduction ? 'console' : 'console_test',
window.__env.nodeEnv !== 'development' ? 'console' : 'console_test',
assetsPath: window.__env.assetsPath, assetsPath: window.__env.assetsPath,
serverVersion: window.__env.serverVersion, serverVersion: window.__env.serverVersion,
consoleAssetVersion: CONSOLE_ASSET_VERSION, // set during console build consoleAssetVersion: CONSOLE_ASSET_VERSION, // set during console build
@ -41,22 +42,24 @@ const globals = {
}; };
if (globals.consoleMode === SERVER_CONSOLE_MODE) { if (globals.consoleMode === SERVER_CONSOLE_MODE) {
if (globals.nodeEnv !== 'development') { if (isProduction) {
const consolePath = window.__env.consolePath; const consolePath = window.__env.consolePath;
if (consolePath) { if (consolePath) {
const currentUrl = stripTrailingSlash(window.location.href); const currentUrl = stripTrailingSlash(window.location.href);
const currentPath = stripTrailingSlash(window.location.pathname);
globals.dataApiUrl = currentUrl.slice( globals.dataApiUrl = currentUrl.slice(
0, 0,
currentUrl.lastIndexOf(consolePath) currentUrl.lastIndexOf(consolePath)
); );
const currentPath = stripTrailingSlash(window.location.pathname);
globals.urlPrefix = globals.urlPrefix =
currentPath.slice(0, currentPath.lastIndexOf(consolePath)) + '/console'; currentPath.slice(0, currentPath.lastIndexOf(consolePath)) + '/console';
} else { } else {
const windowUrl = window.location.protocol + '//' + window.location.host; const windowHostUrl =
window.location.protocol + '//' + window.location.host;
globals.dataApiUrl = windowUrl; globals.dataApiUrl = windowHostUrl;
} }
} }
} }

View File

@ -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(showTelemetryNotification());
dispatch(telemetryNotificationShown()); dispatch(telemetryNotificationShown());
} }

View File

@ -226,7 +226,7 @@ class Main extends React.Component {
const getAdminSecretSection = () => { const getAdminSecretSection = () => {
let adminSecretHtml = null; let adminSecretHtml = null;
if (!(globals.isAdminSecretSet || globals.adminSecret)) { if (!globals.isAdminSecretSet) {
adminSecretHtml = ( adminSecretHtml = (
<div className={styles.secureSection}> <div className={styles.secureSection}>
<a <a

View File

@ -222,7 +222,7 @@ export const dropInconsistentObjects = () => {
}; };
export const isMetadataStatusPage = () => { export const isMetadataStatusPage = () => {
return window.location.pathname.includes('/setting/metadata-status'); return window.location.pathname.includes('/settings/metadata-status');
}; };
export const redirectToMetadataStatus = () => { export const redirectToMetadataStatus = () => {
@ -535,7 +535,7 @@ export const metadataReducer = (state = defaultState, action) => {
...state, ...state,
allowedQueries: [ allowedQueries: [
...state.allowedQueries.map(q => ...state.allowedQueries.map(q =>
(q.name === action.data.queryName ? action.data.newQuery : q) q.name === action.data.queryName ? action.data.newQuery : q
), ),
], ],
}; };

View File

@ -1,31 +1,36 @@
let envObj = ` import { CLI_CONSOLE_MODE } from '../constants';
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},`;
if (process.env.ADMIN_SECRET !== undefined) { const serverEnvVars = `
envObj += ` dataApiUrl: '${process.env.DATA_API_URL}',
adminSecret: '${process.env.ADMIN_SECRET}',`; isAdminSecretSet: '${process.env.IS_ADMIN_SECRET_SET}',
} else { consoleMode: '${process.env.CONSOLE_MODE}',
// ADMIN_SECRET is undefined nodeEnv: '${process.env.NODE_ENV}',
if (process.env.IS_ADMIN_SECRET_SET !== undefined) { serverVersion: '${process.env.SERVER_VERSION}',
envObj += ` urlPrefix: '${process.env.URL_PREFIX}',
isAdminSecretSet: ${process.env.IS_ADMIN_SECRET_SET},`; consolePath: '${process.env.CONSOLE_PATH}',
} enableTelemetry: ${process.env.ENABLE_TELEMETRY},
} assetsPath: '${process.env.ASSETS_PATH}',
assetsVersion: '${process.env.ASSETS_VERSION}',
const env = ` cdnAssets: ${process.env.CDN_ASSETS}
window.__env={
${envObj}
};
`; `;
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}};
`;

View File

@ -2,6 +2,7 @@ import Endpoints, { globalCookiePolicy } from '../Endpoints';
import requestAction from '../utils/requestAction'; import requestAction from '../utils/requestAction';
import dataHeaders from '../components/Services/Data/Common/Headers'; import dataHeaders from '../components/Services/Data/Common/Headers';
import defaultTelemetryState from './State'; import defaultTelemetryState from './State';
import globals from '../Globals';
const SET_CONSOLE_OPTS = 'Telemetry/SET_CONSOLE_OPTS'; const SET_CONSOLE_OPTS = 'Telemetry/SET_CONSOLE_OPTS';
const SET_NOTIFICATION_SHOWN = 'Telemetry/SET_NOTIFICATION_SHOWN'; const SET_NOTIFICATION_SHOWN = 'Telemetry/SET_NOTIFICATION_SHOWN';
@ -43,7 +44,7 @@ const setNotificationShownInDB = () => (dispatch, getState) => {
const loadConsoleTelemetryOpts = () => { const loadConsoleTelemetryOpts = () => {
return (dispatch, getState) => { return (dispatch, getState) => {
if (window.__env.enableTelemetry === undefined) { if (globals.enableTelemetry === undefined) {
return dispatch({ type: SET_TELEMETRY_DISABLED }); return dispatch({ type: SET_TELEMETRY_DISABLED });
} }

View File

@ -9,7 +9,7 @@ import { CLI_CONSOLE_MODE } from '../constants';
const validateLogin = ({ dispatch }) => { const validateLogin = ({ dispatch }) => {
return (nextState, replaceState, cb) => { return (nextState, replaceState, cb) => {
// care about admin secret only if it is set // care about admin secret only if it is set
if (globals.isAdminSecretSet || globals.adminSecret) { if (globals.isAdminSecretSet) {
const validationSuccessCallback = () => { const validationSuccessCallback = () => {
if (nextState.location.pathname === '/login') { if (nextState.location.pathname === '/login') {
replaceState('/'); replaceState('/');