diff --git a/src/common/catalog/catalog-category-registry.ts b/src/common/catalog/catalog-category-registry.ts index 886db3e90a..68e3e811b8 100644 --- a/src/common/catalog/catalog-category-registry.ts +++ b/src/common/catalog/catalog-category-registry.ts @@ -19,26 +19,35 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { action, computed, observable, toJS } from "mobx"; +import { action, computed, observable } from "mobx"; +import { Disposer, ExtendedMap, StrictMap } from "../utils"; import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity"; export class CatalogCategoryRegistry { - @observable protected categories: CatalogCategory[] = []; + protected categories = observable.set(); - @action add(category: CatalogCategory) { - this.categories.push(category); + @action add(category: CatalogCategory): Disposer { + this.categories.add(category); + + return () => this.categories.delete(category); } - @action remove(category: CatalogCategory) { - this.categories = this.categories.filter((cat) => cat.apiVersion !== category.apiVersion && cat.kind !== category.kind); + @computed private get groupKindLookup(): Map> { + const res = new ExtendedMap>(StrictMap.new); + + for (const category of this.categories) { + res.getOrDefault(category.spec.group).strictSet(category.spec.names.kind, category); + } + + return res; } @computed get items() { - return toJS(this.categories); + return Array.from(this.categories); } - getForGroupKind(group: string, kind: string) { - return this.categories.find((c) => c.spec.group === group && c.spec.names.kind === kind) as T; + getForGroupKind(group: string, kind: string): T | undefined { + return this.groupKindLookup.get(group)?.get(kind) as T; } getEntityForData(data: CatalogEntityData & CatalogEntityKindData) { @@ -60,17 +69,11 @@ export class CatalogCategoryRegistry { return new specVersion.entityClass(data); } - getCategoryForEntity(data: CatalogEntityData & CatalogEntityKindData) { + getCategoryForEntity(data: CatalogEntityData & CatalogEntityKindData): T | undefined { const splitApiVersion = data.apiVersion.split("/"); const group = splitApiVersion[0]; - const category = this.categories.find((category) => { - return category.spec.group === group && category.spec.names.kind === data.kind; - }); - - if (!category) return null; - - return category as T; + return this.getForGroupKind(group, data.kind); } } diff --git a/src/common/utils/extended-map.ts b/src/common/utils/extended-map.ts index 9d3a0dc7b3..460e716f1c 100644 --- a/src/common/utils/extended-map.ts +++ b/src/common/utils/extended-map.ts @@ -21,7 +21,28 @@ import { action, IEnhancer, IObservableMapInitialValues, ObservableMap } from "mobx"; +export class StrictMap extends Map { + static new(): StrictMap { + return new StrictMap(); + } + + /** + * @throws if `key` already in map + */ + strictSet(key: K, val: V): this { + if (this.has(key)) { + throw new TypeError("Duplicate key in map"); + } + + return this.set(key, val); + } +} + export class ExtendedMap extends Map { + static new(): ExtendedMap> { + return new ExtendedMap>(() => new Map()); + } + constructor(protected getDefault: () => V, entries?: readonly (readonly [K, V])[] | null) { super(entries); } diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index 42c9b6f224..ac4e6748dc 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -19,28 +19,25 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { action, observable } from "mobx"; +import { computed, observable } from "mobx"; import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc"; import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog"; import "../../common/catalog-entities"; +import { iter } from "../utils"; export class CatalogEntityRegistry { - @observable protected _items: CatalogEntity[] = observable.array([], { deep: true }); + protected rawItems = observable.array([], { deep: true }); @observable protected _activeEntity: CatalogEntity; constructor(private categoryRegistry: CatalogCategoryRegistry) {} init() { subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => { - this.updateItems(items); + this.rawItems.replace(items); }); broadcastMessage("catalog:broadcast"); } - @action updateItems(items: (CatalogEntityData & CatalogEntityKindData)[]) { - this._items = items.map(data => this.categoryRegistry.getEntityForData(data)); - } - set activeEntity(entity: CatalogEntity) { this._activeEntity = entity; } @@ -49,23 +46,27 @@ export class CatalogEntityRegistry { return this._activeEntity; } - get items() { - return this._items; + @computed get items() { + return Array.from(iter.filterMap(this.rawItems, rawItem => this.categoryRegistry.getEntityForData(rawItem))); + } + + @computed get entities(): Map { + return new Map(this.items.map(item => [item.metadata.uid, item])); } getById(id: string) { - return this._items.find((entity) => entity.metadata.uid === id); + return this.entities.get(id); } getItemsForApiKind(apiVersion: string, kind: string): T[] { - const items = this._items.filter((item) => item.apiVersion === apiVersion && item.kind === kind); + const items = this.items.filter((item) => item.apiVersion === apiVersion && item.kind === kind); return items as T[]; } getItemsForCategory(category: CatalogCategory): T[] { const supportedVersions = category.spec.versions.map((v) => `${category.spec.group}/${v.name}`); - const items = this._items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind); + const items = this.items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind); return items as T[]; }