From 02096a6f91210632f88e66859184aac4252fe286 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 25 Jun 2024 15:22:10 +0200 Subject: [PATCH] [OAS] `@kbn/config-schema` conversion tests (#186879) ## Summary Adds some (missing) tests to the config-schema -> OAS conversion logic. Notably for the functions on the `OpenAPIConverter` interface: `convert`, `convertPathParamaters` and `convertQuery`. --- .../src/generate_oas.test.util.ts | 29 +--- .../kbn_config_schema/lib.test.ts | 164 +++++++++++++++++- .../kbn_config_schema/lib.test.util.ts | 36 ++++ .../oas_converter/kbn_config_schema/lib.ts | 6 +- 4 files changed, 206 insertions(+), 29 deletions(-) create mode 100644 packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.util.ts 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 2fb821018dcee6..5b28a7b9296c57 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 @@ -8,32 +8,7 @@ import { schema } from '@kbn/config-schema'; import type { CoreVersionedRouter, Router } from '@kbn/core-http-router-server-internal'; - -/** Intended to cover a wide set of schema configurations */ -export const testSchema = schema.object({ - string: schema.string({ maxLength: 10, minLength: 1 }), - maybeNumber: schema.maybe(schema.number({ max: 1000, min: 1 })), - booleanDefault: schema.boolean({ - defaultValue: true, - meta: { - description: 'defaults to to true', - }, - }), - ipType: schema.ip({ versions: ['ipv4'] }), - literalType: schema.literal('literallythis'), - neverType: schema.never(), - map: schema.mapOf(schema.string(), schema.string()), - record: schema.recordOf(schema.string(), schema.string()), - union: schema.oneOf([ - schema.string({ maxLength: 1, meta: { description: 'Union string' } }), - schema.number({ min: 0, meta: { description: 'Union number' } }), - ]), - uri: schema.uri({ - scheme: ['prototest'], - defaultValue: () => 'prototest://something', - }), - any: schema.any({ meta: { description: 'any type' } }), -}); +import { createLargeSchema } from './oas_converter/kbn_config_schema/lib.test.util'; type RoutesMeta = ReturnType[number]; type VersionedRoutesMeta = ReturnType[number]; @@ -67,7 +42,7 @@ export const getRouterDefaults = () => ({ query: schema.object({ page: schema.number({ max: 999, min: 1, defaultValue: 1, meta: { description: 'page' } }), }), - body: testSchema, + body: createLargeSchema(), }, response: { 200: { diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.ts index 8c0df00303d735..1e0d79d9786d24 100644 --- a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.ts +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.ts @@ -7,7 +7,169 @@ */ import { schema } from '@kbn/config-schema'; -import { is, isNullableObjectType, getParamSchema } from './lib'; +import { + is, + convert, + convertPathParameters, + convertQuery, + isNullableObjectType, + getParamSchema, +} from './lib'; + +import { createLargeSchema } from './lib.test.util'; + +describe('convert', () => { + test('base case', () => { + expect(convert(createLargeSchema())).toEqual({ + schema: { + additionalProperties: false, + properties: { + any: {}, + booleanDefault: { + default: true, + description: 'defaults to to true', + type: 'boolean', + }, + ipType: { + format: 'ipv4', + type: 'string', + }, + literalType: { + enum: ['literallythis'], + type: 'string', + }, + map: { + additionalProperties: { + type: 'string', + }, + type: 'object', + }, + maybeNumber: { + maximum: 1000, + minimum: 1, + type: 'number', + }, + record: { + additionalProperties: { + type: 'string', + }, + type: 'object', + }, + string: { + maxLength: 10, + minLength: 1, + type: 'string', + }, + union: { + anyOf: [ + { + description: 'Union string', + maxLength: 1, + type: 'string', + }, + { + description: 'Union number', + minimum: 0, + type: 'number', + }, + ], + }, + uri: { + default: 'prototest://something', + format: 'uri', + type: 'string', + }, + }, + required: ['string', 'ipType', 'literalType', 'map', 'record', 'union', 'any'], + type: 'object', + }, + shared: {}, + }); + }); + + test('shared schemas', () => { + const idSchema = schema.object({ a: schema.string() }, { meta: { id: 'myId' } }); + const otherSchema = schema.object({ id: idSchema }); + expect(convert(otherSchema)).toEqual({ + schema: { + additionalProperties: false, + properties: { + id: { + $ref: '#/components/schemas/myId', + }, + }, + required: ['id'], + type: 'object', + }, + shared: { + myId: { + additionalProperties: false, + properties: { + a: { + type: 'string', + }, + }, + required: ['a'], + type: 'object', + }, + }, + }); + }); +}); + +describe('convertPathParameters', () => { + test('base conversion', () => { + expect( + convertPathParameters(schema.object({ a: schema.string() }), { a: { optional: false } }) + ).toEqual({ + params: [ + { + in: 'path', + name: 'a', + required: true, + schema: { + type: 'string', + }, + }, + ], + shared: {}, + }); + }); + test('conversion with refs is disallowed', () => { + const sharedSchema = schema.object({ a: schema.string() }, { meta: { id: 'myparams' } }); + expect(() => convertPathParameters(sharedSchema, { a: { optional: false } })).toThrow( + /myparams.*not supported/ + ); + }); + test('throws if known parameters not found', () => { + expect(() => + convertPathParameters(schema.object({ b: schema.string() }), { a: { optional: false } }) + ).toThrow(/Unknown parameter: b/); + }); +}); + +describe('convertQuery', () => { + test('base conversion', () => { + expect(convertQuery(schema.object({ a: schema.string() }))).toEqual({ + query: [ + { + in: 'query', + name: 'a', + required: true, + schema: { + type: 'string', + }, + }, + ], + shared: {}, + }); + }); + + test('conversion with refs is disallowed', () => { + const sharedSchema = schema.object({ a: schema.string() }, { meta: { id: 'myparams' } }); + expect(() => convertQuery(sharedSchema)).toThrow(/myparams.*not supported/); + }); +}); describe('is', () => { test.each([ diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.util.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.util.ts new file mode 100644 index 00000000000000..a8f7547a129fef --- /dev/null +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.test.util.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; + +export function createLargeSchema() { + return schema.object({ + string: schema.string({ maxLength: 10, minLength: 1 }), + maybeNumber: schema.maybe(schema.number({ max: 1000, min: 1 })), + booleanDefault: schema.boolean({ + defaultValue: true, + meta: { + description: 'defaults to to true', + }, + }), + ipType: schema.ip({ versions: ['ipv4'] }), + literalType: schema.literal('literallythis'), + neverType: schema.never(), + map: schema.mapOf(schema.string(), schema.string()), + record: schema.recordOf(schema.string(), schema.string()), + union: schema.oneOf([ + schema.string({ maxLength: 1, meta: { description: 'Union string' } }), + schema.number({ min: 0, meta: { description: 'Union number' } }), + ]), + uri: schema.uri({ + scheme: ['prototest'], + defaultValue: () => 'prototest://something', + }), + any: schema.any({ meta: { description: 'any type' } }), + }); +} diff --git a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.ts b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.ts index 0e59ed3dde5ec2..94a321a2379dfd 100644 --- a/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.ts +++ b/packages/kbn-router-to-openapispec/src/oas_converter/kbn_config_schema/lib.ts @@ -94,7 +94,11 @@ const convertObjectMembersToParameterObjects = ( const anyOf = (result as OpenAPIV3.SchemaObject).anyOf as OpenAPIV3.SchemaObject[]; properties = anyOf.find((s) => s.type === 'object')!.properties!; } else if (isObjectType(schema)) { - const { result } = parse({ schema, ctx }) as { result: OpenAPIV3.SchemaObject }; + const { result } = parse({ schema, ctx }); + if ('$ref' in result) + throw new Error( + `Found a reference to "${result.$ref}". Runtime types with IDs are not supported in path or query parameters.` + ); properties = (result as OpenAPIV3.SchemaObject).properties!; (result.required ?? []).forEach((key) => required.set(key, true)); } else if (isRecordType(schema)) {