From 25026381ec0da6e92d3b444b939d907aa59abdf2 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Tue, 6 Aug 2019 09:46:00 -0400 Subject: [PATCH] Adds config-schema nullable composite type (#41728) The `nullable` type is very similar to the `maybe` type, except that it validates nulls and undefined values to null, instead of undefined. Eg, maybe(T): T | undefined nullable(T): T | null --- packages/kbn-config-schema/src/index.ts | 6 ++ .../__snapshots__/maybe_type.test.ts.snap | 2 + .../__snapshots__/nullable_type.test.ts.snap | 7 +++ packages/kbn-config-schema/src/types/index.ts | 1 + .../src/types/maybe_type.test.ts | 6 ++ .../src/types/nullable_type.test.ts | 63 +++++++++++++++++++ .../src/types/nullable_type.ts | 32 ++++++++++ 7 files changed, 117 insertions(+) create mode 100644 packages/kbn-config-schema/src/types/__snapshots__/nullable_type.test.ts.snap create mode 100644 packages/kbn-config-schema/src/types/nullable_type.test.ts create mode 100644 packages/kbn-config-schema/src/types/nullable_type.ts diff --git a/packages/kbn-config-schema/src/index.ts b/packages/kbn-config-schema/src/index.ts index b4308dc3f80d..4f0a1fc02adf 100644 --- a/packages/kbn-config-schema/src/index.ts +++ b/packages/kbn-config-schema/src/index.ts @@ -36,6 +36,7 @@ import { MapOfOptions, MapOfType, MaybeType, + NullableType, NeverType, NumberOptions, NumberType, @@ -100,6 +101,10 @@ function maybe(type: Type): Type { return new MaybeType(type); } +function nullable(type: Type): Type { + return new NullableType(type); +} + function object

(props: P, options?: ObjectTypeOptions

): ObjectType

{ return new ObjectType(props, options); } @@ -191,6 +196,7 @@ export const schema = { literal, mapOf, maybe, + nullable, never, number, object, diff --git a/packages/kbn-config-schema/src/types/__snapshots__/maybe_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/maybe_type.test.ts.snap index ba3ac821a97c..fdb172df356a 100644 --- a/packages/kbn-config-schema/src/types/__snapshots__/maybe_type.test.ts.snap +++ b/packages/kbn-config-schema/src/types/__snapshots__/maybe_type.test.ts.snap @@ -4,4 +4,6 @@ exports[`fails if null 1`] = `"expected value of type [string] but got [null]"`; exports[`includes namespace in failure 1`] = `"[foo-namespace]: expected value of type [string] but got [null]"`; +exports[`validates basic type 1`] = `"expected value of type [string] but got [number]"`; + exports[`validates contained type 1`] = `"value is [foo] but it must have a maximum length of [1]."`; diff --git a/packages/kbn-config-schema/src/types/__snapshots__/nullable_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/nullable_type.test.ts.snap new file mode 100644 index 000000000000..ae1a34c00d3a --- /dev/null +++ b/packages/kbn-config-schema/src/types/__snapshots__/nullable_type.test.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`includes namespace in failure 1`] = `"[foo-namespace]: value is [foo] but it must have a maximum length of [1]."`; + +exports[`validates basic type 1`] = `"expected value of type [string] but got [number]"`; + +exports[`validates contained type 1`] = `"value is [foo] but it must have a maximum length of [1]."`; diff --git a/packages/kbn-config-schema/src/types/index.ts b/packages/kbn-config-schema/src/types/index.ts index cfa8cc4b7553..4c25cade2ec6 100644 --- a/packages/kbn-config-schema/src/types/index.ts +++ b/packages/kbn-config-schema/src/types/index.ts @@ -26,6 +26,7 @@ export { ConditionalType, ConditionalTypeValue } from './conditional_type'; export { DurationOptions, DurationType } from './duration_type'; export { LiteralType } from './literal_type'; export { MaybeType } from './maybe_type'; +export { NullableType } from './nullable_type'; export { MapOfOptions, MapOfType } from './map_type'; export { NumberOptions, NumberType } from './number_type'; export { ObjectType, ObjectTypeOptions, Props, TypeOf } from './object_type'; diff --git a/packages/kbn-config-schema/src/types/maybe_type.test.ts b/packages/kbn-config-schema/src/types/maybe_type.test.ts index b29f504c03b3..ecc1d218e186 100644 --- a/packages/kbn-config-schema/src/types/maybe_type.test.ts +++ b/packages/kbn-config-schema/src/types/maybe_type.test.ts @@ -45,6 +45,12 @@ test('validates contained type', () => { expect(() => type.validate('foo')).toThrowErrorMatchingSnapshot(); }); +test('validates basic type', () => { + const type = schema.maybe(schema.string()); + + expect(() => type.validate(666)).toThrowErrorMatchingSnapshot(); +}); + test('fails if null', () => { const type = schema.maybe(schema.string()); expect(() => type.validate(null)).toThrowErrorMatchingSnapshot(); diff --git a/packages/kbn-config-schema/src/types/nullable_type.test.ts b/packages/kbn-config-schema/src/types/nullable_type.test.ts new file mode 100644 index 000000000000..e9d6f3ca2fe3 --- /dev/null +++ b/packages/kbn-config-schema/src/types/nullable_type.test.ts @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '..'; + +test('returns value if specified', () => { + const type = schema.nullable(schema.string()); + expect(type.validate('test')).toEqual('test'); +}); + +test('returns null if null', () => { + const type = schema.nullable(schema.string()); + expect(type.validate(null)).toEqual(null); +}); + +test('returns null if undefined', () => { + const type = schema.nullable(schema.string()); + expect(type.validate(undefined)).toEqual(null); +}); + +test('returns null even if contained type has a default value', () => { + const type = schema.nullable( + schema.string({ + defaultValue: 'abc', + }) + ); + + expect(type.validate(undefined)).toEqual(null); +}); + +test('validates contained type', () => { + const type = schema.nullable(schema.string({ maxLength: 1 })); + + expect(() => type.validate('foo')).toThrowErrorMatchingSnapshot(); +}); + +test('validates basic type', () => { + const type = schema.nullable(schema.string()); + + expect(() => type.validate(666)).toThrowErrorMatchingSnapshot(); +}); + +test('includes namespace in failure', () => { + const type = schema.nullable(schema.string({ maxLength: 1 })); + + expect(() => type.validate('foo', {}, 'foo-namespace')).toThrowErrorMatchingSnapshot(); +}); diff --git a/packages/kbn-config-schema/src/types/nullable_type.ts b/packages/kbn-config-schema/src/types/nullable_type.ts new file mode 100644 index 000000000000..c89f3e44c37c --- /dev/null +++ b/packages/kbn-config-schema/src/types/nullable_type.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Type } from './type'; + +export class NullableType extends Type { + constructor(type: Type) { + super( + type + .getSchema() + .optional() + .allow(null) + .default(null) + ); + } +}