1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Kludge "isEnabledForCluster" work again for cluster pages

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-07-06 14:03:55 +03:00
parent d44599af00
commit 2010df9cb5
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
10 changed files with 1674 additions and 7 deletions

View File

@ -0,0 +1,106 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
import type { TestExtensionRenderer } from "../../../renderer/components/test-utils/get-extension-fake";
import { getExtensionFakeFor } from "../../../renderer/components/test-utils/get-extension-fake";
import type { KubernetesCluster } from "../../../common/catalog-entities";
import React from "react";
import extensionShouldBeEnabledForClusterFrameInjectable from "../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
describe("disable-cluster-pages-when-cluster-is-not-relevant", () => {
let builder: ApplicationBuilder;
let rendered: RenderResult;
let rendererTestExtension: TestExtensionRenderer;
let isEnabledForClusterMock: AsyncFnMock<(cluster: KubernetesCluster) => boolean>;
beforeEach(async () => {
builder = getApplicationBuilder();
builder.setEnvironmentToClusterFrame();
builder.dis.rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable);
const getExtensionFake = getExtensionFakeFor(builder);
isEnabledForClusterMock = asyncFn();
const testExtension = getExtensionFake({
id: "test-extension-id",
name: "test-extension",
rendererOptions: {
isEnabledForCluster: isEnabledForClusterMock,
clusterPages: [{
components: {
Page: () => <div data-testid="some-test-page">Some page</div>,
},
}],
},
});
rendererTestExtension = testExtension.renderer;
rendered = await builder.render();
builder.extensions.enable(testExtension);
});
describe("given not yet known if extension should be enabled for the cluster, when navigating", () => {
beforeEach(() => {
rendererTestExtension.navigate();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not show the page", () => {
const actual = rendered.queryByTestId("some-test-page");
expect(actual).not.toBeInTheDocument();
});
});
describe("given extension shouldn't be enabled for the cluster, when navigating", () => {
beforeEach(async () => {
await isEnabledForClusterMock.resolve(false);
rendererTestExtension.navigate();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not show the page", () => {
const actual = rendered.queryByTestId("some-test-page");
expect(actual).not.toBeInTheDocument();
});
});
describe("given extension should be enabled for the cluster, when navigating", () => {
beforeEach(async () => {
await isEnabledForClusterMock.resolve(true);
rendererTestExtension.navigate();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("shows the page", () => {
const actual = rendered.getByTestId("some-test-page");
expect(actual).toBeInTheDocument();
});
});
});

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import type { KubernetesCluster } from "../../common/catalog-entities";
import type { LensRendererExtension } from "../lens-renderer-extension";
interface ExtensionIsEnabledForCluster {
extension: LensRendererExtension;
cluster: KubernetesCluster;
}
const extensionIsEnabledForClusterInjectable = getInjectable({
id: "extension-is-enabled-for-cluster",
instantiate: async (
di,
{ extension, cluster }: ExtensionIsEnabledForCluster,
) => (await extension.isEnabledForCluster(cluster)) as boolean,
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (
di,
{ extension, cluster }: ExtensionIsEnabledForCluster,
) => `${extension.sanitizedExtensionId}-${cluster.getId()}`,
}),
});
export default extensionIsEnabledForClusterInjectable;

View File

@ -9,6 +9,9 @@ import { createExtensionInstanceInjectionToken } from "./create-extension-instan
import extensionInstancesInjectable from "./extension-instances.injectable";
import type { LensExtension } from "../lens-extension";
import extensionInjectable from "./extension/extension.injectable";
import type { LensRendererExtension } from "../lens-renderer-extension";
import extensionIsEnabledForClusterInjectable from "./extension-is-enabled-for-cluster.injectable";
import type { KubernetesCluster } from "../../common/catalog-entities";
const extensionLoaderInjectable = getInjectable({
id: "extension-loader",
@ -18,6 +21,11 @@ const extensionLoaderInjectable = getInjectable({
createExtensionInstance: di.inject(createExtensionInstanceInjectionToken),
extensionInstances: di.inject(extensionInstancesInjectable),
getExtension: (instance: LensExtension) => di.inject(extensionInjectable, instance),
getExtensionIsEnabledForCluster: (extension: LensRendererExtension, cluster: KubernetesCluster) => di.inject(extensionIsEnabledForClusterInjectable, {
extension,
cluster,
}),
}),
});

View File

