-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[HTTP] Versioned router implementation (#153543)
## Summary Implements the designs from #151596 * Move `packages/versioning/*` into `packages/core/http` to follow existing structure more closely * Implements the first iteration of the versioned router as a wrapper/layer around the existing router * Adds some integration tests * Future work needed! Once we have a the versioned spec we should implement it in this wrapper layer * Validation is a little bit tricky because of when the `CoreKibanaResponse` object is instantiated, the approach taken here is to replace body, params, query on the route-level's request object Closes #149286 --------- Co-authored-by: kibanamachine <[email protected]>
- Loading branch information
1 parent
d6a6745
commit e8055e8
Showing
24 changed files
with
789 additions
and
287 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 1 addition & 3 deletions
4
...ioning/core-version-http-server/README.md → ...core-http-server/src/versioning/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
packages/core/http/core-http-server/src/versioning/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export type { | ||
ApiVersion, | ||
AddVersionOpts, | ||
VersionedRouteRequestValidation, | ||
VersionedRouteResponseValidation, | ||
VersionedRoute, | ||
VersionedRouteConfig, | ||
VersionedRouteRegistrar, | ||
VersionedRouter, | ||
} from './types'; |
225 changes: 225 additions & 0 deletions
225
packages/core/http/core-http-server/src/versioning/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
/* | ||
* 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 type { Type } from '@kbn/config-schema'; | ||
import type { MaybePromise } from '@kbn/utility-types'; | ||
import type { | ||
RouteConfig, | ||
RouteMethod, | ||
RequestHandler, | ||
IKibanaResponse, | ||
RouteConfigOptions, | ||
RouteValidatorFullConfig, | ||
RequestHandlerContextBase, | ||
RouteValidationFunction, | ||
} from '../..'; | ||
|
||
type RqCtx = RequestHandlerContextBase; | ||
|
||
/** | ||
* Assuming that version will be a monotonically increasing number where: version > 0. | ||
* @experimental | ||
*/ | ||
export type ApiVersion = `${number}`; | ||
|
||
/** | ||
* Configuration for a versioned route | ||
* @experimental | ||
*/ | ||
export type VersionedRouteConfig<Method extends RouteMethod> = Omit< | ||
RouteConfig<unknown, unknown, unknown, Method>, | ||
'validate' | 'options' | ||
> & { | ||
options?: Omit<RouteConfigOptions<Method>, 'access'>; | ||
/** See {@link RouteConfigOptions<RouteMethod>['access']} */ | ||
access: RouteConfigOptions<Method>['access']; | ||
}; | ||
|
||
/** | ||
* Create an {@link VersionedRoute | versioned route}. | ||
* | ||
* @param config - The route configuration | ||
* @returns A versioned route | ||
* @experimental | ||
*/ | ||
export type VersionedRouteRegistrar<Method extends RouteMethod, Ctx extends RqCtx = RqCtx> = ( | ||
config: VersionedRouteConfig<Method> | ||
) => VersionedRoute<Method, Ctx>; | ||
|
||
/** | ||
* A router, very similar to {@link IRouter} that will return an {@link VersionedRoute} | ||
* instead. | ||
* | ||
* @example | ||
* const versionedRoute = versionedRouter | ||
* .post({ | ||
* access: 'internal', | ||
* path: '/api/my-app/foo/{id?}', | ||
* options: { timeout: { payload: 60000 } }, | ||
* }) | ||
* .addVersion( | ||
* { | ||
* version: '1', | ||
* validate: { | ||
* request: { | ||
* query: schema.object({ | ||
* name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), | ||
* }), | ||
* params: schema.object({ | ||
* id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), | ||
* }), | ||
* body: schema.object({ foo: schema.string() }), | ||
* }, | ||
* response: { | ||
* 200: { | ||
* body: schema.object({ foo: schema.string() }), | ||
* }, | ||
* }, | ||
* }, | ||
* }, | ||
* async (ctx, req, res) => { | ||
* await ctx.fooService.create(req.body.foo, req.params.id, req.query.name); | ||
* return res.ok({ body: { foo: req.body.foo } }); | ||
* } | ||
* ) | ||
* // BREAKING CHANGE: { foo: string } => { fooString: string } in body | ||
* .addVersion( | ||
* { | ||
* version: '2', | ||
* validate: { | ||
* request: { | ||
* query: schema.object({ | ||
* name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), | ||
* }), | ||
* params: schema.object({ | ||
* id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), | ||
* }), | ||
* body: schema.object({ fooString: schema.string() }), | ||
* }, | ||
* response: { | ||
* 200: { | ||
* body: schema.object({ fooName: schema.string() }), | ||
* }, | ||
* }, | ||
* }, | ||
* }, | ||
* async (ctx, req, res) => { | ||
* await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name); | ||
* return res.ok({ body: { fooName: req.body.fooString } }); | ||
* } | ||
* ) | ||
* // BREAKING CHANGES: Enforce min/max length on fooString | ||
* .addVersion( | ||
* { | ||
* version: '3', | ||
* validate: { | ||
* request: { | ||
* query: schema.object({ | ||
* name: schema.maybe(schema.string({ minLength: 2, maxLength: 50 })), | ||
* }), | ||
* params: schema.object({ | ||
* id: schema.maybe(schema.string({ minLength: 10, maxLength: 13 })), | ||
* }), | ||
* body: schema.object({ fooString: schema.string({ minLength: 0, maxLength: 1000 }) }), | ||
* }, | ||
* response: { | ||
* 200: { | ||
* body: schema.object({ fooName: schema.string() }), | ||
* }, | ||
* }, | ||
* }, | ||
* }, | ||
* async (ctx, req, res) => { | ||
* await ctx.fooService.create(req.body.fooString, req.params.id, req.query.name); | ||
* return res.ok({ body: { fooName: req.body.fooString } }); | ||
* } | ||
* ); | ||
* @experimental | ||
*/ | ||
export interface VersionedRouter<Ctx extends RqCtx = RqCtx> { | ||
/** @experimental */ | ||
get: VersionedRouteRegistrar<'get', Ctx>; | ||
/** @experimental */ | ||
put: VersionedRouteRegistrar<'put', Ctx>; | ||
/** @experimental */ | ||
post: VersionedRouteRegistrar<'post', Ctx>; | ||
/** @experimental */ | ||
patch: VersionedRouteRegistrar<'patch', Ctx>; | ||
/** @experimental */ | ||
delete: VersionedRouteRegistrar<'delete', Ctx>; | ||
} | ||
|
||
/** @experimental */ | ||
export type VersionedRouteRequestValidation<P, Q, B> = RouteValidatorFullConfig<P, Q, B>; | ||
|
||
/** @experimental */ | ||
export interface VersionedRouteResponseValidation<R> { | ||
[statusCode: number]: { body: RouteValidationFunction<R> | Type<R> }; | ||
unsafe?: { body?: boolean }; | ||
} | ||
|
||
/** | ||
* Versioned route validation | ||
* @experimental | ||
*/ | ||
interface FullValidationConfig<P, Q, B, R> { | ||
/** | ||
* Validation to run against route inputs: params, query and body | ||
* @experimental | ||
*/ | ||
request?: VersionedRouteRequestValidation<P, Q, B>; | ||
/** | ||
* Validation to run against route output | ||
* @note This validation is only intended to run in development. Do not use this | ||
* for setting default values! | ||
* @experimental | ||
*/ | ||
response?: VersionedRouteResponseValidation<R>; | ||
} | ||
|
||
/** | ||
* Options for a versioned route. Probably needs a lot more options like sunsetting | ||
* of an endpoint etc. | ||
* @experimental | ||
*/ | ||
export interface AddVersionOpts<P, Q, B, R> { | ||
/** | ||
* Version to assign to this route | ||
* @experimental | ||
*/ | ||
version: ApiVersion; | ||
/** | ||
* Validation for this version of a route | ||
* @experimental | ||
*/ | ||
validate: false | FullValidationConfig<P, Q, B, R>; | ||
} | ||
|
||
/** | ||
* A versioned route | ||
* @experimental | ||
*/ | ||
export interface VersionedRoute< | ||
Method extends RouteMethod = RouteMethod, | ||
Ctx extends RqCtx = RqCtx | ||
> { | ||
/** | ||
* Add a new version of this route | ||
* @param opts {@link AddVersionOpts | Options} for this version of a route | ||
* @param handler The request handler for this version of a route | ||
* @returns A versioned route, allows for fluent chaining of version declarations | ||
* @experimental | ||
*/ | ||
addVersion<P = unknown, Q = unknown, B = unknown, R = any>( | ||
options: AddVersionOpts<P, Q, B, R>, | ||
handler: ( | ||
...params: Parameters<RequestHandler<P, Q, B, Ctx>> | ||
) => MaybePromise<IKibanaResponse<R>> | ||
): VersionedRoute<Method, Ctx>; | ||
} |
7 changes: 7 additions & 0 deletions
7
packages/core/http/core-http-versioned-router-server-internal/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# @kbn/core-http-versioned-router-server-internal | ||
|
||
This package contains the implementation for sever-side HTTP versioning. | ||
|
||
## Experimental | ||
|
||
See notes in `@kbn/core-http-server/src/versioning` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ing/core-version-http-server/kibana.jsonc → ...ioned-router-server-internal/kibana.jsonc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"type": "shared-common", | ||
"id": "@kbn/core-version-http-server", | ||
"id": "@kbn/core-http-versioned-router-server-internal", | ||
"owner": "@elastic/kibana-core" | ||
} |
2 changes: 1 addition & 1 deletion
2
...ing/core-version-http-server/package.json → ...ioned-router-server-internal/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.