From 3dbbfe727b2ba69fd30993f2b06fe17fcca535df Mon Sep 17 00:00:00 2001 From: Dan Caddigan Date: Tue, 14 Sep 2021 22:31:08 -0400 Subject: [PATCH] feat(config): switch from individual app env vars Switch from using individual `WARTHOG_APP_*` variables in favor of a single `WARTHOG_API_BASE_URL`. BREAKING CHANGE --- README.md | 17 +++++++-- examples/01-simple-model/.env | 3 +- examples/02-complex-example/.env | 3 +- examples/03-one-to-many-relationship/.env | 3 +- examples/04-many-to-many-relationship/.env | 3 +- examples/05-migrations/.env | 3 +- examples/06-base-service/.env | 3 +- examples/07-feature-flags/.env | 3 +- examples/08-performance/.env | 3 +- examples/09-production/.env | 4 +-- examples/09-production/env.yml | 4 +-- examples/10-subscriptions/.env | 3 +- examples/11-transactions/.env | 3 +- examples/14-base-service-v2/.env | 3 +- src/cli/commands/playground.ts | 6 +--- src/cli/templates/new/_env.ejs | 3 +- src/cli/templates/new/env.yml.ejs | 14 ++++---- src/core/config.test.ts | 3 +- src/core/config.ts | 36 ++++++++++++++----- src/core/server.ts | 20 ++--------- src/core/tests/dotenv-files/.env | 3 +- .../tests/valid-config-file/config.test.ts | 3 +- src/test/functional/server.test.ts | 5 +-- src/test/server-vars.ts | 4 +-- 24 files changed, 72 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 1737c3c0..4fe76d7f 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ Warthog is now on version 3.0! There were a few breaking changes that you should Expand for Breaking change details

+### Deprecated individual `WARTHOG_APP_*` variables in favor of `WARTHOG_API_BASE_URL` + +It's a pain to have to manage 3 separate environment variables for setting the base API URL. We still support this, but prefer the new `WARTHOG_API_BASE_URL` and will use it first if it's present when spinning up a server. + ### BaseModel has been overhauled - SchemaGenerator no longer generates a Binding by default, must now pass a param in and pass `--binding` in the CLI @@ -250,9 +254,7 @@ Almost all config in Warthog is driven by environment variables. The following i | variable | value | default | | ----------------------------- | -------------------------------------------------------- | ------------------------- | -| WARTHOG_APP_HOST | App server host | _none_ | -| WARTHOG_APP_PORT | App server port | 4000 | -| WARTHOG_APP_PROTOCOL | App server protocol | DEV: http, PROD: https | +| WARTHOG_API_BASE_URL | Api base url | _none_ | | WARTHOG_AUTO_GENERATE_FILES | Auto-generate files | DEV: true, PROD: false | | WARTHOG_AUTO_OPEN_PLAYGROUND | Open playground on server start | DEV: true, PROD: false | | WARTHOG_CLI_GENERATE_PATH | Where should CLI generate files | ./src | @@ -274,6 +276,15 @@ Almost all config in Warthog is driven by environment variables. The following i | WARTHOG_SUBSCRIPTIONS | Should we enable subscriptions and open a websocket port | false | | WARTHOG_VALIDATE_RESOLVERS | TypeGraphQL validation enabled? | false | +### Removed Environment Variables + +All of the `WARTHOG_APP_*` environment variables will still work, but it is strongly recommended you use the single variable `WARTHOG_API_BASE_URL` + +| variable | value | +| WARTHOG_APP_HOST | App server host | +| WARTHOG_APP_PORT | App server port | +| WARTHOG_APP_PROTOCOL | App server protocol | + ## Field/Column Decorators All of the auto-generation magic comes from the decorators added to the attributes on your models. Warthog decorators are convenient wrappers around TypeORM decorators (to create DB schema) and TypeGraphQL (to create GraphQL schema). You can find a list of decorators available in the [src/decorators](./src/decorators) folder. Most of these are also used in the [examples](./examples) folder in this project. diff --git a/examples/01-simple-model/.env b/examples/01-simple-model/.env index b7227ae6..7e3161fb 100644 --- a/examples/01-simple-model/.env +++ b/examples/01-simple-model/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-1 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/02-complex-example/.env b/examples/02-complex-example/.env index 397933ca..4f6d1f2f 100644 --- a/examples/02-complex-example/.env +++ b/examples/02-complex-example/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_URL=postgres://postgres:@localhost:5432/warthog-example-2 WARTHOG_DB_SYNCHRONIZE=true WARTHOG_FILTER_BY_DEFAULT=true diff --git a/examples/03-one-to-many-relationship/.env b/examples/03-one-to-many-relationship/.env index a2d2b765..80c41631 100644 --- a/examples/03-one-to-many-relationship/.env +++ b/examples/03-one-to-many-relationship/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-3 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/04-many-to-many-relationship/.env b/examples/04-many-to-many-relationship/.env index 2a05d554..a5390bba 100644 --- a/examples/04-many-to-many-relationship/.env +++ b/examples/04-many-to-many-relationship/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-4 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/05-migrations/.env b/examples/05-migrations/.env index d392c20d..66ee0a7d 100644 --- a/examples/05-migrations/.env +++ b/examples/05-migrations/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-5 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/06-base-service/.env b/examples/06-base-service/.env index bf27c078..17a31d2c 100644 --- a/examples/06-base-service/.env +++ b/examples/06-base-service/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-6 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/07-feature-flags/.env b/examples/07-feature-flags/.env index 0322e439..bb9a2799 100644 --- a/examples/07-feature-flags/.env +++ b/examples/07-feature-flags/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-feature-flag WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/08-performance/.env b/examples/08-performance/.env index b1fb5fae..e11d56be 100644 --- a/examples/08-performance/.env +++ b/examples/08-performance/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-8 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/09-production/.env b/examples/09-production/.env index 6d69bc0e..87067253 100644 --- a/examples/09-production/.env +++ b/examples/09-production/.env @@ -8,9 +8,7 @@ WARTHOG_DB_SUBSCRIBERS=dist/examples/09-production/src/**/*.model.js WARTHOG_RESOLVERS_PATH=dist/examples/09-production/src/**/*.resolver.js WARTHOG_DB_MIGRATIONS=dist/examples/09-production/db/migrations/**/*.js PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 -WARTHOG_APP_PROTOCOL=http +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-9 WARTHOG_DB_HOST=localhost WARTHOG_DB_PASSWORD= diff --git a/examples/09-production/env.yml b/examples/09-production/env.yml index 1475c5c3..21e2fbb5 100644 --- a/examples/09-production/env.yml +++ b/examples/09-production/env.yml @@ -1,8 +1,6 @@ shared: &shared PGUSER: postgres - WARTHOG_APP_HOST: localhost - WARTHOG_APP_PORT: 4100 - WARTHOG_APP_PROTOCOL: http + WARTHOG_API_BASE_URL: http://localhost:4100 WARTHOG_DB_DATABASE: warthog-example-9 WARTHOG_DB_HOST: localhost WARTHOG_DB_PASSWORD: diff --git a/examples/10-subscriptions/.env b/examples/10-subscriptions/.env index 0da0e335..7b3583d4 100644 --- a/examples/10-subscriptions/.env +++ b/examples/10-subscriptions/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-10-subscriptions WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/11-transactions/.env b/examples/11-transactions/.env index 1851b0ce..e2da76ff 100644 --- a/examples/11-transactions/.env +++ b/examples/11-transactions/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-11-transactions WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/examples/14-base-service-v2/.env b/examples/14-base-service-v2/.env index fabec6e9..d1236df9 100644 --- a/examples/14-base-service-v2/.env +++ b/examples/14-base-service-v2/.env @@ -1,7 +1,6 @@ NODE_ENV=development PGUSER=postgres -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-14-base-service-v2 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/src/cli/commands/playground.ts b/src/cli/commands/playground.ts index 146c035b..e31b76a2 100644 --- a/src/cli/commands/playground.ts +++ b/src/cli/commands/playground.ts @@ -7,10 +7,6 @@ export default { run: async (toolbox: WarthogGluegunToolbox) => { const config: any = toolbox.config.load(); - const host = config.get('APP_HOST'); - const port = config.get('APP_PORT'); - const url = `http://${host}:${port}/playground`; - - return open(url, { wait: false }); + return open(config.getApiUrl('/playground'), { wait: false }); } }; diff --git a/src/cli/templates/new/_env.ejs b/src/cli/templates/new/_env.ejs index 72af512f..bf1fd297 100644 --- a/src/cli/templates/new/_env.ejs +++ b/src/cli/templates/new/_env.ejs @@ -3,8 +3,7 @@ PGUSER=postgres NODE_ENV=development WARTHOG_AUTO_OPEN_PLAYGROUND=false WARTHOG_AUTO_GENERATE_FILES=false -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4000 WARTHOG_DB_DATABASE=<%= props.kebabName %> WARTHOG_DB_HOST=localhost WARTHOG_DB_LOGGING=all diff --git a/src/cli/templates/new/env.yml.ejs b/src/cli/templates/new/env.yml.ejs index d6eb4b26..834ae8f5 100644 --- a/src/cli/templates/new/env.yml.ejs +++ b/src/cli/templates/new/env.yml.ejs @@ -8,9 +8,8 @@ test: PGUSER: postgres # Otherwise tries to use system user WARTHOG_DB_LOGGING: none -local_app: &local_app - WARTHOG_APP_HOST: localhost - WARTHOG_APP_PORT: 4100 +local_api: &local_api + WARTHOG_API_BASE_URL: http://localhost:4000 build_env: &build_env WARTHOG_DB_ENTITIES: dist/src/**/*.model.js @@ -38,26 +37,25 @@ prod_db: &prod_db development: <<: *local - <<: *local_app + <<: *local_api <<: *local_db development:build: <<: *local - <<: *local_app + <<: *local_api <<: *build_env <<: *local_db development:prod-like: WARTHOG_APP_PROTOCOL: http <<: *local - <<: *local_app + <<: *local_api <<: *build_env <<: *prod_db production: NODE_ENV: production - WARTHOG_APP_HOST: localhost - WARTHOG_APP_PORT: ${env:PORT} + WARTHOG_API_BASE_URL: ${env:WARTHOG_API_BASE_URL} WARTHOG_INTROSPECTION: true WARTHOG_PLAYGROUND: true <<: *prod_db diff --git a/src/core/config.test.ts b/src/core/config.test.ts index a744b0eb..400ea7e2 100644 --- a/src/core/config.test.ts +++ b/src/core/config.test.ts @@ -1,5 +1,4 @@ import { clearConfig } from '../test/server-vars'; - import { Config } from './config'; describe('Config', () => { @@ -14,7 +13,7 @@ describe('Config', () => { try { new Config({ configSearchPath: __dirname }); } catch (error) { - expect(error.message).toContain('WARTHOG_APP_HOST is required'); + expect(error.message).toContain('WARTHOG_DB_HOST is required'); } }); }); diff --git a/src/core/config.ts b/src/core/config.ts index 08c1047f..ec798160 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -67,7 +67,7 @@ export class Config { WARTHOG_DB_CONNECTION: 'postgres', WARTHOG_ROOT_FOLDER: this.PROJECT_ROOT, WARTHOG_ALLOW_OPTIONAL_ID_ON_CREATE: 'false', - WARTHOG_APP_PROTOCOL: 'https', + WARTHOG_APP_PROTOCOL: 'https', // DEPRECATED WARTHOG_AUTO_GENERATE_FILES: 'false', WARTHOG_AUTO_OPEN_PLAYGROUND: 'false', WARTHOG_INTROSPECTION: 'true', @@ -98,9 +98,10 @@ export class Config { }; this.devDefaults = { - WARTHOG_APP_HOST: 'localhost', - WARTHOG_APP_PORT: '4000', - WARTHOG_APP_PROTOCOL: 'http', + WARTHOG_API_BASE_URL: 'http://localhost:4000', + WARTHOG_APP_HOST: 'localhost', // DEPRECATED + WARTHOG_APP_PORT: '4000', // DEPRECATED + WARTHOG_APP_PROTOCOL: 'http', // DEPRECATED WARTHOG_AUTO_GENERATE_FILES: 'true', WARTHOG_AUTO_OPEN_PLAYGROUND: 'true', WARTHOG_DB_HOST: 'localhost', @@ -119,9 +120,10 @@ export class Config { }; this.testDefaults = { - WARTHOG_APP_HOST: 'localhost', - WARTHOG_APP_PORT: '4000', - WARTHOG_APP_PROTOCOL: 'http', + WARTHOG_API_BASE_URL: 'http://localhost:4000', + WARTHOG_APP_HOST: 'localhost', // DEPRECATED + WARTHOG_APP_PORT: '4000', // DEPRECATED + WARTHOG_APP_PROTOCOL: 'http', // DEPRECATED WARTHOG_AUTO_GENERATE_FILES: 'false', WARTHOG_AUTO_OPEN_PLAYGROUND: 'false', WARTHOG_DB_DATABASE: 'warthog-test', @@ -194,8 +196,6 @@ export class Config { debug('Config', this.config); // Must be after config is set above - this.validateEntryExists('WARTHOG_APP_HOST'); - this.validateEntryExists('WARTHOG_APP_PORT'); this.validateEntryExists('WARTHOG_GENERATED_FOLDER'); this.validateEntryExists('WARTHOG_DB_CONNECTION'); this.validateEntryExists('WARTHOG_DB_HOST'); @@ -259,6 +259,24 @@ export class Config { return config; } + public getApiBaseUrl() { + // Prefer the new API_BASE_URL variable + if (this.get('API_BASE_URL')) { + return this.get('API_BASE_URL'); + } + + // Continue to support passing variables as pieces (from v1 and v2) + return `${this.get('APP_PROTOCOL')}://${this.get('APP_HOST')}:${this.get('APP_PORT')}`; + } + + public getApiUrl(path?: string) { + return new URL(path ?? '', this.getApiBaseUrl()).href; + } + + public getApiPort() { + return new URL('', this.getApiBaseUrl()).port; + } + public get(key?: string) { if (typeof key === 'undefined') { return this.config; diff --git a/src/core/server.ts b/src/core/server.ts index 6cee453f..a41dc5aa 100644 --- a/src/core/server.ts +++ b/src/core/server.ts @@ -32,13 +32,11 @@ export interface ServerOptions { autoGenerateFiles?: boolean; context?: (request: Request) => object; expressApp?: express.Application; - host?: string; generatedFolder?: string; logger?: Logger; middlewares?: any[]; // TODO: fix pubSub?: PubSubEngine | PubSubOptions; openPlayground?: boolean; - port?: string | number; resolversPath?: string[]; warthogImportPath?: string; introspection?: boolean; // DEPRECATED @@ -65,16 +63,6 @@ export class Server { private appOptions: ServerOptions, private dbOptions: Partial = {} ) { - if (typeof this.appOptions.host !== 'undefined') { - process.env.WARTHOG_APP_HOST = this.appOptions.host; - // When we move to v2.0 we'll officially deprecate these config values in favor of ENV vars - // throw new Error( - // '`host` option has been removed, please set `WARTHOG_APP_HOST` environment variable instead' - // ); - } - if (typeof this.appOptions.port !== 'undefined') { - process.env.WARTHOG_APP_PORT = this.appOptions.port.toString(); - } if (typeof this.appOptions.generatedFolder !== 'undefined') { process.env.WARTHOG_GENERATED_FOLDER = this.appOptions.generatedFolder; } @@ -136,13 +124,11 @@ export class Server { } getServerUrl() { - return `${this.config.get('APP_PROTOCOL')}://${this.config.get('APP_HOST')}:${this.config.get( - 'APP_PORT' - )}`; + return this.config.getApiUrl(); } getGraphQLServerUrl() { - return `${this.getServerUrl()}/graphql`; + return this.config.getApiUrl('/graphql'); } async getBinding(options: { origin?: string; token?: string } = {}): Promise { @@ -185,7 +171,7 @@ export class Server { const keepAliveTimeout = Number(this.config.get('WARTHOG_KEEP_ALIVE_TIMEOUT_MS')); const headersTimeout = Number(this.config.get('WARTHOG_HEADERS_TIMEOUT_MS')); - this.httpServer = this.expressApp.listen({ port: this.config.get('APP_PORT') }, () => + this.httpServer = this.expressApp.listen({ port: this.config.getApiPort() }, () => this.logger.info(`🚀 Server ready at ${url}`) ); diff --git a/src/core/tests/dotenv-files/.env b/src/core/tests/dotenv-files/.env index bee39652..608930d8 100644 --- a/src/core/tests/dotenv-files/.env +++ b/src/core/tests/dotenv-files/.env @@ -5,8 +5,7 @@ WARTHOG_C=ENV WARTHOG_D=ENV # required variables -WARTHOG_APP_HOST=localhost -WARTHOG_APP_PORT=4100 +WARTHOG_API_BASE_URL=http://localhost:4100 WARTHOG_DB_DATABASE=warthog-example-1 WARTHOG_DB_USERNAME=postgres WARTHOG_DB_PASSWORD= diff --git a/src/core/tests/valid-config-file/config.test.ts b/src/core/tests/valid-config-file/config.test.ts index 71d40d86..f0733131 100644 --- a/src/core/tests/valid-config-file/config.test.ts +++ b/src/core/tests/valid-config-file/config.test.ts @@ -6,8 +6,7 @@ describe('Config (valid file)', () => { test('loads static config', async () => { // Set some defaults or the constructor will blow up in CI - process.env.WARTHOG_APP_HOST = 'localhost'; - process.env.WARTHOG_APP_PORT = '80'; + process.env.WARTHOG_API_BASE_URL = 'http://localhost:4000'; process.env.WARTHOG_DB_HOST = 'localhost'; config = new Config({ configSearchPath: __dirname }); diff --git a/src/test/functional/server.test.ts b/src/test/functional/server.test.ts index 9121671e..8ad00168 100644 --- a/src/test/functional/server.test.ts +++ b/src/test/functional/server.test.ts @@ -669,7 +669,7 @@ describe('server', () => { }); test('failed transaction should not save any items', async done => { - expect.assertions(2); + expect.assertions(3); const name = `Tx Fail ${runKey}`; const result = await callAPIError( @@ -679,7 +679,8 @@ describe('server', () => { ) ); - expect(result.message).toBe('null value in column "name" violates not-null constraint'); + expect(result.message).toContain('null value in column "name"'); + expect(result.message).toContain('violates not-null constraint'); let savedDishes: Dish[] = []; try { diff --git a/src/test/server-vars.ts b/src/test/server-vars.ts index 91092f7a..f912e0d5 100644 --- a/src/test/server-vars.ts +++ b/src/test/server-vars.ts @@ -19,9 +19,7 @@ export function setTestServerEnvironmentVariables(overrides?: StringMap) { export function getStandardEnvironmentVariables(): StringMap { return { - WARTHOG_APP_HOST: 'localhost', - WARTHOG_APP_PORT: '4000', - WARTHOG_APP_PROTOCOL: 'http', + WARTHOG_API_BASE_URL: 'http://localhost:4000', WARTHOG_AUTO_GENERATE_FILES: 'false', WARTHOG_AUTO_OPEN_PLAYGROUND: 'false', WARTHOG_DB_CONNECTION: 'postgres',