mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[OAS] Support setting availability (#196647)
## Summary Close https://github.com/elastic/kibana/issues/181995 Related https://github.com/elastic/kibana/pull/195325 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Christiane (Tina) Heiligers <christiane.heiligers@elastic.co>
This commit is contained in:
parent
68b3267ca2
commit
608cc70be5
10 changed files with 197 additions and 5 deletions
|
@ -54,6 +54,10 @@ describe('Router', () => {
|
|||
discontinued: 'post test discontinued',
|
||||
summary: 'post test summary',
|
||||
description: 'post test description',
|
||||
availability: {
|
||||
since: '1.0.0',
|
||||
stability: 'experimental',
|
||||
},
|
||||
},
|
||||
},
|
||||
(context, req, res) => res.ok()
|
||||
|
@ -72,6 +76,10 @@ describe('Router', () => {
|
|||
discontinued: 'post test discontinued',
|
||||
summary: 'post test summary',
|
||||
description: 'post test description',
|
||||
availability: {
|
||||
since: '1.0.0',
|
||||
stability: 'experimental',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -52,6 +52,46 @@ describe('Versioned route', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('#getRoutes', () => {
|
||||
it('returns the expected metadata', () => {
|
||||
const versionedRouter = CoreVersionedRouter.from({ router });
|
||||
versionedRouter
|
||||
.get({
|
||||
path: '/test/{id}',
|
||||
access: 'public',
|
||||
options: {
|
||||
httpResource: true,
|
||||
availability: {
|
||||
since: '1.0.0',
|
||||
stability: 'experimental',
|
||||
},
|
||||
excludeFromOAS: true,
|
||||
tags: ['1', '2', '3'],
|
||||
},
|
||||
description: 'test',
|
||||
summary: 'test',
|
||||
enableQueryVersion: false,
|
||||
})
|
||||
.addVersion({ version: '2023-10-31', validate: false }, handlerFn);
|
||||
|
||||
expect(versionedRouter.getRoutes()[0].options).toMatchObject({
|
||||
access: 'public',
|
||||
enableQueryVersion: false,
|
||||
description: 'test',
|
||||
summary: 'test',
|
||||
options: {
|
||||
httpResource: true,
|
||||
availability: {
|
||||
since: '1.0.0',
|
||||
stability: 'experimental',
|
||||
},
|
||||
excludeFromOAS: true,
|
||||
tags: ['1', '2', '3'],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can register multiple handlers', () => {
|
||||
const versionedRouter = CoreVersionedRouter.from({ router });
|
||||
versionedRouter
|
||||
|
@ -133,6 +173,8 @@ describe('Versioned route', () => {
|
|||
const opts: Parameters<typeof versionedRouter.post>[0] = {
|
||||
path: '/test/{id}',
|
||||
access: 'internal',
|
||||
summary: 'test',
|
||||
description: 'test',
|
||||
options: {
|
||||
authRequired: true,
|
||||
tags: ['access:test'],
|
||||
|
@ -140,7 +182,6 @@ describe('Versioned route', () => {
|
|||
xsrfRequired: false,
|
||||
excludeFromOAS: true,
|
||||
httpResource: true,
|
||||
summary: `test`,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -321,6 +321,23 @@ export interface RouteConfigOptions<Method extends RouteMethod> {
|
|||
* @default false
|
||||
*/
|
||||
httpResource?: boolean;
|
||||
|
||||
/**
|
||||
* Based on the the ES API specification (see https://github.com/elastic/elasticsearch-specification)
|
||||
* Kibana APIs can also specify some metadata about API availability.
|
||||
*
|
||||
* This setting is only applicable if your route `access` is `public`.
|
||||
*
|
||||
* @remark intended to be used for informational purposes only.
|
||||
*/
|
||||
availability?: {
|
||||
/** @default stable */
|
||||
stability?: 'experimental' | 'beta' | 'stable';
|
||||
/**
|
||||
* The stack version in which the route was introduced (eg: 8.15.0).
|
||||
*/
|
||||
since?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,7 +35,7 @@ export type VersionedRouteConfig<Method extends RouteMethod> = Omit<
|
|||
> & {
|
||||
options?: Omit<
|
||||
RouteConfigOptions<Method>,
|
||||
'access' | 'description' | 'deprecated' | 'discontinued' | 'security'
|
||||
'access' | 'description' | 'summary' | 'deprecated' | 'discontinued' | 'security'
|
||||
>;
|
||||
/** See {@link RouteConfigOptions<RouteMethod>['access']} */
|
||||
access: Exclude<RouteConfigOptions<Method>['access'], undefined>;
|
||||
|
|
|
@ -13,7 +13,7 @@ export * from 'openapi-types';
|
|||
declare module 'openapi-types' {
|
||||
export namespace OpenAPIV3 {
|
||||
export interface BaseSchemaObject {
|
||||
// Custom OpenAPI field added by Kibana for a new field at the shema level.
|
||||
// Custom OpenAPI field added by Kibana for a new field at the schema level.
|
||||
'x-discontinued'?: string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -321,4 +321,103 @@ describe('generateOpenApiDocument', () => {
|
|||
expect(result.paths['/v2-1']!.get!.tags).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('availability', () => {
|
||||
it('creates the expected availability entries', () => {
|
||||
const [routers, versionedRouters] = createTestRouters({
|
||||
routers: {
|
||||
testRouter1: {
|
||||
routes: [
|
||||
{
|
||||
path: '/1-1/{id}/{path*}',
|
||||
options: { availability: { stability: 'experimental' } },
|
||||
},
|
||||
{
|
||||
path: '/1-2/{id}/{path*}',
|
||||
options: { availability: { stability: 'beta' } },
|
||||
},
|
||||
{
|
||||
path: '/1-3/{id}/{path*}',
|
||||
options: { availability: { stability: 'stable' } },
|
||||
},
|
||||
],
|
||||
},
|
||||
testRouter2: {
|
||||
routes: [{ path: '/2-1/{id}/{path*}' }],
|
||||
},
|
||||
},
|
||||
versionedRouters: {
|
||||
testVersionedRouter1: {
|
||||
routes: [
|
||||
{
|
||||
path: '/v1-1',
|
||||
options: {
|
||||
access: 'public',
|
||||
options: { availability: { stability: 'experimental' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/v1-2',
|
||||
options: {
|
||||
access: 'public',
|
||||
options: { availability: { stability: 'beta' } },
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/v1-3',
|
||||
options: {
|
||||
access: 'public',
|
||||
options: { availability: { stability: 'stable' } },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
testVersionedRouter2: {
|
||||
routes: [{ path: '/v2-1', options: { access: 'public' } }],
|
||||
},
|
||||
},
|
||||
});
|
||||
const result = generateOpenApiDocument(
|
||||
{
|
||||
routers,
|
||||
versionedRouters,
|
||||
},
|
||||
{
|
||||
title: 'test',
|
||||
baseUrl: 'https://test.oas',
|
||||
version: '99.99.99',
|
||||
}
|
||||
);
|
||||
|
||||
// router paths
|
||||
expect(result.paths['/1-1/{id}/{path*}']!.get).toMatchObject({
|
||||
'x-state': 'Technical Preview',
|
||||
});
|
||||
expect(result.paths['/1-2/{id}/{path*}']!.get).toMatchObject({
|
||||
'x-state': 'Beta',
|
||||
});
|
||||
|
||||
expect(result.paths['/1-3/{id}/{path*}']!.get).not.toMatchObject({
|
||||
'x-state': expect.any(String),
|
||||
});
|
||||
expect(result.paths['/2-1/{id}/{path*}']!.get).not.toMatchObject({
|
||||
'x-state': expect.any(String),
|
||||
});
|
||||
|
||||
// versioned router paths
|
||||
expect(result.paths['/v1-1']!.get).toMatchObject({
|
||||
'x-state': 'Technical Preview',
|
||||
});
|
||||
expect(result.paths['/v1-2']!.get).toMatchObject({
|
||||
'x-state': 'Beta',
|
||||
});
|
||||
|
||||
expect(result.paths['/v1-3']!.get).not.toMatchObject({
|
||||
'x-state': expect.any(String),
|
||||
});
|
||||
expect(result.paths['/v2-1']!.get).not.toMatchObject({
|
||||
'x-state': expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,9 +23,11 @@ import {
|
|||
getVersionedHeaderParam,
|
||||
mergeResponseContent,
|
||||
prepareRoutes,
|
||||
setXState,
|
||||
} from './util';
|
||||
import type { OperationIdCounter } from './operation_id_counter';
|
||||
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
|
||||
import type { CustomOperationObject } from './type';
|
||||
|
||||
export const processRouter = (
|
||||
appRouter: Router,
|
||||
|
@ -61,7 +63,7 @@ export const processRouter = (
|
|||
parameters.push(...pathObjects, ...queryObjects);
|
||||
}
|
||||
|
||||
const operation: OpenAPIV3.OperationObject = {
|
||||
const operation: CustomOperationObject = {
|
||||
summary: route.options.summary ?? '',
|
||||
tags: route.options.tags ? extractTags(route.options.tags) : [],
|
||||
...(route.options.description ? { description: route.options.description } : {}),
|
||||
|
@ -81,6 +83,8 @@ export const processRouter = (
|
|||
operationId: getOpId(route.path),
|
||||
};
|
||||
|
||||
setXState(route.options.availability, operation);
|
||||
|
||||
const path: OpenAPIV3.PathItemObject = {
|
||||
[route.method]: operation,
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
extractTags,
|
||||
mergeResponseContent,
|
||||
getXsrfHeaderForMethod,
|
||||
setXState,
|
||||
} from './util';
|
||||
|
||||
export const processVersionedRouter = (
|
||||
|
@ -112,6 +113,9 @@ export const processVersionedRouter = (
|
|||
parameters,
|
||||
operationId: getOpId(route.path),
|
||||
};
|
||||
|
||||
setXState(route.options.options?.availability, operation);
|
||||
|
||||
const path: OpenAPIV3.PathItemObject = {
|
||||
[route.method]: operation,
|
||||
};
|
||||
|
|
|
@ -34,3 +34,8 @@ export interface OpenAPIConverter {
|
|||
|
||||
is(type: unknown): boolean;
|
||||
}
|
||||
|
||||
export type CustomOperationObject = OpenAPIV3.OperationObject<{
|
||||
// Custom OpenAPI from ES API spec based on @availability
|
||||
'x-state'?: 'Technical Preview' | 'Beta';
|
||||
}>;
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
type RouterRoute,
|
||||
type RouteValidatorConfig,
|
||||
} from '@kbn/core-http-server';
|
||||
import { KnownParameters } from './type';
|
||||
import { CustomOperationObject, KnownParameters } from './type';
|
||||
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
|
||||
|
||||
const tagPrefix = 'oas-tag:';
|
||||
|
@ -165,3 +165,17 @@ export const getXsrfHeaderForMethod = (
|
|||
},
|
||||
];
|
||||
};
|
||||
|
||||
export function setXState(
|
||||
availability: RouteConfigOptions<RouteMethod>['availability'],
|
||||
operation: CustomOperationObject
|
||||
): void {
|
||||
if (availability) {
|
||||
if (availability.stability === 'experimental') {
|
||||
operation['x-state'] = 'Technical Preview';
|
||||
}
|
||||
if (availability.stability === 'beta') {
|
||||
operation['x-state'] = 'Beta';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue