diff --git a/packages/kbn-config-schema/index.ts b/packages/kbn-config-schema/index.ts index dfb701f0a039cc..c5b46932463dbe 100644 --- a/packages/kbn-config-schema/index.ts +++ b/packages/kbn-config-schema/index.ts @@ -246,6 +246,7 @@ export type Schema = typeof schema; import { META_FIELD_X_OAS_REF_ID, META_FIELD_X_OAS_OPTIONAL, + META_FIELD_X_OAS_DEPRECATED, META_FIELD_X_OAS_MAX_LENGTH, META_FIELD_X_OAS_MIN_LENGTH, META_FIELD_X_OAS_GET_ADDITIONAL_PROPERTIES, @@ -254,6 +255,7 @@ import { export const metaFields = Object.freeze({ META_FIELD_X_OAS_REF_ID, META_FIELD_X_OAS_OPTIONAL, + META_FIELD_X_OAS_DEPRECATED, META_FIELD_X_OAS_MAX_LENGTH, META_FIELD_X_OAS_MIN_LENGTH, META_FIELD_X_OAS_GET_ADDITIONAL_PROPERTIES, diff --git a/packages/kbn-config-schema/src/oas_meta_fields.ts b/packages/kbn-config-schema/src/oas_meta_fields.ts index 44422793558d4c..ad04f9b30ef16b 100644 --- a/packages/kbn-config-schema/src/oas_meta_fields.ts +++ b/packages/kbn-config-schema/src/oas_meta_fields.ts @@ -16,3 +16,4 @@ export const META_FIELD_X_OAS_MAX_LENGTH = 'x-oas-max-length' as const; export const META_FIELD_X_OAS_GET_ADDITIONAL_PROPERTIES = 'x-oas-get-additional-properties' as const; export const META_FIELD_X_OAS_REF_ID = 'x-oas-ref-id' as const; +export const META_FIELD_X_OAS_DEPRECATED = 'x-oas-deprecated' as const; diff --git a/packages/kbn-config-schema/src/types/type.test.ts b/packages/kbn-config-schema/src/types/type.test.ts index 4d6636a55b9ca9..a4784af2e8b62f 100644 --- a/packages/kbn-config-schema/src/types/type.test.ts +++ b/packages/kbn-config-schema/src/types/type.test.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { internals } from '../internals'; import { Type, TypeOptions } from './type'; -import { META_FIELD_X_OAS_REF_ID } from '../oas_meta_fields'; +import { META_FIELD_X_OAS_REF_ID, META_FIELD_X_OAS_DEPRECATED } from '../oas_meta_fields'; class MyType extends Type { constructor(opts: TypeOptions = {}) { @@ -17,9 +17,21 @@ class MyType extends Type { } } -test('meta', () => { - const type = new MyType({ meta: { description: 'my description', id: 'foo' } }); - const meta = type.getSchema().describe(); - expect(get(meta, 'flags.description')).toBe('my description'); - expect(get(meta, `metas[0].${META_FIELD_X_OAS_REF_ID}`)).toBe('foo'); +describe('meta', () => { + it('sets meta when provided', () => { + const type = new MyType({ + meta: { description: 'my description', id: 'foo', deprecated: true }, + }); + const meta = type.getSchema().describe(); + expect(get(meta, 'flags.description')).toBe('my description'); + expect(get(meta, `metas[0].${META_FIELD_X_OAS_REF_ID}`)).toBe('foo'); + expect(get(meta, `metas[1].${META_FIELD_X_OAS_DEPRECATED}`)).toBe(true); + }); + + it('does not set meta when no provided', () => { + const type = new MyType(); + const meta = type.getSchema().describe(); + expect(get(meta, 'flags.description')).toBeUndefined(); + expect(get(meta, 'metas')).toBeUndefined(); + }); }); diff --git a/packages/kbn-config-schema/src/types/type.ts b/packages/kbn-config-schema/src/types/type.ts index 1e312cc7adc7e9..80ed3f90fdd2a8 100644 --- a/packages/kbn-config-schema/src/types/type.ts +++ b/packages/kbn-config-schema/src/types/type.ts @@ -7,16 +7,23 @@ */ import type { AnySchema, CustomValidator, ErrorReport } from 'joi'; -import { META_FIELD_X_OAS_REF_ID } from '../oas_meta_fields'; +import { META_FIELD_X_OAS_DEPRECATED, META_FIELD_X_OAS_REF_ID } from '../oas_meta_fields'; import { SchemaTypeError, ValidationError } from '../errors'; import { Reference } from '../references'; -/** Meta fields used when introspecting runtime validation */ +/** + * Meta fields used when introspecting runtime validation. Most notably for + * generating OpenAPI spec. + */ export interface TypeMeta { /** * A human-friendly description of this type to be used in documentation. */ description?: string; + /** + * Whether this field is deprecated. + */ + deprecated?: boolean; /** * A string that uniquely identifies this schema. Used when generating OAS * to create refs instead of inline schemas. @@ -108,6 +115,9 @@ export abstract class Type { if (options.meta.id) { schema = schema.meta({ [META_FIELD_X_OAS_REF_ID]: options.meta.id }); } + if (options.meta.deprecated) { + schema = schema.meta({ [META_FIELD_X_OAS_DEPRECATED]: true }); + } } // Attach generic error handler only if it hasn't been attached yet since diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap index 42deee2907f43c..aac31188c49855 100644 --- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap +++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap @@ -155,6 +155,10 @@ Object { "schema": Object { "additionalProperties": false, "properties": Object { + "deprecatedFoo": Object { + "deprecated": true, + "type": "string", + }, "foo": Object { "type": "string", }, diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts index e435065f0089f5..41a2458619000e 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts @@ -79,7 +79,12 @@ const getVersionedRouterDefaults = () => ({ fn: jest.fn(), options: { validate: { - request: { body: schema.object({ foo: schema.string() }) }, + request: { + body: schema.object({ + foo: schema.string(), + deprecatedFoo: schema.maybe(schema.string({ meta: { deprecated: true } })), + }), + }, response: { [200]: { body: schema.object({ fooResponse: schema.string() }) }, }, diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/index.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/index.ts index 4f5b31030695e9..e200ba7d1ec278 100644 --- a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/index.ts +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/index.ts @@ -10,7 +10,7 @@ import Joi from 'joi'; import { metaFields } from '@kbn/config-schema'; import type { OpenAPIV3 } from 'openapi-types'; import { parse } from '../../parse'; -import { deleteField, stripBadDefault } from './utils'; +import { deleteField, stripBadDefault, processDeprecated } from './utils'; import { IContext } from '../context'; const { @@ -62,6 +62,7 @@ export const processMap = (ctx: IContext, schema: OpenAPIV3.SchemaObject): void }; export const processAny = (schema: OpenAPIV3.SchemaObject): void => { + processDeprecated(schema); stripBadDefault(schema); }; diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts index 5bad28b276d103..eacc005936a28b 100644 --- a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/post_process_mutations/mutations/utils.ts @@ -7,6 +7,7 @@ */ import type { OpenAPIV3 } from 'openapi-types'; +import { metaFields } from '@kbn/config-schema'; export const stripBadDefault = (schema: OpenAPIV3.SchemaObject): void => { if (schema.default?.special === 'deep') { @@ -26,6 +27,13 @@ export const stripBadDefault = (schema: OpenAPIV3.SchemaObject): void => { } }; +export const processDeprecated = (schema: OpenAPIV3.SchemaObject): void => { + if (metaFields.META_FIELD_X_OAS_DEPRECATED in schema) { + schema.deprecated = true; + deleteField(schema, metaFields.META_FIELD_X_OAS_DEPRECATED); + } +}; + /** Just for type convenience */ export const deleteField = (schema: Record, field: string): void => { delete schema[field];