config-loader: apply review feedback
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -55,7 +55,7 @@ export const cacheServiceFactory: () => ServiceFactory<CacheClient, 'plugin'>;
|
||||
// @public (undocumented)
|
||||
export interface ConfigFactoryOptions {
|
||||
argv?: string[];
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadIntervalSeconds'>;
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadInterval'>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ export interface ConfigFactoryOptions {
|
||||
/**
|
||||
* Enables and sets options for remote configuration loading.
|
||||
*/
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadIntervalSeconds'>;
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadInterval'>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
```ts
|
||||
import { AppConfig } from '@backstage/config';
|
||||
import { Config } from '@backstage/config';
|
||||
import { HumanDuration } from '@backstage/types';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { Observable } from '@backstage/types';
|
||||
@@ -31,7 +32,7 @@ export interface AsyncConfigSourceIterator
|
||||
// @public
|
||||
export interface BaseConfigSourcesOptions {
|
||||
// (undocumented)
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadIntervalSeconds'>;
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadInterval'>;
|
||||
// (undocumented)
|
||||
rootDir?: string;
|
||||
// (undocumented)
|
||||
@@ -246,7 +247,7 @@ export class RemoteConfigSource implements ConfigSource {
|
||||
|
||||
// @public
|
||||
export interface RemoteConfigSourceOptions {
|
||||
reloadIntervalSeconds?: number;
|
||||
reloadInterval?: HumanDuration;
|
||||
substitutionFunc?: EnvFunc;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
"json-schema": "^0.4.0",
|
||||
"json-schema-merge-allof": "^0.8.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"lodash": "^4.14.151",
|
||||
"minimist": "^1.2.8",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.5",
|
||||
"node-fetch": "^2.6.7",
|
||||
"typescript-json-schema": "^0.55.0",
|
||||
"yaml": "^2.0.0",
|
||||
|
||||
@@ -104,7 +104,9 @@ export async function loadConfig(
|
||||
): Promise<LoadConfigResult> {
|
||||
const source = ConfigSources.default({
|
||||
substitutionFunc: options.experimentalEnvFunc,
|
||||
remote: options.remote,
|
||||
remote: options.remote && {
|
||||
reloadInterval: { seconds: options.remote.reloadIntervalSeconds },
|
||||
},
|
||||
rootDir: options.configRoot,
|
||||
argv: options.configTargets.flatMap(t => [
|
||||
'--config',
|
||||
|
||||
@@ -135,14 +135,14 @@ describe('ConfigSources', () => {
|
||||
ConfigSources.defaultForTargets({
|
||||
rootDir: '/',
|
||||
targets: [{ type: 'url', target: 'http://example.com/config.yaml' }],
|
||||
remote: { reloadIntervalSeconds: 5 },
|
||||
remote: { reloadInterval: { minutes: 2 } },
|
||||
}),
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
name: 'RemoteConfigSource',
|
||||
url: 'http://example.com/config.yaml',
|
||||
reloadIntervalSeconds: 5,
|
||||
reloadInterval: { minutes: 2 },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ export interface ClosableConfig extends Config {
|
||||
*/
|
||||
export interface BaseConfigSourcesOptions {
|
||||
rootDir?: string;
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadIntervalSeconds'>;
|
||||
remote?: Pick<RemoteConfigSourceOptions, 'reloadInterval'>;
|
||||
substitutionFunc?: SubstitutionFunc;
|
||||
}
|
||||
|
||||
@@ -112,8 +112,12 @@ export class ConfigSources {
|
||||
const args: string[] = [parseArgs(argv).config].flat().filter(Boolean);
|
||||
return args.map(target => {
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(target);
|
||||
const url = new URL(target);
|
||||
|
||||
// Some file paths are valid relative URLs, so check if the host is empty too
|
||||
if (!url.host) {
|
||||
return { type: 'path', target };
|
||||
}
|
||||
return { type: 'url', target };
|
||||
} catch {
|
||||
return { type: 'path', target };
|
||||
@@ -151,7 +155,7 @@ export class ConfigSources {
|
||||
return RemoteConfigSource.create({
|
||||
url: arg.target,
|
||||
substitutionFunc: options.substitutionFunc,
|
||||
reloadIntervalSeconds: options.remote.reloadIntervalSeconds,
|
||||
reloadInterval: options.remote.reloadInterval,
|
||||
});
|
||||
}
|
||||
return FileConfigSource.create({
|
||||
|
||||
@@ -147,9 +147,11 @@ export class FileConfigSource implements ConfigSource {
|
||||
}
|
||||
};
|
||||
|
||||
signal?.addEventListener('abort', () => {
|
||||
const onAbort = () => {
|
||||
signal?.removeEventListener('abort', onAbort);
|
||||
watcher.close();
|
||||
});
|
||||
};
|
||||
signal?.addEventListener('abort', onAbort);
|
||||
|
||||
yield { configs: await readConfigFile() };
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ app:
|
||||
|
||||
const source = RemoteConfigSource.create({
|
||||
url: 'http://localhost/config.yaml',
|
||||
reloadIntervalSeconds: 0,
|
||||
reloadInterval: { seconds: 0 },
|
||||
});
|
||||
|
||||
await expect(readN(source, 2)).resolves.toEqual([
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { HumanDuration, JsonObject } from '@backstage/types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import fetch from 'node-fetch';
|
||||
import yaml from 'yaml';
|
||||
@@ -27,7 +27,28 @@ import {
|
||||
ReadConfigDataOptions,
|
||||
} from './types';
|
||||
|
||||
const DEFAULT_RELOAD_INTERVAL_SECONDS = 60;
|
||||
const DEFAULT_RELOAD_INTERVAL = { seconds: 60 };
|
||||
|
||||
function durationToMs(duration: HumanDuration): number {
|
||||
const {
|
||||
years = 0,
|
||||
months = 0,
|
||||
weeks = 0,
|
||||
days = 0,
|
||||
hours = 0,
|
||||
minutes = 0,
|
||||
seconds = 0,
|
||||
milliseconds = 0,
|
||||
} = duration;
|
||||
|
||||
const totalDays = years * 365 + months * 30 + weeks * 7 + days;
|
||||
const totalHours = totalDays * 24 + hours;
|
||||
const totalMinutes = totalHours * 60 + minutes;
|
||||
const totalSeconds = totalMinutes * 60 + seconds;
|
||||
const totalMilliseconds = totalSeconds * 1000 + milliseconds;
|
||||
|
||||
return totalMilliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link RemoteConfigSource.create}.
|
||||
@@ -45,7 +66,7 @@ export interface RemoteConfigSourceOptions {
|
||||
*
|
||||
* Set to Infinity to disable reloading.
|
||||
*/
|
||||
reloadIntervalSeconds?: number;
|
||||
reloadInterval?: HumanDuration;
|
||||
|
||||
/**
|
||||
* A substitution function to use instead of the default environment substitution.
|
||||
@@ -78,13 +99,14 @@ export class RemoteConfigSource implements ConfigSource {
|
||||
}
|
||||
|
||||
readonly #url: string;
|
||||
readonly #reloadIntervalSeconds: number;
|
||||
readonly #reloadIntervalMs: number;
|
||||
readonly #transformer: ConfigTransformer;
|
||||
|
||||
private constructor(options: RemoteConfigSourceOptions) {
|
||||
this.#url = options.url;
|
||||
this.#reloadIntervalSeconds =
|
||||
options.reloadIntervalSeconds ?? DEFAULT_RELOAD_INTERVAL_SECONDS;
|
||||
this.#reloadIntervalMs = durationToMs(
|
||||
options.reloadInterval ?? DEFAULT_RELOAD_INTERVAL,
|
||||
);
|
||||
this.#transformer = createConfigTransformer({
|
||||
substitutionFunc: options.substitutionFunc,
|
||||
});
|
||||
@@ -98,12 +120,12 @@ export class RemoteConfigSource implements ConfigSource {
|
||||
yield { configs: [{ data, context: this.#url }] };
|
||||
|
||||
for (;;) {
|
||||
await this.#wait(options?.signal);
|
||||
|
||||
if (options?.signal?.aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.#wait(options?.signal);
|
||||
|
||||
try {
|
||||
const newData = await this.#load(options?.signal);
|
||||
if (newData && !isEqual(data, newData)) {
|
||||
@@ -146,7 +168,7 @@ export class RemoteConfigSource implements ConfigSource {
|
||||
|
||||
async #wait(signal?: AbortSignal) {
|
||||
return new Promise<void>(resolve => {
|
||||
const timeoutId = setTimeout(onDone, this.#reloadIntervalSeconds * 1000);
|
||||
const timeoutId = setTimeout(onDone, this.#reloadIntervalMs);
|
||||
signal?.addEventListener('abort', onDone);
|
||||
|
||||
function onDone() {
|
||||
|
||||
@@ -3940,8 +3940,8 @@ __metadata:
|
||||
json-schema: ^0.4.0
|
||||
json-schema-merge-allof: ^0.8.1
|
||||
json-schema-traverse: ^1.0.0
|
||||
lodash: ^4.14.151
|
||||
minimist: ^1.2.8
|
||||
lodash: ^4.17.21
|
||||
minimist: ^1.2.5
|
||||
mock-fs: ^5.1.0
|
||||
msw: ^1.0.0
|
||||
node-fetch: ^2.6.7
|
||||
@@ -29512,7 +29512,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash@npm:4.17.21, lodash@npm:^4.14.151, lodash@npm:^4.15.0, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15":
|
||||
"lodash@npm:4.17.21, lodash@npm:^4.15.0, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15":
|
||||
version: 4.17.21
|
||||
resolution: "lodash@npm:4.17.21"
|
||||
checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
|
||||
@@ -30756,7 +30756,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimist@npm:>=1.2.2, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.7, minimist@npm:^1.2.8":
|
||||
"minimist@npm:>=1.2.2, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.7":
|
||||
version: 1.2.8
|
||||
resolution: "minimist@npm:1.2.8"
|
||||
checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0
|
||||
|
||||
Reference in New Issue
Block a user