From ac42a6565f65d5b5f844a667baf9a595bb2a3dca Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Mon, 31 Jan 2022 16:16:29 +0100 Subject: [PATCH] Replace kube object menu registry with reactive solution (#4731) Co-authored-by: Sebastian Malton --- __mocks__/monaco-editor.ts | 5 + 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 - .../service-account-menu.tsx | 22 ++++ .../+service-accounts/view.tsx | 15 --- .../+workloads-cronjobs/cron-job-menu.tsx | 63 +++++++++ .../+workloads-cronjobs/cronjobs.tsx | 57 -------- .../deployment-menu.tsx | 47 +++++++ .../+workloads-deployments/deployments.tsx | 43 +----- .../replica-set-menu.tsx | 23 ++++ .../+workloads-replicasets/replicasets.tsx | 17 --- .../stateful-set-menu.tsx | 23 ++++ .../+workloads-statefulsets/statefulsets.tsx | 17 --- .../get-kube-object-menu-items.ts | 31 +++-- .../kube-object-menu-items.injectable.ts | 4 +- .../kube-object-menu-registration.ts} | 17 +-- .../kube-object-menu-registry.injectable.ts | 13 -- .../static-kube-object-menu-items.ts | 47 +++++++ .../kube-object-menu.test.tsx | 124 ++++++++++-------- src/renderer/initializers/index.ts | 1 - .../initializers/kube-object-menu-registry.ts | 52 -------- src/renderer/initializers/registries.ts | 1 - 25 files changed, 333 insertions(+), 299 deletions(-) create mode 100644 __mocks__/monaco-editor.ts create mode 100644 src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx create mode 100644 src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx create mode 100644 src/renderer/components/+workloads-deployments/deployment-menu.tsx create mode 100644 src/renderer/components/+workloads-replicasets/replica-set-menu.tsx create mode 100644 src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx rename src/{extensions/registries/kube-object-menu-registry.ts => renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration.ts} (50%) delete mode 100644 src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registry.injectable.ts create mode 100644 src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/static-kube-object-menu-items.ts delete mode 100644 src/renderer/initializers/kube-object-menu-registry.ts diff --git a/__mocks__/monaco-editor.ts b/__mocks__/monaco-editor.ts new file mode 100644 index 0000000000..cbe02cb296 --- /dev/null +++ b/__mocks__/monaco-editor.ts @@ -0,0 +1,5 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +export default {}; diff --git a/src/extensions/common-api/registrations.ts b/src/extensions/common-api/registrations.ts index 5178dd9ee4..be3e8d87cd 100644 --- a/src/extensions/common-api/registrations.ts +++ b/src/extensions/common-api/registrations.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ export type { StatusBarRegistration } from "../../renderer/components/cluster-manager/status-bar-registration"; +export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration"; export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../renderer/components/+preferences/app-preferences/app-preference-registration"; export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../registries/kube-object-detail-registry"; -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"; export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../registries/page-menu-registry"; diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 11ace2cbf1..e954a1f460 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -276,7 +276,6 @@ export class ExtensionLoader { const removeItems = [ 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 9104f8871b..9d69b4485a 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -19,6 +19,7 @@ import type { AppPreferenceRegistration } from "../renderer/components/+preferen import type { AdditionalCategoryColumnRegistration } from "../renderer/components/+catalog/custom-category-columns"; import type { CustomCategoryViewRegistration } from "../renderer/components/+catalog/custom-views"; import type { StatusBarRegistration } from "../renderer/components/cluster-manager/status-bar-registration"; +import type { KubeObjectMenuRegistration } from "../renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -29,7 +30,7 @@ export class LensRendererExtension extends LensExtension { entitySettings: registries.EntitySettingRegistration[] = []; statusBarItems: StatusBarRegistration[] = []; kubeObjectDetailItems: registries.KubeObjectDetailRegistration[] = []; - kubeObjectMenuItems: registries.KubeObjectMenuRegistration[] = []; + kubeObjectMenuItems: KubeObjectMenuRegistration[] = []; kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: CommandRegistration[] = []; welcomeMenus: WelcomeMenuRegistration[] = []; diff --git a/src/extensions/registries/index.ts b/src/extensions/registries/index.ts index 464d19cd4e..0713e3eede 100644 --- a/src/extensions/registries/index.ts +++ b/src/extensions/registries/index.ts @@ -8,7 +8,6 @@ export * from "./page-registry"; export * from "./page-menu-registry"; export * from "./kube-object-detail-registry"; -export * from "./kube-object-menu-registry"; export * from "./kube-object-status-registry"; export * from "./entity-setting-registry"; export * from "./catalog-entity-detail-registry"; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 9be509ab61..5593134586 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -73,9 +73,6 @@ export async function bootstrap(di: DependencyInjectionContainer) { logger.info(`${logPrefix} initializing EntitySettingsRegistry`); initializers.initEntitySettingsRegistry(); - logger.info(`${logPrefix} initializing KubeObjectMenuRegistry`); - initializers.initKubeObjectMenuRegistry(); - logger.info(`${logPrefix} initializing KubeObjectDetailRegistry`); initializers.initKubeObjectDetailRegistry(); diff --git a/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx b/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx new file mode 100644 index 0000000000..6fc180a2e9 --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/service-account-menu.tsx @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; + +import type { KubeObjectMenuProps } from "../../kube-object-menu"; +import type { ServiceAccount } from "../../../../common/k8s-api/endpoints"; +import { MenuItem } from "../../menu"; +import { openServiceAccountKubeConfig } from "../../kubeconfig-dialog"; +import { Icon } from "../../icon"; + +export function ServiceAccountMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; + + return ( + openServiceAccountKubeConfig(object)}> + + Kubeconfig + + ); +} diff --git a/src/renderer/components/+user-management/+service-accounts/view.tsx b/src/renderer/components/+user-management/+service-accounts/view.tsx index b8405781b0..94c7370639 100644 --- a/src/renderer/components/+user-management/+service-accounts/view.tsx +++ b/src/renderer/components/+user-management/+service-accounts/view.tsx @@ -8,13 +8,8 @@ import "./view.scss"; import { observer } from "mobx-react"; import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { ServiceAccount } from "../../../../common/k8s-api/endpoints/service-accounts.api"; -import { Icon } from "../../icon"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import type { KubeObjectMenuProps } from "../../kube-object-menu"; -import { openServiceAccountKubeConfig } from "../../kubeconfig-dialog"; -import { MenuItem } from "../../menu"; import { CreateServiceAccountDialog } from "./create-dialog"; import { serviceAccountsStore } from "./store"; import type { ServiceAccountsRouteParams } from "../../../../common/routes"; @@ -69,13 +64,3 @@ export class ServiceAccounts extends React.Component { } } -export function ServiceAccountMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; - - return ( - openServiceAccountKubeConfig(object)}> - - Kubeconfig - - ); -} diff --git a/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx b/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx new file mode 100644 index 0000000000..b7f6184519 --- /dev/null +++ b/src/renderer/components/+workloads-cronjobs/cron-job-menu.tsx @@ -0,0 +1,63 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import type { KubeObjectMenuProps } from "../kube-object-menu"; +import { CronJob, cronJobApi } from "../../../common/k8s-api/endpoints"; +import { MenuItem } from "../menu"; +import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; +import { Icon } from "../icon"; +import { ConfirmDialog } from "../confirm-dialog"; +import { Notifications } from "../notifications"; + +export function CronJobMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; + + return ( + <> + CronJobTriggerDialog.open(object)}> + + Trigger + + + {object.isSuspend() ? + ConfirmDialog.open({ + ok: async () => { + try { + await cronJobApi.resume({ namespace: object.getNs(), name: object.getName() }); + } catch (err) { + Notifications.error(err); + } + }, + labelOk: `Resume`, + message: ( +

+ Resume CronJob {object.getName()}? +

), + })}> + + Resume +
+ + : ConfirmDialog.open({ + ok: async () => { + try { + await cronJobApi.suspend({ namespace: object.getNs(), name: object.getName() }); + } catch (err) { + Notifications.error(err); + } + }, + labelOk: `Suspend`, + message: ( +

+ Suspend CronJob {object.getName()}? +

), + })}> + + Suspend +
+ } + + ); +} diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index c4605ff45f..30e476502e 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -8,18 +8,11 @@ import "./cronjobs.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; -import { CronJob, cronJobApi } from "../../../common/k8s-api/endpoints/cron-job.api"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; import { cronJobStore } from "./cronjob.store"; import { jobStore } from "../+workloads-jobs/job.store"; import { eventStore } from "../+events/event.store"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { CronJobTriggerDialog } from "./cronjob-trigger-dialog"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { ConfirmDialog } from "../confirm-dialog/confirm-dialog"; -import { Notifications } from "../notifications/notifications"; import type { CronJobsRouteParams } from "../../../common/routes"; import moment from "moment"; @@ -87,53 +80,3 @@ export class CronJobs extends React.Component { } } -export function CronJobMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; - - return ( - <> - CronJobTriggerDialog.open(object)}> - - Trigger - - - {object.isSuspend() ? - ConfirmDialog.open({ - ok: async () => { - try { - await cronJobApi.resume({ namespace: object.getNs(), name: object.getName() }); - } catch (err) { - Notifications.error(err); - } - }, - labelOk: `Resume`, - message: ( -

