Skip to content

Commit 93eb97e

Browse files
committed
feat: adds validateOptions utility in extension-utils
1 parent 23a96c8 commit 93eb97e

File tree

4 files changed

+108
-9
lines changed

4 files changed

+108
-9
lines changed

app-config-extension-utils/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"prepublishOnly": "yarn clean && yarn build && yarn build:es"
3131
},
3232
"dependencies": {
33-
"@app-config/core": "^2.1.5"
33+
"@app-config/core": "^2.1.5",
34+
"@serafin/schema-builder": "0.14"
3435
},
3536
"devDependencies": {},
3637
"prettier": "@lcdev/prettier",

app-config-extension-utils/src/index.test.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { LiteralSource } from '@app-config/core';
2-
import { forKey, composeExtensions } from './index';
2+
import {
3+
forKey,
4+
composeExtensions,
5+
validateOptions,
6+
ParsingExtensionInvalidOptions,
7+
} from './index';
38

49
const foo = forKey('$foo', () => (parse) => parse('foo!'));
510
const bar = forKey('$bar', () => (parse) => parse('bar!'));
@@ -86,3 +91,48 @@ describe('composeExtensions', () => {
8691
});
8792
});
8893
});
94+
95+
describe('validateOptions', () => {
96+
const ext1 = validateOptions(
97+
(SchemaBuilder) => SchemaBuilder.stringSchema(),
98+
(value) => (parse) => parse(value + '!', { shouldFlatten: true }),
99+
);
100+
101+
const ext2 = validateOptions(
102+
(SchemaBuilder) => SchemaBuilder.integerSchema(),
103+
(value) => (parse) => parse(value + 42, { shouldFlatten: true }),
104+
);
105+
106+
const ext3 = forKey(
107+
'$ext3',
108+
validateOptions(
109+
(SchemaBuilder) => SchemaBuilder.integerSchema(),
110+
(value) => (parse) => parse(value + 42, { shouldFlatten: true }),
111+
),
112+
);
113+
114+
it('allows valid options', async () => {
115+
expect(await new LiteralSource('start').readToJSON([ext1])).toEqual('start!');
116+
expect(await new LiteralSource(42).readToJSON([ext2])).toEqual(84);
117+
});
118+
119+
it('disallows invalid options', async () => {
120+
await expect(new LiteralSource(42).readToJSON([ext1])).rejects.toThrow(
121+
ParsingExtensionInvalidOptions,
122+
);
123+
await expect(new LiteralSource('start').readToJSON([ext2])).rejects.toThrow(
124+
ParsingExtensionInvalidOptions,
125+
);
126+
});
127+
128+
it('composes forKey and validateOptions', async () => {
129+
expect(await new LiteralSource('start').readToJSON([ext3])).toEqual('start');
130+
expect(await new LiteralSource({ $ext3: 0 }).readToJSON([ext3])).toEqual(42);
131+
await expect(new LiteralSource({ $ext3: 'start' }).readToJSON([ext3])).rejects.toThrow(
132+
'Validation failed in "$ext3": Invalid parameters: data should be integer',
133+
);
134+
await expect(new LiteralSource({ a: { $ext3: 'start' } }).readToJSON([ext3])).rejects.toThrow(
135+
'Validation failed in "a.$ext3": Invalid parameters: data should be integer',
136+
);
137+
});
138+
});

app-config-extension-utils/src/index.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1-
import type { ParsingExtension, ParsingExtensionKey } from '@app-config/core';
2-
import { parseValue, Root } from '@app-config/core';
1+
import type {
2+
ParsingExtension,
3+
ParsingExtensionKey,
4+
ParsingExtensionTransform,
5+
} from '@app-config/core';
6+
import { parseValue, Root, AppConfigError } from '@app-config/core';
7+
import { SchemaBuilder } from '@serafin/schema-builder';
8+
9+
export function composeExtensions(extensions: ParsingExtension[]): ParsingExtension {
10+
return (value, [k]) => {
11+
if (k !== Root) return false;
12+
13+
return (_, __, source) => parseValue(value, source, extensions, { shouldFlatten: true });
14+
};
15+
}
316

417
export function forKey(
518
key: string | string[],
@@ -24,10 +37,37 @@ export function forKey(
2437
};
2538
}
2639

27-
export function composeExtensions(extensions: ParsingExtension[]): ParsingExtension {
28-
return (value, [k]) => {
29-
if (k !== Root) return false;
40+
export class ParsingExtensionInvalidOptions extends AppConfigError {}
3041

31-
return (_, __, source) => parseValue(value, source, extensions, { shouldFlatten: true });
42+
export function validateOptions<T>(
43+
builder: (builder: typeof SchemaBuilder) => SchemaBuilder<T>,
44+
extension: (
45+
value: T,
46+
key: ParsingExtensionKey,
47+
context: ParsingExtensionKey[],
48+
) => ParsingExtensionTransform | false,
49+
): ParsingExtension {
50+
const schema = builder(SchemaBuilder);
51+
52+
schema.cacheValidationFunction();
53+
54+
return (value, ctxKey, ctx) => {
55+
const valid = (value as unknown) as T;
56+
57+
try {
58+
schema.validate(valid);
59+
} catch (error) {
60+
const message = error instanceof Error ? error.message : 'unknown';
61+
62+
const parents =
63+
[...ctx, ctxKey]
64+
.map(([, k]) => k)
65+
.filter((v) => !!v)
66+
.join('.') || 'root';
67+
68+
throw new ParsingExtensionInvalidOptions(`Validation failed in "${parents}": ${message}`);
69+
}
70+
71+
return extension(valid, ctxKey, ctx);
3272
};
3373
}

yarn.lock

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,14 @@
27122712
dependencies:
27132713
any-observable "^0.3.0"
27142714

2715+
"@serafin/[email protected]":
2716+
version "0.14.2"
2717+
resolved "https://registry.yarnpkg.com/@serafin/schema-builder/-/schema-builder-0.14.2.tgz#38d3c712e8642787a4e87e272b0a60ca5b1f671c"
2718+
integrity sha512-JvGTNp5P4BNxB+olM4E89E1W7/GXAi4JRpaxKnN4S6C/YpMq5RIJzaJUYEmSaCWWpmANK9juEx2x1DgVueE5Uw==
2719+
dependencies:
2720+
ajv "^6.12.4"
2721+
verror "^1.10.0"
2722+
27152723
"@sindresorhus/is@^0.14.0":
27162724
version "0.14.0"
27172725
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@@ -17165,7 +17173,7 @@ vendors@^1.0.0:
1716517173
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
1716617174
integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==
1716717175

17168-
17176+
[email protected], verror@^1.10.0:
1716917177
version "1.10.0"
1717017178
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
1717117179
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=

0 commit comments

Comments
 (0)