@ -32,6 +32,7 @@ interface Dependencies {
createExtensionInstance: CreateExtensionInstance;
readonly extensionInstances: ObservableMap<LensExtensionId, LensExtension>;
getExtension: (instance: LensExtension) => Extension;
getExtensionIsEnabledForCluster: (extension: LensRendererExtension, cluster: KubernetesCluster) => Promise<boolean>;
}
export interface ExtensionLoading {
@ -282,7 +283,7 @@ export class ExtensionLoader {
const extension = ext as LensRendererExtension;
// getCluster must be a callback, as the entity might be available only after an extension has been loaded
if ((await extension.isEnabledForCluster(entity)) === false) {
if (await this.dependencies.getExtensionIsEnabledForCluster(extension, entity)) {
return [];
}

View File

@ -0,0 +1,28 @@
/**
* 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 catalogEntityRegistryInjectable from "../api/catalog/entity/registry.injectable";
import { computed } from "mobx";
import { isKubernetesCluster } from "../../common/catalog-entities";
const activeKubernetesClusterInjectable = getInjectable({
id: "active-kubernetes-cluster",
instantiate: (di) => {
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
return computed(() => {
const activeEntity = catalogEntityRegistry.activeEntity;
if (!isKubernetesCluster(activeEntity)) {
return null;
}
return activeEntity;
});
},
});
export default activeKubernetesClusterInjectable;

View File

@ -58,6 +58,8 @@ import { renderFor } from "./renderFor";
import { RootFrame } from "../../frames/root-frame/root-frame";
import { ClusterFrame } from "../../frames/cluster-frame/cluster-frame";
import hostedClusterIdInjectable from "../../cluster-frame-context/hosted-cluster-id.injectable";
import activeKubernetesClusterInjectable from "../../cluster-frame-context/active-kubernetes-cluster.injectable";
import { catalogEntityFromCluster } from "../../../main/cluster-manager";
type Callback = (dis: DiContainers) => void | Promise<void>;
@ -382,6 +384,10 @@ export const getApplicationBuilder = () => {
accessibleNamespaces: [],
} as unknown as Cluster;
rendererDi.override(activeKubernetesClusterInjectable, () =>
computed(() => catalogEntityFromCluster(clusterStub)),
);
const namespaceStoreStub = {
contextNamespaces: [],
items: [],

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
import type { KubernetesCluster } from "../../common/catalog-entities";
import extensionIsEnabledForClusterInjectable from "../../extensions/extension-loader/extension-is-enabled-for-cluster.injectable";
import activeKubernetesClusterInjectable from "../cluster-frame-context/active-kubernetes-cluster.injectable";
const extensionShouldBeEnabledForClusterFrameInjectable = getInjectable({
id: "extension-should-be-enabled-for-cluster-frame",
instantiate: (di, extension: LensRendererExtension) => {
const activeKubernetesCluster = di.inject(activeKubernetesClusterInjectable);
const getExtensionIsEnabledForCluster = (
extension: LensRendererExtension,
cluster: KubernetesCluster,
) =>
di.inject(extensionIsEnabledForClusterInjectable, { extension, cluster });
return asyncComputed(
async () => {
const cluster = activeKubernetesCluster.get();
if (!cluster) {
return false;
}
return getExtensionIsEnabledForCluster(
extension,
cluster,
);
},
false,
);
},
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, extension: LensRendererExtension) =>
extension.sanitizedExtensionId,
}),
});
export default extensionShouldBeEnabledForClusterFrameInjectable;

View File

@ -69,6 +69,8 @@ import kubeObjectDetailsClusterFrameChildComponentInjectable from "./components/
import kubeconfigDialogClusterFrameChildComponentInjectable from "./components/kubeconfig-dialog/kubeconfig-dialog-cluster-frame-child-component.injectable";
import portForwardDialogClusterFrameChildComponentInjectable from "./port-forward/port-forward-dialog-cluster-frame-child-component.injectable";
import setupSystemCaInjectable from "./frames/root-frame/setup-system-ca.injectable";
import extensionShouldBeEnabledForClusterFrameInjectable from "./extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => {
const {
@ -119,6 +121,11 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {})
shouldRender: computed(() => false),
}));
// TODO: Remove after "LensRendererExtension.isEnabledForCluster" is removed
di.override(extensionShouldBeEnabledForClusterFrameInjectable, () =>
asyncComputed(async () => true, true),
);
// TODO: Remove side-effects and shared global state
const clusterFrameChildComponentInjectables: Injectable<any, any, any>[] = [
commandContainerClusterFrameChildComponentInjectable,

View File

@ -14,9 +14,11 @@ import { extensionRegistratorInjectionToken } from "../../extensions/extension-l
import { SiblingsInTabLayout } from "../components/layout/siblings-in-tab-layout";
import extensionPageParametersInjectable from "./extension-page-parameters.injectable";
import { routeSpecificComponentInjectionToken } from "./route-specific-component-injection-token";
import type { IComputedValue } from "mobx";
import { computed } from "mobx";
import { frontEndRouteInjectionToken } from "../../common/front-end-routing/front-end-route-injection-token";
import { getExtensionRoutePath } from "./for-extension";
import extensionShouldBeEnabledForClusterFrameInjectable from "../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
const extensionRouteRegistratorInjectable = getInjectable({
id: "extension-route-registrator",
@ -24,14 +26,27 @@ const extensionRouteRegistratorInjectable = getInjectable({
instantiate: (di) => {
return (ext) => {
const extension = ext as LensRendererExtension;
const toRouteInjectable = toRouteInjectableFor(
di,
const toRouteInjectable = toRouteInjectableFor(di, extension);
const extensionShouldBeEnabledForClusterFrame = di.inject(
extensionShouldBeEnabledForClusterFrameInjectable,
extension,
);
return [
...extension.globalPages.map(toRouteInjectable(false)),
...extension.clusterPages.map(toRouteInjectable(true)),
...extension.globalPages.map(
toRouteInjectable(
false,
computed(() => true),
),
),
...extension.clusterPages.map(
toRouteInjectable(
true,
computed(() => extensionShouldBeEnabledForClusterFrame.value.get()),
),
),
].flat();
};
},
@ -46,7 +61,7 @@ const toRouteInjectableFor =
di: DiContainerForInjection,
extension: LensRendererExtension,
) =>
(clusterFrame: boolean) =>
(clusterFrame: boolean, isEnabled: IComputedValue<boolean>) =>
(registration: PageRegistration) => {
const routeInjectable = getInjectable({
id: `route-${registration.id}-for-extension-${extension.sanitizedExtensionId}`,
@ -54,7 +69,7 @@ const toRouteInjectableFor =
instantiate: () => ({
path: getExtensionRoutePath(extension, registration.id),
clusterFrame,
isEnabled: computed(() => true),
isEnabled,
extension,
}),