Merge pull request #1871 from spotify/rugvip/can-has-config

config: added config.has() and allow config.get() to be called with no args
This commit is contained in:
Patrik Oldsberg
2020-08-10 18:35:27 +02:00
committed by GitHub
16 changed files with 143 additions and 82 deletions
+56 -7
View File
@@ -30,6 +30,7 @@ const DATA = {
worstStrings: ['string1', 'string2', {}] as string[],
nested: {
one: 1,
null: null,
string: 'string',
strings: ['string1', 'string2'],
},
@@ -39,6 +40,13 @@ const DATA = {
function expectValidValues(config: ConfigReader) {
expect(config.keys()).toEqual(Object.keys(DATA));
expect(config.get('zero')).toBe(0);
expect(config.has('zero')).toBe(true);
expect(config.has('false')).toBe(true);
expect(config.has('null')).toBe(true);
expect(config.has('missing')).toBe(false);
expect(config.has('nested.one')).toBe(true);
expect(config.has('nested.missing')).toBe(false);
expect(config.has('nested.null')).toBe(true);
expect(config.getNumber('zero')).toBe(0);
expect(config.getNumber('one')).toBe(1);
expect(config.getOptional('true')).toBe(true);
@@ -50,6 +58,7 @@ function expectValidValues(config: ConfigReader) {
expect(config.getConfig('nested').getNumber('one')).toBe(1);
expect(config.get('nested')).toEqual({
one: 1,
null: null,
string: 'string',
strings: ['string1', 'string2'],
});
@@ -128,6 +137,8 @@ describe('ConfigReader', () => {
it('should read empty config with valid keys', () => {
const config = new ConfigReader({}, CTX);
expect(config.keys()).toEqual([]);
expect(config.getOptional()).toEqual({});
expect(config.getOptional('x')).toBeUndefined();
expect(config.getOptionalString('x')).toBeUndefined();
expect(config.getOptionalString('x_x')).toBeUndefined();
expect(config.getOptionalString('x-X')).toBeUndefined();
@@ -144,14 +155,14 @@ describe('ConfigReader', () => {
it('should throw on invalid keys', () => {
const config = new ConfigReader({}, CTX);
expect(() => config.getString('.')).toThrow(/^Invalid config key/);
expect(() => config.getString('0')).toThrow(/^Invalid config key/);
expect(() => config.getString('(')).toThrow(/^Invalid config key/);
expect(() => config.has('.')).toThrow(/^Invalid config key/);
expect(() => config.get('0')).toThrow(/^Invalid config key/);
expect(() => config.getOptional('(')).toThrow(/^Invalid config key/);
expect(() => config.getString('z-_')).toThrow(/^Invalid config key/);
expect(() => config.getString('-')).toThrow(/^Invalid config key/);
expect(() => config.getString('.a')).toThrow(/^Invalid config key/);
expect(() => config.getString('0.a')).toThrow(/^Invalid config key/);
expect(() => config.getString('0a')).toThrow(/^Invalid config key/);
expect(() => config.getOptionalString('-')).toThrow(/^Invalid config key/);
expect(() => config.getNumber('.a')).toThrow(/^Invalid config key/);
expect(() => config.getConfig('0.a')).toThrow(/^Invalid config key/);
expect(() => config.getOptionalConfig('0a')).toThrow(/^Invalid config key/);
expect(() => config.getString('a.0a')).toThrow(/^Invalid config key/);
expect(() => config.getString('a..a')).toThrow(/^Invalid config key/);
expect(() => config.getString('a.')).toThrow(/^Invalid config key/);
@@ -308,6 +319,12 @@ describe('ConfigReader with fallback', () => {
const config = new ConfigReader(a, CTX, new ConfigReader(b, CTX));
expect(config.keys()).toEqual(['merged']);
expect(config.has('merged.x')).toBe(true);
expect(config.has('merged.y')).toBe(true);
expect(config.has('merged.w')).toBe(false);
expect(config.getConfig('merged').has('x')).toBe(true);
expect(config.getConfig('merged').has('y')).toBe(true);
expect(config.getConfig('merged').has('w')).toBe(false);
expect(config.getConfig('merged').keys()).toEqual([
'x',
'z',
@@ -427,6 +444,21 @@ describe('ConfigReader.get()', () => {
});
it('should merge in fallback configs', () => {
expect(
ConfigReader.fromConfigs([configs[0], configs[1], configs[2]]).get(),
).toEqual({
a: {
x: 'x1',
y: ['y11', 'y12', 'y13'],
z: false,
},
b: {
x: 'x2',
y: ['y21', 'y22'],
z: 'z2',
},
c: { c1: 'c1' },
});
expect(ConfigReader.fromConfigs([configs[1], configs[0]]).get('a')).toEqual(
{
x: 'x1',
@@ -530,5 +562,22 @@ describe('ConfigReader.get()', () => {
expect(config.get('f')).toEqual('foo');
expect(config.get('g')).toEqual({ z: 'z' });
expect(config.get('h')).toEqual({ a: 'a1', b: 'b2', c: 'c1' });
expect(config.getConfig('h').get()).toEqual({ a: 'a1', b: 'b2', c: 'c1' });
expect(config.getOptional()).toEqual({
a: ['1', '2'],
b: ['1'],
c: [],
d: {
x: 'x',
},
e: ['3'],
f: 'foo',
g: { z: 'z' },
h: {
a: 'a1',
b: 'b2',
c: 'c1',
},
});
});
});
+13 -5
View File
@@ -71,21 +71,29 @@ export class ConfigReader implements Config {
private readonly prefix: string = '',
) {}
has(key: string): boolean {
const value = this.readValue(key);
if (value !== undefined) {
return true;
}
return this.fallback?.has(key) ?? false;
}
keys(): string[] {
const localKeys = this.data ? Object.keys(this.data) : [];
const fallbackKeys = this.fallback?.keys() ?? [];
return [...new Set([...localKeys, ...fallbackKeys])];
}
get(key: string): JsonValue {
get(key?: string): JsonValue {
const value = this.getOptional(key);
if (value === undefined) {
throw new Error(errors.missing(this.fullKey(key)));
throw new Error(errors.missing(this.fullKey(key ?? '')));
}
return value;
}
getOptional(key: string): JsonValue | undefined {
getOptional(key?: string): JsonValue | undefined {
const value = this.readValue(key);
const fallbackValue = this.fallback?.getOptional(key);
@@ -271,8 +279,8 @@ export class ConfigReader implements Config {
return value as T;
}
private readValue(key: string): JsonValue | undefined {
const parts = key.split('.');
private readValue(key?: string): JsonValue | undefined {
const parts = key ? key.split('.') : [];
for (const part of parts) {
if (!CONFIG_KEY_PART_PATTERN.test(part)) {
throw new TypeError(`Invalid config key '${key}'`);