-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make config-schema extensible for handling of unknown fields #156214
Changes from all commits
0302a5d
262805f
f3cd265
ac50762
0be20c9
db1c624
8d5abe3
fc31e32
532f5ee
0f1357b
132cfed
dedf230
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -9,7 +9,7 @@ | |||
import type { AnySchema } from 'joi'; | ||||
import typeDetect from 'type-detect'; | ||||
import { internals } from '../internals'; | ||||
import { Type, TypeOptions } from './type'; | ||||
import { Type, TypeOptions, ExtendsDeepOptions, OptionsForUnknowns } from './type'; | ||||
import { ValidationError } from '../errors'; | ||||
|
||||
export type Props = Record<string, Type<any>>; | ||||
|
@@ -60,13 +60,7 @@ type ExtendedObjectTypeOptions<P extends Props, NP extends NullableProps> = Obje | |||
>; | ||||
|
||||
interface UnknownOptions { | ||||
/** | ||||
* Options for dealing with unknown keys: | ||||
* - allow: unknown keys will be permitted | ||||
* - ignore: unknown keys will not fail validation, but will be stripped out | ||||
* - forbid (default): unknown keys will fail validation | ||||
*/ | ||||
unknowns?: 'allow' | 'ignore' | 'forbid'; | ||||
unknowns?: OptionsForUnknowns; | ||||
} | ||||
|
||||
export type ObjectTypeOptions<P extends Props = any> = TypeOptions<ObjectResultType<P>> & | ||||
|
@@ -181,6 +175,25 @@ export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>> | |||
return new ObjectType(extendedProps, extendedOptions); | ||||
} | ||||
|
||||
public extendsDeep(options: ExtendsDeepOptions) { | ||||
const extendedProps = Object.entries(this.props).reduce((memo, [key, value]) => { | ||||
if (value !== null && value !== undefined) { | ||||
return { | ||||
Comment on lines
+178
to
+181
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (thinking out loud here) I'm overall fine with the recursive approach, I can understand that it would be complicated / tedious to manually redefine everything. Now, if this I was more thinking of something like: const type = schema.object({ foo: schema.string() });
const savedObject = { foo: 'test', bar: 'test' };
// ... later
type.validate(savedObject, { unknowns: 'ignore' }); Now, technically, internally we don't want to create a new schema everytime However, the
So I don't think it would be that easy to adapt it to have a good DX, as it would look like type.validate(savedObject, {}, undefined, { unknowns: 'ignore' }); So in summary, your approach is probably the most pragmatic one. |
||||
...memo, | ||||
[key]: value.extendsDeep(options), | ||||
}; | ||||
} | ||||
return memo; | ||||
}, {} as P); | ||||
|
||||
const extendedOptions: ObjectTypeOptions<P> = { | ||||
...this.options, | ||||
...(options.unknowns ? { unknowns: options.unknowns } : {}), | ||||
}; | ||||
|
||||
return new ObjectType(extendedProps, extendedOptions); | ||||
} | ||||
|
||||
protected handleError(type: string, { reason, value }: Record<string, any>) { | ||||
switch (type) { | ||||
case 'any.required': | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(unrelated to this line) I wonder if that
extendsDeep
will be sufficient covering our 'eviction schema' needs. Like, isn't there going to be scenarios were teams would need a more finely grained configuration, e.gignore
for some props,allow
for some others and so on?I guess in that case, they can still just manually redefine the schema (if such scenario even make sense)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can only explain from how I plan to use this but feel free to propose changes. In the example of Task Manager, we plan to evict unknown properties when reading from the
task.state
object while preventing tasks from running iftask.params
contains unknown properties (ex: properties created by a newer Kibana version).We would build two different task schema objects to satisfy the needs:
task.state
while forbid unknowns fortask.params
task.state
andtask.params
Since we have control of the
params
schema by task type and thestate
schema by task type, we can intercept those and only apply.extendsDeep(...)
as needed before building thetask
read/write schema for a given task type.