-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[HTTP] Versioned router implementation #153543
Conversation
…level config, was in "options"
…tests and functionality though
packages/core/http/core-http-router-server-internal/src/request.ts
Outdated
Show resolved
Hide resolved
...ges/core/versioning/core-versioned-http-server-internal/src/internal_versioned_route.test.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server-internal/src/internal_versioned_router.ts
Outdated
Show resolved
Hide resolved
// This validation is a pass-through so that we can apply our version-specific validation later | ||
const passThroughValidation = { body: schema.any(), params: schema.any(), query: schema.any() }; | ||
|
||
export class InternalVersionedRoute implements VersionedRoute { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect most of the code in here will be implementing the actual API specification and making sure versioned routes adhere to it.
* | ||
* @experimental | ||
*/ | ||
export interface VersionHTTPToolkit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to remove the concept of "Toolkit" as I don't think it adds anything.
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing major to report, but a bunch of NITs, remarks and questions
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server/src/types.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server-internal/src/types.ts
Outdated
Show resolved
Hide resolved
if (this.validateResponses && handler.options.validate && handler.options.validate.response) { | ||
const { response } = handler.options.validate; | ||
try { | ||
validate( | ||
req, | ||
{ body: response.body, unsafe: { body: response.unsafe } }, | ||
handler.options.version | ||
); | ||
} catch (e) { | ||
return res.custom({ | ||
statusCode: 500, | ||
body: `Failed output validation: ${e.message}`, | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:oh_no_you_didnt:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 gotta love structural typing
packages/core/versioning/core-versioned-http-server-internal/src/internal_versioned_route.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server-internal/src/internal_versioned_router.ts
Outdated
Show resolved
Hide resolved
packages/core/versioning/core-versioned-http-server-internal/src/internal_versioned_router.ts
Outdated
Show resolved
Hide resolved
Pinging @elastic/kibana-core (Team:Core) |
|
||
const validation = handler.options.validate || undefined; | ||
|
||
const mutableCoreKibanaRequest = req as Mutable<CoreKibanaRequest>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(moving comment from previous location)
This is so that we can hide/remove unvalidated inputs after CoreKibanaRequest was constructed. Not super happy about this, but it seemed like the least trouble without doing one of:
(1) Exposing more knowledge of Router internals so that we can dynamically set validation schema (right now we just use IRouter)
(2) Changing our approach, integrating the versioned router functionality into the Router implementation more directly
} | ||
|
||
/** @experimental */ | ||
export type RequestValidation<P, Q, B> = RouteValidatorFullConfig<P, Q, B>; | ||
|
||
/** @experimental */ | ||
export interface ResponseValidation<R> { | ||
body: RouteValidationFunction<R> | Type<R>; | ||
[statusCode: number]: { body: RouteValidationFunction<R> | Type<R> }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following OAS's example here. For the responses we added validation for specific status codes. At the moment this is used to scope validation to a specific status code, but we could go further and throw if an unknown status code was provided.
Note: output validation is intended to run in dev only.
…t validation defined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation looks good to me.
What are the next steps / what are we missing to effectively expose this feature to our consumers via Core APIs?
export type { | ||
AddVersionOpts, | ||
RequestValidation, | ||
ResponseValidation, | ||
Version, | ||
VersionedRoute, | ||
VersionedRouteConfig, | ||
VersionedRouteRegistrar, | ||
VersionedRouter, | ||
} from './src/versioning'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: I wonder if we shouldn't add a more explicit prefix to some of those types, just to make it more understandable that they're bound to the versioned router. Mostly thinking about:
Version
RequestValidation
ResponseValidation
Maybe (I know, those are long names...):
RouteVersion
(orApiVersion
?)VersionedRouteRequestValidation
VersionedRouteResponseValidation
const router = {} as unknown as IRouter<MyCustomContext>; | ||
|
||
const versionedRouter = vtk.createVersionedRouter({ router }); | ||
const versionedRouter = {} as unknown as VersionedRouter<MyCustomContext>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have an implementation, should we get rid of this file, or convert it to documentation or TSDoc?
// TODO: Make "true" dev-only | ||
private readonly validateResponses: boolean = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a TODO
for now or a TODO
for later 😄?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning on addressing this when exposing the versioned router on our http router instance :thu:
I'll update the PR description with this info @pgayvallet |
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Public APIs missing comments
Public APIs missing exports
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What a great effort! This is exciting! LGTM!
I added 2 comments for your consideration. But feel free to tackle them in the upcoming follow ups.
AddVersionOpts, | ||
VersionedRoute, | ||
VersionedRouteConfig, | ||
} from '@kbn/core-http-server'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super-nit: 2 imports from the same package can be merged
validation.request, | ||
handler.options.version | ||
); | ||
mutableCoreKibanaRequest.body = body; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will also mutate req
. Do we want to use clone
?
## Summary Now that we merged #153543, this PR exposes the versioned router for teams to start using. The versioned router will be available on `IRouter` under a new `versioned` property. Primary benefit of this approach is that plugin developers will not need to do anything other than "get" the `versioned` property to get a versioned router. Drawback is that this precludes us from passing in additional configuration, like a version, to scope the versioned router instance. For that we would need some kind of `createVersionedRouter({ version: ... })`. At this point it is not clear this is necessary, we could revisit this decision based on actual usage. Plugin developers could also do something like: ```ts // common const const MY_API_VERSION: ApiVersion = '1'; // in routes import {MY_API_VERSION} from '../from/common'; router.versioned.get({ path: ... }) .addVersion({ version: MY_API_VERSION }); ``` In this way they could get many of the same benefits of a version-scoped version router, with the drawback that they need to pass this in for every route. ### TODO - [x] Add an integration test for the versioned router ### Future work * We still need to consider revisiting some of the router design to better support internal cases like adding support for registering a handler for a version range and adding a default version to continue supporting on-prem where introducing versions will be a breaking change --------- Co-authored-by: kibanamachine <[email protected]>
Summary
Implements the designs from #151596
packages/versioning/*
intopackages/core/http
to follow existing structure more closelyCoreKibanaResponse
object is instantiated, the approach taken here is to replace body, params, query on the route-level's request objectCloses #149286
Next steps:
IRouter
interface