-
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
[RFC] Handler interface #36509
[RFC] Handler interface #36509
Conversation
Pinging @elastic/kibana-platform |
@joshdover Take a look at this. It's not completely done yet, but I think it overlaps with your application mounting RFC. Specifically, I think the mounting interface should function as a handler rather than us passing entire plugin contracts down into application business logic, as you mentioned here. |
Gahh woops, I hit "Ready for Review" on the wrong PR. |
@elastic/kibana-platform This is now ready for review. |
Might just be me, but this RFC would be clearer with an additional example that uses handlers for application mounting or something besides HTTP requests. As the "Drawbacks" section accurately points out, handlers as a pattern are commonly used for HTTP routes, but people might not necessarily associate them with other areas. A concrete example could help bridge that gap. |
@lukeelmers Additional examples have been added! |
those are super helpful & much clearer - thanks! |
@elastic/kibana-platform Still looking for approvals here. Once I have one approval, I'll put this RFC into the "final comment period" and will presume that it's good enough to accept. |
rfcs/text/0003_handler_interface.md
Outdated
http.router.route({ | ||
path: '/foo', | ||
async routeHandler(context) { | ||
context.elasticsearch.search(); // === callWithRequest(request, 'search') |
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.
How does context get created when the request
argument is only known at the time of execution?
Like I'd think this route handler would have to be something like:
sync routeHandler(context, request) {
context.getElasticsearch(request).search();
}
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.
The context gets created on a per-request basis, so in this example each route handler would technically be getting access to a different Elasticsearch client that is already pre-configured for the current request.
@joshdover We should make this super clear in the RFC since it's pretty much the most important detail of this whole proposal :)
Looks nice! This is interesting from my perspective from dealing with actions and I'm wondering how it fits. If I'm following this all correctly, My Actions are somewhat similar, but So it might look something like this:
So if actions want to use a similar Api it seems like the context should be passed in when it's executed... but then I'm still not sure how context gets created using the providers and the dynamic data. |
|
||
```js | ||
http.router.registerRequestContext('elasticsearch', async request => { | ||
const client = await core.elasticsearch.client$.toPromise(); |
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.
it's an implementation detail, but imo important don't forget to make context extension lazy (as a getter?) and execute this function every time when someone reads from context.[extension point]
otherwise context may have stale version of the client.
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 context needs to be generated immediately before invoking the handler. The idea is that context does not change once its corresponding handler begins executing, so even if the ES client were to update at the service level, a handler that has already started executing would continue to use the original client until it finishes executing.
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.
@joshdover We should call this out specifically in the RFC because it's an important detail.
@stacey-gammon I've coalesced answers to your questions here, but let me know if I missed anything.
No, context is created right before a handler is executed by calling each context provider and constructing an object from the return values. I've added a section to the RFC that explains how and when context is created.
The arguments available to context providers will differ by service. In the HTTP example, context providers would have access to The service that implements the Handler Context pattern will have to define which arguments are available to context providers and which are available to handlers themselves.
Unlikely, I'll add some more fleshed out examples in the RFC itself, but one off the top of my head is for the ApplicationService's
I think one of the main benefits of using this pattern over plugin contracts is that this pattern does not require the developer to know all of the available APIs in every plugin in order to get a useful set of functionality in a handler. Without handler context, I think many developers would re-implement the same logic over-and-over (possibly with bugs) rather than being able to leverage TypeScript/Intellisense to explore what's available in the |
Another benefit of this pattern is that you don't need to pass around core and dependent services throughout all of your business logic. Contextual handlers allow for service capabilities to be injected directly where you need them. With this proposal implemented, we won't even need to pass things like the saved object client to plugin lifecycle functions because you'll be able to access it directly from your handlers. |
This RFC is now in the final comment period. If you have any comments about a fundamental problem with this proposal, make them now. Otherwise this will be accepted on Monday, June 17th. |
provider so neither service depends directly on the other. | ||
- The existence of plugins that a given plugin does not depend on may leak |
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.
Since this RFC is about a broad concept rather than a specific feature, I'm fine proceeding with this RFC as-is, but this detail is an important problem to address for implementations. Realistically, I think this means that core must provide plugin developers a mechanism to build a context since by nature a plugin won't even know about plugins that it does not depend on, so they can't properly construct the context themselves.
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.
Agreed. We may actually need to expose a service to plugins that allows them to offer context registration and construction. This service would handle the dependency management aspect of constructing context.
This needs to be built before any plugins begin offering context in their handlers.
This is still a work in progress, but I wanted to post it since it overlaps with at least one other RFC that is in flight.
View Rendered RFC
[skip-ci]