Skip to content

Commit 904f1fd

Browse files
committed
feat(#108): adds '$parseInt', '$parseFloat' and '$parseBool'
1 parent 434e3c0 commit 904f1fd

File tree

2 files changed

+122
-10
lines changed

2 files changed

+122
-10
lines changed

app-config-extensions/src/index.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,86 @@ describe('$substitute directive', () => {
958958
await expect(source.read([environmentVariableSubstitution()])).rejects.toThrow();
959959
});
960960

961+
it('parses ints', async () => {
962+
process.env.FOO = '11';
963+
964+
const source = new LiteralSource({
965+
$substitute: { $name: 'FOO', $parseInt: true },
966+
});
967+
968+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(11);
969+
});
970+
971+
it('fails when int is invalid', async () => {
972+
process.env.FOO = 'not a number';
973+
974+
const source = new LiteralSource({
975+
$substitute: { $name: 'FOO', $parseInt: true },
976+
});
977+
978+
await expect(source.read([environmentVariableSubstitution()])).rejects.toThrow();
979+
});
980+
981+
it('parses float', async () => {
982+
process.env.FOO = '11.2';
983+
984+
const source = new LiteralSource({
985+
$substitute: { $name: 'FOO', $parseFloat: true },
986+
});
987+
988+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(11.2);
989+
});
990+
991+
it('fails when float is invalid', async () => {
992+
process.env.FOO = 'not a number';
993+
994+
const source = new LiteralSource({
995+
$substitute: { $name: 'FOO', $parseFloat: true },
996+
});
997+
998+
await expect(source.read([environmentVariableSubstitution()])).rejects.toThrow();
999+
});
1000+
1001+
it('parses boolean = true', async () => {
1002+
process.env.FOO = 'true';
1003+
1004+
const source = new LiteralSource({
1005+
$substitute: { $name: 'FOO', $parseBool: true },
1006+
});
1007+
1008+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(true);
1009+
});
1010+
1011+
it('parses boolean = 1', async () => {
1012+
process.env.FOO = '1';
1013+
1014+
const source = new LiteralSource({
1015+
$substitute: { $name: 'FOO', $parseBool: true },
1016+
});
1017+
1018+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(true);
1019+
});
1020+
1021+
it('parses boolean = 0', async () => {
1022+
process.env.FOO = '0';
1023+
1024+
const source = new LiteralSource({
1025+
$substitute: { $name: 'FOO', $parseBool: true },
1026+
});
1027+
1028+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(false);
1029+
});
1030+
1031+
it('parses boolean = false', async () => {
1032+
process.env.FOO = 'false';
1033+
1034+
const source = new LiteralSource({
1035+
$substitute: { $name: 'FOO', $parseBool: true },
1036+
});
1037+
1038+
expect(await source.readToJSON([environmentVariableSubstitution()])).toEqual(false);
1039+
});
1040+
9611041
it('doesnt visit fallback if name is defined', async () => {
9621042
const failDirective = forKey('$fail', () => () => {
9631043
throw new Error();

app-config-extensions/src/index.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -264,17 +264,53 @@ export function environmentVariableSubstitution(
264264
validateObject(value, [...ctx, key]);
265265
if (Array.isArray(value)) throw new AppConfigError('$substitute was given an array');
266266

267-
const { $name: variableName, $fallback: fallback, $allowNull: allowNull } = value;
268-
validateString(variableName, [...ctx, key, [InObject, '$name']]);
267+
const { $name, $fallback, $allowNull, $parseInt, $parseFloat, $parseBool } = value;
269268

270-
const resolvedValue = process.env[variableName];
269+
const name = (await parse($name)).toJSON();
270+
271+
validateString(name, [...ctx, key, [InObject, '$name']]);
272+
273+
const resolvedValue = process.env[name];
271274

272275
if (resolvedValue) {
276+
const parseInt = (await parse($parseInt)).toJSON();
277+
278+
if (parseInt) {
279+
const parsed = Number.parseInt(resolvedValue, 10);
280+
281+
if (Number.isNaN(parsed)) {
282+
throw new AppConfigError(`Failed to parseInt(${resolvedValue})`);
283+
}
284+
285+
return parse(parsed, { shouldFlatten: true });
286+
}
287+
288+
const parseFloat = (await parse($parseFloat)).toJSON();
289+
290+
if (parseFloat) {
291+
const parsed = Number.parseFloat(resolvedValue);
292+
293+
if (Number.isNaN(parsed)) {
294+
throw new AppConfigError(`Failed to parseFloat(${resolvedValue})`);
295+
}
296+
297+
return parse(parsed, { shouldFlatten: true });
298+
}
299+
300+
const parseBool = (await parse($parseBool)).toJSON();
301+
302+
if (parseBool) {
303+
const parsed = resolvedValue.toLowerCase() !== 'false' && resolvedValue !== '0';
304+
305+
return parse(parsed, { shouldFlatten: true });
306+
}
307+
273308
return parse(resolvedValue, { shouldFlatten: true });
274309
}
275310

276-
if (fallback !== undefined) {
277-
const fallbackValue = (await parse(fallback)).toJSON();
311+
if ($fallback !== undefined) {
312+
const fallbackValue = (await parse($fallback)).toJSON();
313+
const allowNull = (await parse($allowNull)).toJSON();
278314

279315
if (allowNull) {
280316
validateStringOrNull(fallbackValue, [...ctx, key, [InObject, '$fallback']]);
@@ -285,11 +321,7 @@ export function environmentVariableSubstitution(
285321
return parse(fallbackValue, { shouldFlatten: true });
286322
}
287323

288-
if (!resolvedValue) {
289-
throw new AppConfigError(`$substitute could not find ${variableName} environment variable`);
290-
}
291-
292-
return parse(resolvedValue, { shouldFlatten: true });
324+
throw new AppConfigError(`$substitute could not find ${name} environment variable`);
293325
});
294326
}
295327

0 commit comments

Comments
 (0)