-
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
[Discuss] Generics, registries, type mappings, factory overrides #62815
Comments
I'm having trouble determining which parts of the example are axioms. Obviously the plugin structure is axiomatic. Is the enhancement registration pattern axiomatic as well? My summary of the problem -
Can the registration function return the secondary item? I think I'm confused about the case where we need to use a generic I largely agree with what you're saying but would like to rule out the possibility that we're discussing the consequences of other alterable coding decisions. |
No, because we don't know if another plugin has yet to set a custom enhancement provider. |
Can you sketch that out in code? I'm not sure what a |
Sure thing, I added a few example plugins in this PR that showcase the pattern: This example does not make use of generics, didn't want to over complicate the basic example. |
Pinging @elastic/kibana-app-arch (Team:AppArch) |
To provide more context around this item, what is being referred to here is the usage of // plugin `foo` exposing a registry
export interface RegistryMapping {}
export registry = {
get<T extends string>(id: T) => RegistryMapping[T];
}
// plugin `bar` registering an item to a registry
interface BarItem {
id: string;
hiya: boolean;
}
declare module '../../../plugins/foo/public' {
interface RegistryMapping {
barItem: BarItem;
}
}
const item: BarItem = { id: 'barItem', hiya: true };
foo.register(item);
// plugin `baz` which consumes `foo` registry and needs access to `barItem`
const i = foo.get('barItem'); // barItem is correctly typed The suggested alternative would be to prefer to have |
I am not fully following, but if i understand correclty:
this is a problem:
i agree, from two perspectives:
there is yet another problem however as someone might just pass in a string: to resolve this issue we could say what i think Stacey is suggesting, to actually export getFooBar on the contract, and that the actual registry should only be used when id is not known at compile time (for example dashboard doesn't know what embeddables are there at compile time as new ones can be added thru plugins) i think this makes sense in some scenarios, but doesn't in others. for example with expressions each plugin might register functions and i think it really doesn't make sense that it would expose a getter on the contract. To get the function you need to get it thru expression plugin which will inject the handlers in it for you. |
You could do it with expressions like this: type FooExpressionFn = ExpressionFn<...>;
class FooExpressionPlugin {
private getFooExpressionFn: () => FooExpressionFn;
setup(core, expressions) {
this.getFooExpressionFn = expressions.registerFn(getMyFooFunctionDefinition()) as FooExpressionFn;
}
start: () => ({
fooExpressionFn: this.getFooExpressionFn()
})
} Benefits:
Downsides:
|
closing this due to inactivity, please re-open if its still relevant |
What
There are two usage patterns I have been thinking about lately, that are somewhat intertwined:
Type generics on registries
There are two ways to access registry items:
Using a generic type in the former case does not make sense and will only be a source of bugs, since you don't actually know the value of id. The second case is a pattern we use now, but there is a better way:
Getting the instance, correctly typed, from the registering plugin directly is safer because it will require the user to list
fooBarPlugin
as a direct dependency.This is all well and good... until I started thinking about the pattern for the second situation.
Plugins adding enhancements
A pattern to support this is to provide a
setCustomProvider
function in thesetup
method of the plugin that is providing the base implementation.If we use the above pattern of registering definitions in the setup lifecycle and returning instances in the start lifecycle, we can support an injection point for enhancements. Now however, generics come back into play because the plugin registering a custom implementation didn't create the instance.
The only way to solve this the "best" way would be to introduce more lifecycle methods, so the flow would be
fooBar
instance in Phase 3.Summary
We have discussed this in the Kibana Repo Review and the best interim path forward is probably to:
The best long term path forward is to figure out how to solve the problem of needing
n+1
lifecycles.The text was updated successfully, but these errors were encountered: