diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts index b73a63b308..b9fc9d1e99 100644 --- a/src/common/__tests__/hotbar-store.test.ts +++ b/src/common/__tests__/hotbar-store.test.ts @@ -4,45 +4,61 @@ */ import { anyObject } from "jest-mock-extended"; -import { merge } from "lodash"; import mockFs from "mock-fs"; import logger from "../../main/logger"; import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog"; import { HotbarStore } from "../hotbar-store"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import directoryForUserDataInjectable - from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; jest.mock("../../main/catalog/catalog-entity-registry", () => ({ catalogEntityRegistry: { items: [ - { + getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", + }, metadata: { uid: "1dfa26e2ebab15780a3547e9c7fa785c", name: "mycluster", source: "local", + labels: {}, + }, + }), + getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", }, - }, - { metadata: { uid: "55b42c3c7ba3b04193416cda405269a5", name: "my_shiny_cluster", source: "remote", + labels: {}, + }, + }), + getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", }, - }, - { metadata: { uid: "catalog-entity", name: "Catalog", source: "app", + labels: {}, }, - }, + }), ], }, })); function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { - return merge(data, { + return { getName: jest.fn(() => data.metadata?.name), getId: jest.fn(() => data.metadata?.uid), getSource: jest.fn(() => data.metadata?.source ?? "unknown"), @@ -52,7 +68,8 @@ function getMockCatalogEntity(data: Partial & CatalogEntityKi metadata: {}, spec: {}, status: {}, - }) as CatalogEntity; + ...data, + } as CatalogEntity; } const testCluster = getMockCatalogEntity({ diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index dcc732a2ca..71cefe6bfc 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -67,22 +67,22 @@ export class KubernetesCluster extends CatalogEntity { if (app) { - await ClusterStore.getInstance().getById(this.metadata.uid)?.activate(); + await ClusterStore.getInstance().getById(this.getId())?.activate(); } else { - await requestClusterActivation(this.metadata.uid, false); + await requestClusterActivation(this.getId(), false); } } async disconnect(): Promise { if (app) { - ClusterStore.getInstance().getById(this.metadata.uid)?.disconnect(); + ClusterStore.getInstance().getById(this.getId())?.disconnect(); } else { - await requestClusterDisconnection(this.metadata.uid, false); + await requestClusterDisconnection(this.getId(), false); } } async onRun(context: CatalogEntityActionContext) { - context.navigate(`/cluster/${this.metadata.uid}`); + context.navigate(`/cluster/${this.getId()}`); } onDetailsOpen(): void { @@ -100,7 +100,7 @@ export class KubernetesCluster extends CatalogEntity broadcastMessage( IpcRendererNavigationEvents.NAVIGATE_IN_APP, - `/entity/${this.metadata.uid}/settings`, + `/entity/${this.getId()}/settings`, ), }); } @@ -111,14 +111,14 @@ export class KubernetesCluster extends CatalogEntity requestClusterDisconnection(this.metadata.uid), + onClick: () => requestClusterDisconnection(this.getId()), }); break; case LensKubernetesClusterStatus.DISCONNECTED: context.menuItems.push({ title: "Connect", icon: "link", - onClick: () => context.navigate(`/cluster/${this.metadata.uid}`), + onClick: () => context.navigate(`/cluster/${this.getId()}`), }); break; } diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index be59135ae1..a014b22aa4 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -38,9 +38,9 @@ export class WebLink extends CatalogEntity WeblinkStore.getInstance().removeById(this.metadata.uid), + onClick: async () => WeblinkStore.getInstance().removeById(this.getId()), confirm: { - message: `Remove Web Link "${this.metadata.name}" from ${productName}?`, + message: `Remove Web Link "${this.getName()}" from ${productName}?`, }, }); } diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index 46ce8228ff..7f5f8bbe73 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -315,11 +315,24 @@ export abstract class CatalogEntity< @observable status: Status; @observable spec: Spec; - constructor(data: CatalogEntityData) { + constructor({ metadata, status, spec }: CatalogEntityData) { makeObservable(this); - this.metadata = data.metadata; - this.status = data.status; - this.spec = data.spec; + + if (!metadata || typeof metadata !== "object") { + throw new TypeError("CatalogEntity's metadata must be a defined object"); + } + + if (!status || typeof status !== "object") { + throw new TypeError("CatalogEntity's status must be a defined object"); + } + + if (!spec || typeof spec !== "object") { + throw new TypeError("CatalogEntity's spec must be a defined object"); + } + + this.metadata = metadata; + this.status = status; + this.spec = spec; } /** diff --git a/src/common/hotbar-store.ts b/src/common/hotbar-store.ts index 3b4593111e..82c697d0d7 100644 --- a/src/common/hotbar-store.ts +++ b/src/common/hotbar-store.ts @@ -154,28 +154,28 @@ export class HotbarStore extends BaseStore { @action addToHotbar(item: CatalogEntity, cellIndex?: number) { const hotbar = this.getActive(); - const uid = item.metadata?.uid; - const name = item.metadata?.name; + const uid = item.getId(); + const name = item.getName(); if (typeof uid !== "string") { - throw new TypeError("CatalogEntity.metadata.uid must be a string"); + throw new TypeError("CatalogEntity's ID must be a string"); } if (typeof name !== "string") { - throw new TypeError("CatalogEntity.metadata.name must be a string"); + throw new TypeError("CatalogEntity's NAME must be a string"); } - const newItem = { entity: { - uid, - name, - source: item.metadata.source, - }}; - - if (this.isAddedToActive(item)) { return; } + const entity = { + uid, + name, + source: item.metadata.source, + }; + const newItem = { entity }; + if (cellIndex === undefined) { // Add item to empty cell const emptyCellIndex = hotbar.items.indexOf(null); @@ -278,11 +278,14 @@ export class HotbarStore extends BaseStore { } /** - * Checks if entity already pinned to hotbar - * @returns boolean + * Checks if entity already pinned to the active hotbar */ - isAddedToActive(entity: CatalogEntity) { - return !!this.getActive().items.find(item => item?.entity.uid === entity.metadata.uid); + isAddedToActive(entity: CatalogEntity | null | undefined): boolean { + if (!entity) { + return false; + } + + return this.getActive().items.findIndex(item => item?.entity.uid === entity.getId()) >= 0; } getDisplayLabel(hotbar: Hotbar): string { diff --git a/src/main/catalog/catalog-entity-registry.ts b/src/main/catalog/catalog-entity-registry.ts index 65f60d8e4e..5c6e760052 100644 --- a/src/main/catalog/catalog-entity-registry.ts +++ b/src/main/catalog/catalog-entity-registry.ts @@ -36,7 +36,7 @@ export class CatalogEntityRegistry { } getById(id: string): T | undefined { - return this.items.find((entity) => entity.metadata.uid === id) as T | undefined; + return this.items.find(entity => entity.getId() === id) as T | undefined; } getItemsForApiKind(apiVersion: string, kind: string): T[] { diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 12924a4b18..d42a0c60a7 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -85,7 +85,7 @@ export class ClusterManager extends Singleton { } protected updateEntityFromCluster(cluster: Cluster) { - const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id); + const index = catalogEntityRegistry.items.findIndex((entity) => entity.getId() === cluster.id); if (index === -1) { return; @@ -169,11 +169,11 @@ export class ClusterManager extends Singleton { @action protected syncClustersFromCatalog(entities: KubernetesCluster[]) { for (const entity of entities) { - const cluster = this.store.getById(entity.metadata.uid); + const cluster = this.store.getById(entity.getId()); if (!cluster) { const model = { - id: entity.metadata.uid, + id: entity.getId(), kubeConfigPath: entity.spec.kubeconfigPath, contextName: entity.spec.kubeconfigContext, accessibleNamespaces: entity.spec.accessibleNamespaces ?? [], diff --git a/src/migrations/hotbar-store/5.0.0-beta.5.ts b/src/migrations/hotbar-store/5.0.0-beta.5.ts index 421bc2b908..bb7ceb2567 100644 --- a/src/migrations/hotbar-store/5.0.0-beta.5.ts +++ b/src/migrations/hotbar-store/5.0.0-beta.5.ts @@ -16,7 +16,7 @@ export default { for (const hotbar of hotbars) { for (let i = 0; i < hotbar.items.length; i += 1) { const item = hotbar.items[i]; - const entity = catalogEntityRegistry.items.find((entity) => entity.metadata.uid === item?.entity.uid); + const entity = catalogEntityRegistry.items.find((entity) => entity.getId() === item?.entity.uid); if (!entity) { // Clear disabled item diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index b7c97aa968..cc6af170c8 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -120,7 +120,7 @@ export class CatalogEntityRegistry { const entity = this.categoryRegistry.getEntityForData(item); if (entity) { - this._entities.set(entity.metadata.uid, entity); + this._entities.set(entity.getId(), entity); } else { this.rawEntities.push(item); } diff --git a/src/renderer/components/+catalog/catalog-entity-item.tsx b/src/renderer/components/+catalog/catalog-entity-item.tsx deleted file mode 100644 index f909eaf602..0000000000 --- a/src/renderer/components/+catalog/catalog-entity-item.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import styles from "./catalog.module.scss"; -import React from "react"; -import { action, computed } from "mobx"; -import { CatalogEntity } from "../../api/catalog-entity"; -import type { ItemObject } from "../../../common/item.store"; -import { Badge } from "../badge"; -import { navigation } from "../../navigation"; -import { searchUrlParam } from "../input"; -import { KubeObject } from "../../../common/k8s-api/kube-object"; -import type { CatalogEntityRegistry } from "../../api/catalog-entity-registry"; - -export class CatalogEntityItem implements ItemObject { - constructor(public entity: T, private registry: CatalogEntityRegistry) { - if (!(entity instanceof CatalogEntity)) { - throw Object.assign(new TypeError("CatalogEntityItem cannot wrap a non-CatalogEntity type"), { typeof: typeof entity, prototype: Object.getPrototypeOf(entity) }); - } - } - - get kind() { - return this.entity.kind; - } - - get apiVersion() { - return this.entity.apiVersion; - } - - get name() { - return this.entity.metadata.name; - } - - getName() { - return this.entity.metadata.name; - } - - get id() { - return this.entity.metadata.uid; - } - - getId() { - return this.id; - } - - @computed get phase() { - return this.entity.status.phase; - } - - get enabled() { - return this.entity.status.enabled ?? true; - } - - get labels() { - return KubeObject.stringifyLabels(this.entity.metadata.labels); - } - - getLabelBadges(onClick?: React.MouseEventHandler) { - return this.labels - .map(label => ( - { - navigation.searchParams.set(searchUrlParam.name, label); - onClick?.(event); - event.stopPropagation(); - }} - expandable={false} - /> - )); - } - - get source() { - return this.entity.metadata.source || "unknown"; - } - - get searchFields() { - return [ - this.name, - this.id, - this.phase, - `source=${this.source}`, - ...this.labels, - ]; - } - - onRun() { - this.registry.onRun(this.entity); - } - - @action - async onContextMenuOpen(ctx: any) { - return this.entity.onContextMenuOpen(ctx); - } -} diff --git a/src/renderer/components/+entity-settings/entity-settings.tsx b/src/renderer/components/+entity-settings/entity-settings.tsx index 7b5e9c816e..6bfa97195f 100644 --- a/src/renderer/components/+entity-settings/entity-settings.tsx +++ b/src/renderer/components/+entity-settings/entity-settings.tsx @@ -81,14 +81,14 @@ export class EntitySettings extends React.Component { <>
- {this.entity.metadata.name} + {this.entity.getName()}
diff --git a/src/renderer/components/cluster-settings/cluster-settings.tsx b/src/renderer/components/cluster-settings/cluster-settings.tsx index bfd89bda36..e89a600095 100644 --- a/src/renderer/components/cluster-settings/cluster-settings.tsx +++ b/src/renderer/components/cluster-settings/cluster-settings.tsx @@ -11,7 +11,7 @@ import type { CatalogEntity } from "../../api/catalog-entity"; import * as components from "./components"; function getClusterForEntity(entity: CatalogEntity) { - return ClusterStore.getInstance().getById(entity.metadata.uid); + return ClusterStore.getInstance().getById(entity.getId()); } export function GeneralSettings({ entity }: EntitySettingViewProps) { diff --git a/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx b/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx index 7300dc92e0..afd23b221f 100644 --- a/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-icon-settings.tsx @@ -75,8 +75,8 @@ export class ClusterIconSetting extends React.Component { accept="image/*" label={ diff --git a/src/renderer/components/cluster-settings/components/cluster-name-setting.tsx b/src/renderer/components/cluster-settings/components/cluster-name-setting.tsx index 3e80fdbb1f..4802d79d41 100644 --- a/src/renderer/components/cluster-settings/components/cluster-name-setting.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-name-setting.tsx @@ -29,7 +29,7 @@ export class ClusterNameSetting extends React.Component { componentDidMount() { disposeOnUnmount(this, autorun(() => { - this.name = this.props.cluster.preferences.clusterName || this.props.entity.metadata.name; + this.name = this.props.cluster.preferences.clusterName || this.props.entity.getName(); }), ); } diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index 63bf7dc6c9..a20160f8df 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -73,7 +73,7 @@ export class HotbarEntityIcon extends React.Component { menuItems.unshift({ title: "Remove from Hotbar", - onClick: () => this.props.remove(this.props.entity.metadata.uid), + onClick: () => this.props.remove(this.props.entity.getId()), }); this.contextMenu.menuItems = menuItems; @@ -90,8 +90,8 @@ export class HotbarEntityIcon extends React.Component { return ( { onMenuOpen={() => this.onMenuOpen()} disabled={!entity} menuItems={this.contextMenu.menuItems} - tooltip={`${entity.metadata.name} (${entity.metadata.source})`} + tooltip={`${entity.getName()} (${entity.metadata.source})`} {...elemProps} > { this.ledIcon } diff --git a/src/renderer/components/layout/sidebar-cluster.tsx b/src/renderer/components/layout/sidebar-cluster.tsx index 0126059468..58cae55c99 100644 --- a/src/renderer/components/layout/sidebar-cluster.tsx +++ b/src/renderer/components/layout/sidebar-cluster.tsx @@ -73,7 +73,7 @@ export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity ? "Remove from Hotbar" : "Add to Hotbar"; const onClick = isAddedToActive - ? () => hotbarStore.removeFromHotbar(metadata.uid) + ? () => hotbarStore.removeFromHotbar(clusterEntity.getId()) : () => hotbarStore.addToHotbar(clusterEntity); contextMenu.menuItems = [{ title, onClick }]; @@ -92,8 +92,7 @@ export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity setOpened(!opened); }; - const { metadata, spec } = clusterEntity; - const id = `cluster-${metadata.uid}`; + const id = `cluster-${clusterEntity.getId()}`; const tooltipId = `tooltip-${id}`; return ( @@ -106,17 +105,17 @@ export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity data-testid="sidebar-cluster-dropdown" >
- {metadata.name} + {clusterEntity.getName()}
- {metadata.name} + {clusterEntity.getName()} onClusterDelete(entity.metadata.uid), + onClick: () => onClusterDelete(entity.getId()), }); } });