Skip to content

Commit 608cc70

Browse files
[OAS] Support setting availability (#196647)
## Summary Close #181995 Related #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 <[email protected]>
1 parent 68b3267 commit 608cc70

File tree

10 files changed

+197
-5
lines changed

10 files changed

+197
-5
lines changed

packages/core/http/core-http-router-server-internal/src/router.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ describe('Router', () => {
5454
discontinued: 'post test discontinued',
5555
summary: 'post test summary',
5656
description: 'post test description',
57+
availability: {
58+
since: '1.0.0',
59+
stability: 'experimental',
60+
},
5761
},
5862
},
5963
(context, req, res) => res.ok()
@@ -72,6 +76,10 @@ describe('Router', () => {
7276
discontinued: 'post test discontinued',
7377
summary: 'post test summary',
7478
description: 'post test description',
79+
availability: {
80+
since: '1.0.0',
81+
stability: 'experimental',
82+
},
7583
},
7684
});
7785
});

packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.test.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,46 @@ describe('Versioned route', () => {
5252
jest.clearAllMocks();
5353
});
5454

55+
describe('#getRoutes', () => {
56+
it('returns the expected metadata', () => {
57+
const versionedRouter = CoreVersionedRouter.from({ router });
58+
versionedRouter
59+
.get({
60+
path: '/test/{id}',
61+
access: 'public',
62+
options: {
63+
httpResource: true,
64+
availability: {
65+
since: '1.0.0',
66+
stability: 'experimental',
67+
},
68+
excludeFromOAS: true,
69+
tags: ['1', '2', '3'],
70+
},
71+
description: 'test',
72+
summary: 'test',
73+
enableQueryVersion: false,
74+
})
75+
.addVersion({ version: '2023-10-31', validate: false }, handlerFn);
76+
77+
expect(versionedRouter.getRoutes()[0].options).toMatchObject({
78+
access: 'public',
79+
enableQueryVersion: false,
80+
description: 'test',
81+
summary: 'test',
82+
options: {
83+
httpResource: true,
84+
availability: {
85+
since: '1.0.0',
86+
stability: 'experimental',
87+
},
88+
excludeFromOAS: true,
89+
tags: ['1', '2', '3'],
90+
},
91+
});
92+
});
93+
});
94+
5595
it('can register multiple handlers', () => {
5696
const versionedRouter = CoreVersionedRouter.from({ router });
5797
versionedRouter
@@ -133,14 +173,15 @@ describe('Versioned route', () => {
133173
const opts: Parameters<typeof versionedRouter.post>[0] = {
134174
path: '/test/{id}',
135175
access: 'internal',
176+
summary: 'test',
177+
description: 'test',
136178
options: {
137179
authRequired: true,
138180
tags: ['access:test'],
139181
timeout: { payload: 60_000, idleSocket: 10_000 },
140182
xsrfRequired: false,
141183
excludeFromOAS: true,
142184
httpResource: true,
143-
summary: `test`,
144185
},
145186
};
146187

packages/core/http/core-http-server/src/router/route.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,23 @@ export interface RouteConfigOptions<Method extends RouteMethod> {
321321
* @default false
322322
*/
323323
httpResource?: boolean;
324+
325+
/**
326+
* Based on the the ES API specification (see https://github.com/elastic/elasticsearch-specification)
327+
* Kibana APIs can also specify some metadata about API availability.
328+
*
329+
* This setting is only applicable if your route `access` is `public`.
330+
*
331+
* @remark intended to be used for informational purposes only.
332+
*/
333+
availability?: {
334+
/** @default stable */
335+
stability?: 'experimental' | 'beta' | 'stable';
336+
/**
337+
* The stack version in which the route was introduced (eg: 8.15.0).
338+
*/
339+
since?: string;
340+
};
324341
}
325342

326343
/**

packages/core/http/core-http-server/src/versioning/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export type VersionedRouteConfig<Method extends RouteMethod> = Omit<
3535
> & {
3636
options?: Omit<
3737
RouteConfigOptions<Method>,
38-
'access' | 'description' | 'deprecated' | 'discontinued' | 'security'
38+
'access' | 'description' | 'summary' | 'deprecated' | 'discontinued' | 'security'
3939
>;
4040
/** See {@link RouteConfigOptions<RouteMethod>['access']} */
4141
access: Exclude<RouteConfigOptions<Method>['access'], undefined>;

packages/kbn-router-to-openapispec/openapi-types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export * from 'openapi-types';
1313
declare module 'openapi-types' {
1414
export namespace OpenAPIV3 {
1515
export interface BaseSchemaObject {
16-
// Custom OpenAPI field added by Kibana for a new field at the shema level.
16+
// Custom OpenAPI field added by Kibana for a new field at the schema level.
1717
'x-discontinued'?: string;
1818
}
1919
}

packages/kbn-router-to-openapispec/src/generate_oas.test.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,103 @@ describe('generateOpenApiDocument', () => {
321321
expect(result.paths['/v2-1']!.get!.tags).toEqual([]);
322322
});
323323
});
324+
325+
describe('availability', () => {
326+
it('creates the expected availability entries', () => {
327+
const [routers, versionedRouters] = createTestRouters({
328+
routers: {
329+
testRouter1: {
330+
routes: [
331+
{
332+
path: '/1-1/{id}/{path*}',
333+
options: { availability: { stability: 'experimental' } },
334+
},
335+
{
336+
path: '/1-2/{id}/{path*}',
337+
options: { availability: { stability: 'beta' } },
338+
},
339+
{
340+
path: '/1-3/{id}/{path*}',
341+
options: { availability: { stability: 'stable' } },
342+
},
343+
],
344+
},
345+
testRouter2: {
346+
routes: [{ path: '/2-1/{id}/{path*}' }],
347+
},
348+
},
349+
versionedRouters: {
350+
testVersionedRouter1: {
351+
routes: [
352+
{
353+
path: '/v1-1',
354+
options: {
355+
access: 'public',
356+
options: { availability: { stability: 'experimental' } },
357+
},
358+
},
359+
{
360+
path: '/v1-2',
361+
options: {
362+
access: 'public',
363+
options: { availability: { stability: 'beta' } },
364+
},
365+
},
366+
{
367+
path: '/v1-3',
368+
options: {
369+
access: 'public',
370+
options: { availability: { stability: 'stable' } },
371+
},
372+
},
373+
],
374+
},
375+
testVersionedRouter2: {
376+
routes: [{ path: '/v2-1', options: { access: 'public' } }],
377+
},
378+
},
379+
});
380+
const result = generateOpenApiDocument(
381+
{
382+
routers,
383+
versionedRouters,
384+
},
385+
{
386+
title: 'test',
387+
baseUrl: 'https://test.oas',
388+
version: '99.99.99',
389+
}
390+
);
391+
392+
// router paths
393+
expect(result.paths['/1-1/{id}/{path*}']!.get).toMatchObject({
394+
'x-state': 'Technical Preview',
395+
});
396+
expect(result.paths['/1-2/{id}/{path*}']!.get).toMatchObject({
397+
'x-state': 'Beta',
398+
});
399+
400+
expect(result.paths['/1-3/{id}/{path*}']!.get).not.toMatchObject({
401+
'x-state': expect.any(String),
402+
});
403+
expect(result.paths['/2-1/{id}/{path*}']!.get).not.toMatchObject({
404+
'x-state': expect.any(String),
405+
});
406+
407+
// versioned router paths
408+
expect(result.paths['/v1-1']!.get).toMatchObject({
409+
'x-state': 'Technical Preview',
410+
});
411+
expect(result.paths['/v1-2']!.get).toMatchObject({
412+
'x-state': 'Beta',
413+
});
414+
415+
expect(result.paths['/v1-3']!.get).not.toMatchObject({
416+
'x-state': expect.any(String),
417+
});
418+
expect(result.paths['/v2-1']!.get).not.toMatchObject({
419+
'x-state': expect.any(String),
420+
});
421+
});
422+
});
324423
});

packages/kbn-router-to-openapispec/src/process_router.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import {
2323
getVersionedHeaderParam,
2424
mergeResponseContent,
2525
prepareRoutes,
26+
setXState,
2627
} from './util';
2728
import type { OperationIdCounter } from './operation_id_counter';
2829
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
30+
import type { CustomOperationObject } from './type';
2931

3032
export const processRouter = (
3133
appRouter: Router,
@@ -61,7 +63,7 @@ export const processRouter = (
6163
parameters.push(...pathObjects, ...queryObjects);
6264
}
6365

64-
const operation: OpenAPIV3.OperationObject = {
66+
const operation: CustomOperationObject = {
6567
summary: route.options.summary ?? '',
6668
tags: route.options.tags ? extractTags(route.options.tags) : [],
6769
...(route.options.description ? { description: route.options.description } : {}),
@@ -81,6 +83,8 @@ export const processRouter = (
8183
operationId: getOpId(route.path),
8284
};
8385

86+
setXState(route.options.availability, operation);
87+
8488
const path: OpenAPIV3.PathItemObject = {
8589
[route.method]: operation,
8690
};

packages/kbn-router-to-openapispec/src/process_versioned_router.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
extractTags,
3030
mergeResponseContent,
3131
getXsrfHeaderForMethod,
32+
setXState,
3233
} from './util';
3334

3435
export const processVersionedRouter = (
@@ -112,6 +113,9 @@ export const processVersionedRouter = (
112113
parameters,
113114
operationId: getOpId(route.path),
114115
};
116+
117+
setXState(route.options.options?.availability, operation);
118+
115119
const path: OpenAPIV3.PathItemObject = {
116120
[route.method]: operation,
117121
};

packages/kbn-router-to-openapispec/src/type.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@ export interface OpenAPIConverter {
3434

3535
is(type: unknown): boolean;
3636
}
37+
38+
export type CustomOperationObject = OpenAPIV3.OperationObject<{
39+
// Custom OpenAPI from ES API spec based on @availability
40+
'x-state'?: 'Technical Preview' | 'Beta';
41+
}>;

packages/kbn-router-to-openapispec/src/util.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
type RouterRoute,
1818
type RouteValidatorConfig,
1919
} from '@kbn/core-http-server';
20-
import { KnownParameters } from './type';
20+
import { CustomOperationObject, KnownParameters } from './type';
2121
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
2222

2323
const tagPrefix = 'oas-tag:';
@@ -165,3 +165,17 @@ export const getXsrfHeaderForMethod = (
165165
},
166166
];
167167
};
168+
169+
export function setXState(
170+
availability: RouteConfigOptions<RouteMethod>['availability'],
171+
operation: CustomOperationObject
172+
): void {
173+
if (availability) {
174+
if (availability.stability === 'experimental') {
175+
operation['x-state'] = 'Technical Preview';
176+
}
177+
if (availability.stability === 'beta') {
178+
operation['x-state'] = 'Beta';
179+
}
180+
}
181+
}

0 commit comments

Comments
 (0)