From fd9a52b33f9e51012449a9180d5c0bdd905e8d39 Mon Sep 17 00:00:00 2001 From: Zach Bolan Date: Thu, 18 May 2023 12:51:39 -0700 Subject: [PATCH 1/3] Templatized ENV in config --- package-lock.json | 4 +- packages/cli/src/config.test.ts | 89 +++++++++++++++++++++++++++++++++ packages/cli/src/config.ts | 62 +++++++++++++++++++---- 3 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 packages/cli/src/config.test.ts diff --git a/package-lock.json b/package-lock.json index fe810787..99b0c6f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12696,7 +12696,7 @@ "devDependencies": { "@types/chalk": "^2.2.0", "@types/debug": "^4.1.4", - "esbuild": "^0.17.19" + "esbuild": "^0.17.4" }, "engines": { "node": ">=14.16" @@ -15610,7 +15610,7 @@ "@types/debug": "^4.1.4", "chalk": "^4.1.0", "debug": "^4.1.1", - "esbuild": "0.17.19" + "esbuild": "^0.17.4" } }, "@pgtyped/wire": { diff --git a/packages/cli/src/config.test.ts b/packages/cli/src/config.test.ts new file mode 100644 index 00000000..e95ca714 --- /dev/null +++ b/packages/cli/src/config.test.ts @@ -0,0 +1,89 @@ +import { DBConfigArgs, getEnvDBConfig } from './config'; + +describe('getEnvDBConfig', () => { + const env = { + ...process.env, + }; + + beforeEach(() => { + // Default PG + process.env.PGHOST = 'pg_host'; + process.env.PGUSER = 'pg_user'; + process.env.PGPASSWORD = 'pg_password'; + process.env.PGDATABASE = 'pg_db_name'; + process.env.PGPORT = '1111'; + process.env.PGURI = 'pg_uri'; + + // Custom + process.env.URI_ENV = 'host_from_env'; + process.env.HOST_ENV = 'host_from_env'; + process.env.USER_ENV = 'user_from_env'; + process.env.PASSWORD_ENV = 'password_from_env'; + process.env.DB_NAME_ENV = 'db_name_from_env'; + process.env.PORT_ENV = '2222'; + process.env.URI_ENV = 'uri_from_env'; + }); + + afterEach(() => { + process.env = env; + }); + + test('Parses template ENV', () => { + const dbConfig: Partial = { + host: '{{HOST_ENV}}', + user: '{{USER_ENV}}', + password: '{{PASSWORD_ENV}}', + dbName: '{{DB_NAME_ENV}}', + port: '{{PORT_ENV}}', + uri: '{{URI_ENV}}', + }; + + const parsedConfig = getEnvDBConfig(dbConfig); + + expect(parsedConfig).toEqual({ + host: 'host_from_env', + user: 'user_from_env', + password: 'password_from_env', + dbName: 'db_name_from_env', + port: 2222, + uri: 'uri_from_env', + }); + }); + + test('Parses default ENV', () => { + const dbConfig: Partial = {}; + + const parsedConfig = getEnvDBConfig(dbConfig); + + expect(parsedConfig).toEqual({ + host: 'pg_host', + user: 'pg_user', + password: 'pg_password', + dbName: 'pg_db_name', + port: 1111, + uri: 'pg_uri', + }); + }); + + test('Parses default ENV with invalid templates', () => { + // All invalid templates + const dbConfig: Partial = { + host: '{{{HOST_ENV}}', + user: '{{USER_ENV}}}', + password: 'invalid', + dbName: '', + port: '1234', + uri: '_', + }; + const parsedConfig = getEnvDBConfig(dbConfig); + + expect(parsedConfig).toEqual({ + host: undefined, + user: undefined, + password: 'pg_password', + dbName: 'pg_db_name', + port: 1111, + uri: 'pg_uri', + }); + }); +}); diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index 7a73eae1..b0abc6cd 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -45,7 +45,7 @@ const configParser = t.type({ t.type({ host: t.union([t.string, t.undefined]), password: t.union([t.string, t.undefined]), - port: t.union([t.number, t.undefined]), + port: t.union([t.number, t.string, t.undefined]), user: t.union([t.string, t.undefined]), dbName: t.union([t.string, t.undefined]), ssl: t.union([t.UnknownRecord, t.boolean, t.undefined]), @@ -67,8 +67,44 @@ const configParser = t.type({ ]), }); +export type DBConfigArgs = { + host: string; + user: string; + password: string; + dbName: string; + port: number | string; + ssl: tls.ConnectionOptions | boolean; + uri: string; +}; + export type IConfig = typeof configParser._O; +function parseEnvTemplate(input?: string): string | undefined { + const templateStringRegex = new RegExp('{{\\w+}}', 'g'); + const result = input ? templateStringRegex.exec(input) : undefined; + return result + ? result.input.substring(2, result.input.length - 2) + : undefined; +} + +export function getEnvDBConfig(dBConfig: Partial) { + const host = parseEnvTemplate(dBConfig.host) ?? 'PGHOST'; + const user = parseEnvTemplate(dBConfig.user) ?? 'PGUSER'; + const password = parseEnvTemplate(dBConfig.password) ?? 'PGPASSWORD'; + const dbName = parseEnvTemplate(dBConfig.dbName) ?? 'PGDATABASE'; + const port = parseEnvTemplate(dBConfig?.port?.toString()) ?? 'PGPORT'; + const uri = parseEnvTemplate(dBConfig.uri) ?? 'PGURI'; + + return { + host: process.env[host], + user: process.env[user], + password: process.env[password], + dbName: process.env[dbName], + port: process.env[port] ? Number(process.env[port]) : undefined, + uri: process.env[uri] ?? process.env.DATABASE_URL, + }; +} + export interface ParsedConfig { db: { host: string; @@ -162,15 +198,6 @@ export function parseConfig( port: 5432, }; - const envDBConfig = { - host: process.env.PGHOST, - user: process.env.PGUSER, - password: process.env.PGPASSWORD, - dbName: process.env.PGDATABASE, - port: process.env.PGPORT ? Number(process.env.PGPORT) : undefined, - uri: process.env.PGURI ?? process.env.DATABASE_URL, - }; - const { db = defaultDBConfig, dbUrl: configDbUri, @@ -182,6 +209,8 @@ export function parseConfig( typesOverrides, } = configObject as IConfig; + const envDBConfig = getEnvDBConfig(db); + // CLI connectionUri flag takes precedence over the env and config one const dbUri = argConnectionUri || envDBConfig.uri || configDbUri; @@ -196,7 +225,18 @@ export function parseConfig( ); } - const finalDBConfig = merge(defaultDBConfig, db, urlDBConfig, envDBConfig); + // The port may be a template string + const dbConfig = { + ...db, + port: typeof db.port === 'string' ? Number(db.port) : db.port, + }; + + const finalDBConfig = merge( + defaultDBConfig, + dbConfig, + urlDBConfig, + envDBConfig, + ); const parsedTypesOverrides: Record> = {}; From 1e39826af9417977f8c3cb3b1382e7dbafe57ac0 Mon Sep 17 00:00:00 2001 From: Zach Bolan Date: Thu, 18 May 2023 13:30:18 -0700 Subject: [PATCH 2/3] little cleanup --- packages/cli/src/config.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/config.test.ts b/packages/cli/src/config.test.ts index e95ca714..b8e0f626 100644 --- a/packages/cli/src/config.test.ts +++ b/packages/cli/src/config.test.ts @@ -37,7 +37,6 @@ describe('getEnvDBConfig', () => { port: '{{PORT_ENV}}', uri: '{{URI_ENV}}', }; - const parsedConfig = getEnvDBConfig(dbConfig); expect(parsedConfig).toEqual({ @@ -52,7 +51,6 @@ describe('getEnvDBConfig', () => { test('Parses default ENV', () => { const dbConfig: Partial = {}; - const parsedConfig = getEnvDBConfig(dbConfig); expect(parsedConfig).toEqual({ From ec01607c93c7a04ec0f9944f93be001aafae9f40 Mon Sep 17 00:00:00 2001 From: Zach Bolan Date: Thu, 18 May 2023 13:57:33 -0700 Subject: [PATCH 3/3] add test for extra uri env var --- packages/cli/src/config.test.ts | 17 +++++++++++++++++ packages/cli/src/config.ts | 4 +--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/config.test.ts b/packages/cli/src/config.test.ts index b8e0f626..fe09b21d 100644 --- a/packages/cli/src/config.test.ts +++ b/packages/cli/src/config.test.ts @@ -13,6 +13,7 @@ describe('getEnvDBConfig', () => { process.env.PGDATABASE = 'pg_db_name'; process.env.PGPORT = '1111'; process.env.PGURI = 'pg_uri'; + process.env.DATABASE_URL = 'pg_url'; // Custom process.env.URI_ENV = 'host_from_env'; @@ -63,6 +64,22 @@ describe('getEnvDBConfig', () => { }); }); + test('Parses default ENV port=DATABASE_URL', () => { + process.env.PGURI = undefined; + + const dbConfig: Partial = {}; + const parsedConfig = getEnvDBConfig(dbConfig); + + expect(parsedConfig).toEqual({ + host: 'pg_host', + user: 'pg_user', + password: 'pg_password', + dbName: 'pg_db_name', + port: 1111, + uri: 'pg_url', + }); + }); + test('Parses default ENV with invalid templates', () => { // All invalid templates const dbConfig: Partial = { diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index b0abc6cd..663283d6 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -82,9 +82,7 @@ export type IConfig = typeof configParser._O; function parseEnvTemplate(input?: string): string | undefined { const templateStringRegex = new RegExp('{{\\w+}}', 'g'); const result = input ? templateStringRegex.exec(input) : undefined; - return result - ? result.input.substring(2, result.input.length - 2) - : undefined; + return result?.input?.substring(2, result.input.length - 2); } export function getEnvDBConfig(dBConfig: Partial) {