-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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] Registries #36705
Comments
Pinging @elastic/kibana-app-arch |
My two cents: I don't have any strong feelings on the exact interface, mostly I just think we should keep things minimal & avoid overdoing it. In other words, I'm not even certain we need to replace I agree a lightweight class / TS interface could be one way to accomplish this. I see the benefits of that approach mostly being around developer experience for folks building apps in Kibana... would be nice if any registries they interacted with behaved similarly. But that's primarily a question of convention: it doesn't necessarily require a full registry-building "service" to be implemented. I know @timroes has put a lot of thought into this; would be interested to hear this opinion. |
+++
++ @lizozom and I discussed this at GAH. The benefit of having a base interface is simply just like you say @lukeelmers - convention. e.g. do you call |
This is how I imagine this, there would be some interface that anyone can use (in functional or OOP way — up to you): export interface Registry<T extends {}> {
get: (id: string) => T | undefined;
set: (id: string, obj: T) => void;
set$: Observable<T>;
reset: () => void;
reset$: Observable<void>;
} (This But you don't have to implement the import {Observable, Subject} from 'rxjs';
export interface Registry<T extends {}> {
get: (id: string) => T | undefined;
set: (id: string, obj: T) => void;
set$: Observable<T>;
reset: () => void;
reset$: Observable<void>;
}
export const createRegistry = <T>(): Registry<T> => {
let data = new Map<string, T>();
const set$ = new Subject();
const reset$ = new Subject();
const get = (id: string) => data.get(id);
const set = (id: string, obj: T) => {
data.set(id, obj);
set$.next(obj);
};
const reset = () => {
data = new Map<string, T>();
reset$.next();
};
return {
get,
set,
set$: set$ as any as Observable<T>,
reset,
reset$: reset$ as any as Observable<void>,
}
} Then new platform plugins would "uiExport" their registries for all other plugins to use: // NP setup life-cycle
function setup () {
const visualizationRegistry = createRegistry();
return {
visualizationRegistry,
};
} One thing the |
@streamich I'm curious if we have existing use cases that could take advantage of the observable interface. |
hm, yea good point @mattkime. Until there is a need, why bother including it? Right now I don't think there are any needs for it. Most of the time registries aren't added to dynamically at run time, though with the new actions stuff, they will be. Still don't think we'll need observables for it. |
@mattkime @stacey-gammon I'm not aware of any existing registry use cases that would benefit from observables; I was thinking some UI component could use that to re-render when registry state changes and non-UI things too—like telemetry could track something. But, yeah, we should not overcomplicate, it is just an example. |
Since @lukeelmers wanted my two cents, here they are: I think we have a couple of use-cases in the
With regards to Observable or not: I think we currently have both use-cases. The problem is, that due to the way Angular initializes things in two phases (config and run phase) we can in most places assume that in the run phase every registry is already settled and not updating anymore. But I already came to issues in PoCs where visualizations needed to register dynamically and in that case I needed some rather nasty workaround for Since we have a similar concept in the new platform of a But there might also be cases where we might want to allow users to register items later on, and in this case we imho need Observables. If we don't have any use-case for it right in that moment (and I couldn't come up with any right now, especially since the Code sampleJust for clarification what I mean by the frozen registry: // NP setup life-cycle
function setup () {
const visualizationRegistry = createRegistry();
return {
visualizationRegistry,
};
} // NP start life-cycle
function start () {
return {
// Or however we get access to the previously created object
visualizationRegistry: this.visualizationRegistry.freeze(),
};
} With: freeze: Pick<Registry, 'get' /* and potentially all other getter methods */>; So the |
So... I started cleaning up some stuff in my embeddable PR in the registries and I'm essentially just going to use this method locally. When the official version is ready, it should be easy to migrate over. |
The idea of leaning on the lifecycle hooks to keep the registry simple is good, but in practice this is going to be a challenge. This registry concept needs to support the legacy world during this migration, and that is going to be around for 18 months. The legacy world doesn't have these lifecycle hooks, so I don't see a way around going the observable route. Even with lifecycles, observable backed state makes sense in many situations. They don't necessarily add a ton of complexity in practice for consumers, and they help future proof your API as your needs change over time. |
List of our registries:
|
#37310 — PoC of moving a registry to NP. |
Below are totals of number of registered items in each Those counts are generated at runtime and change depending on which app you open, but the 27 items in Discussed with @spalger, for a list of 27 items we don't need any indexing, so we can remove It should be super fast to iterate though 27 items, or even 100 items. So, instead of indexing we can add a type Predicate<T> = (item: T) => boolean;
interface Registry<T> {
// ...
find: (predicate: Predicate<T>) => T | undefined;
filter: (predicate: Predicate<T>) => T[];
} Other options:
|
It looks like some |
I agree completely with this. I think a centralized registry pattern is over engineering for problems that don't actually exist in our code base. Even if it does make sense for something like visTypes (I don't think it does), that doesn't mean it needs to be a shared abstraction. At the scale we're talking about, sorting arrays and finding elements in an array is fast and easy. And it requires no extra knowledge or migration efforts outside of native JavaScript. I've been saying this for years, but at some point I recognize I'm shouting into the wind. |
We decided to NOT have a common implementation for registries. Instead, registries can internally have any implementation and can be replaced by some semantically named function like In the NP that function could be exported as plugin contract, but registry itself kept private: class Plugin {
indexPatternTypes = [];
setup () {
return {
addIndexPatternType: type => this.indexPatternTypes.push(type),
};
}
} |
I unfortunately don't understand what you're suggesting here @epixa? What would you suggest instead of using a registry? Maybe I am just understanding that sentence wrong, but the second half of the sentence sounds like you doesn't just mean using a shared abstraction doesn't make sense for I've synced with @ppisljar this morning about that and here a couple of points we discussed, why it might make sense still implementing that registries:
|
Having no shared abstraction here and just using native data structures, usually array or map. As far as I understand, that's where the app arch team ended up as well. Regarding sorting and lifecycle events, I don't share your concerns broadly. I agree that people can introduce bugs when implementing a sort function, and I agree that people can architect their plugins in such a way that dependent plugins can directly mutate the state they manage outside the context of lifecycle, but I think in practice these will be rare. Don't want someone to add things to your collection outside of the function you provide? Don't give them access to the raw collection, perhaps give them a shallow copy instead. Don't want someone to add things to your collection after In other words, these aren't just solvable problems, they're problems with trivial solutions. |
@mattkime I just came across Inspector's |
@streamich posted this proposed solution for deangularizing situations where you want to get rid of |
@streamich @lukeelmers @ppisljar @kertal Following up on the proposed fix above, I want to make sure we're aligned: We don't want to expose the registries themselves to solution \ 3rd party developers, as before. The registry itself now becomes an internal implementation detail, and the plugin should expose a registration\add method. So if previously a developer would import the registry and register to it: import { DocViewsRegistryProvider } from 'ui/registry/doc_views';
DocViewsRegistryProvider.register(...); In the new platform, it would look something like: somePlugin.registerDocView(...); |
Re-opening as we use this list for tracking. |
Is this issue still up to date? It seems like it has been updated the last time in November. If this is not used anymore, let's close it, please. |
Registries provide a method of augmenting functionality in Kibana. In the new platform registries will be exposed via plugins rather than the
ui
directory, where most registries currently live. TheuiRegistry
function generates registry instances with angular dependency injection and IndexedArray functionality, the former is being removed out of necessity and the later for simplicity.What should replace
uiRegistry
?My research into registries indicates that they're simply arrays with accessors. The accessors may be as simple as returning the whole array or provide some sort of search or sort or both. Even the most complex of case can be expressed in 2-3 lines of code.
That said, there is some desire to provide a uniform interface. Perhaps it could be implemented with a class or a typescript interface. I think this is mostly about communication hence my desire get opinions.
Relevant code -
https://github.com/elastic/kibana/blob/master/src/legacy/ui/public/registry/_registry.js
https://github.com/elastic/kibana/blob/master/src/legacy/ui/public/indexed_array/indexed_array.js
@timroes @stacey-gammon @lizozom @lukeelmers @streamich @ppisljar
The text was updated successfully, but these errors were encountered: