mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix cluster frame display issue (#5518)
* Fix cluster frame display issue - Add some defensive code to prevent this sort of infinite loop - Add some unit tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix unit tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix build Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * Factor out injectable for getting of cluster config data Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
8c31e56462
commit
de43df1cca
40 files changed
+2126
-815
No files matched your search
@@ -39,9 +39,9 @@ interface Dependencies {
|
||||
function getContexts(config: KubeConfig): Map<string, Option> {
|
||||
return new Map(
|
||||
splitConfig(config)
|
||||
.map(({ config, error }) => [config.currentContext, {
|
||||
.map(({ config, validationResult }) => [config.currentContext, {
|
||||
config,
|
||||
error,
|
||||
error: validationResult.error?.toString(),
|
||||
}]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import { ClusterIssues } from "./cluster-issues";
|
||||
import { ClusterMetrics } from "./cluster-metrics";
|
||||
import type { ClusterOverviewStore } from "./cluster-overview-store/cluster-overview-store";
|
||||
import { ClusterPieCharts } from "./cluster-pie-charts";
|
||||
import { getActiveClusterEntity } from "../../api/catalog/entity/legacy-globals";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
import type { EventStore } from "../+events/store";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
@@ -71,7 +70,7 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
this.metricPoller.stop();
|
||||
}
|
||||
|
||||
renderMetrics(isMetricsHidden?: boolean) {
|
||||
renderMetrics(isMetricsHidden: boolean) {
|
||||
if (isMetricsHidden) {
|
||||
return null;
|
||||
}
|
||||
@@ -84,7 +83,7 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
);
|
||||
}
|
||||
|
||||
renderClusterOverview(isLoaded: boolean, isMetricsHidden?: boolean) {
|
||||
renderClusterOverview(isLoaded: boolean, isMetricsHidden: boolean) {
|
||||
if (!isLoaded) {
|
||||
return <Spinner center/>;
|
||||
}
|
||||
@@ -98,9 +97,9 @@ class NonInjectedClusterOverview extends React.Component<Dependencies> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { eventStore, nodeStore } = this.props;
|
||||
const { eventStore, nodeStore, hostedCluster } = this.props;
|
||||
const isLoaded = nodeStore.isLoaded && eventStore.isLoaded;
|
||||
const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Cluster);
|
||||
const isMetricHidden = hostedCluster.isMetricHidden(ClusterMetricsResourceType.Cluster);
|
||||
|
||||
return (
|
||||
<TabLayout>
|
||||
|
||||
@@ -12,51 +12,48 @@ import { CommandDialog } from "./command-dialog";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import type { CommandOverlay } from "./command-overlay.injectable";
|
||||
import commandOverlayInjectable from "./command-overlay.injectable";
|
||||
import { isMac } from "../../../common/vars";
|
||||
import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry";
|
||||
import { broadcastMessage, ipcRendererOn } from "../../../common/ipc";
|
||||
import type { Disposer } from "../../utils";
|
||||
import type { ipcRendererOn } from "../../../common/ipc";
|
||||
import { broadcastMessage } from "../../../common/ipc";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { AddWindowEventListener } from "../../window/event-listener.injectable";
|
||||
import windowAddEventListenerInjectable from "../../window/event-listener.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import matchedClusterIdInjectable from "../../navigation/matched-cluster-id.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
|
||||
import hostedClusterIdInjectable from "../../cluster-frame-context/hosted-cluster-id.injectable";
|
||||
import isMacInjectable from "../../../common/vars/is-mac.injectable";
|
||||
import legacyOnChannelListenInjectable from "../../ipc/legacy-channel-listen.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
addWindowEventListener: <K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions) => Disposer;
|
||||
addWindowEventListener: AddWindowEventListener;
|
||||
commandOverlay: CommandOverlay;
|
||||
clusterId?: ClusterId;
|
||||
matchedClusterId: IComputedValue<ClusterId>;
|
||||
entityRegistry: CatalogEntityRegistry;
|
||||
clusterId: ClusterId | undefined;
|
||||
matchedClusterId: IComputedValue<ClusterId | undefined>;
|
||||
isMac: boolean;
|
||||
legacyOnChannelListen: typeof ipcRendererOn;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedCommandContainer extends React.Component<Dependencies> {
|
||||
private escHandler(event: KeyboardEvent) {
|
||||
const { commandOverlay } = this.props;
|
||||
|
||||
private escHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
event.stopPropagation();
|
||||
commandOverlay.close();
|
||||
this.props.commandOverlay.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleCommandPalette = () => {
|
||||
const { commandOverlay, entityRegistry } = this.props;
|
||||
const clusterIsActive = this.props.matchedClusterId.get() !== undefined;
|
||||
const matchedClusterId = this.props.matchedClusterId.get();
|
||||
|
||||
if (clusterIsActive) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
broadcastMessage(`command-palette:${entityRegistry.activeEntity!.getId()}:open`);
|
||||
if (matchedClusterId !== undefined) {
|
||||
broadcastMessage(`command-palette:${matchedClusterId}:open`);
|
||||
} else {
|
||||
commandOverlay.open(<CommandDialog />);
|
||||
this.props.commandOverlay.open(<CommandDialog />);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyboardShortcut(action: () => void) {
|
||||
return ({ key, shiftKey, ctrlKey, altKey, metaKey }: KeyboardEvent) => {
|
||||
const ctrlOrCmd = isMac ? metaKey && !ctrlKey : !metaKey && ctrlKey;
|
||||
const ctrlOrCmd = this.props.isMac ? metaKey && !ctrlKey : !metaKey && ctrlKey;
|
||||
|
||||
if (key === "p" && shiftKey && ctrlOrCmd && !altKey) {
|
||||
action();
|
||||
@@ -75,9 +72,9 @@ class NonInjectedCommandContainer extends React.Component<Dependencies> {
|
||||
: "command-palette:open";
|
||||
|
||||
disposeOnUnmount(this, [
|
||||
ipcRendererOn(ipcChannel, action),
|
||||
this.props.legacyOnChannelListen(ipcChannel, action),
|
||||
addWindowEventListener("keydown", this.onKeyboardShortcut(action)),
|
||||
addWindowEventListener("keyup", (e) => this.escHandler(e), true),
|
||||
addWindowEventListener("keyup", this.escHandler, true),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -106,6 +103,7 @@ export const CommandContainer = withInjectables<Dependencies>(NonInjectedCommand
|
||||
addWindowEventListener: di.inject(windowAddEventListenerInjectable),
|
||||
commandOverlay: di.inject(commandOverlayInjectable),
|
||||
matchedClusterId: di.inject(matchedClusterIdInjectable),
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
isMac: di.inject(isMacInjectable),
|
||||
legacyOnChannelListen: di.inject(legacyOnChannelListenInjectable),
|
||||
}),
|
||||
});
|
||||
+20
-8
@@ -9,9 +9,8 @@ import type { RenderResult } from "@testing-library/react";
|
||||
import mockFs from "mock-fs";
|
||||
import React from "react";
|
||||
import * as selectEvent from "react-select-event";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import { DeleteClusterDialog } from "../view";
|
||||
import type { ClusterModel } from "../../../../common/cluster-types";
|
||||
import type { CreateCluster } from "../../../../common/cluster/create-cluster-injection-token";
|
||||
import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token";
|
||||
import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable";
|
||||
import type { OpenDeleteClusterDialog } from "../open.injectable";
|
||||
@@ -46,14 +45,16 @@ jest.mock("electron", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const currentClusterServerUrl = "https://localhost";
|
||||
const nonCurrentClusterServerUrl = "http://localhost";
|
||||
const multiClusterConfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://localhost
|
||||
server: ${currentClusterServerUrl}
|
||||
name: some-current-context-cluster
|
||||
- cluster:
|
||||
server: http://localhost
|
||||
server: ${nonCurrentClusterServerUrl}
|
||||
name: some-non-current-context-cluster
|
||||
contexts:
|
||||
- context:
|
||||
@@ -73,11 +74,12 @@ users:
|
||||
token: kubeconfig-user-q4lm4:xxxyyyy
|
||||
`;
|
||||
|
||||
const singleClusterServerUrl = "http://localhost";
|
||||
const singleClusterConfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: http://localhost
|
||||
server: ${singleClusterServerUrl}
|
||||
name: some-cluster
|
||||
contexts:
|
||||
- context:
|
||||
@@ -97,7 +99,7 @@ let config: KubeConfig;
|
||||
|
||||
describe("<DeleteClusterDialog />", () => {
|
||||
let applicationBuilder: ApplicationBuilder;
|
||||
let createCluster: (model: ClusterModel) => Cluster;
|
||||
let createCluster: CreateCluster;
|
||||
let openDeleteClusterDialog: OpenDeleteClusterDialog;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -157,6 +159,8 @@ describe("<DeleteClusterDialog />", () => {
|
||||
clusterName: "some-current-context-cluster",
|
||||
},
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: currentClusterServerUrl,
|
||||
});
|
||||
|
||||
openDeleteClusterDialog({ cluster, config });
|
||||
@@ -204,6 +208,8 @@ describe("<DeleteClusterDialog />", () => {
|
||||
clusterName: "minikube",
|
||||
},
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: nonCurrentClusterServerUrl,
|
||||
});
|
||||
|
||||
openDeleteClusterDialog({ cluster, config });
|
||||
@@ -221,6 +227,8 @@ describe("<DeleteClusterDialog />", () => {
|
||||
clusterName: "some-current-context-cluster",
|
||||
},
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: currentClusterServerUrl,
|
||||
});
|
||||
|
||||
openDeleteClusterDialog({ cluster, config });
|
||||
@@ -236,6 +244,8 @@ describe("<DeleteClusterDialog />", () => {
|
||||
clusterName: "some-current-context-cluster",
|
||||
},
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: currentClusterServerUrl,
|
||||
});
|
||||
|
||||
openDeleteClusterDialog({ cluster, config });
|
||||
@@ -259,12 +269,12 @@ describe("<DeleteClusterDialog />", () => {
|
||||
const cluster = createCluster({
|
||||
id: "some-non-current-context-cluster",
|
||||
contextName: "some-non-current-context",
|
||||
|
||||
preferences: {
|
||||
clusterName: "some-non-current-context-cluster",
|
||||
},
|
||||
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: nonCurrentClusterServerUrl,
|
||||
});
|
||||
|
||||
const spy = jest.spyOn(cluster, "isInLocalKubeconfig").mockImplementation(() => true);
|
||||
@@ -301,6 +311,8 @@ describe("<DeleteClusterDialog />", () => {
|
||||
clusterName: "some-cluster",
|
||||
},
|
||||
kubeConfigPath: "./temp-kube-config",
|
||||
}, {
|
||||
clusterServerUrl: singleClusterServerUrl,
|
||||
});
|
||||
|
||||
openDeleteClusterDialog({ cluster, config });
|
||||
|
||||
@@ -80,9 +80,7 @@ class NonInjectedSidebarItem extends React.Component<
|
||||
return (
|
||||
<div
|
||||
className={cssNames("SidebarItem")}
|
||||
data-testid="sidebar-item"
|
||||
data-test-id={this.id}
|
||||
data-id-test={this.id}
|
||||
data-testid={`sidebar-item-${this.id}`}
|
||||
data-is-active-test={this.isActive}
|
||||
data-parent-id-test={this.registration.parentId}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ const createClusterInjectable = getInjectable({
|
||||
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
};
|
||||
|
||||
return (model) => new Cluster(dependencies, model);
|
||||
return (model, configData) => new Cluster(dependencies, model, configData);
|
||||
},
|
||||
|
||||
injectionToken: createClusterInjectionToken,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
.centering {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { RenderResult } from "@testing-library/react";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import { render as testingLibraryRender } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { DiContextProvider } from "@ogre-tools/injectable-react";
|
||||
import { Router } from "react-router";
|
||||
import { DefaultProps } from "../../mui-base-theme";
|
||||
import { ClusterFrame } from "./cluster-frame";
|
||||
import historyInjectable from "../../navigation/history.injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import createClusterInjectable from "../../create-cluster/create-cluster.injectable";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
|
||||
import legacyOnChannelListenInjectable from "../../ipc/legacy-channel-listen.injectable";
|
||||
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
|
||||
import allowedResourcesInjectable from "../../cluster-frame-context/allowed-resources.injectable";
|
||||
import hostedClusterIdInjectable from "../../cluster-frame-context/hosted-cluster-id.injectable";
|
||||
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
|
||||
|
||||
describe("<ClusterFrame />", () => {
|
||||
let render: () => RenderResult;
|
||||
let di: DiContainer;
|
||||
let cluster: Cluster;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
render = () => testingLibraryRender((
|
||||
<DiContextProvider value={{ di }}>
|
||||
<Router history={di.inject(historyInjectable)}>
|
||||
{DefaultProps(ClusterFrame)}
|
||||
</Router>
|
||||
</DiContextProvider>
|
||||
));
|
||||
|
||||
di.override(subscribeStoresInjectable, () => jest.fn().mockImplementation(() => jest.fn()));
|
||||
di.override(legacyOnChannelListenInjectable, () => jest.fn().mockImplementation(() => jest.fn()));
|
||||
di.override(directoryForUserDataInjectable, () => "/some/irrelavent/path");
|
||||
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||
|
||||
const createCluster = di.inject(createClusterInjectable);
|
||||
|
||||
cluster = createCluster(
|
||||
{
|
||||
contextName: "my-cluster",
|
||||
id: "123456",
|
||||
kubeConfigPath: "/irrelavent",
|
||||
},
|
||||
{
|
||||
clusterServerUrl: "https://localhost",
|
||||
},
|
||||
);
|
||||
|
||||
di.override(hostedClusterInjectable, () => cluster);
|
||||
di.override(hostedClusterIdInjectable, () => cluster.id);
|
||||
});
|
||||
|
||||
describe("given cluster with list nodes and namespaces permissions", () => {
|
||||
beforeEach(() => {
|
||||
di.override(allowedResourcesInjectable, () => computed(() => new Set(["nodes", "namespaces"])));
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
const result = render();
|
||||
|
||||
expect(result.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows cluster overview sidebar item as active", () => {
|
||||
const result = render();
|
||||
const clusterOverviewSidebarItem = result.getByTestId("sidebar-item-cluster-overview");
|
||||
|
||||
expect(clusterOverviewSidebarItem.getAttribute("data-is-active-test")).toBe("true");
|
||||
});
|
||||
|
||||
describe("given no matching component", () => {
|
||||
beforeEach(() => {
|
||||
di.override(currentRouteComponentInjectable, () => computed(() => undefined));
|
||||
});
|
||||
|
||||
describe("given current url is starting url", () => {
|
||||
it("renders", () => {
|
||||
const result = render();
|
||||
|
||||
expect(result.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows warning message", () => {
|
||||
const result = render();
|
||||
|
||||
expect(
|
||||
result.getByText("An error has occured. No route can be found matching the current route, which is also the starting route."),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given cluster without list nodes, but with namespaces permissions", () => {
|
||||
beforeEach(() => {
|
||||
di.override(allowedResourcesInjectable, () => computed(() => new Set(["namespaces"])));
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
const result = render();
|
||||
|
||||
expect(result.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows workloads overview sidebar item as active", () => {
|
||||
const result = render();
|
||||
const workloadsOverviewSidebarItem = result.getByTestId("sidebar-item-workloads");
|
||||
|
||||
expect(workloadsOverviewSidebarItem.getAttribute("data-is-active-test")).toBe("true");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import styles from "./cluster-frame.module.css";
|
||||
import React, { useEffect } from "react";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
@@ -30,12 +31,14 @@ import { disposer } from "../../utils";
|
||||
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
|
||||
import startUrlInjectable from "./start-url.injectable";
|
||||
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
|
||||
import currentPathInjectable from "../../routes/current-path.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
namespaceStore: NamespaceStore;
|
||||
currentRouteComponent: IComputedValue<React.ElementType<{}> | undefined | null>;
|
||||
currentRouteComponent: IComputedValue<React.ElementType<{}> | undefined>;
|
||||
startUrl: IComputedValue<string>;
|
||||
subscribeStores: SubscribeStores;
|
||||
currentPath: IComputedValue<string>;
|
||||
}
|
||||
|
||||
export const NonInjectedClusterFrame = observer(({
|
||||
@@ -43,6 +46,7 @@ export const NonInjectedClusterFrame = observer(({
|
||||
currentRouteComponent,
|
||||
startUrl,
|
||||
subscribeStores,
|
||||
currentPath,
|
||||
}: Dependencies) => {
|
||||
useEffect(() => disposer(
|
||||
subscribeStores([
|
||||
@@ -52,6 +56,8 @@ export const NonInjectedClusterFrame = observer(({
|
||||
), []);
|
||||
|
||||
const Component = currentRouteComponent.get();
|
||||
const starting = startUrl.get();
|
||||
const current = currentPath.get();
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
@@ -62,7 +68,16 @@ export const NonInjectedClusterFrame = observer(({
|
||||
{
|
||||
Component
|
||||
? <Component />
|
||||
: <Redirect to={startUrl.get()} />
|
||||
// NOTE: this check is to prevent an infinite loop
|
||||
: starting !== current
|
||||
? <Redirect to={startUrl.get()} />
|
||||
: (
|
||||
<div className={styles.centering}>
|
||||
<div className="error">
|
||||
An error has occured. No route can be found matching the current route, which is also the starting route.
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</MainLayout>
|
||||
|
||||
@@ -87,6 +102,7 @@ export const ClusterFrame = withInjectables<Dependencies>(NonInjectedClusterFram
|
||||
subscribeStores: di.inject(subscribeStoresInjectable),
|
||||
startUrl: di.inject(startUrlInjectable),
|
||||
currentRouteComponent: di.inject(currentRouteComponentInjectable),
|
||||
currentPath: di.inject(currentPathInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,29 +4,30 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import type { KubeResource } from "../../../common/rbac";
|
||||
import isAllowedResourceInjectable from "../../../common/utils/is-allowed-resource.injectable";
|
||||
import clusterOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable";
|
||||
import workloadsOverviewRouteInjectable from "../../../common/front-end-routing/routes/cluster/workloads/overview/workloads-overview-route.injectable";
|
||||
import { buildURL } from "../../../common/utils/buildUrl";
|
||||
|
||||
const startUrlInjectable = getInjectable({
|
||||
id: "start-url",
|
||||
|
||||
instantiate: (di) => {
|
||||
const isAllowedResource = (resourceName: string) => di.inject(isAllowedResourceInjectable, resourceName);
|
||||
|
||||
const clusterOverviewRoute = di.inject(clusterOverviewRouteInjectable);
|
||||
const workloadOverviewRoute = di.inject(workloadsOverviewRouteInjectable);
|
||||
const clusterOverviewUrl = buildURL(clusterOverviewRoute.path);
|
||||
const workloadOverviewUrl = buildURL(workloadOverviewRoute.path);
|
||||
|
||||
return computed(() => {
|
||||
const resources: KubeResource[] = ["events", "nodes", "pods"];
|
||||
if (clusterOverviewRoute.isEnabled.get()) {
|
||||
return clusterOverviewRoute.path;
|
||||
}
|
||||
|
||||
return resources.every((resourceName) => isAllowedResource(resourceName))
|
||||
? clusterOverviewUrl
|
||||
: workloadOverviewUrl;
|
||||
if (workloadOverviewRoute.isEnabled.get()) {
|
||||
return workloadOverviewRoute.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: This will never be executed as `workloadOverviewRoute.isEnabled` always is true. It
|
||||
* is here is guard against accidental changes at a distance within `workloadOverviewRoute`.
|
||||
*/
|
||||
throw new Error("Exhausted all possible starting locations and none are active. This is a bug.");
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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 { ipcRendererOn } from "../../common/ipc";
|
||||
|
||||
const legacyOnChannelListenInjectable = getInjectable({
|
||||
id: "legacy-on-channel-listen",
|
||||
instantiate: () => ipcRendererOn,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default legacyOnChannelListenInjectable;
|
||||
@@ -6,6 +6,8 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { Disposer } from "../utils";
|
||||
|
||||
export type AddWindowEventListener = typeof addWindowEventListener;
|
||||
|
||||
function addWindowEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): Disposer {
|
||||
window.addEventListener(type, listener, options);
|
||||
|
||||
|
||||
Reference in new issue
Block a user