- Resume CronJob {object.getName()}? -

), - })}> - - Resume -
- - : ConfirmDialog.open({ - ok: async () => { - try { - await cronJobApi.suspend({ namespace: object.getNs(), name: object.getName() }); - } catch (err) { - Notifications.error(err); - } - }, - labelOk: `Suspend`, - message: ( -

- Suspend CronJob {object.getName()}? -

), - })}> - - Suspend -
- } - - ); -} diff --git a/src/renderer/components/+workloads-deployments/deployment-menu.tsx b/src/renderer/components/+workloads-deployments/deployment-menu.tsx new file mode 100644 index 0000000000..9c7544a1c4 --- /dev/null +++ b/src/renderer/components/+workloads-deployments/deployment-menu.tsx @@ -0,0 +1,47 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import type { KubeObjectMenuProps } from "../kube-object-menu"; +import { Deployment, deploymentApi } from "../../../common/k8s-api/endpoints"; +import { MenuItem } from "../menu"; +import { DeploymentScaleDialog } from "./deployment-scale-dialog"; +import { Icon } from "../icon"; +import { ConfirmDialog } from "../confirm-dialog"; +import { Notifications } from "../notifications"; + +export function DeploymentMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; + + return ( + <> + DeploymentScaleDialog.open(object)}> + + Scale + + ConfirmDialog.open({ + ok: async () => + { + try { + await deploymentApi.restart({ + namespace: object.getNs(), + name: object.getName(), + }); + } catch (err) { + Notifications.error(err); + } + }, + labelOk: `Restart`, + message: ( +

+ Are you sure you want to restart deployment {object.getName()}? +

+ ), + })}> + + Restart +
+ + ); +} diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index 79ce4af810..3506db0486 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -8,12 +8,7 @@ import "./deployments.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; -import { Deployment, deploymentApi } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; -import { MenuItem } from "../menu"; -import { Icon } from "../icon"; -import { DeploymentScaleDialog } from "./deployment-scale-dialog"; -import { ConfirmDialog } from "../confirm-dialog"; +import type { Deployment } from "../../../common/k8s-api/endpoints"; import { deploymentStore } from "./deployments.store"; import { eventStore } from "../+events/event.store"; import { KubeObjectListLayout } from "../kube-object-list-layout"; @@ -21,7 +16,6 @@ import { cssNames } from "../../utils"; import kebabCase from "lodash/kebabCase"; import orderBy from "lodash/orderBy"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { Notifications } from "../notifications"; import type { DeploymentsRouteParams } from "../../../common/routes"; enum columnId { @@ -95,38 +89,3 @@ export class Deployments extends React.Component { ); } } - -export function DeploymentMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; - - return ( - <> - DeploymentScaleDialog.open(object)}> - - Scale - - ConfirmDialog.open({ - ok: async () => - { - try { - await deploymentApi.restart({ - namespace: object.getNs(), - name: object.getName(), - }); - } catch (err) { - Notifications.error(err); - } - }, - labelOk: `Restart`, - message: ( -

