diff --git a/graphql/env/__fixtures__/env.base.json b/graphql/env/__fixtures__/env.base.json deleted file mode 100644 index e44e4fc6d..000000000 --- a/graphql/env/__fixtures__/env.base.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "PGHOST": "env-host", - "PGUSER": "env-user", - "GRAPHILE_SCHEMA": "env_schema_a,env_schema_b", - "FEATURES_SIMPLE_INFLECTION": "true", - "FEATURES_POSTGIS": "false", - "API_ENABLE_META": "true", - "API_IS_PUBLIC": "true", - "API_EXPOSED_SCHEMAS": "public,app", - "API_META_SCHEMAS": "env_meta1,env_meta2", - "API_ANON_ROLE": "env_anon", - "API_ROLE_NAME": "env_role", - "API_DEFAULT_DATABASE_ID": "env_db" -} diff --git a/graphql/env/__fixtures__/env.dedupe.json b/graphql/env/__fixtures__/env.dedupe.json deleted file mode 100644 index dbd27b873..000000000 --- a/graphql/env/__fixtures__/env.dedupe.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "GRAPHILE_SCHEMA": "shared_schema,env_schema", - "API_EXPOSED_SCHEMAS": "shared,env_schema", - "API_META_SCHEMAS": "meta_public,env_meta" -} diff --git a/graphql/env/__fixtures__/env.keys.json b/graphql/env/__fixtures__/env.keys.json deleted file mode 100644 index 3acbf1cb1..000000000 --- a/graphql/env/__fixtures__/env.keys.json +++ /dev/null @@ -1,54 +0,0 @@ -[ - "PGROOTDATABASE", - "PGTEMPLATE", - "DB_PREFIX", - "DB_EXTENSIONS", - "DB_CWD", - "DB_CONNECTION_USER", - "DB_CONNECTION_PASSWORD", - "DB_CONNECTION_ROLE", - "DB_CONNECTIONS_APP_USER", - "DB_CONNECTIONS_APP_PASSWORD", - "DB_CONNECTIONS_ADMIN_USER", - "DB_CONNECTIONS_ADMIN_PASSWORD", - "PORT", - "SERVER_HOST", - "SERVER_TRUST_PROXY", - "SERVER_ORIGIN", - "SERVER_STRICT_AUTH", - "PGHOST", - "PGPORT", - "PGUSER", - "PGPASSWORD", - "PGDATABASE", - "BUCKET_NAME", - "AWS_REGION", - "AWS_ACCESS_KEY", - "AWS_ACCESS_KEY_ID", - "AWS_SECRET_KEY", - "AWS_SECRET_ACCESS_KEY", - "MINIO_ENDPOINT", - "DEPLOYMENT_USE_TX", - "DEPLOYMENT_FAST", - "DEPLOYMENT_USE_PLAN", - "DEPLOYMENT_CACHE", - "DEPLOYMENT_TO_CHANGE", - "MIGRATIONS_CODEGEN_USE_TX", - "JOBS_SCHEMA", - "JOBS_SUPPORT_ANY", - "JOBS_SUPPORTED", - "INTERNAL_GATEWAY_URL", - "INTERNAL_JOBS_CALLBACK_URL", - "INTERNAL_JOBS_CALLBACK_PORT", - "GRAPHILE_SCHEMA", - "FEATURES_SIMPLE_INFLECTION", - "FEATURES_OPPOSITE_BASE_NAMES", - "FEATURES_POSTGIS", - "API_ENABLE_META", - "API_IS_PUBLIC", - "API_EXPOSED_SCHEMAS", - "API_META_SCHEMAS", - "API_ANON_ROLE", - "API_ROLE_NAME", - "API_DEFAULT_DATABASE_ID" -] diff --git a/graphql/env/__tests__/__snapshots__/merge.test.ts.snap b/graphql/env/__tests__/__snapshots__/merge.test.ts.snap index 2f6891824..25b7dd53a 100644 --- a/graphql/env/__tests__/__snapshots__/merge.test.ts.snap +++ b/graphql/env/__tests__/__snapshots__/merge.test.ts.snap @@ -26,6 +26,7 @@ exports[`getEnvOptions merges pgpm defaults, graphql defaults, config, env, and "awsSecretKey": "minioadmin", "bucketName": "test-bucket", "minioEndpoint": "http://localhost:9000", + "provider": "minio", }, "db": { "connections": { diff --git a/graphql/env/__tests__/merge.test.ts b/graphql/env/__tests__/merge.test.ts index 649065850..d1edcd7e2 100644 --- a/graphql/env/__tests__/merge.test.ts +++ b/graphql/env/__tests__/merge.test.ts @@ -2,31 +2,15 @@ import { getEnvOptions } from '../src/merge'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { - applyEnvFixture, - loadEnvFixture, - loadEnvKeys, - restoreEnv, - snapshotAndClearEnv -} from '../../../pgpm/env/__tests__/test-utils'; const writeConfig = (dir: string, config: Record): void => { fs.writeFileSync(path.join(dir, 'pgpm.json'), JSON.stringify(config, null, 2)); }; -const fixturesDir = path.join(__dirname, '..', '__fixtures__'); -const envKeys = loadEnvKeys(fixturesDir, 'env.keys.json'); - describe('getEnvOptions', () => { - let envSnapshot: Record; let tempDir = ''; - beforeEach(() => { - envSnapshot = snapshotAndClearEnv(envKeys); - }); - afterEach(() => { - restoreEnv(envSnapshot); if (tempDir) { fs.rmSync(tempDir, { recursive: true, force: true }); tempDir = ''; @@ -56,8 +40,20 @@ describe('getEnvOptions', () => { } }); - const fixtureEnv = loadEnvFixture(fixturesDir, 'env.base.json'); - applyEnvFixture(fixtureEnv); + const testEnv: NodeJS.ProcessEnv = { + PGHOST: 'env-host', + PGUSER: 'env-user', + GRAPHILE_SCHEMA: 'env_schema_a,env_schema_b', + FEATURES_SIMPLE_INFLECTION: 'true', + FEATURES_POSTGIS: 'false', + API_ENABLE_META: 'true', + API_IS_PUBLIC: 'true', + API_EXPOSED_SCHEMAS: 'public,app', + API_META_SCHEMAS: 'env_meta1,env_meta2', + API_ANON_ROLE: 'env_anon', + API_ROLE_NAME: 'env_role', + API_DEFAULT_DATABASE_ID: 'env_db' + }; const result = getEnvOptions( { @@ -81,7 +77,8 @@ describe('getEnvOptions', () => { defaultDatabaseId: 'override_db' } }, - tempDir + tempDir, + testEnv ); expect(result).toMatchSnapshot(); @@ -99,8 +96,11 @@ describe('getEnvOptions', () => { } }); - const fixtureEnv = loadEnvFixture(fixturesDir, 'env.dedupe.json'); - applyEnvFixture(fixtureEnv); + const testEnv: NodeJS.ProcessEnv = { + GRAPHILE_SCHEMA: 'shared_schema,env_schema', + API_EXPOSED_SCHEMAS: 'shared,env_schema', + API_META_SCHEMAS: 'meta_public,env_meta' + }; const result = getEnvOptions( { @@ -112,7 +112,8 @@ describe('getEnvOptions', () => { metaSchemas: ['env_meta', 'override_meta'] } }, - tempDir + tempDir, + testEnv ); expect(result.graphile?.schema).toEqual([ diff --git a/graphql/env/src/env.ts b/graphql/env/src/env.ts index 976238d16..6c846c7da 100644 --- a/graphql/env/src/env.ts +++ b/graphql/env/src/env.ts @@ -9,7 +9,10 @@ const parseEnvBoolean = (val?: string): boolean | undefined => { return ['true', '1', 'yes'].includes(val.toLowerCase()); }; -export const getGraphQLEnvVars = (): Partial => { +/** + * @param env - Environment object to read from (defaults to process.env for backwards compatibility) + */ +export const getGraphQLEnvVars = (env: NodeJS.ProcessEnv = process.env): Partial => { const { GRAPHILE_SCHEMA, @@ -24,7 +27,7 @@ export const getGraphQLEnvVars = (): Partial => { API_ANON_ROLE, API_ROLE_NAME, API_DEFAULT_DATABASE_ID, - } = process.env; + } = env; return { graphile: { diff --git a/graphql/env/src/merge.ts b/graphql/env/src/merge.ts index 1cde8f0e6..c9e1e235a 100644 --- a/graphql/env/src/merge.ts +++ b/graphql/env/src/merge.ts @@ -13,16 +13,21 @@ import { getGraphQLEnvVars } from './env'; * * This is the main entry point for Constructive packages that need * both core PGPM options and GraphQL/Graphile options. + * + * @param overrides - Runtime overrides to apply last + * @param cwd - Working directory for config file resolution + * @param env - Environment object to read from (defaults to process.env for backwards compatibility) */ export const getEnvOptions = ( overrides: Partial = {}, - cwd: string = process.cwd() + cwd: string = process.cwd(), + env: NodeJS.ProcessEnv = process.env ): ConstructiveOptions => { // Get core PGPM options (includes pgpmDefaults + config + core env vars) - const coreOptions = getPgpmEnvOptions({}, cwd); + const coreOptions = getPgpmEnvOptions({}, cwd, env); // Get GraphQL-specific env vars - const graphqlEnvOptions = getGraphQLEnvVars(); + const graphqlEnvOptions = getGraphQLEnvVars(env); // Load config again to get any GraphQL-specific config // Config files can contain Constructive options (graphile, features, api) diff --git a/pgpm/env/__fixtures__/env.base.json b/pgpm/env/__fixtures__/env.base.json deleted file mode 100644 index ebf461056..000000000 --- a/pgpm/env/__fixtures__/env.base.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "PGHOST": "env-host", - "PGPORT": "6543", - "PGUSER": "env-user", - "PGPASSWORD": "env-pass", - "DB_PREFIX": "env-", - "DB_CONNECTIONS_APP_PASSWORD": "env-app-pass", - "DB_CONNECTIONS_ADMIN_USER": "env-admin-user", - "PORT": "7777", - "DEPLOYMENT_FAST": "false", - "JOBS_SUPPORT_ANY": "false", - "JOBS_SUPPORTED": "alpha,beta" -} diff --git a/pgpm/env/__fixtures__/env.dedupe.json b/pgpm/env/__fixtures__/env.dedupe.json deleted file mode 100644 index 84ee0c6bd..000000000 --- a/pgpm/env/__fixtures__/env.dedupe.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "DB_EXTENSIONS": "postgis,pgcrypto", - "JOBS_SUPPORTED": "beta,gamma,delta" -} diff --git a/pgpm/env/__fixtures__/env.keys.json b/pgpm/env/__fixtures__/env.keys.json deleted file mode 100644 index f1fd1fca5..000000000 --- a/pgpm/env/__fixtures__/env.keys.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - "PGROOTDATABASE", - "PGTEMPLATE", - "DB_PREFIX", - "DB_EXTENSIONS", - "DB_CWD", - "DB_CONNECTION_USER", - "DB_CONNECTION_PASSWORD", - "DB_CONNECTION_ROLE", - "DB_CONNECTIONS_APP_USER", - "DB_CONNECTIONS_APP_PASSWORD", - "DB_CONNECTIONS_ADMIN_USER", - "DB_CONNECTIONS_ADMIN_PASSWORD", - "PORT", - "SERVER_HOST", - "SERVER_TRUST_PROXY", - "SERVER_ORIGIN", - "SERVER_STRICT_AUTH", - "PGHOST", - "PGPORT", - "PGUSER", - "PGPASSWORD", - "PGDATABASE", - "BUCKET_NAME", - "AWS_REGION", - "AWS_ACCESS_KEY", - "AWS_ACCESS_KEY_ID", - "AWS_SECRET_KEY", - "AWS_SECRET_ACCESS_KEY", - "MINIO_ENDPOINT", - "DEPLOYMENT_USE_TX", - "DEPLOYMENT_FAST", - "DEPLOYMENT_USE_PLAN", - "DEPLOYMENT_CACHE", - "DEPLOYMENT_TO_CHANGE", - "MIGRATIONS_CODEGEN_USE_TX", - "JOBS_SCHEMA", - "JOBS_SUPPORT_ANY", - "JOBS_SUPPORTED", - "INTERNAL_GATEWAY_URL", - "INTERNAL_JOBS_CALLBACK_URL", - "INTERNAL_JOBS_CALLBACK_PORT" -] diff --git a/pgpm/env/__tests__/merge.test.ts b/pgpm/env/__tests__/merge.test.ts index 46fc25041..f6db3d26e 100644 --- a/pgpm/env/__tests__/merge.test.ts +++ b/pgpm/env/__tests__/merge.test.ts @@ -3,21 +3,11 @@ import { pgpmDefaults, PgpmOptions } from '@pgpmjs/types'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { - applyEnvFixture, - loadEnvFixture, - loadEnvKeys, - restoreEnv, - snapshotAndClearEnv -} from './test-utils'; const writeConfig = (dir: string, config: Record): void => { fs.writeFileSync(path.join(dir, 'pgpm.json'), JSON.stringify(config, null, 2)); }; -const fixturesDir = path.join(__dirname, '..', '__fixtures__'); -const envKeys = loadEnvKeys(fixturesDir, 'env.keys.json'); - describe('getConnEnvOptions', () => { describe('roles resolution', () => { it('should always return roles with default values when no overrides provided', () => { @@ -120,16 +110,10 @@ describe('getConnEnvOptions', () => { }); describe('getEnvOptions', () => { - let envSnapshot: Record; let tempDir = ''; type PgpmOptionsWithPackages = PgpmOptions & { packages?: string[] }; - beforeEach(() => { - envSnapshot = snapshotAndClearEnv(envKeys); - }); - afterEach(() => { - restoreEnv(envSnapshot); if (tempDir) { fs.rmSync(tempDir, { recursive: true, force: true }); tempDir = ''; @@ -159,8 +143,19 @@ describe('getEnvOptions', () => { } }); - const fixtureEnv = loadEnvFixture(fixturesDir, 'env.base.json'); - applyEnvFixture(fixtureEnv); + const testEnv: NodeJS.ProcessEnv = { + PGHOST: 'env-host', + PGPORT: '6543', + PGUSER: 'env-user', + PGPASSWORD: 'env-pass', + DB_PREFIX: 'env-', + DB_CONNECTIONS_APP_PASSWORD: 'env-app-pass', + DB_CONNECTIONS_ADMIN_USER: 'env-admin-user', + PORT: '7777', + DEPLOYMENT_FAST: 'false', + JOBS_SUPPORT_ANY: 'false', + JOBS_SUPPORTED: 'alpha,beta' + }; const result = getEnvOptions( { @@ -178,7 +173,8 @@ describe('getEnvOptions', () => { cache: true } }, - tempDir + tempDir, + testEnv ); expect(result).toMatchSnapshot(); @@ -201,8 +197,10 @@ describe('getEnvOptions', () => { packages: ['testing/*', 'packages/*'] }); - const fixtureEnv = loadEnvFixture(fixturesDir, 'env.dedupe.json'); - applyEnvFixture(fixtureEnv); + const testEnv: NodeJS.ProcessEnv = { + DB_EXTENSIONS: 'postgis,pgcrypto', + JOBS_SUPPORTED: 'beta,gamma,delta' + }; const overrides: PgpmOptionsWithPackages = { db: { @@ -219,7 +217,7 @@ describe('getEnvOptions', () => { packages: ['testing/*', 'extensions/*'] }; - const result = getEnvOptions(overrides, tempDir) as PgpmOptionsWithPackages; + const result = getEnvOptions(overrides, tempDir, testEnv) as PgpmOptionsWithPackages; expect(result.db?.extensions).toEqual([ 'uuid', diff --git a/pgpm/env/__tests__/test-utils.ts b/pgpm/env/__tests__/test-utils.ts deleted file mode 100644 index d4cca240a..000000000 --- a/pgpm/env/__tests__/test-utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -export const loadEnvFixture = ( - fixtureDir: string, - name: string -): Record => { - const filePath = path.join(fixtureDir, name); - return JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record; -}; - -export const loadEnvKeys = (fixtureDir: string, name: string): string[] => { - const filePath = path.join(fixtureDir, name); - return JSON.parse(fs.readFileSync(filePath, 'utf8')) as string[]; -}; - -export const snapshotAndClearEnv = ( - keys: string[] -): Record => { - const snapshot: Record = {}; - for (const key of keys) { - snapshot[key] = process.env[key]; - delete process.env[key]; - } - return snapshot; -}; - -export const applyEnvFixture = ( - fixture: Record -): Record => { - const previous: Record = {}; - for (const [key, value] of Object.entries(fixture)) { - previous[key] = process.env[key]; - process.env[key] = value; - } - return previous; -}; - -export const restoreEnv = (snapshot: Record): void => { - for (const [key, value] of Object.entries(snapshot)) { - if (value === undefined) { - delete process.env[key]; - } else { - process.env[key] = value; - } - } -}; diff --git a/pgpm/env/src/env.ts b/pgpm/env/src/env.ts index 4b34c8c11..e2dead9a0 100644 --- a/pgpm/env/src/env.ts +++ b/pgpm/env/src/env.ts @@ -21,8 +21,10 @@ const parseEnvStringArray = (val?: string): string[] | undefined => { /** * Parse core PGPM environment variables. * GraphQL-related env vars (GRAPHILE_*, FEATURES_*, API_*) are handled by @constructive-io/graphql-env. + * + * @param env - Environment object to read from (defaults to process.env for backwards compatibility) */ -export const getEnvVars = (): PgpmOptions => { +export const getEnvVars = (env: NodeJS.ProcessEnv = process.env): PgpmOptions => { const { PGROOTDATABASE, PGTEMPLATE, @@ -74,7 +76,7 @@ export const getEnvVars = (): PgpmOptions => { INTERNAL_GATEWAY_URL, INTERNAL_JOBS_CALLBACK_URL, INTERNAL_JOBS_CALLBACK_PORT - } = process.env; + } = env; return { db: { diff --git a/pgpm/env/src/merge.ts b/pgpm/env/src/merge.ts index d2d8f06b9..1e0a08074 100644 --- a/pgpm/env/src/merge.ts +++ b/pgpm/env/src/merge.ts @@ -12,10 +12,18 @@ import { mergeArraysUnique } from './utils'; * 4. Runtime overrides * * For Constructive applications that need GraphQL options, use @constructive-io/graphql-env instead. + * + * @param overrides - Runtime overrides to apply last + * @param cwd - Working directory for config file resolution + * @param env - Environment object to read from (defaults to process.env for backwards compatibility) */ -export const getEnvOptions = (overrides: PgpmOptions = {}, cwd: string = process.cwd()): PgpmOptions => { +export const getEnvOptions = ( + overrides: PgpmOptions = {}, + cwd: string = process.cwd(), + env: NodeJS.ProcessEnv = process.env +): PgpmOptions => { const configOptions = loadConfigSync(cwd); - const envOptions = getEnvVars(); + const envOptions = getEnvVars(env); return deepmerge.all([pgpmDefaults, configOptions, envOptions, overrides], { arrayMerge: mergeArraysUnique