From 160ff1a532c78479db438abcc9ee72515b0f6295 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 31 Mar 2023 11:35:51 +0200 Subject: [PATCH] config-loader: refactor source types Signed-off-by: Patrik Oldsberg --- packages/config-loader/src/loader.ts | 2 +- .../src/sources/ConfigSources.ts | 73 ++++++++++++------- .../src/sources/FileConfigSource.ts | 10 +-- .../src/sources/RemoteConfigSource.ts | 8 +- packages/config-loader/src/sources/index.ts | 2 +- .../src/sources/transform/apply.ts | 11 +-- .../src/sources/transform/include.ts | 4 +- .../src/sources/transform/substitution.ts | 6 +- packages/config-loader/src/sources/types.ts | 2 +- 9 files changed, 70 insertions(+), 48 deletions(-) diff --git a/packages/config-loader/src/loader.ts b/packages/config-loader/src/loader.ts index 685c6851bf..8cbfd1c9e0 100644 --- a/packages/config-loader/src/loader.ts +++ b/packages/config-loader/src/loader.ts @@ -92,7 +92,7 @@ export async function loadConfig( options: LoadConfigOptions, ): Promise { const source = ConfigSources.default({ - envFunc: options.experimentalEnvFunc, + substitutionFunc: options.experimentalEnvFunc, remote: options.remote, rootDir: options.configRoot, argv: options.configTargets.flatMap(t => [ diff --git a/packages/config-loader/src/sources/ConfigSources.ts b/packages/config-loader/src/sources/ConfigSources.ts index 54a6aa4d90..67cf8d59e7 100644 --- a/packages/config-loader/src/sources/ConfigSources.ts +++ b/packages/config-loader/src/sources/ConfigSources.ts @@ -21,15 +21,45 @@ import parseArgs from 'minimist'; import { EnvConfigSource } from './EnvConfigSource'; import { FileConfigSource } from './FileConfigSource'; import { MergedConfigSource } from './MergedConfigSource'; -import { RemoteConfigSource } from './RemoteConfigSource'; -import { ConfigSource, EnvFunc } from './types'; +import { + RemoteConfigSource, + RemoteConfigSourceOptions, +} from './RemoteConfigSource'; +import { ConfigSource, SubstitutionFunc } from './types'; import { ObservableConfigProxy } from './ObservableConfigProxy'; -import { LoadConfigOptionsRemote } from '../loader'; + +export type ConfigSourceTarget = + | { + type: 'path'; + target: string; + } + | { + type: 'url'; + target: string; + }; + +export interface ClosableConfig extends Config { + close(): void; +} + +export interface BaseConfigSourcesOptions { + rootDir: string; + remote?: Pick; + substitutionFunc?: SubstitutionFunc; +} + +export interface ConfigSourcesDefaultForTargetsOptions + extends BaseConfigSourcesOptions { + targets: ConfigSourceTarget[]; +} + +export interface ConfigSourcesDefaultOptions extends BaseConfigSourcesOptions { + argv?: string[]; + env?: Record; +} export class ConfigSources { - static parseArgs( - argv: string[] = process.argv, - ): Array<{ type: 'url' | 'path'; target: string }> { + static parseArgs(argv: string[] = process.argv): Array { const args: string[] = [parseArgs(argv).config].flat().filter(Boolean); return args.map(target => { try { @@ -42,12 +72,9 @@ export class ConfigSources { }); } - static defaultForTargets(options: { - rootDir: string; - targets: Array<{ type: 'url' | 'path'; target: string }>; - remote?: LoadConfigOptionsRemote; - envFunc?: EnvFunc; - }): ConfigSource { + static defaultForTargets( + options: ConfigSourcesDefaultForTargetsOptions, + ): ConfigSource { const argSources = options.targets.map(arg => { if (arg.type === 'url') { if (!options.remote) { @@ -57,13 +84,13 @@ export class ConfigSources { } return RemoteConfigSource.create({ url: arg.target, - envFunc: options.envFunc, + substitutionFunc: options.substitutionFunc, reloadIntervalSeconds: options.remote.reloadIntervalSeconds, }); } return FileConfigSource.create({ path: arg.target, - envFunc: options.envFunc, + substitutionFunc: options.substitutionFunc, }); }); @@ -74,14 +101,14 @@ export class ConfigSources { argSources.push( FileConfigSource.create({ path: defaultPath, - envFunc: options.envFunc, + substitutionFunc: options.substitutionFunc, }), ); if (fs.pathExistsSync(localPath)) { argSources.push( FileConfigSource.create({ path: localPath, - envFunc: options.envFunc, + substitutionFunc: options.substitutionFunc, }), ); } @@ -90,13 +117,7 @@ export class ConfigSources { return this.merge(argSources); } - static default(options: { - rootDir: string; - argv?: string[]; - remote?: LoadConfigOptionsRemote; - env?: Record; - envFunc?: EnvFunc; - }): ConfigSource { + static default(options: ConfigSourcesDefaultOptions): ConfigSource { const argSource = this.defaultForTargets({ ...options, targets: this.parseArgs(options.argv), @@ -111,7 +132,7 @@ export class ConfigSources { return MergedConfigSource.from(sources); } - static toConfig(source: ConfigSource): Promise { + static toConfig(source: ConfigSource): Promise { return new Promise(async (resolve, reject) => { let config: ObservableConfigProxy | undefined = undefined; try { @@ -133,7 +154,3 @@ export class ConfigSources { }); } } - -interface LiveConfig extends Config { - close(): void; -} diff --git a/packages/config-loader/src/sources/FileConfigSource.ts b/packages/config-loader/src/sources/FileConfigSource.ts index ffd80a3424..765b943f42 100644 --- a/packages/config-loader/src/sources/FileConfigSource.ts +++ b/packages/config-loader/src/sources/FileConfigSource.ts @@ -22,7 +22,7 @@ import { AsyncConfigSourceIterator, ConfigSource, ConfigSourceData, - EnvFunc, + SubstitutionFunc, ReadConfigDataOptions, } from './types'; import { createConfigTransformer } from './transform'; @@ -35,7 +35,7 @@ export interface FileConfigSourceOptions { /** * Function used to resolve environment variables. */ - envFunc?: EnvFunc; + substitutionFunc?: SubstitutionFunc; } export class FileConfigSource implements ConfigSource { @@ -47,11 +47,11 @@ export class FileConfigSource implements ConfigSource { } readonly #path: string; - readonly #envFunc?: EnvFunc; + readonly #substitutionFunc?: SubstitutionFunc; private constructor(options: FileConfigSourceOptions) { this.#path = options.path; - this.#envFunc = options.envFunc; + this.#substitutionFunc = options.substitutionFunc; } // Work is duplicated across each read, in practice that should not @@ -71,7 +71,7 @@ export class FileConfigSource implements ConfigSource { const dir = dirname(this.#path); const transformer = createConfigTransformer({ - envFunc: this.#envFunc, + substitutionFunc: this.#substitutionFunc, async readFile(path) { const fullPath = resolvePath(dir, path); // Any files discovered while reading this config should be watched too diff --git a/packages/config-loader/src/sources/RemoteConfigSource.ts b/packages/config-loader/src/sources/RemoteConfigSource.ts index 5112f687dd..1180ed2e80 100644 --- a/packages/config-loader/src/sources/RemoteConfigSource.ts +++ b/packages/config-loader/src/sources/RemoteConfigSource.ts @@ -23,7 +23,7 @@ import { ConfigTransformer, createConfigTransformer } from './transform'; import { AsyncConfigSourceIterator, ConfigSource, - EnvFunc, + SubstitutionFunc, ReadConfigDataOptions, } from './types'; @@ -32,7 +32,7 @@ const DEFAULT_RELOAD_INTERVAL_SECONDS = 60; export interface RemoteConfigSourceOptions { url: string; reloadIntervalSeconds?: number; - envFunc?: EnvFunc; + substitutionFunc?: SubstitutionFunc; } export class RemoteConfigSource implements ConfigSource { @@ -56,7 +56,9 @@ export class RemoteConfigSource implements ConfigSource { this.#url = options.url; this.#reloadIntervalSeconds = options.reloadIntervalSeconds ?? DEFAULT_RELOAD_INTERVAL_SECONDS; - this.#transformer = createConfigTransformer({ envFunc: options.envFunc }); + this.#transformer = createConfigTransformer({ + substitutionFunc: options.substitutionFunc, + }); } async *readConfigData( diff --git a/packages/config-loader/src/sources/index.ts b/packages/config-loader/src/sources/index.ts index faa3f6af41..9b1429639d 100644 --- a/packages/config-loader/src/sources/index.ts +++ b/packages/config-loader/src/sources/index.ts @@ -25,7 +25,7 @@ export type { RemoteConfigSourceOptions } from './RemoteConfigSource'; export { StaticConfigSource } from './StaticConfigSource'; export type { StaticConfigSourceOptions } from './StaticConfigSource'; export type { - EnvFunc, + SubstitutionFunc as EnvFunc, ConfigSource, ConfigSourceData, ReadConfigDataOptions, diff --git a/packages/config-loader/src/sources/transform/apply.ts b/packages/config-loader/src/sources/transform/apply.ts index 12de4b1286..d0d8650fe7 100644 --- a/packages/config-loader/src/sources/transform/apply.ts +++ b/packages/config-loader/src/sources/transform/apply.ts @@ -20,7 +20,7 @@ import { TransformContext, TransformFunc } from './types'; import { isObject } from './utils'; import { createSubstitutionTransform } from './substitution'; import { createIncludeTransform } from './include'; -import { EnvFunc } from '../types'; +import { SubstitutionFunc } from '../types'; /** * Applies a set of transforms to raw configuration data. @@ -102,15 +102,16 @@ export type ConfigTransformer = ( /** @internal */ export function createConfigTransformer(options: { - envFunc?: EnvFunc; + substitutionFunc?: SubstitutionFunc; readFile?(path: string): Promise; }): ConfigTransformer { - const { envFunc = async name => process.env[name], readFile } = options; - const substitutionTransform = createSubstitutionTransform(envFunc); + const { substitutionFunc = async name => process.env[name], readFile } = + options; + const substitutionTransform = createSubstitutionTransform(substitutionFunc); const transforms = [substitutionTransform]; if (readFile) { const includeTransform = createIncludeTransform( - envFunc, + substitutionFunc, readFile, substitutionTransform, ); diff --git a/packages/config-loader/src/sources/transform/include.ts b/packages/config-loader/src/sources/transform/include.ts index c3618503a9..4a4122aefc 100644 --- a/packages/config-loader/src/sources/transform/include.ts +++ b/packages/config-loader/src/sources/transform/include.ts @@ -19,7 +19,7 @@ import { extname, dirname, resolve as resolvePath } from 'path'; import { JsonObject, JsonValue } from '@backstage/types'; import { isObject } from './utils'; import { TransformFunc, ReadFileFunc } from './types'; -import { EnvFunc } from '../types'; +import { SubstitutionFunc } from '../types'; // Parsers for each type of included file const includeFileParser: { @@ -34,7 +34,7 @@ const includeFileParser: { * Transforms a include description into the actual included value. */ export function createIncludeTransform( - env: EnvFunc, + env: SubstitutionFunc, readFile: ReadFileFunc, substitute: TransformFunc, ): TransformFunc { diff --git a/packages/config-loader/src/sources/transform/substitution.ts b/packages/config-loader/src/sources/transform/substitution.ts index a60ebae18b..f10e5dc8e7 100644 --- a/packages/config-loader/src/sources/transform/substitution.ts +++ b/packages/config-loader/src/sources/transform/substitution.ts @@ -16,14 +16,16 @@ import { JsonValue } from '@backstage/types'; import { TransformFunc } from './types'; -import { EnvFunc } from '../types'; +import { SubstitutionFunc } from '../types'; /** * A environment variable substitution transform that transforms e.g. 'token ${MY_TOKEN}' * to 'token abc' if MY_TOKEN is 'abc'. If any of the substituted variables are undefined, * the entire expression ends up undefined. */ -export function createSubstitutionTransform(env: EnvFunc): TransformFunc { +export function createSubstitutionTransform( + env: SubstitutionFunc, +): TransformFunc { return async (input: JsonValue) => { if (typeof input !== 'string') { return { applied: false }; diff --git a/packages/config-loader/src/sources/types.ts b/packages/config-loader/src/sources/types.ts index a55aa05492..035fbd8de8 100644 --- a/packages/config-loader/src/sources/types.ts +++ b/packages/config-loader/src/sources/types.ts @@ -40,4 +40,4 @@ export interface ConfigSource { readConfigData(options?: ReadConfigDataOptions): AsyncConfigSourceIterator; } -export type EnvFunc = (name: string) => Promise; +export type SubstitutionFunc = (name: string) => Promise;