- Are you sure you want to restart deployment {object.getName()}? -

- ), - })}> - - Restart -
- - ); -} diff --git a/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx b/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx new file mode 100644 index 0000000000..deab2193cd --- /dev/null +++ b/src/renderer/components/+workloads-replicasets/replica-set-menu.tsx @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import type { KubeObjectMenuProps } from "../kube-object-menu"; +import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; +import { MenuItem } from "../menu"; +import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog"; +import { Icon } from "../icon"; + +export function ReplicaSetMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; + + return ( + <> + ReplicaSetScaleDialog.open(object)}> + + Scale + + + ); +} diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx index f3986125b6..be21604719 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx @@ -7,15 +7,10 @@ import "./replicasets.scss"; import React from "react"; import { observer } from "mobx-react"; -import type { ReplicaSet } from "../../../common/k8s-api/endpoints"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; import { replicaSetStore } from "./replicasets.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import type { RouteComponentProps } from "react-router"; import { KubeObjectListLayout } from "../kube-object-list-layout"; -import { MenuItem } from "../menu/menu"; -import { Icon } from "../icon/icon"; -import { ReplicaSetScaleDialog } from "./replicaset-scale-dialog"; import type { ReplicaSetsRouteParams } from "../../../common/routes"; import { eventStore } from "../+events/event.store"; @@ -75,15 +70,3 @@ export class ReplicaSets extends React.Component { } } -export function ReplicaSetMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; - - return ( - <> - ReplicaSetScaleDialog.open(object)}> - - Scale - - - ); -} diff --git a/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx b/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx new file mode 100644 index 0000000000..b9725f6e13 --- /dev/null +++ b/src/renderer/components/+workloads-statefulsets/stateful-set-menu.tsx @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import type { KubeObjectMenuProps } from "../kube-object-menu"; +import type { StatefulSet } from "../../../common/k8s-api/endpoints"; +import { MenuItem } from "../menu"; +import { StatefulSetScaleDialog } from "./statefulset-scale-dialog"; +import { Icon } from "../icon"; + +export function StatefulSetMenu(props: KubeObjectMenuProps) { + const { object, toolbar } = props; + + return ( + <> + StatefulSetScaleDialog.open(object)}> + + Scale + + + ); +} diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx index c00f9456ae..d7aa9e847f 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx @@ -12,12 +12,8 @@ import type { StatefulSet } from "../../../common/k8s-api/endpoints"; import { podsStore } from "../+workloads-pods/pods.store"; import { statefulSetStore } from "./statefulset.store"; import { eventStore } from "../+events/event.store"; -import type { KubeObjectMenuProps } from "../kube-object-menu"; import { KubeObjectListLayout } from "../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import { StatefulSetScaleDialog } from "./statefulset-scale-dialog"; -import { MenuItem } from "../menu/menu"; -import { Icon } from "../icon/icon"; import type { StatefulSetsRouteParams } from "../../../common/routes"; enum columnId { @@ -76,16 +72,3 @@ export class StatefulSets extends React.Component { ); } } - -export function StatefulSetMenu(props: KubeObjectMenuProps) { - const { object, toolbar } = props; - - return ( - <> - StatefulSetScaleDialog.open(object)}> - - Scale - - - ); -} diff --git a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/get-kube-object-menu-items.ts b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/get-kube-object-menu-items.ts index 0e8baf98e6..7091a7c242 100644 --- a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/get-kube-object-menu-items.ts +++ b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/get-kube-object-menu-items.ts @@ -2,21 +2,36 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { KubeObjectMenuRegistry } from "../../../../../extensions/registries"; +import { conforms, includes, eq } from "lodash/fp"; import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; +import type { LensRendererExtension } from "../../../../../extensions/lens-renderer-extension"; +import { staticKubeObjectMenuItems as staticMenuItems } from "./static-kube-object-menu-items"; + +interface Dependencies { + extensions: LensRendererExtension[]; + kubeObject: KubeObject; +} export const getKubeObjectMenuItems = ({ - kubeObjectMenuRegistry, + extensions, kubeObject, -}: { - kubeObjectMenuRegistry: KubeObjectMenuRegistry; - kubeObject: KubeObject; -}) => { +}: Dependencies) => { if (!kubeObject) { return []; } - return kubeObjectMenuRegistry - .getItemsForKind(kubeObject.kind, kubeObject.apiVersion) + const extensionMenuItems = extensions.flatMap( + (extension) => extension.kubeObjectMenuItems, + ); + + return [...staticMenuItems, ...extensionMenuItems] + .filter( + conforms({ + kind: eq(kubeObject.kind), + apiVersions: includes(kubeObject.apiVersion), + }), + ) + .map((item) => item.components.MenuItem); }; + diff --git a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-items.injectable.ts b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-items.injectable.ts index 3d8f16de8e..703374ab2d 100644 --- a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-items.injectable.ts +++ b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-items.injectable.ts @@ -3,15 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; -import kubeObjectMenuRegistryInjectable from "./kube-object-menu-registry.injectable"; import { getKubeObjectMenuItems } from "./get-kube-object-menu-items"; import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; +import rendererExtensionsInjectable from "../../../../../extensions/renderer-extensions.injectable"; const kubeObjectMenuItemsInjectable = getInjectable({ instantiate: (di, { kubeObject }: { kubeObject: KubeObject }) => getKubeObjectMenuItems({ - kubeObjectMenuRegistry: di.inject(kubeObjectMenuRegistryInjectable), + extensions: di.inject(rendererExtensionsInjectable).get(), kubeObject, }), diff --git a/src/extensions/registries/kube-object-menu-registry.ts b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration.ts similarity index 50% rename from src/extensions/registries/kube-object-menu-registry.ts rename to src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration.ts index a1db08e26d..b3a0904634 100644 --- a/src/extensions/registries/kube-object-menu-registry.ts +++ b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registration.ts @@ -4,10 +4,15 @@ */ import type React from "react"; -import { BaseRegistry } from "./base-registry"; +import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; + +export interface KubeObjectMenuItemProps { + object: KubeObject; + toolbar?: boolean; +} export interface KubeObjectMenuComponents { - MenuItem: React.ComponentType; + MenuItem: React.ComponentType; } export interface KubeObjectMenuRegistration { @@ -15,11 +20,3 @@ export interface KubeObjectMenuRegistration { apiVersions: string[]; components: KubeObjectMenuComponents; } - -export class KubeObjectMenuRegistry extends BaseRegistry { - getItemsForKind(kind: string, apiVersion: string) { - return this.getItems().filter((item) => { - return item.kind === kind && item.apiVersions.includes(apiVersion); - }); - } -} diff --git a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registry.injectable.ts b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registry.injectable.ts deleted file mode 100644 index 1bbcc031ae..0000000000 --- a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/kube-object-menu-registry.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { KubeObjectMenuRegistry } from "../../../../../extensions/registries"; -import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; - -const kubeObjectMenuRegistryInjectable = getInjectable({ - instantiate: () => KubeObjectMenuRegistry.getInstance(), - lifecycle: lifecycleEnum.singleton, -}); - -export default kubeObjectMenuRegistryInjectable; diff --git a/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/static-kube-object-menu-items.ts b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/static-kube-object-menu-items.ts new file mode 100644 index 0000000000..0368fbb241 --- /dev/null +++ b/src/renderer/components/kube-object-menu/dependencies/kube-object-menu-items/static-kube-object-menu-items.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { ServiceAccountMenu } from "../../../+user-management/+service-accounts/service-account-menu"; +import { CronJobMenu } from "../../../+workloads-cronjobs/cron-job-menu"; +import { DeploymentMenu } from "../../../+workloads-deployments/deployment-menu"; +import { ReplicaSetMenu } from "../../../+workloads-replicasets/replica-set-menu"; +import { StatefulSetMenu } from "../../../+workloads-statefulsets/stateful-set-menu"; + +export const staticKubeObjectMenuItems = [ + { + kind: "ServiceAccount", + apiVersions: ["v1"], + components: { + MenuItem: ServiceAccountMenu, + }, + }, + { + kind: "CronJob", + apiVersions: ["batch/v1beta1"], + components: { + MenuItem: CronJobMenu, + }, + }, + { + kind: "Deployment", + apiVersions: ["apps/v1"], + components: { + MenuItem: DeploymentMenu, + }, + }, + { + kind: "ReplicaSet", + apiVersions: ["apps/v1"], + components: { + MenuItem: ReplicaSetMenu, + }, + }, + { + kind: "StatefulSet", + apiVersions: ["apps/v1"], + components: { + MenuItem: StatefulSetMenu, + }, + }, +]; diff --git a/src/renderer/components/kube-object-menu/kube-object-menu.test.tsx b/src/renderer/components/kube-object-menu/kube-object-menu.test.tsx index 9c22de1b27..ac6b55f36f 100644 --- a/src/renderer/components/kube-object-menu/kube-object-menu.test.tsx +++ b/src/renderer/components/kube-object-menu/kube-object-menu.test.tsx @@ -2,76 +2,108 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ - import React from "react"; import { screen } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import { KubeObject } from "../../../common/k8s-api/kube-object"; import userEvent from "@testing-library/user-event"; import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; -import type { KubeObjectMenuRegistration } from "../../../extensions/registries"; -import { KubeObjectMenuRegistry } from "../../../extensions/registries"; import { ConfirmDialog } from "../confirm-dialog"; import asyncFn, { AsyncFnMock } from "@async-fn/jest"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import clusterInjectable from "./dependencies/cluster.injectable"; import hideDetailsInjectable from "./dependencies/hide-details.injectable"; -import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable"; -import kubeObjectMenuRegistryInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-registry.injectable"; import { DiRender, renderFor } from "../test-utils/renderFor"; import type { Cluster } from "../../../common/cluster/cluster"; import type { ApiManager } from "../../../common/k8s-api/api-manager"; import apiManagerInjectable from "./dependencies/api-manager.injectable"; import { KubeObjectMenu } from "./index"; +import type { KubeObjectMenuRegistration } from "./dependencies/kube-object-menu-items/kube-object-menu-registration"; +import { computed } from "mobx"; +import { LensRendererExtension } from "../../../extensions/lens-renderer-extension"; +import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable"; +import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable"; // TODO: Make tooltips free of side effects by making it deterministic jest.mock("../tooltip"); +class SomeTestExtension extends LensRendererExtension { + constructor( + kubeObjectMenuItems: KubeObjectMenuRegistration[], + ) { + super({ + id: "some-id", + absolutePath: "irrelevant", + isBundled: false, + isCompatible: false, + isEnabled: false, + manifest: { name: "some-id", version: "some-version" }, + manifestPath: "irrelevant", + }); + + this.kubeObjectMenuItems = kubeObjectMenuItems; + } +} + describe("kube-object-menu", () => { let di: ConfigurableDependencyInjectionContainer; let render: DiRender; beforeEach(async () => { + const MenuItemComponent: React.FC = () =>
  • Some menu item
  • ; + + const kubeObjectMenuItems = [ + { + apiVersions: ["some-api-version"], + kind: "some-kind", + components: { MenuItem: MenuItemComponent }, + }, + + { + apiVersions: ["some-unrelated-api-version"], + kind: "some-kind", + components: { MenuItem: MenuItemComponent }, + }, + + { + apiVersions: ["some-api-version"], + kind: "some-unrelated-kind", + components: { MenuItem: MenuItemComponent }, + }, + ]; + + const someTestExtension = new SomeTestExtension(kubeObjectMenuItems); + di = getDiForUnitTesting({ doGeneralOverrides: true }); - await di.runSetups(); - - // TODO: Remove global shared state - KubeObjectMenuRegistry.resetInstance(); - KubeObjectMenuRegistry.createInstance(); - render = renderFor(di); - di.override(clusterInjectable, () => ({ - name: "Some name", - }) as Cluster); + di.override(rendererExtensionsInjectable, () => + computed(() => [someTestExtension]), + ); - di.override(apiManagerInjectable, () => ({ - getStore: api => void api, - }) as ApiManager); + await di.runSetups(); + + di.override( + clusterInjectable, + () => + ({ + name: "Some name", + } as Cluster), + ); + + di.override( + apiManagerInjectable, + () => + ({ + getStore: (api) => void api, + } as ApiManager), + ); di.override(hideDetailsInjectable, () => () => {}); di.override(createEditResourceTabInjectable, () => () => "irrelevant"); - - addDynamicMenuItem({ - di, - apiVersions: ["some-api-version"], - kind: "some-kind", - }); - - addDynamicMenuItem({ - di, - apiVersions: ["some-unrelated-api-version"], - kind: "some-kind", - }); - - addDynamicMenuItem({ - di, - apiVersions: ["some-api-version"], - kind: "some-unrelated-kind", - }); }); it("given no cluster, does not crash", () => { @@ -242,25 +274,3 @@ describe("kube-object-menu", () => { }); }); }); - -const addDynamicMenuItem = ({ - di, - apiVersions, - kind, -}: { - di: ConfigurableDependencyInjectionContainer; - apiVersions: string[]; - kind: string; -}) => { - const MenuItemComponent: React.FC = () =>
  • Some menu item
  • ; - - const dynamicMenuItemStub: KubeObjectMenuRegistration = { - apiVersions, - kind, - components: { MenuItem: MenuItemComponent }, - }; - - const kubeObjectMenuRegistry = di.inject(kubeObjectMenuRegistryInjectable); - - kubeObjectMenuRegistry.add([dynamicMenuItemStub]); -}; diff --git a/src/renderer/initializers/index.ts b/src/renderer/initializers/index.ts index 3965d45fcf..11c318090c 100644 --- a/src/renderer/initializers/index.ts +++ b/src/renderer/initializers/index.ts @@ -8,7 +8,6 @@ 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"; export * from "./catalog-category-registry"; diff --git a/src/renderer/initializers/kube-object-menu-registry.ts b/src/renderer/initializers/kube-object-menu-registry.ts deleted file mode 100644 index d25ef0aaf7..0000000000 --- a/src/renderer/initializers/kube-object-menu-registry.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { KubeObjectMenuRegistry } from "../../extensions/registries"; -import { ServiceAccountMenu } from "../components/+user-management/+service-accounts"; -import { CronJobMenu } from "../components/+workloads-cronjobs"; -import { DeploymentMenu } from "../components/+workloads-deployments"; -import { ReplicaSetMenu } from "../components/+workloads-replicasets"; -import { StatefulSetMenu } from "../components/+workloads-statefulsets"; - -export function initKubeObjectMenuRegistry() { - KubeObjectMenuRegistry.getInstance() - .add([ - { - kind: "ServiceAccount", - apiVersions: ["v1"], - components: { - MenuItem: ServiceAccountMenu, - }, - }, - { - kind: "CronJob", - apiVersions: ["batch/v1beta1"], - components: { - MenuItem: CronJobMenu, - }, - }, - { - kind: "Deployment", - apiVersions: ["apps/v1"], - components: { - MenuItem: DeploymentMenu, - }, - }, - { - kind: "ReplicaSet", - apiVersions: ["apps/v1"], - components: { - MenuItem: ReplicaSetMenu, - }, - }, - { - kind: "StatefulSet", - apiVersions: ["apps/v1"], - components: { - MenuItem: StatefulSetMenu, - }, - }, - ]); -} diff --git a/src/renderer/initializers/registries.ts b/src/renderer/initializers/registries.ts index 1c9e5662d4..374e114b7c 100644 --- a/src/renderer/initializers/registries.ts +++ b/src/renderer/initializers/registries.ts @@ -12,7 +12,6 @@ export function initRegistries() { registries.EntitySettingRegistry.createInstance(); registries.GlobalPageRegistry.createInstance(); registries.KubeObjectDetailRegistry.createInstance(); - registries.KubeObjectMenuRegistry.createInstance(); registries.KubeObjectStatusRegistry.createInstance(); registries.WorkloadsOverviewDetailRegistry.createInstance(); }