From 98e42cf25f85167b35a4421cc0fc567453620ba9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 22 Nov 2022 06:10:22 -0800 Subject: [PATCH] Make ClusterFrameManager fully injectable (#6590) * Make ClusterFrameManager fully injectable Signed-off-by: Sebastian Malton * Fix type errors Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- src/common/cluster/visibility-channel.ts | 11 +++++ src/common/ipc/cluster.ts | 1 - src/main/cluster/manager.injectable.ts | 2 + src/main/cluster/manager.ts | 9 ++-- .../cluster/visibility-handler.injectable.ts | 19 +++++++++ .../cluster/visible-cluster.injectable.ts | 14 +++++++ .../setup-ipc-main-handlers.injectable.ts | 3 -- .../setup-ipc-main-handlers.ts | 9 +--- .../cluster-frame-handler.injectable.ts | 9 +++- .../cluster-manager/cluster-frame-handler.ts | 41 +++++++++++-------- .../emit-cluster-visibility.injectable.ts | 21 ++++++++++ 11 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 src/common/cluster/visibility-channel.ts create mode 100644 src/main/cluster/visibility-handler.injectable.ts create mode 100644 src/main/cluster/visible-cluster.injectable.ts create mode 100644 src/renderer/components/cluster-manager/emit-cluster-visibility.injectable.ts diff --git a/src/common/cluster/visibility-channel.ts b/src/common/cluster/visibility-channel.ts new file mode 100644 index 0000000000..8a1a297ff2 --- /dev/null +++ b/src/common/cluster/visibility-channel.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { ClusterId } from "../cluster-types"; +import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; + +export const clusterVisibilityChannel: MessageChannel = { + id: "cluster-visibility", +}; diff --git a/src/common/ipc/cluster.ts b/src/common/ipc/cluster.ts index c5bec1f59f..c89e2f5010 100644 --- a/src/common/ipc/cluster.ts +++ b/src/common/ipc/cluster.ts @@ -5,7 +5,6 @@ export const clusterActivateHandler = "cluster:activate"; export const clusterSetFrameIdHandler = "cluster:set-frame-id"; -export const clusterVisibilityHandler = "cluster:visibility"; export const clusterRefreshHandler = "cluster:refresh"; export const clusterDisconnectHandler = "cluster:disconnect"; export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all"; diff --git a/src/main/cluster/manager.injectable.ts b/src/main/cluster/manager.injectable.ts index 13f04f16ac..e4c22f0e38 100644 --- a/src/main/cluster/manager.injectable.ts +++ b/src/main/cluster/manager.injectable.ts @@ -7,6 +7,7 @@ import clusterStoreInjectable from "../../common/cluster-store/cluster-store.inj import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable"; import { ClusterManager } from "./manager"; +import visibleClusterInjectable from "./visible-cluster.injectable"; const clusterManagerInjectable = getInjectable({ id: "cluster-manager", @@ -15,6 +16,7 @@ const clusterManagerInjectable = getInjectable({ store: di.inject(clusterStoreInjectable), catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable), + visibleCluster: di.inject(visibleClusterInjectable), }), }); diff --git a/src/main/cluster/manager.ts b/src/main/cluster/manager.ts index 8ae6e5ce99..d5c06c0619 100644 --- a/src/main/cluster/manager.ts +++ b/src/main/cluster/manager.ts @@ -5,8 +5,8 @@ import "../../common/ipc/cluster"; import type http from "http"; -import type { ObservableSet } from "mobx"; -import { action, makeObservable, observable, observe, reaction, toJS } from "mobx"; +import type { IObservableValue, ObservableSet } from "mobx"; +import { action, makeObservable, observe, reaction, toJS } from "mobx"; import type { Cluster } from "../../common/cluster/cluster"; import logger from "../logger"; import { apiKubePrefix } from "../../common/vars"; @@ -27,11 +27,10 @@ interface Dependencies { readonly store: ClusterStore; readonly catalogEntityRegistry: CatalogEntityRegistry; readonly clustersThatAreBeingDeleted: ObservableSet; + readonly visibleCluster: IObservableValue; } export class ClusterManager { - @observable visibleCluster: ClusterId | undefined = undefined; - constructor(private readonly dependencies: Dependencies) { makeObservable(this); } @@ -58,7 +57,7 @@ export class ClusterManager { reaction(() => [ this.dependencies.catalogEntityRegistry.filterItemsByPredicate(isKubernetesCluster), - this.visibleCluster, + this.dependencies.visibleCluster.get(), ] as const, ([entities, visibleCluster]) => { for (const entity of entities) { if (entity.getId() === visibleCluster) { diff --git a/src/main/cluster/visibility-handler.injectable.ts b/src/main/cluster/visibility-handler.injectable.ts new file mode 100644 index 0000000000..31f6ff13ec --- /dev/null +++ b/src/main/cluster/visibility-handler.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { clusterVisibilityChannel } from "../../common/cluster/visibility-channel"; +import { getMessageChannelListenerInjectable } from "../../common/utils/channel/message-channel-listener-injection-token"; +import visibleClusterInjectable from "./visible-cluster.injectable"; + +const clusterVisibilityHandlerInjectable = getMessageChannelListenerInjectable({ + channel: clusterVisibilityChannel, + id: "base", + handler: (di) => { + const visibleCluster = di.inject(visibleClusterInjectable); + + return (clusterId) => visibleCluster.set(clusterId); + }, +}); + +export default clusterVisibilityHandlerInjectable; diff --git a/src/main/cluster/visible-cluster.injectable.ts b/src/main/cluster/visible-cluster.injectable.ts new file mode 100644 index 0000000000..2d5817b20c --- /dev/null +++ b/src/main/cluster/visible-cluster.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { ClusterId } from "../../common/cluster-types"; + +const visibleClusterInjectable = getInjectable({ + id: "visible-cluster", + instantiate: () => observable.box(null), +}); + +export default visibleClusterInjectable; diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index bec1edce33..bc642e9df1 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -5,7 +5,6 @@ import { getInjectable } from "@ogre-tools/injectable"; import { setupIpcMainHandlers } from "./setup-ipc-main-handlers"; import loggerInjectable from "../../../../common/logger.injectable"; -import clusterManagerInjectable from "../../../cluster/manager.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token"; import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable"; @@ -20,7 +19,6 @@ const setupIpcMainHandlersInjectable = getInjectable({ instantiate: (di) => { const logger = di.inject(loggerInjectable); - const clusterManager = di.inject(clusterManagerInjectable); const applicationMenuItemComposite = di.inject(applicationMenuItemCompositeInjectable); const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const clusterStore = di.inject(clusterStoreInjectable); @@ -36,7 +34,6 @@ const setupIpcMainHandlersInjectable = getInjectable({ setupIpcMainHandlers({ applicationMenuItemComposite, - clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index 5685b5a201..165d1beb64 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -5,13 +5,12 @@ import type { IpcMainInvokeEvent } from "electron"; import { BrowserWindow, Menu } from "electron"; import { clusterFrameMap } from "../../../../common/cluster-frames"; -import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../../../../common/ipc/cluster"; +import { clusterActivateHandler, clusterSetFrameIdHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../../../../common/ipc/cluster"; import type { ClusterId } from "../../../../common/cluster-types"; import { ClusterStore } from "../../../../common/cluster-store/cluster-store"; import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc"; import type { CatalogEntityRegistry } from "../../../catalog"; import { pushCatalogToRenderer } from "../../../catalog-pusher"; -import type { ClusterManager } from "../../../cluster/manager"; import type { IComputedValue } from "mobx"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; @@ -28,7 +27,6 @@ import type { CreateResourceApplier } from "../../../resource-applier/create-res interface Dependencies { applicationMenuItemComposite: IComputedValue>; - clusterManager: ClusterManager; catalogEntityRegistry: CatalogEntityRegistry; clusterStore: ClusterStore; operatingSystemTheme: IComputedValue; @@ -39,7 +37,6 @@ interface Dependencies { export const setupIpcMainHandlers = ({ applicationMenuItemComposite, - clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, @@ -64,10 +61,6 @@ export const setupIpcMainHandlers = ({ } }); - ipcMainOn(clusterVisibilityHandler, (event, clusterId?: ClusterId) => { - clusterManager.visibleCluster = clusterId; - }); - ipcMainHandle(clusterRefreshHandler, (event, clusterId: ClusterId) => { return ClusterStore.getInstance() .getById(clusterId) diff --git a/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts index 19611f3117..3f40455527 100644 --- a/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts +++ b/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts @@ -3,11 +3,18 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; import { ClusterFrameHandler } from "./cluster-frame-handler"; +import emitClusterVisibilityInjectable from "./emit-cluster-visibility.injectable"; const clusterFrameHandlerInjectable = getInjectable({ id: "cluster-frame-handler", - instantiate: () => new ClusterFrameHandler(), + instantiate: (di) => new ClusterFrameHandler({ + emitClusterVisibility: di.inject(emitClusterVisibilityInjectable), + getClusterById: di.inject(getClusterByIdInjectable), + logger: di.inject(loggerInjectable), + }), }); export default clusterFrameHandlerInjectable; diff --git a/src/renderer/components/cluster-manager/cluster-frame-handler.ts b/src/renderer/components/cluster-manager/cluster-frame-handler.ts index 7791837e0d..d7d298dd80 100644 --- a/src/renderer/components/cluster-manager/cluster-frame-handler.ts +++ b/src/renderer/components/cluster-manager/cluster-frame-handler.ts @@ -4,24 +4,29 @@ */ import { action, makeObservable, observable, when } from "mobx"; -import logger from "../../../main/logger"; -import { clusterVisibilityHandler } from "../../../common/ipc/cluster"; -import { ClusterStore } from "../../../common/cluster-store/cluster-store"; import type { ClusterId } from "../../../common/cluster-types"; import type { Disposer } from "../../utils"; import { getClusterFrameUrl, onceDefined } from "../../utils"; -import { ipcRenderer } from "electron"; import assert from "assert"; +import type { Logger } from "../../../common/logger"; +import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; +import type { EmitClusterVisibility } from "./emit-cluster-visibility.injectable"; export interface LensView { isLoaded: boolean; frame: HTMLIFrameElement; } +interface Dependencies { + readonly logger: Logger; + getClusterById: GetClusterById; + emitClusterVisibility: EmitClusterVisibility; +} + export class ClusterFrameHandler { private readonly views = observable.map(); - constructor() { + constructor(protected readonly dependencies: Dependencies) { makeObservable(this); } @@ -31,7 +36,7 @@ export class ClusterFrameHandler { @action public initView(clusterId: ClusterId) { - const cluster = ClusterStore.getInstance().getById(clusterId); + const cluster = this.dependencies.getClusterById(clusterId); const parentElem = document.getElementById("lens-views"); assert(parentElem, "DOM with #lens-views must be present"); @@ -40,14 +45,14 @@ export class ClusterFrameHandler { return; } - logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`); + this.dependencies.logger.info(`[LENS-VIEW]: init dashboard, clusterId=${clusterId}`); const iframe = document.createElement("iframe"); iframe.id = `cluster-frame-${cluster.id}`; iframe.name = cluster.contextName; iframe.setAttribute("src", getClusterFrameUrl(clusterId)); iframe.addEventListener("load", action(() => { - logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`); + this.dependencies.logger.info(`[LENS-VIEW]: frame for clusterId=${clusterId} has loaded`); const view = this.views.get(clusterId); assert(view, `view for ${clusterId} MUST still exist here`); @@ -56,11 +61,11 @@ export class ClusterFrameHandler { this.views.set(clusterId, { frame: iframe, isLoaded: false }); parentElem.appendChild(iframe); - logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`); + this.dependencies.logger.info(`[LENS-VIEW]: waiting cluster to be ready, clusterId=${clusterId}`); const dispose = when( () => cluster.ready, - () => logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`), + () => this.dependencies.logger.info(`[LENS-VIEW]: cluster is ready, clusterId=${clusterId}`), ); when( @@ -69,12 +74,12 @@ export class ClusterFrameHandler { () => { when( () => { - const cluster = ClusterStore.getInstance().getById(clusterId); + const cluster = this.dependencies.getClusterById(clusterId); return Boolean(!cluster || (cluster.disconnected && this.views.get(clusterId)?.isLoaded)); }, () => { - logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`); + this.dependencies.logger.info(`[LENS-VIEW]: remove dashboard, clusterId=${clusterId}`); this.views.delete(clusterId); parentElem.removeChild(iframe); dispose(); @@ -86,19 +91,19 @@ export class ClusterFrameHandler { private prevVisibleClusterChange?: Disposer; - public setVisibleCluster(clusterId: ClusterId | null) { + public setVisibleCluster(clusterId: ClusterId | null): void { // Clear the previous when ASAP this.prevVisibleClusterChange?.(); - logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${clusterId}`); - ipcRenderer.send(clusterVisibilityHandler); + this.dependencies.logger.info(`[LENS-VIEW]: refreshing iframe views, visible cluster id=${clusterId}`); + this.dependencies.emitClusterVisibility(null); for (const { frame: view } of this.views.values()) { view.classList.add("hidden"); } const cluster = clusterId - ? ClusterStore.getInstance().getById(clusterId) + ? this.dependencies.getClusterById(clusterId) : undefined; if (cluster && clusterId) { @@ -113,10 +118,10 @@ export class ClusterFrameHandler { return undefined; }, (view: LensView) => { - logger.info(`[LENS-VIEW]: cluster id=${clusterId} should now be visible`); + this.dependencies.logger.info(`[LENS-VIEW]: cluster id=${clusterId} should now be visible`); view.frame.classList.remove("hidden"); view.frame.focus(); - ipcRenderer.send(clusterVisibilityHandler, clusterId); + this.dependencies.emitClusterVisibility(clusterId); }, ); } diff --git a/src/renderer/components/cluster-manager/emit-cluster-visibility.injectable.ts b/src/renderer/components/cluster-manager/emit-cluster-visibility.injectable.ts new file mode 100644 index 0000000000..261582d136 --- /dev/null +++ b/src/renderer/components/cluster-manager/emit-cluster-visibility.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { MessageChannelHandler } from "../../../common/utils/channel/message-channel-listener-injection-token"; +import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token"; +import { clusterVisibilityChannel } from "../../../common/cluster/visibility-channel"; + +export type EmitClusterVisibility = MessageChannelHandler; + +const emitClusterVisibilityInjectable = getInjectable({ + id: "emit-cluster-visibility", + instantiate: (di): EmitClusterVisibility => { + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return (id) => sendMessageToChannel(clusterVisibilityChannel, id); + }, +}); + +export default emitClusterVisibilityInjectable;