diff --git a/src/common/catalog/catalog-category-registry.ts b/src/common/catalog/catalog-category-registry.ts index a0319d773b..3b1d5bb00a 100644 --- a/src/common/catalog/catalog-category-registry.ts +++ b/src/common/catalog/catalog-category-registry.ts @@ -25,24 +25,22 @@ import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./cat export class CatalogCategoryRegistry { protected categories = observable.set(); + protected groupKinds = new ExtendedMap>(); @action add(category: CatalogCategory): Disposer { this.categories.add(category); + this.updateGroupKinds(category); - return () => this.categories.delete(category); + return () => { + this.categories.delete(category); + this.groupKinds.clear(); + }; } - @computed private get groupKindLookup(): Map> { - // ExtendedMap has the convenience methods `getOrInsert` and `strictSet` - const res = new ExtendedMap>(); - - for (const category of this.categories) { - res - .getOrInsert(category.spec.group, ExtendedMap.new) - .strictSet(category.spec.names.kind, category); - } - - return res; + private updateGroupKinds(category: CatalogCategory) { + this.groupKinds + .getOrInsert(category.spec.group, ExtendedMap.new) + .strictSet(category.spec.names.kind, category); } @computed get items() { @@ -50,7 +48,7 @@ export class CatalogCategoryRegistry { } getForGroupKind(group: string, kind: string): T | undefined { - return this.groupKindLookup.get(group)?.get(kind) as T; + return this.groupKinds.get(group)?.get(kind) as T; } getEntityForData(data: CatalogEntityData & CatalogEntityKindData) { diff --git a/src/common/catalog/index.ts b/src/common/catalog/index.ts index 5dee2c22b2..81d166c870 100644 --- a/src/common/catalog/index.ts +++ b/src/common/catalog/index.ts @@ -21,4 +21,3 @@ export * from "./catalog-category-registry"; export * from "./catalog-entity"; -export * from "./catalog-entity-registry"; diff --git a/src/extensions/core-api/catalog.ts b/src/extensions/core-api/catalog.ts index 304ab7bbd0..14ba0c94d9 100644 --- a/src/extensions/core-api/catalog.ts +++ b/src/extensions/core-api/catalog.ts @@ -20,7 +20,8 @@ */ -import { CatalogEntity, catalogEntityRegistry as registry } from "../../common/catalog"; +import type { CatalogEntity } from "../../common/catalog"; +import { catalogEntityRegistry as registry } from "../../main/catalog"; export { catalogCategoryRegistry as catalogCategories } from "../../common/catalog/catalog-category-registry"; export * from "../../common/catalog-entities"; diff --git a/src/extensions/lens-main-extension.ts b/src/extensions/lens-main-extension.ts index 7e0ac71707..023a8b92f9 100644 --- a/src/extensions/lens-main-extension.ts +++ b/src/extensions/lens-main-extension.ts @@ -22,7 +22,8 @@ import { LensExtension } from "./lens-extension"; import { WindowManager } from "../main/window-manager"; import { getExtensionPageUrl } from "./registries/page-registry"; -import { CatalogEntity, catalogEntityRegistry } from "../common/catalog"; +import { catalogEntityRegistry } from "../main/catalog"; +import type { CatalogEntity } from "../common/catalog"; import type { IObservableArray } from "mobx"; import type { MenuRegistration } from "./registries"; diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index 362ba9a770..c2971017cc 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -21,7 +21,7 @@ import { reaction, toJS } from "mobx"; import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc"; -import type { CatalogEntityRegistry} from "../common/catalog"; +import type { CatalogEntityRegistry} from "./catalog"; import "../common/catalog-entities/kubernetes-cluster"; import type { Disposer } from "../common/utils"; diff --git a/src/main/catalog-sources/kubeconfig-sync.ts b/src/main/catalog-sources/kubeconfig-sync.ts index c4a8b0a514..c72b6081f2 100644 --- a/src/main/catalog-sources/kubeconfig-sync.ts +++ b/src/main/catalog-sources/kubeconfig-sync.ts @@ -20,7 +20,8 @@ */ import { action, observable, IComputedValue, computed, ObservableMap, runInAction } from "mobx"; -import { CatalogEntity, catalogEntityRegistry } from "../../common/catalog"; +import type { CatalogEntity } from "../../common/catalog"; +import { catalogEntityRegistry } from "../../main/catalog"; import { watch } from "chokidar"; import fs from "fs"; import fse from "fs-extra"; diff --git a/src/common/__tests__/catalog-entity-registry.test.ts b/src/main/catalog/__tests__/catalog-entity-registry.test.ts similarity index 61% rename from src/common/__tests__/catalog-entity-registry.test.ts rename to src/main/catalog/__tests__/catalog-entity-registry.test.ts index 3c32bc835e..fff3faa3d9 100644 --- a/src/common/__tests__/catalog-entity-registry.test.ts +++ b/src/main/catalog/__tests__/catalog-entity-registry.test.ts @@ -20,8 +20,30 @@ */ import { observable, reaction } from "mobx"; -import { WebLink } from "../catalog-entities"; -import { CatalogEntityRegistry } from "../catalog"; +import { WebLink, WebLinkSpec, WebLinkStatus } from "../../../common/catalog-entities"; +import { catalogCategoryRegistry, CatalogEntity, CatalogEntityMetadata } from "../../../common/catalog"; +import { CatalogEntityRegistry } from "../catalog-entity-registry"; + +class InvalidEntity extends CatalogEntity { + public readonly apiVersion = "entity.k8slens.dev/v1alpha1"; + public readonly kind = "Invalid"; + + async onRun() { + return; + } + + public onSettingsOpen(): void { + return; + } + + public onDetailsOpen(): void { + return; + } + + public onContextMenuOpen(): void { + return; + } +} describe("CatalogEntityRegistry", () => { let registry: CatalogEntityRegistry; @@ -39,9 +61,23 @@ describe("CatalogEntityRegistry", () => { phase: "valid" } }); + const invalidEntity = new InvalidEntity({ + metadata: { + uid: "invalid", + name: "test-link", + source: "test", + labels: {} + }, + spec: { + url: "https://k8slens.dev" + }, + status: { + phase: "valid" + } + }); beforeEach(() => { - registry = new CatalogEntityRegistry(); + registry = new CatalogEntityRegistry(catalogCategoryRegistry); }); describe("addSource", () => { @@ -79,4 +115,22 @@ describe("CatalogEntityRegistry", () => { expect(registry.items.length).toEqual(0); }); }); + + describe("items", () => { + it("returns added items", () => { + expect(registry.items.length).toBe(0); + + const source = observable.array([entity]); + + registry.addObservableSource("test", source); + expect(registry.items.length).toBe(1); + }); + + it("does not return items without matching category", () => { + const source = observable.array([invalidEntity]); + + registry.addObservableSource("test", source); + expect(registry.items.length).toBe(0); + }); + }); }); diff --git a/src/common/catalog/catalog-entity-registry.ts b/src/main/catalog/catalog-entity-registry.ts similarity index 79% rename from src/common/catalog/catalog-entity-registry.ts rename to src/main/catalog/catalog-entity-registry.ts index 80a6c66a4c..48b8316774 100644 --- a/src/common/catalog/catalog-entity-registry.ts +++ b/src/main/catalog/catalog-entity-registry.ts @@ -20,12 +20,14 @@ */ import { action, computed, observable, IComputedValue, IObservableArray } from "mobx"; -import type { CatalogEntity } from "./catalog-entity"; -import { iter } from "../utils"; +import { CatalogCategoryRegistry, catalogCategoryRegistry, CatalogEntity } from "../../common/catalog"; +import { iter } from "../../common/utils"; export class CatalogEntityRegistry { protected sources = observable.map>([], { deep: true }); + constructor(private categoryRegistry: CatalogCategoryRegistry) {} + @action addObservableSource(id: string, source: IObservableArray) { this.sources.set(id, computed(() => source)); } @@ -39,7 +41,9 @@ export class CatalogEntityRegistry { } @computed get items(): CatalogEntity[] { - return Array.from(iter.flatMap(this.sources.values(), source => source.get())); + const allItems = Array.from(iter.flatMap(this.sources.values(), source => source.get())); + + return allItems.filter((entity) => this.categoryRegistry.getCategoryForEntity(entity) !== undefined); } getItemsForApiKind(apiVersion: string, kind: string): T[] { @@ -49,4 +53,4 @@ export class CatalogEntityRegistry { } } -export const catalogEntityRegistry = new CatalogEntityRegistry(); +export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); diff --git a/src/main/catalog/index.ts b/src/main/catalog/index.ts new file mode 100644 index 0000000000..13e660b878 --- /dev/null +++ b/src/main/catalog/index.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export * from "./catalog-entity-registry"; diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 211172a2a6..9ef28c8f9f 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -28,7 +28,7 @@ import type { Cluster } from "./cluster"; import logger from "./logger"; import { apiKubePrefix } from "../common/vars"; import { Singleton } from "../common/utils"; -import { catalogEntityRegistry } from "../common/catalog"; +import { catalogEntityRegistry } from "./catalog"; import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster"; export class ClusterManager extends Singleton { diff --git a/src/main/index.ts b/src/main/index.ts index 73b2ca5fd1..d54babde8b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -50,7 +50,7 @@ import { bindBroadcastHandlers } from "../common/ipc"; import { startUpdateChecking } from "./app-updater"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import { CatalogPusher } from "./catalog-pusher"; -import { catalogEntityRegistry } from "../common/catalog"; +import { catalogEntityRegistry } from "./catalog"; import { HotbarStore } from "../common/hotbar-store"; import { HelmRepoManager } from "./helm/helm-repo-manager"; import { KubeconfigSyncManager } from "./catalog-sources"; diff --git a/src/renderer/api/__tests__/catalog-entity-registry.test.ts b/src/renderer/api/__tests__/catalog-entity-registry.test.ts index 359b293993..8a26aabc00 100644 --- a/src/renderer/api/__tests__/catalog-entity-registry.test.ts +++ b/src/renderer/api/__tests__/catalog-entity-registry.test.ts @@ -22,11 +22,18 @@ import { CatalogEntityRegistry } from "../catalog-entity-registry"; import "../../../common/catalog-entities"; import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry"; +import type { CatalogEntityData, CatalogEntityKindData } from "../catalog-entity"; + +class TestCatalogEntityRegistry extends CatalogEntityRegistry { + replaceItems(items: Array) { + this.rawItems.replace(items); + } +} describe("CatalogEntityRegistry", () => { describe("updateItems", () => { it("adds new catalog item", () => { - const catalog = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry); const items = [{ apiVersion: "entity.k8slens.dev/v1alpha1", kind: "KubernetesCluster", @@ -42,7 +49,7 @@ describe("CatalogEntityRegistry", () => { spec: {} }]; - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); expect(catalog.items.length).toEqual(1); items.push({ @@ -60,12 +67,12 @@ describe("CatalogEntityRegistry", () => { spec: {} }); - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); expect(catalog.items.length).toEqual(2); }); it("updates existing items", () => { - const catalog = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry); const items = [{ apiVersion: "entity.k8slens.dev/v1alpha1", kind: "KubernetesCluster", @@ -81,19 +88,19 @@ describe("CatalogEntityRegistry", () => { spec: {} }]; - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); expect(catalog.items.length).toEqual(1); expect(catalog.items[0].status.phase).toEqual("disconnected"); items[0].status.phase = "connected"; - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); expect(catalog.items.length).toEqual(1); expect(catalog.items[0].status.phase).toEqual("connected"); }); it("removes deleted items", () => { - const catalog = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry); const items = [ { apiVersion: "entity.k8slens.dev/v1alpha1", @@ -125,11 +132,51 @@ describe("CatalogEntityRegistry", () => { } ]; - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); items.splice(0, 1); - (catalog as any).rawItems.replace(items); + catalog.replaceItems(items); expect(catalog.items.length).toEqual(1); expect(catalog.items[0].metadata.uid).toEqual("456"); }); }); + + describe("items", () => { + it("does not return items without matching category", () => { + const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry); + const items = [ + { + apiVersion: "entity.k8slens.dev/v1alpha1", + kind: "KubernetesCluster", + metadata: { + uid: "123", + name: "foobar", + source: "test", + labels: {} + }, + status: { + phase: "disconnected" + }, + spec: {} + }, + { + apiVersion: "entity.k8slens.dev/v1alpha1", + kind: "FooBar", + metadata: { + uid: "456", + name: "barbaz", + source: "test", + labels: {} + }, + status: { + phase: "disconnected" + }, + spec: {} + } + ]; + + catalog.replaceItems(items); + + expect(catalog.items.length).toBe(1); + }); + }); });