Skip to content

Commit

Permalink
allow kbn-config-schema to ignore unknown keys
Browse files Browse the repository at this point in the history
  • Loading branch information
legrego committed Mar 6, 2020
1 parent 4977e57 commit 12a2cde
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 6 deletions.
43 changes: 43 additions & 0 deletions packages/kbn-config-schema/src/types/object_type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,46 @@ test('does not allow unknown keys when allowUnknowns = false', () => {
})
).toThrowErrorMatchingInlineSnapshot(`"[bar]: definition for this key is missing"`);
});

test('allow and remove unknown keys when ignoreUnknowns = true', () => {
const type = schema.object(
{ foo: schema.string({ defaultValue: 'test' }) },
{ ignoreUnknowns: true }
);

expect(
type.validate({
bar: 'baz',
})
).toEqual({
foo: 'test',
});
});

test('ignoreUnknowns = true affects only own keys', () => {
const type = schema.object(
{ foo: schema.object({ bar: schema.string() }) },
{ ignoreUnknowns: true }
);

expect(() =>
type.validate({
foo: {
bar: 'bar',
baz: 'baz',
},
})
).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`);
});

test('does not allow unknown keys when ignoreUnknowns = false', () => {
const type = schema.object(
{ foo: schema.string({ defaultValue: 'test' }) },
{ ignoreUnknowns: false }
);
expect(() =>
type.validate({
bar: 'baz',
})
).toThrowErrorMatchingInlineSnapshot(`"[bar]: definition for this key is missing"`);
});
31 changes: 25 additions & 6 deletions packages/kbn-config-schema/src/types/object_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,35 @@ export type TypeOf<RT extends Type<any>> = RT['type'];
// this might not have perfect _rendering_ output, but it will be typed.
export type ObjectResultType<P extends Props> = Readonly<{ [K in keyof P]: TypeOf<P[K]> }>;

interface AllowUnknowns {
allowUnknowns: true;
ignoreUnknowns?: false;
}

interface IgnoreUnknowns {
allowUnknowns?: false;
ignoreUnknowns: true;
}

interface ForbidUnknowns {
allowUnknowns?: false;
ignoreUnknowns?: false;
}
// type check to not permit both to be set to `true` at the same time
type UnknownOptions = AllowUnknowns | IgnoreUnknowns | ForbidUnknowns;

export type ObjectTypeOptions<P extends Props = any> = TypeOptions<
{ [K in keyof P]: TypeOf<P[K]> }
> & {
/** Should uknown keys not be defined in the schema be allowed. Defaults to `false` */
allowUnknowns?: boolean;
};
> &
UnknownOptions;

export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>> {
private props: Record<string, AnySchema>;

constructor(props: P, { allowUnknowns = false, ...typeOptions }: ObjectTypeOptions<P> = {}) {
constructor(
props: P,
{ allowUnknowns = false, ignoreUnknowns = false, ...typeOptions }: ObjectTypeOptions<P> = {}
) {
const schemaKeys = {} as Record<string, AnySchema>;
for (const [key, value] of Object.entries(props)) {
schemaKeys[key] = value.getSchema();
Expand All @@ -50,7 +68,8 @@ export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>>
.keys(schemaKeys)
.default()
.optional()
.unknown(Boolean(allowUnknowns));
.unknown(Boolean(allowUnknowns))
.options({ stripUnknown: { objects: ignoreUnknowns } });

super(schema, typeOptions);
this.props = schemaKeys;
Expand Down

0 comments on commit 12a2cde

Please sign in to comment.