Enforce required presence for value/key validation of recordOf and mapOf. (#60406)

This commit is contained in:
Aleh Zasypkin 2020-03-18 12:19:50 +01:00 committed by GitHub
parent fd16c46128
commit 45f59f7d9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 7 deletions

View file

@ -314,7 +314,8 @@ export const internals = Joi.extend([
for (const [entryKey, entryValue] of value) {
const { value: validatedEntryKey, error: keyError } = Joi.validate(
entryKey,
params.key
params.key,
{ presence: 'required' }
);
if (keyError) {
@ -323,7 +324,8 @@ export const internals = Joi.extend([
const { value: validatedEntryValue, error: valueError } = Joi.validate(
entryValue,
params.value
params.value,
{ presence: 'required' }
);
if (valueError) {
@ -374,7 +376,8 @@ export const internals = Joi.extend([
for (const [entryKey, entryValue] of Object.entries(value)) {
const { value: validatedEntryKey, error: keyError } = Joi.validate(
entryKey,
params.key
params.key,
{ presence: 'required' }
);
if (keyError) {
@ -383,7 +386,8 @@ export const internals = Joi.extend([
const { value: validatedEntryValue, error: valueError } = Joi.validate(
entryValue,
params.value
params.value,
{ presence: 'required' }
);
if (valueError) {

View file

@ -159,6 +159,24 @@ test('object within mapOf', () => {
expect(type.validate(value)).toEqual(expected);
});
test('enforces required object fields within mapOf', () => {
const type = schema.mapOf(
schema.string(),
schema.object({
bar: schema.object({
baz: schema.number(),
}),
})
);
const value = {
foo: {},
};
expect(() => type.validate(value)).toThrowErrorMatchingInlineSnapshot(
`"[foo.bar.baz]: expected value of type [number] but got [undefined]"`
);
});
test('error preserves full path', () => {
const type = schema.object({
grandParentKey: schema.object({

View file

@ -57,7 +57,10 @@ export class MapOfType<K, V> extends Type<Map<K, V>> {
path.length,
0,
// If `key` validation failed, let's stress that to make error more obvious.
type === 'map.key' ? `key("${entryKey}")` : entryKey.toString()
type === 'map.key' ? `key("${entryKey}")` : entryKey.toString(),
// Error could have happened deep inside value/key schema and error message should
// include full path.
...(reason instanceof SchemaTypeError ? reason.path : [])
);
return reason instanceof SchemaTypesError

View file

@ -159,6 +159,24 @@ test('object within recordOf', () => {
expect(type.validate(value)).toEqual({ foo: { bar: 123 } });
});
test('enforces required object fields within recordOf', () => {
const type = schema.recordOf(
schema.string(),
schema.object({
bar: schema.object({
baz: schema.number(),
}),
})
);
const value = {
foo: {},
};
expect(() => type.validate(value)).toThrowErrorMatchingInlineSnapshot(
`"[foo.bar.baz]: expected value of type [number] but got [undefined]"`
);
});
test('error preserves full path', () => {
const type = schema.object({
grandParentKey: schema.object({

View file

@ -49,7 +49,10 @@ export class RecordOfType<K extends string, V> extends Type<Record<K, V>> {
path.length,
0,
// If `key` validation failed, let's stress that to make error more obvious.
type === 'record.key' ? `key("${entryKey}")` : entryKey.toString()
type === 'record.key' ? `key("${entryKey}")` : entryKey.toString(),
// Error could have happened deep inside value/key schema and error message should
// include full path.
...(reason instanceof SchemaTypeError ? reason.path : [])
);
return reason instanceof SchemaTypesError

View file

@ -28,7 +28,7 @@ describe('Put payload schema', () => {
kibana: [{ feature: { foo: ['!foo'] } }],
})
).toThrowErrorMatchingInlineSnapshot(
`"[kibana.0.feature.foo]: only a-z, A-Z, 0-9, '_', and '-' are allowed"`
`"[kibana.0.feature.foo.0]: only a-z, A-Z, 0-9, '_', and '-' are allowed"`
);
});