From 6e0fdc2e5713d471cf2bbe1321fee404a0740754 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 5 Jan 2022 15:38:05 -0500 Subject: [PATCH 1/2] Replace KubeObjectDetailRegistry with injectable version Signed-off-by: Sebastian Malton --- .eslintrc.js | 6 + .../monaco-editor.ts | 16 +- package.json | 4 +- src/extensions/common-api/registrations.ts | 2 +- .../extension-loader/extension-loader.ts | 1 - src/extensions/lens-renderer-extension.ts | 3 +- src/extensions/registries/index.ts | 1 - src/renderer/bootstrap.tsx | 3 - .../+custom-resources/crd-details.tsx | 31 +- .../kube-details-items/internal-items.tsx | 446 +++++++++++++++++ .../kube-detail-items.d.ts} | 21 +- .../kube-details.injectable.ts | 61 +++ .../kube-object-details.tsx | 197 ++++---- .../layout/top-bar/top-bar-win-linux.test.tsx | 5 +- .../layout/top-bar/top-bar.test.tsx | 1 - src/renderer/initializers/index.ts | 1 - .../kube-object-detail-registry.tsx | 449 ------------------ src/renderer/initializers/registries.ts | 1 - yarn.lock | 13 +- 19 files changed, 667 insertions(+), 595 deletions(-) rename src/renderer/api/kube-object-detail-registry.ts => __mocks__/monaco-editor.ts (79%) create mode 100644 src/renderer/components/kube-object-details/kube-details-items/internal-items.tsx rename src/{extensions/registries/kube-object-detail-registry.ts => renderer/components/kube-object-details/kube-details-items/kube-detail-items.d.ts} (71%) create mode 100644 src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts delete mode 100644 src/renderer/initializers/kube-object-detail-registry.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 514ab56bd748..5c2941bf5bdb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,12 @@ module.exports = { react: { version: packageJson.devDependencies.react || "detect", }, + // the package eslint-import-resolver-typescript is required for this line which fixes errors when using .d.ts files + "import/resolver": { + "typescript": { + "alwaysTryTypes": true, + }, + }, }, overrides: [ { diff --git a/src/renderer/api/kube-object-detail-registry.ts b/__mocks__/monaco-editor.ts similarity index 79% rename from src/renderer/api/kube-object-detail-registry.ts rename to __mocks__/monaco-editor.ts index 1693fd56f17d..82a5753385fe 100644 --- a/src/renderer/api/kube-object-detail-registry.ts +++ b/__mocks__/monaco-editor.ts @@ -19,4 +19,18 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export { KubeObjectDetailRegistry } from "../../extensions/registries/kube-object-detail-registry"; +module.exports = { + languages: { + register: jest.fn(), + setMonarchTokensProvider: jest.fn(), + registerCompletionItemProvider: jest.fn(), + }, + editor: { + defineTheme: jest.fn(), + getModel: jest.fn(), + createModel: jest.fn(), + }, + Uri: { + file: jest.fn(), + }, +}; diff --git a/package.json b/package.json index 43497e5edd53..ad89383be7d9 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ }, "moduleNameMapper": { "\\.(css|scss)$": "/__mocks__/styleMock.ts", - "\\.(svg)$": "/__mocks__/imageMock.ts" + "\\.(svg)$": "/__mocks__/imageMock.ts", + "^monaco-editor": "/node_modules/monaco-editor" }, "modulePathIgnorePatterns": [ "/dist", @@ -340,6 +341,7 @@ "esbuild": "^0.13.15", "esbuild-loader": "^2.16.0", "eslint": "^7.32.0", + "eslint-import-resolver-typescript": "^2.5.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-import": "^2.25.3", "eslint-plugin-react": "^7.27.1", diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts index 7b7688e85ff9..b7345a9c6aa9 100644 --- a/src/extensions/common-api/registrations.ts +++ b/src/extensions/common-api/registrations.ts @@ -20,7 +20,7 @@ */ export type { AppPreferenceRegistration, AppPreferenceComponents } from "../registries/app-preference-registry"; -export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry"; +export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../../renderer/components/kube-object-details/kube-details-items/kube-detail-items"; export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry"; export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry"; export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../registries/page-registry"; diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 33a716e03a3a..63c31c45e281 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -289,7 +289,6 @@ export class ExtensionLoader { registries.ClusterPageRegistry.getInstance().add(extension.clusterPages, extension), registries.ClusterPageMenuRegistry.getInstance().add(extension.clusterPageMenus, extension), registries.KubeObjectMenuRegistry.getInstance().add(extension.kubeObjectMenuItems), - registries.KubeObjectDetailRegistry.getInstance().add(extension.kubeObjectDetailItems), registries.KubeObjectStatusRegistry.getInstance().add(extension.kubeObjectStatusTexts), registries.WorkloadsOverviewDetailRegistry.getInstance().add(extension.kubeWorkloadsOverviewItems), ]; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 718f25f7c5e2..40ba09b1c479 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -31,6 +31,7 @@ import type { KubernetesCluster } from "../common/catalog-entities"; import type { WelcomeMenuRegistration } from "../renderer/components/+welcome/welcome-menu-items/welcome-menu-registration"; import type { WelcomeBannerRegistration } from "../renderer/components/+welcome/welcome-banner-items/welcome-banner-registration"; import type { CommandRegistration } from "../renderer/components/command-palette/registered-commands/commands"; +import type { KubeObjectDetailRegistration } from "../renderer/components/kube-object-details/kube-details-items/kube-detail-items"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -40,7 +41,7 @@ export class LensRendererExtension extends LensExtension { appPreferences: registries.AppPreferenceRegistration[] = []; entitySettings: registries.EntitySettingRegistration[] = []; statusBarItems: registries.StatusBarRegistration[] = []; - kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = []; + kubeObjectDetailItems: KubeObjectDetailRegistration[] = []; kubeObjectMenuItems: registries.KubeObjectMenuRegistration[] = []; kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: CommandRegistration[] = []; diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts index e8c9930176e9..2159f848f815 100644 --- a/src/extensions/registries/index.ts +++ b/src/extensions/registries/index.ts @@ -25,7 +25,6 @@ export * from "./page-registry"; export * from "./page-menu-registry"; export * from "./app-preference-registry"; export * from "./status-bar-registry"; -export * from "./kube-object-detail-registry"; export * from "./kube-object-menu-registry"; export * from "./kube-object-status-registry"; export * from "./entity-setting-registry"; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index b9f48b2b8c2e..4d1807330ff6 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -109,9 +109,6 @@ export async function bootstrap(comp: () => Promise, di: Dependenc logger.info(`${logPrefix} initializing KubeObjectMenuRegistry`); initializers.initKubeObjectMenuRegistry(); - logger.info(`${logPrefix} initializing KubeObjectDetailRegistry`); - initializers.initKubeObjectDetailRegistry(); - logger.info(`${logPrefix} initializing WorkloadsOverviewDetailRegistry`); initializers.initWorkloadsOverviewDetailRegistry(); diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index 7754c40f7086..b44890d6f1d6 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -88,10 +88,8 @@ export class CRDDetails extends React.Component { { - crd.getConditions().map(condition => { - const { type, message, lastTransitionTime, status } = condition; - - return ( + crd.getConditions() + .map(({ type, message, lastTransitionTime, status }) => ( { )} /> - ); - }) + )) } @@ -133,19 +130,15 @@ export class CRDDetails extends React.Component { JSON Path { - printerColumns.map((column, index) => { - const { name, type, jsonPath } = column; - - return ( - - {name} - {type} - - - - - ); - }) + printerColumns.map(({ name, type, jsonPath }, index) => ( + + {name} + {type} + + + + + )) } diff --git a/src/renderer/components/kube-object-details/kube-details-items/internal-items.tsx b/src/renderer/components/kube-object-details/kube-details-items/internal-items.tsx new file mode 100644 index 000000000000..fce1678ee5ec --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-details-items/internal-items.tsx @@ -0,0 +1,446 @@ +/** + * 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. + */ + +import React from "react"; +import type { KubeObjectDetailsProps } from ".."; +import { type HpaDetailsProps, HpaDetails } from "../../+config-autoscalers"; +import { LimitRangeDetails } from "../../+config-limit-ranges"; +import { ConfigMapDetails } from "../../+config-maps"; +import { PodDisruptionBudgetDetails } from "../../+config-pod-disruption-budgets"; +import { ResourceQuotaDetails } from "../../+config-resource-quotas"; +import { SecretDetails } from "../../+config-secrets"; +import { CRDDetails } from "../../+custom-resources"; +import { EventDetails } from "../../+events"; +import { KubeEventDetails } from "../../+events/kube-event-details"; +import { NamespaceDetails } from "../../+namespaces"; +import { EndpointDetails } from "../../+network-endpoints"; +import { IngressDetails } from "../../+network-ingresses"; +import { NetworkPolicyDetails } from "../../+network-policies"; +import { ServiceDetails } from "../../+network-services"; +import { NodeDetails } from "../../+nodes"; +import { PodSecurityPolicyDetails } from "../../+pod-security-policies"; +import { StorageClassDetails } from "../../+storage-classes"; +import { PersistentVolumeClaimDetails } from "../../+storage-volume-claims"; +import { PersistentVolumeDetails } from "../../+storage-volumes"; +import { ClusterRoleBindingDetails } from "../../+user-management/+cluster-role-bindings"; +import { ClusterRoleDetails } from "../../+user-management/+cluster-roles"; +import { RoleBindingDetails } from "../../+user-management/+role-bindings"; +import { RoleDetails } from "../../+user-management/+roles"; +import { ServiceAccountsDetails } from "../../+user-management/+service-accounts"; +import { CronJobDetails } from "../../+workloads-cronjobs"; +import { DaemonSetDetails } from "../../+workloads-daemonsets"; +import { DeploymentDetails } from "../../+workloads-deployments"; +import { JobDetails } from "../../+workloads-jobs"; +import { PodDetails } from "../../+workloads-pods"; +import { ReplicaSetDetails } from "../../+workloads-replicasets"; +import { StatefulSetDetails } from "../../+workloads-statefulsets"; +import type { KubeObjectDetailRegistration } from "./kube-detail-items"; + +export const internalItems: Required[] = [ + { + kind: "HorizontalPodAutoscaler", + apiVersions: ["autoscaling/v2beta1"], + components: { + // Note: this line is left in the long form as a validation that this usecase is valid + Details: (props: HpaDetailsProps) => , + }, + }, + { + kind: "HorizontalPodAutoscaler", + apiVersions: ["autoscaling/v2beta1"], + priority: 5, + components: { + // Note: this line is left in the long form as a validation that this usecase is valid + Details: (props: KubeObjectDetailsProps) => , + }, + }, + { + kind: "LimitRange", + apiVersions: ["v1"], + components: { + Details: LimitRangeDetails, + }, + }, + { + kind: "ConfigMap", + apiVersions: ["v1"], + components: { + Details: ConfigMapDetails, + }, + }, + { + kind: "ConfigMap", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "PodDisruptionBudget", + apiVersions: ["policy/v1beta1"], + components: { + Details: PodDisruptionBudgetDetails, + }, + }, + { + kind: "ResourceQuota", + apiVersions: ["v1"], + components: { + Details: ResourceQuotaDetails, + }, + }, + { + kind: "Secret", + apiVersions: ["v1"], + components: { + Details: SecretDetails, + }, + }, + { + kind: "CustomResourceDefinition", + apiVersions: ["apiextensions.k8s.io/v1", "apiextensions.k8s.io/v1beta1"], + components: { + Details: CRDDetails, + }, + }, + { + kind: "Event", + apiVersions: ["v1"], + components: { + Details: EventDetails, + }, + }, + { + kind: "Namespace", + apiVersions: ["v1"], + components: { + Details: NamespaceDetails, + }, + }, + { + kind: "Endpoints", + apiVersions: ["v1"], + components: { + Details: EndpointDetails, + }, + }, + { + kind: "Endpoints", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Ingress", + apiVersions: ["networking.k8s.io/v1", "extensions/v1beta1"], + components: { + Details: IngressDetails, + }, + }, + { + kind: "Ingress", + apiVersions: ["networking.k8s.io/v1", "extensions/v1beta1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "NetworkPolicy", + apiVersions: ["networking.k8s.io/v1"], + components: { + Details: NetworkPolicyDetails, + }, + }, + { + kind: "NetworkPolicy", + apiVersions: ["networking.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Service", + apiVersions: ["v1"], + components: { + Details: ServiceDetails, + }, + }, + { + kind: "Service", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Node", + apiVersions: ["v1"], + components: { + Details: NodeDetails, + }, + }, + { + kind: "Node", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "PodSecurityPolicy", + apiVersions: ["policy/v1beta1"], + components: { + Details: PodSecurityPolicyDetails, + }, + }, + { + kind: "StorageClass", + apiVersions: ["storage.k8s.io/v1"], + components: { + Details: StorageClassDetails, + }, + }, + { + kind: "StorageClass", + apiVersions: ["storage.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "PersistentVolumeClaim", + apiVersions: ["v1"], + components: { + Details: PersistentVolumeClaimDetails, + }, + }, + { + kind: "PersistentVolumeClaim", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "PersistentVolume", + apiVersions: ["v1"], + components: { + Details: PersistentVolumeDetails, + }, + }, + { + kind: "PersistentVolume", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Role", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: RoleDetails, + }, + }, + { + kind: "Role", + apiVersions: ["rbac.authorization.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "ClusterRole", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: ClusterRoleDetails, + }, + }, + { + kind: "ClusterRole", + apiVersions: ["rbac.authorization.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "RoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: RoleBindingDetails, + }, + }, + { + kind: "RoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "ClusterRoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: ClusterRoleBindingDetails, + }, + }, + { + kind: "ClusterRoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "ServiceAccount", + apiVersions: ["v1"], + components: { + Details: ServiceAccountsDetails, + }, + }, + { + kind: "ServiceAccount", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "CronJob", + apiVersions: ["batch/v1beta1"], + components: { + Details: CronJobDetails, + }, + }, + { + kind: "CronJob", + apiVersions: ["batch/v1beta1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "DaemonSet", + apiVersions: ["apps/v1"], + components: { + Details: DaemonSetDetails, + }, + }, + { + kind: "DaemonSet", + apiVersions: ["apps/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Deployment", + apiVersions: ["apps/v1"], + components: { + Details: DeploymentDetails, + }, + }, + { + kind: "Deployment", + apiVersions: ["apps/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Job", + apiVersions: ["batch/v1"], + components: { + Details: JobDetails, + }, + }, + { + kind: "Job", + apiVersions: ["batch/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "Pod", + apiVersions: ["v1"], + components: { + Details: PodDetails, + }, + }, + { + kind: "Pod", + apiVersions: ["v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "ReplicaSet", + apiVersions: ["apps/v1"], + components: { + Details: ReplicaSetDetails, + }, + }, + { + kind: "ReplicaSet", + apiVersions: ["apps/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, + { + kind: "StatefulSet", + apiVersions: ["apps/v1"], + components: { + Details: StatefulSetDetails, + }, + }, + { + kind: "StatefulSet", + apiVersions: ["apps/v1"], + priority: 5, + components: { + Details: KubeEventDetails, + }, + }, +].map(({ priority = 50, ...item }) => ({ priority, ...item })); diff --git a/src/extensions/registries/kube-object-detail-registry.ts b/src/renderer/components/kube-object-details/kube-details-items/kube-detail-items.d.ts similarity index 71% rename from src/extensions/registries/kube-object-detail-registry.ts rename to src/renderer/components/kube-object-details/kube-details-items/kube-detail-items.d.ts index 58c807f57a30..38da2ccce9cb 100644 --- a/src/extensions/registries/kube-object-detail-registry.ts +++ b/src/renderer/components/kube-object-details/kube-details-items/kube-detail-items.d.ts @@ -19,28 +19,19 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type React from "react"; -import type { KubeObjectDetailsProps } from "../renderer-api/components"; -import type { KubeObject } from "../renderer-api/k8s-api"; -import { BaseRegistry } from "./base-registry"; - +/** + * The components for a details item + */ export interface KubeObjectDetailComponents { Details: React.ComponentType>; } +/** + * The registration type for extensions + */ export interface KubeObjectDetailRegistration { kind: string; apiVersions: string[]; components: KubeObjectDetailComponents; priority?: number; } - -export class KubeObjectDetailRegistry extends BaseRegistry { - getItemsForKind(kind: string, apiVersion: string) { - const items = this.getItems().filter((item) => { - return item.kind === kind && item.apiVersions.includes(apiVersion); - }); - - return items.sort((a, b) => (b.priority ?? 50) - (a.priority ?? 50)); - } -} diff --git a/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts b/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts new file mode 100644 index 000000000000..da999aec67ce --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts @@ -0,0 +1,61 @@ +/** + * 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. + */ + +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { orderBy } from "lodash"; +import { computed } from "mobx"; +import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; +import { internalItems } from "./internal-items"; +import type { KubeObjectDetailComponents } from "./kube-detail-items"; + +type Kind = string; +type ApiVersion = string; + +const kubeDetailItemsInjectable = getInjectable({ + instantiate: di => computed(() => { + const res = new Map>(); + const extensionItems = di.inject(rendererExtensionsInjectable).get() + .flatMap(ext => ext.kubeObjectDetailItems) + .map(({ priority = 50, ...item }) => ({ priority, ...item })); + const items = orderBy([...internalItems, ...extensionItems], "priority", "desc"); + + for (const item of items) { + if (!res.has(item.kind)) { + res.set(item.kind, new Map()); + } + + const byVersions = res.get(item.kind); + + for (const apiVersion of item.apiVersions) { + if (!byVersions.has(apiVersion)) { + byVersions.set(apiVersion, []); + } + + byVersions.get(apiVersion).push(item.components); + } + } + + return res; + }), + lifecycle: lifecycleEnum.singleton, +}); + +export default kubeDetailItemsInjectable; diff --git a/src/renderer/components/kube-object-details/kube-object-details.tsx b/src/renderer/components/kube-object-details/kube-object-details.tsx index e01c109fc5ae..df7d24c3e918 100644 --- a/src/renderer/components/kube-object-details/kube-object-details.tsx +++ b/src/renderer/components/kube-object-details/kube-object-details.tsx @@ -21,19 +21,21 @@ import "./kube-object-details.scss"; -import React from "react"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { computed, observable, reaction, makeObservable } from "mobx"; +import React, { ReactNode, useEffect, useState } from "react"; +import { observer } from "mobx-react"; +import { reaction, IComputedValue } from "mobx"; import { Drawer } from "../drawer"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { Spinner } from "../spinner"; import { apiManager } from "../../../common/k8s-api/api-manager"; import { crdStore } from "../+custom-resources/crd.store"; import { KubeObjectMenu } from "../kube-object-menu"; -import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; import { CrdResourceDetails } from "../+custom-resources"; import { KubeObjectMeta } from "../kube-object-meta"; import { hideDetails, kubeDetailsUrlParam } from "../kube-detail-params"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import type { KubeObjectDetailComponents } from "./kube-details-items/kube-detail-items"; +import kubeDetailItemsInjectable from "./kube-details-items/kube-details.injectable"; export interface KubeObjectDetailsProps { @@ -41,115 +43,116 @@ export interface KubeObjectDetailsProps { object: T; } -@observer -export class KubeObjectDetails extends React.Component { - @observable isLoading = false; - @observable.ref loadingError: React.ReactNode; - - constructor(props: {}) { - super(props); - makeObservable(this); - } - - @computed get path() { - return kubeDetailsUrlParam.get(); - } +interface Dependencies { + kubeDetailItems: IComputedValue[]>>>; +} - @computed get object() { - try { - return apiManager - .getStore(this.path) - ?.getByPath(this.path); - } catch (error) { - console.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path: this.path }); +function getKubeObjectByPath(path: string): KubeObject | undefined { + try { + return apiManager + .getStore(path) + ?.getByPath(path); + } catch (error) { + console.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path }); - return undefined; - } + return undefined; } +} - @disposeOnUnmount - loader = reaction(() => [ - this.path, - this.object, // resource might be updated via watch-event or from already opened details - crdStore.items.length, // crd stores initialized after loading - ], async () => { - this.loadingError = ""; - const { path, object } = this; - - if (!object) { - const store = apiManager.getStore(path); - - if (store) { - this.isLoading = true; - - try { - await store.loadFromPath(path); - } catch (err) { - this.loadingError = <>Resource loading has failed: {err.toString()}; - } finally { - this.isLoading = false; +const NonInjectedKubeObjectDetails = observer(({ kubeDetailItems }: Dependencies) => { + const [loading, setLoading] = useState(false); + const [loadingError, setLoadingError] = useState(""); + + useEffect(() => reaction( + () => [ + kubeDetailsUrlParam.get(), + getKubeObjectByPath(kubeDetailsUrlParam.get()), // resource might be updated via watch-event or from already opened details + crdStore.items.length, // crd stores initialized after loading + ] as const, async ([path, kubeObject]) => { + setLoadingError(""); + + if (!kubeObject) { + const store = apiManager.getStore(path); + + if (store) { + setLoading(true); + + try { + await store.loadFromPath(path); + } catch (err) { + setLoadingError(<>Resource loading has failed: {err.toString()}); + } finally { + setLoading(false); + } } } - } - }); - - render() { - const { object, isLoading, loadingError } = this; - const isOpen = !!(object || isLoading || loadingError); - - if (!object) { - return ( - } - onClose={hideDetails} - > - {isLoading && } - {loadingError &&
{loadingError}
} -
- ); - } + }, + ), []); - const { kind, getName } = object; - const title = `${kind}: ${getName()}`; - const details = KubeObjectDetailRegistry - .getInstance() - .getItemsForKind(object.kind, object.apiVersion) - .map((item, index) => ( - - )); - - if (details.length === 0) { - const crd = crdStore.getByObject(object); - - /** - * This is a fallback so that if a custom resource object doesn't have - * any defined details we should try and display at least some details - */ - if (crd) { - details.push(); - } - } + const detailsPath = kubeDetailsUrlParam.get(); + const kubeObject = getKubeObjectByPath(detailsPath); - if (details.length === 0) { - // if we still don't have any details to show, just show the standard object metadata - details.push(); - } + const isOpen = !!(kubeObject || loading || loadingError); + if (!kubeObject) { return ( } + title="" + toolbar={} onClose={hideDetails} > - {isLoading && } + {loading && } {loadingError &&
{loadingError}
} - {details}
); } -} + + const { kind, getName } = kubeObject; + const title = `${kind}: ${getName()}`; + const details = kubeDetailItems.get() + .get(kubeObject.kind) + .get(kubeObject.apiVersion) + .map(({ Details }, index) => ( +
+ )); + + if (details.length === 0) { + const crd = crdStore.getByObject(kubeObject); + + /** + * This is a fallback so that if a custom resource object doesn't have + * any defined details we should try and display at least some details + */ + if (crd) { + details.push(); + } + } + + if (details.length === 0) { + // if we still don't have any details to show, just show the standard object metadata + details.push(); + } + + return ( + } + onClose={hideDetails} + > + {loading && } + {loadingError &&
{loadingError}
} + {details} +
+ ); +}); + +export const KubeObjectDetails = withInjectables(NonInjectedKubeObjectDetails, { + getProps: (di, props) => ({ + kubeDetailItems: di.inject(kubeDetailItemsInjectable), + ...props, + }), +}); diff --git a/src/renderer/components/layout/top-bar/top-bar-win-linux.test.tsx b/src/renderer/components/layout/top-bar/top-bar-win-linux.test.tsx index c71210ed4f9d..23a355a72374 100644 --- a/src/renderer/components/layout/top-bar/top-bar-win-linux.test.tsx +++ b/src/renderer/components/layout/top-bar/top-bar-win-linux.test.tsx @@ -28,6 +28,7 @@ import { broadcastMessage } from "../../../../common/ipc"; import * as vars from "../../../../common/vars"; import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; import { DiRender, renderFor } from "../../test-utils/renderFor"; +import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; const mockConfig = vars as { isWindows: boolean; isLinux: boolean }; @@ -65,10 +66,10 @@ jest.mock("@electron/remote", () => { describe(" in Windows and Linux", () => { let render: DiRender; + let di: ConfigurableDependencyInjectionContainer; beforeEach(() => { - const di = getDiForUnitTesting(); - + di = getDiForUnitTesting(); render = renderFor(di); }); diff --git a/src/renderer/components/layout/top-bar/top-bar.test.tsx b/src/renderer/components/layout/top-bar/top-bar.test.tsx index 2c3d5c2c9a76..c30c74d53a92 100644 --- a/src/renderer/components/layout/top-bar/top-bar.test.tsx +++ b/src/renderer/components/layout/top-bar/top-bar.test.tsx @@ -90,7 +90,6 @@ describe("", () => { beforeEach(() => { di = getDiForUnitTesting(); - render = renderFor(di); }); diff --git a/src/renderer/initializers/index.ts b/src/renderer/initializers/index.ts index 758c9042ad07..ff9b815d4488 100644 --- a/src/renderer/initializers/index.ts +++ b/src/renderer/initializers/index.ts @@ -23,7 +23,6 @@ export * from "./catalog-entity-detail-registry"; export * from "./catalog"; export * from "./entity-settings-registry"; export * from "./ipc"; -export * from "./kube-object-detail-registry"; export * from "./kube-object-menu-registry"; export * from "./registries"; export * from "./workloads-overview-detail-registry"; diff --git a/src/renderer/initializers/kube-object-detail-registry.tsx b/src/renderer/initializers/kube-object-detail-registry.tsx deleted file mode 100644 index 00be5110eadc..000000000000 --- a/src/renderer/initializers/kube-object-detail-registry.tsx +++ /dev/null @@ -1,449 +0,0 @@ -/** - * 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. - */ - -import React from "react"; -import { KubeObjectDetailRegistry } from "../api/kube-object-detail-registry"; -import { HpaDetails, HpaDetailsProps } from "../components/+config-autoscalers"; -import { LimitRangeDetails } from "../components/+config-limit-ranges"; -import { ConfigMapDetails } from "../components/+config-maps"; -import { PodDisruptionBudgetDetails } from "../components/+config-pod-disruption-budgets"; -import { ResourceQuotaDetails } from "../components/+config-resource-quotas"; -import { SecretDetails } from "../components/+config-secrets"; -import { CRDDetails } from "../components/+custom-resources"; -import { EventDetails } from "../components/+events"; -import { KubeEventDetails } from "../components/+events/kube-event-details"; -import { NamespaceDetails } from "../components/+namespaces"; -import { EndpointDetails } from "../components/+network-endpoints"; -import { IngressDetails } from "../components/+network-ingresses"; -import { NetworkPolicyDetails } from "../components/+network-policies"; -import { ServiceDetails } from "../components/+network-services"; -import { NodeDetails } from "../components/+nodes"; -import { PodSecurityPolicyDetails } from "../components/+pod-security-policies"; -import { StorageClassDetails } from "../components/+storage-classes"; -import { PersistentVolumeClaimDetails } from "../components/+storage-volume-claims"; -import { PersistentVolumeDetails } from "../components/+storage-volumes"; -import { ClusterRoleDetails } from "../components/+user-management/+cluster-roles"; -import { ClusterRoleBindingDetails } from "../components/+user-management/+cluster-role-bindings"; -import { RoleDetails } from "../components/+user-management/+roles"; -import { RoleBindingDetails } from "../components/+user-management/+role-bindings"; -import { ServiceAccountsDetails } from "../components/+user-management/+service-accounts"; -import { CronJobDetails } from "../components/+workloads-cronjobs"; -import { DaemonSetDetails } from "../components/+workloads-daemonsets"; -import { DeploymentDetails } from "../components/+workloads-deployments"; -import { JobDetails } from "../components/+workloads-jobs"; -import { PodDetails } from "../components/+workloads-pods"; -import { ReplicaSetDetails } from "../components/+workloads-replicasets"; -import { StatefulSetDetails } from "../components/+workloads-statefulsets"; -import type { KubeObjectDetailsProps } from "../components/kube-object-details"; - -export function initKubeObjectDetailRegistry() { - KubeObjectDetailRegistry.getInstance() - .add([ - { - kind: "HorizontalPodAutoscaler", - apiVersions: ["autoscaling/v2beta1"], - components: { - // Note: this line is left in the long form as a validation that this usecase is valid - Details: (props: HpaDetailsProps) => , - }, - }, - { - kind: "HorizontalPodAutoscaler", - apiVersions: ["autoscaling/v2beta1"], - priority: 5, - components: { - // Note: this line is left in the long form as a validation that this usecase is valid - Details: (props: KubeObjectDetailsProps) => , - }, - }, - { - kind: "LimitRange", - apiVersions: ["v1"], - components: { - Details: LimitRangeDetails, - }, - }, - { - kind: "ConfigMap", - apiVersions: ["v1"], - components: { - Details: ConfigMapDetails, - }, - }, - { - kind: "ConfigMap", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "PodDisruptionBudget", - apiVersions: ["policy/v1beta1"], - components: { - Details: PodDisruptionBudgetDetails, - }, - }, - { - kind: "ResourceQuota", - apiVersions: ["v1"], - components: { - Details: ResourceQuotaDetails, - }, - }, - { - kind: "Secret", - apiVersions: ["v1"], - components: { - Details: SecretDetails, - }, - }, - { - kind: "CustomResourceDefinition", - apiVersions: ["apiextensions.k8s.io/v1", "apiextensions.k8s.io/v1beta1"], - components: { - Details: CRDDetails, - }, - }, - { - kind: "Event", - apiVersions: ["v1"], - components: { - Details: EventDetails, - }, - }, - { - kind: "Namespace", - apiVersions: ["v1"], - components: { - Details: NamespaceDetails, - }, - }, - { - kind: "Endpoints", - apiVersions: ["v1"], - components: { - Details: EndpointDetails, - }, - }, - { - kind: "Endpoints", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Ingress", - apiVersions: ["networking.k8s.io/v1", "extensions/v1beta1"], - components: { - Details: IngressDetails, - }, - }, - { - kind: "Ingress", - apiVersions: ["networking.k8s.io/v1", "extensions/v1beta1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "NetworkPolicy", - apiVersions: ["networking.k8s.io/v1"], - components: { - Details: NetworkPolicyDetails, - }, - }, - { - kind: "NetworkPolicy", - apiVersions: ["networking.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Service", - apiVersions: ["v1"], - components: { - Details: ServiceDetails, - }, - }, - { - kind: "Service", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Node", - apiVersions: ["v1"], - components: { - Details: NodeDetails, - }, - }, - { - kind: "Node", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "PodSecurityPolicy", - apiVersions: ["policy/v1beta1"], - components: { - Details: PodSecurityPolicyDetails, - }, - }, - { - kind: "StorageClass", - apiVersions: ["storage.k8s.io/v1"], - components: { - Details: StorageClassDetails, - }, - }, - { - kind: "StorageClass", - apiVersions: ["storage.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "PersistentVolumeClaim", - apiVersions: ["v1"], - components: { - Details: PersistentVolumeClaimDetails, - }, - }, - { - kind: "PersistentVolumeClaim", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "PersistentVolume", - apiVersions: ["v1"], - components: { - Details: PersistentVolumeDetails, - }, - }, - { - kind: "PersistentVolume", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Role", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: RoleDetails, - }, - }, - { - kind: "Role", - apiVersions: ["rbac.authorization.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "ClusterRole", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: ClusterRoleDetails, - }, - }, - { - kind: "ClusterRole", - apiVersions: ["rbac.authorization.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "RoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: RoleBindingDetails, - }, - }, - { - kind: "RoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "ClusterRoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: ClusterRoleBindingDetails, - }, - }, - { - kind: "ClusterRoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "ServiceAccount", - apiVersions: ["v1"], - components: { - Details: ServiceAccountsDetails, - }, - }, - { - kind: "ServiceAccount", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "CronJob", - apiVersions: ["batch/v1beta1"], - components: { - Details: CronJobDetails, - }, - }, - { - kind: "CronJob", - apiVersions: ["batch/v1beta1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "DaemonSet", - apiVersions: ["apps/v1"], - components: { - Details: DaemonSetDetails, - }, - }, - { - kind: "DaemonSet", - apiVersions: ["apps/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Deployment", - apiVersions: ["apps/v1"], - components: { - Details: DeploymentDetails, - }, - }, - { - kind: "Deployment", - apiVersions: ["apps/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Job", - apiVersions: ["batch/v1"], - components: { - Details: JobDetails, - }, - }, - { - kind: "Job", - apiVersions: ["batch/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "Pod", - apiVersions: ["v1"], - components: { - Details: PodDetails, - }, - }, - { - kind: "Pod", - apiVersions: ["v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "ReplicaSet", - apiVersions: ["apps/v1"], - components: { - Details: ReplicaSetDetails, - }, - }, - { - kind: "ReplicaSet", - apiVersions: ["apps/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - { - kind: "StatefulSet", - apiVersions: ["apps/v1"], - components: { - Details: StatefulSetDetails, - }, - }, - { - kind: "StatefulSet", - apiVersions: ["apps/v1"], - priority: 5, - components: { - Details: KubeEventDetails, - }, - }, - ]); -} diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index a68b4e0abd1a..3c1dbf88d7c4 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -28,7 +28,6 @@ export function initRegistries() { registries.ClusterPageRegistry.createInstance(); registries.EntitySettingRegistry.createInstance(); registries.GlobalPageRegistry.createInstance(); - registries.KubeObjectDetailRegistry.createInstance(); registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance(); registries.StatusBarRegistry.createInstance(); diff --git a/yarn.lock b/yarn.lock index 70fb22d9fbdd..0a0e7d82b111 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5467,6 +5467,17 @@ eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" +eslint-import-resolver-typescript@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" + integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== + dependencies: + debug "^4.3.1" + glob "^7.1.7" + is-glob "^4.0.1" + resolve "^1.20.0" + tsconfig-paths "^3.9.0" + eslint-module-utils@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" @@ -6525,7 +6536,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== From b0baf4da5cfa1cb072acf017bfe63b1cdde0c7db Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 12 Jan 2022 16:16:35 -0500 Subject: [PATCH 2/2] Move kubeDetailItems impl to file Signed-off-by: Sebastian Malton --- .../kube-details-items/kube-details.impl.ts | 58 +++++++++++++++++++ .../kube-details.injectable.ts | 34 +---------- 2 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 src/renderer/components/kube-object-details/kube-details-items/kube-details.impl.ts diff --git a/src/renderer/components/kube-object-details/kube-details-items/kube-details.impl.ts b/src/renderer/components/kube-object-details/kube-details-items/kube-details.impl.ts new file mode 100644 index 000000000000..e8f463b267ee --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-details-items/kube-details.impl.ts @@ -0,0 +1,58 @@ +/** + * 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. + */ + +import { orderBy } from "lodash"; +import { computed, IComputedValue } from "mobx"; +import type { LensRendererExtension } from "../../../../extensions/lens-renderer-extension"; +import { internalItems } from "./internal-items"; +import type { KubeObjectDetailComponents } from "./kube-detail-items"; + +interface Dependencies { + extensions: IComputedValue; +} + +export function getKubeDetailItemsMap({ extensions }: Dependencies): IComputedValue>> { + return computed(() => { + const res = new Map>(); + const extensionItems = extensions.get() + .flatMap(ext => ext.kubeObjectDetailItems) + .map(({ priority = 50, ...item }) => ({ priority, ...item })); + const items = orderBy([...internalItems, ...extensionItems], "priority", "desc"); + + for (const item of items) { + if (!res.has(item.kind)) { + res.set(item.kind, new Map()); + } + + const byVersions = res.get(item.kind); + + for (const apiVersion of item.apiVersions) { + if (!byVersions.has(apiVersion)) { + byVersions.set(apiVersion, []); + } + + byVersions.get(apiVersion).push(item.components); + } + } + + return res; + }); +} diff --git a/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts b/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts index da999aec67ce..892c04ee07c2 100644 --- a/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts +++ b/src/renderer/components/kube-object-details/kube-details-items/kube-details.injectable.ts @@ -20,40 +20,12 @@ */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import { orderBy } from "lodash"; -import { computed } from "mobx"; import rendererExtensionsInjectable from "../../../../extensions/renderer-extensions.injectable"; -import { internalItems } from "./internal-items"; -import type { KubeObjectDetailComponents } from "./kube-detail-items"; - -type Kind = string; -type ApiVersion = string; +import { getKubeDetailItemsMap } from "./kube-details.impl"; const kubeDetailItemsInjectable = getInjectable({ - instantiate: di => computed(() => { - const res = new Map>(); - const extensionItems = di.inject(rendererExtensionsInjectable).get() - .flatMap(ext => ext.kubeObjectDetailItems) - .map(({ priority = 50, ...item }) => ({ priority, ...item })); - const items = orderBy([...internalItems, ...extensionItems], "priority", "desc"); - - for (const item of items) { - if (!res.has(item.kind)) { - res.set(item.kind, new Map()); - } - - const byVersions = res.get(item.kind); - - for (const apiVersion of item.apiVersions) { - if (!byVersions.has(apiVersion)) { - byVersions.set(apiVersion, []); - } - - byVersions.get(apiVersion).push(item.components); - } - } - - return res; + instantiate: di => getKubeDetailItemsMap({ + extensions: di.inject(rendererExtensionsInjectable), }), lifecycle: lifecycleEnum.singleton, });