mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce kludge to make "isEnabledForCluster" work again for cluster page menus PART 2 (#5801)
* Kludge "isEnabledForCluster" work again for cluster page menus (sidebar items) Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce kludge to make "isEnabledForCluster" work again for kube object status texts PART 3 (#5802) * Kludge "isEnabledForCluster" work again for kube object status texts Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kludge "isEnabledForCluster" work again for kube object menu items (#5803) Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
fb6ad38880
commit
3ec61133a3
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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 { 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 sidebar items when cluster is not relevant", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
let rendered: RenderResult;
|
||||
let isEnabledForClusterMock: AsyncFnMock<(cluster: KubernetesCluster) => Promise<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>,
|
||||
},
|
||||
}],
|
||||
|
||||
clusterPageMenus: [
|
||||
{
|
||||
id: "some-sidebar-item",
|
||||
title: "Some sidebar item",
|
||||
|
||||
components: {
|
||||
Icon: () => <div>Some icon</div>,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
rendered = await builder.render();
|
||||
|
||||
builder.extensions.enable(testExtension);
|
||||
});
|
||||
|
||||
describe("given not yet known if extension should be enabled for the cluster", () => {
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the sidebar item", () => {
|
||||
const actual = rendered.queryByTestId(
|
||||
"sidebar-item-some-extension-name-some-sidebar-item",
|
||||
);
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension shouldn't be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(false);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the sidebar item", () => {
|
||||
const actual = rendered.queryByTestId(
|
||||
"sidebar-item-some-extension-name-some-sidebar-item",
|
||||
);
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension should be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(true);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows the sidebar item", () => {
|
||||
const actual = rendered.getByTestId(
|
||||
"sidebar-item-test-extension-some-sidebar-item",
|
||||
);
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 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 type { KubernetesCluster } from "../../../../common/catalog-entities";
|
||||
import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder";
|
||||
import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
|
||||
import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token";
|
||||
import { KubeObject } from "../../../../common/k8s-api/kube-object";
|
||||
import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
|
||||
import { KubeObjectMenu } from "../../../../renderer/components/kube-object-menu";
|
||||
import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
|
||||
|
||||
describe("disable kube object menu items when cluster is not relevant", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
let rendered: RenderResult;
|
||||
let isEnabledForClusterMock: AsyncFnMock<
|
||||
(cluster: KubernetesCluster) => Promise<boolean>
|
||||
>;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder = getApplicationBuilder();
|
||||
|
||||
builder.beforeApplicationStart(({ mainDi }) => {
|
||||
mainDi.override(apiManagerInjectable, () => ({}));
|
||||
});
|
||||
|
||||
const rendererDi = builder.dis.rendererDi;
|
||||
|
||||
rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable);
|
||||
|
||||
rendererDi.register(testRouteInjectable, testRouteComponentInjectable);
|
||||
|
||||
builder.setEnvironmentToClusterFrame();
|
||||
|
||||
const getExtensionFake = getExtensionFakeFor(builder);
|
||||
|
||||
isEnabledForClusterMock = asyncFn();
|
||||
|
||||
const testExtension = getExtensionFake({
|
||||
id: "test-extension-id",
|
||||
name: "test-extension",
|
||||
|
||||
rendererOptions: {
|
||||
isEnabledForCluster: isEnabledForClusterMock,
|
||||
|
||||
kubeObjectMenuItems: [
|
||||
{
|
||||
kind: "some-kind",
|
||||
apiVersions: ["some-api-version"],
|
||||
components: {
|
||||
MenuItem: () => (
|
||||
<div data-testid="some-test-id">Some menu item</div>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
rendered = await builder.render();
|
||||
|
||||
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
|
||||
const testRoute = rendererDi.inject(testRouteInjectable);
|
||||
|
||||
navigateToRoute(testRoute);
|
||||
|
||||
builder.extensions.enable(testExtension);
|
||||
});
|
||||
|
||||
describe("given not yet known if extension should be enabled for the cluster", () => {
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the kube object menu item", () => {
|
||||
const actual = rendered.queryByTestId("some-test-id");
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension shouldn't be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(false);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the kube object menu item", () => {
|
||||
const actual = rendered.queryByTestId("some-test-id");
|
||||
|
||||
expect(actual).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension should be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(true);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows the kube object menu item", () => {
|
||||
const actual = rendered.getByTestId("some-test-id");
|
||||
|
||||
expect(actual).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const testRouteInjectable = getInjectable({
|
||||
id: "test-route",
|
||||
|
||||
instantiate: () => ({
|
||||
path: "/test-route",
|
||||
clusterFrame: true,
|
||||
isEnabled: computed(() => true),
|
||||
}),
|
||||
|
||||
injectionToken: frontEndRouteInjectionToken,
|
||||
});
|
||||
|
||||
const testRouteComponentInjectable = getInjectable({
|
||||
id: "test-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(testRouteInjectable),
|
||||
|
||||
Component: () => (
|
||||
<KubeObjectMenu
|
||||
toolbar={true}
|
||||
object={getKubeObjectStub("some-kind", "some-api-version")}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
const getKubeObjectStub = (kind: string, apiVersion: string) =>
|
||||
KubeObject.create({
|
||||
apiVersion,
|
||||
kind,
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
resourceVersion: "some-resource-version",
|
||||
namespace: "some-namespace",
|
||||
selfLink: "",
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* 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 type { KubernetesCluster } from "../../../../common/catalog-entities";
|
||||
import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder";
|
||||
import { getExtensionFakeFor } from "../../../../renderer/components/test-utils/get-extension-fake";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { frontEndRouteInjectionToken } from "../../../../common/front-end-routing/front-end-route-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import React from "react";
|
||||
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
|
||||
import { routeSpecificComponentInjectionToken } from "../../../../renderer/routes/route-specific-component-injection-token";
|
||||
import { KubeObjectStatusIcon } from "../../../../renderer/components/kube-object-status-icon/kube-object-status-icon";
|
||||
import { KubeObject } from "../../../../common/k8s-api/kube-object";
|
||||
import { KubeObjectStatusLevel } from "../../../../common/k8s-api/kube-object-status";
|
||||
import extensionShouldBeEnabledForClusterFrameInjectable from "../../../../renderer/extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
|
||||
|
||||
describe("disable kube object statuses when cluster is not relevant", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
let rendered: RenderResult;
|
||||
let isEnabledForClusterMock: AsyncFnMock<
|
||||
(cluster: KubernetesCluster) => Promise<boolean>
|
||||
>;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder = getApplicationBuilder();
|
||||
|
||||
const rendererDi = builder.dis.rendererDi;
|
||||
|
||||
rendererDi.unoverride(extensionShouldBeEnabledForClusterFrameInjectable);
|
||||
|
||||
rendererDi.register(testRouteInjectable, testRouteComponentInjectable);
|
||||
|
||||
builder.setEnvironmentToClusterFrame();
|
||||
|
||||
const getExtensionFake = getExtensionFakeFor(builder);
|
||||
|
||||
isEnabledForClusterMock = asyncFn();
|
||||
|
||||
const testExtension = getExtensionFake({
|
||||
id: "test-extension-id",
|
||||
name: "test-extension",
|
||||
|
||||
rendererOptions: {
|
||||
isEnabledForCluster: isEnabledForClusterMock,
|
||||
|
||||
kubeObjectStatusTexts: [
|
||||
{
|
||||
kind: "some-kind",
|
||||
apiVersions: ["some-api-version"],
|
||||
|
||||
resolve: () => ({
|
||||
level: KubeObjectStatusLevel.CRITICAL,
|
||||
text: "some-kube-object-status-text",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
rendered = await builder.render();
|
||||
|
||||
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
|
||||
const testRoute = rendererDi.inject(testRouteInjectable);
|
||||
|
||||
navigateToRoute(testRoute);
|
||||
|
||||
builder.extensions.enable(testExtension);
|
||||
});
|
||||
|
||||
describe("given not yet known if extension should be enabled for the cluster", () => {
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the status", () => {
|
||||
const actual = rendered.baseElement.querySelectorAll(".KubeObjectStatusIcon");
|
||||
|
||||
expect(actual).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension shouldn't be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(false);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not show the status", () => {
|
||||
const actual = rendered.baseElement.querySelectorAll(".KubeObjectStatusIcon");
|
||||
|
||||
expect(actual).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("given extension should be enabled for the cluster", () => {
|
||||
beforeEach(async () => {
|
||||
await isEnabledForClusterMock.resolve(true);
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
expect(rendered.baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("shows the status", () => {
|
||||
const actual = rendered.baseElement.querySelectorAll(".KubeObjectStatusIcon");
|
||||
|
||||
expect(actual).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const testRouteInjectable = getInjectable({
|
||||
id: "test-route",
|
||||
|
||||
instantiate: () => ({
|
||||
path: "/test-route",
|
||||
clusterFrame: true,
|
||||
isEnabled: computed(() => true),
|
||||
}),
|
||||
|
||||
injectionToken: frontEndRouteInjectionToken,
|
||||
});
|
||||
|
||||
const testRouteComponentInjectable = getInjectable({
|
||||
id: "test-route-component",
|
||||
|
||||
instantiate: (di) => ({
|
||||
route: di.inject(testRouteInjectable),
|
||||
|
||||
Component: () => (
|
||||
<KubeObjectStatusIcon
|
||||
object={getKubeObjectStub("some-kind", "some-api-version")}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
|
||||
injectionToken: routeSpecificComponentInjectionToken,
|
||||
});
|
||||
|
||||
const getKubeObjectStub = (kind: string, apiVersion: string) =>
|
||||
KubeObject.create({
|
||||
apiVersion,
|
||||
kind,
|
||||
metadata: {
|
||||
uid: "some-uid",
|
||||
name: "some-name",
|
||||
resourceVersion: "some-resource-version",
|
||||
namespace: "some-namespace",
|
||||
selfLink: "/foo",
|
||||
},
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
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 | null | undefined;
|
||||
}
|
||||
|
||||
export const getKubeObjectMenuItems = ({
|
||||
extensions,
|
||||
kubeObject,
|
||||
}: Dependencies) => {
|
||||
return [
|
||||
...staticMenuItems,
|
||||
...extensions.flatMap((extension) => extension.kubeObjectMenuItems),
|
||||
]
|
||||
.filter(
|
||||
conforms({
|
||||
kind: eq(kubeObject?.kind),
|
||||
apiVersions: includes(kubeObject?.apiVersion),
|
||||
}),
|
||||
)
|
||||
.map((item) => item.components.MenuItem);
|
||||
};
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 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 { 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({
|
||||
id: "kube-object-menu-items",
|
||||
instantiate: (di, { kubeObject }: { kubeObject: KubeObject }) =>
|
||||
getKubeObjectMenuItems({
|
||||
extensions: di.inject(rendererExtensionsInjectable).get(),
|
||||
kubeObject,
|
||||
}),
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
export default kubeObjectMenuItemsInjectable;
|
||||
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* 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";
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { KubeObjectMenuProps } from "./kube-object-menu";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type React from "react";
|
||||
|
||||
export type KubeObjectMenuItemComponent = React.ElementType<
|
||||
KubeObjectMenuProps<KubeObject>
|
||||
>;
|
||||
|
||||
export interface KubeObjectMenuItem {
|
||||
kind: string;
|
||||
apiVersions: string[];
|
||||
enabled: IComputedValue<boolean>;
|
||||
Component: KubeObjectMenuItemComponent;
|
||||
orderNumber: number;
|
||||
}
|
||||
|
||||
export const kubeObjectMenuItemInjectionToken = getInjectionToken<KubeObjectMenuItem>({
|
||||
id: "kube-object-menu-item-injection-token",
|
||||
});
|
||||
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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 { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable";
|
||||
import extensionShouldBeEnabledForClusterFrameInjectable from "../../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
|
||||
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
|
||||
import { computed } from "mobx";
|
||||
|
||||
const kubeObjectMenuItemRegistratorInjectable = getInjectable({
|
||||
id: "kube-object-menu-item-registrator",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getRandomId = di.inject(getRandomIdInjectable);
|
||||
|
||||
const getExtensionShouldBeEnabledForClusterFrame = (
|
||||
extension: LensRendererExtension,
|
||||
) =>
|
||||
di.inject(extensionShouldBeEnabledForClusterFrameInjectable, extension);
|
||||
|
||||
return (ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
|
||||
const extensionShouldBeEnabledForClusterFrame =
|
||||
getExtensionShouldBeEnabledForClusterFrame(extension);
|
||||
|
||||
return extension.kubeObjectMenuItems.map((registration) => {
|
||||
const id = `kube-object-menu-item-registration-from-${
|
||||
extension.sanitizedExtensionId
|
||||
}-${getRandomId()}`;
|
||||
|
||||
return getInjectable({
|
||||
id,
|
||||
|
||||
instantiate: () => ({
|
||||
kind: registration.kind,
|
||||
apiVersions: registration.apiVersions,
|
||||
Component: registration.components.MenuItem,
|
||||
|
||||
enabled: computed(() =>
|
||||
extensionShouldBeEnabledForClusterFrame.value.get(),
|
||||
),
|
||||
|
||||
orderNumber: 100,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default kubeObjectMenuItemRegistratorInjectable;
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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 { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { computed } from "mobx";
|
||||
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
|
||||
import { filter, map, sortBy } from "lodash/fp";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
|
||||
const kubeObjectMenuItemsInjectable = getInjectable({
|
||||
id: "kube-object-menu-items",
|
||||
|
||||
instantiate: (di, kubeObject: KubeObject) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
const menuItems = computedInjectMany(kubeObjectMenuItemInjectionToken);
|
||||
|
||||
return computed(() =>
|
||||
pipeline(
|
||||
menuItems.get(),
|
||||
|
||||
filter(
|
||||
(item) =>
|
||||
item.kind === kubeObject?.kind &&
|
||||
item.apiVersions.includes(kubeObject?.apiVersion) &&
|
||||
item.enabled.get(),
|
||||
),
|
||||
|
||||
sortBy((item) => item.orderNumber),
|
||||
map((item) => item.Component),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, kubeObject: KubeObject) => kubeObject?.getId(),
|
||||
}),
|
||||
});
|
||||
|
||||
export default kubeObjectMenuItemsInjectable;
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { KubeObjectMenuItemComponent } from "../kube-object-menu-item-injection-token";
|
||||
import { kubeObjectMenuItemInjectionToken } from "../kube-object-menu-item-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import { CronJobMenu } from "../../+workloads-cronjobs/cron-job-menu";
|
||||
|
||||
const cronJobMenuInjectable = getInjectable({
|
||||
id: "cron-job-menu-kube-object-menu",
|
||||
|
||||
instantiate: () => ( {
|
||||
kind: "CronJob",
|
||||
apiVersions: ["batch/v1beta1"],
|
||||
Component: CronJobMenu as KubeObjectMenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 20,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default cronJobMenuInjectable;
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { KubeObjectMenuItemComponent } from "../kube-object-menu-item-injection-token";
|
||||
import { kubeObjectMenuItemInjectionToken } from "../kube-object-menu-item-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import { DeploymentMenu } from "../../+workloads-deployments/deployment-menu";
|
||||
|
||||
const deploymentMenuInjectable = getInjectable({
|
||||
id: "deployment-menu-kube-object-menu",
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "Deployment",
|
||||
apiVersions: ["apps/v1"],
|
||||
Component: DeploymentMenu as KubeObjectMenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 30,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default deploymentMenuInjectable;
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { KubeObjectMenuItemComponent } from "../kube-object-menu-item-injection-token";
|
||||
import { kubeObjectMenuItemInjectionToken } from "../kube-object-menu-item-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import { ReplicaSetMenu } from "../../+workloads-replicasets/replica-set-menu";
|
||||
|
||||
const replicaSetMenuInjectable = getInjectable({
|
||||
id: "replica-set-menu-kube-object-menu",
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "ReplicaSet",
|
||||
apiVersions: ["apps/v1"],
|
||||
Component: ReplicaSetMenu as KubeObjectMenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 40,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default replicaSetMenuInjectable;
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 { KubeObjectMenuItemComponent } from "../kube-object-menu-item-injection-token";
|
||||
import { kubeObjectMenuItemInjectionToken } from "../kube-object-menu-item-injection-token";
|
||||
import { ServiceAccountMenu } from "../../+user-management/+service-accounts/service-account-menu";
|
||||
import { computed } from "mobx";
|
||||
|
||||
const serviceAccountMenuInjectable = getInjectable({
|
||||
id: "service-account-menu-kube-object-menu",
|
||||
|
||||
instantiate: () => ({
|
||||
kind: "ServiceAccount",
|
||||
apiVersions: ["v1"],
|
||||
Component: ServiceAccountMenu as KubeObjectMenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 10,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default serviceAccountMenuInjectable;
|
||||
@ -8,12 +8,13 @@ import { screen, waitFor } 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 { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
|
||||
import { computed } from "mobx";
|
||||
import clusterInjectable from "./dependencies/cluster.injectable";
|
||||
import type { DiRender } from "../test-utils/renderFor";
|
||||
import { renderFor } from "../test-utils/renderFor";
|
||||
@ -21,12 +22,9 @@ import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import { KubeObjectMenu } from "./index";
|
||||
import type { KubeObjectMenuRegistration } from "./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";
|
||||
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
|
||||
import { kubeObjectMenuItemInjectionToken } from "./kube-object-menu-item-injection-token";
|
||||
|
||||
// TODO: Make tooltips free of side effects by making it deterministic
|
||||
jest.mock("../tooltip/tooltip");
|
||||
@ -35,59 +33,21 @@ jest.mock("../tooltip/withTooltip", () => ({
|
||||
}));
|
||||
|
||||
// TODO: make `animated={false}` not required to make tests deterministic
|
||||
|
||||
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", engines: { lens: "^5.5.0" }},
|
||||
manifestPath: "irrelevant",
|
||||
});
|
||||
|
||||
this.kubeObjectMenuItems = kubeObjectMenuItems;
|
||||
}
|
||||
}
|
||||
|
||||
describe("kube-object-menu", () => {
|
||||
let di: DiContainer;
|
||||
let render: DiRender;
|
||||
|
||||
beforeEach(() => {
|
||||
const MenuItemComponent = () => <li>Some menu item</li>;
|
||||
const someTestExtension = new SomeTestExtension([
|
||||
{
|
||||
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 },
|
||||
},
|
||||
]);
|
||||
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
di.override(rendererExtensionsInjectable, () =>
|
||||
computed(() => [someTestExtension]),
|
||||
di.register(
|
||||
someMenuItemInjectable,
|
||||
someOtherMenuItemInjectable,
|
||||
someAnotherMenuItemInjectable,
|
||||
);
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
di.override(
|
||||
clusterInjectable,
|
||||
() =>
|
||||
@ -318,3 +278,47 @@ describe("kube-object-menu", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const MenuItemComponent = () => <li>Some menu item</li>;
|
||||
|
||||
const someMenuItemInjectable = getInjectable({
|
||||
id: "some-menu-item",
|
||||
|
||||
instantiate: () => ({
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-kind",
|
||||
Component: MenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 1,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
const someOtherMenuItemInjectable = getInjectable({
|
||||
id: "some-other-menu-item",
|
||||
|
||||
instantiate: () => ({
|
||||
apiVersions: ["some-unrelated-api-version"],
|
||||
kind: "some-kind",
|
||||
Component: MenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 1,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
const someAnotherMenuItemInjectable = getInjectable({
|
||||
id: "some-another-menu-item",
|
||||
|
||||
instantiate: () => ({
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-unrelated-kind",
|
||||
Component: MenuItemComponent,
|
||||
enabled: computed(() => true),
|
||||
orderNumber: 1,
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
@ -13,13 +13,14 @@ import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import clusterNameInjectable from "./dependencies/cluster-name.injectable";
|
||||
import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./kube-object-menu-items.injectable";
|
||||
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
|
||||
import type { HideDetails } from "../kube-detail-params/hide-details.injectable";
|
||||
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
|
||||
import type { OnKubeObjectContextMenuOpen } from "./on-context-menu-open.injectable";
|
||||
import onKubeObjectContextMenuOpenInjectable from "./on-context-menu-open.injectable";
|
||||
import type { KubeObjectContextMenuItem } from "../../kube-object/handler";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observable, runInAction } from "mobx";
|
||||
import type { WithConfirmation } from "../confirm-dialog/with-confirm.injectable";
|
||||
import type { Navigate } from "../../navigation/navigate.injectable";
|
||||
@ -36,7 +37,7 @@ export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends Men
|
||||
|
||||
interface Dependencies {
|
||||
apiManager: ApiManager;
|
||||
kubeObjectMenuItems: React.ElementType[];
|
||||
kubeObjectMenuItems: IComputedValue<React.ElementType[]>;
|
||||
clusterName: string | undefined;
|
||||
hideDetails: HideDetails;
|
||||
createEditResourceTab: (kubeObject: KubeObject) => void;
|
||||
@ -73,7 +74,7 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
|
||||
private renderMenuItems() {
|
||||
const { object, toolbar } = this.props;
|
||||
|
||||
return this.props.kubeObjectMenuItems.map((MenuItem, index) => (
|
||||
return this.props.kubeObjectMenuItems.get().map((MenuItem, index) => (
|
||||
<MenuItem
|
||||
object={object}
|
||||
toolbar={toolbar}
|
||||
@ -206,9 +207,7 @@ export const KubeObjectMenu = withInjectables<Dependencies, KubeObjectMenuProps<
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
createEditResourceTab: di.inject(createEditResourceTabInjectable),
|
||||
hideDetails: di.inject(hideDetailsInjectable),
|
||||
kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, {
|
||||
kubeObject: props.object,
|
||||
}),
|
||||
kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, props.object),
|
||||
onContextMenuOpen: di.inject(onKubeObjectContextMenuOpenInjectable),
|
||||
navigate: di.inject(navigateInjectable),
|
||||
withConfirmation: di.inject(withConfirmationInjectable),
|
||||
|
||||
@ -3,38 +3,29 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
import type { DiRender } from "../test-utils/renderFor";
|
||||
import { renderFor } from "../test-utils/renderFor";
|
||||
import { computed } from "mobx";
|
||||
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import type { KubeObjectStatus } from "../../../common/k8s-api/kube-object-status";
|
||||
import { KubeObjectStatusLevel } from "../../../common/k8s-api/kube-object-status";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { KubeObjectStatusIcon } from "./kube-object-status-icon";
|
||||
import React from "react";
|
||||
import type { KubeObjectStatusRegistration } from "./kube-object-status-registration";
|
||||
import { useFakeTime } from "../../../common/test-utils/use-fake-time";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { kubeObjectStatusTextInjectionToken } from "./kube-object-status-text-injection-token";
|
||||
import { computed } from "mobx";
|
||||
|
||||
describe("kube-object-status-icon", () => {
|
||||
let render: DiRender;
|
||||
let kubeObjectStatusRegistrations: KubeObjectStatusRegistration[];
|
||||
let di: DiContainer;
|
||||
|
||||
beforeEach(() => {
|
||||
// TODO: Make mocking of date in unit tests global
|
||||
global.Date.now = () => new Date("2015-10-21T07:28:00Z").getTime();
|
||||
useFakeTime("2015-10-21T07:28:00Z");
|
||||
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
render = renderFor(di);
|
||||
|
||||
kubeObjectStatusRegistrations = [];
|
||||
|
||||
const someTestExtension = new SomeTestExtension(
|
||||
kubeObjectStatusRegistrations,
|
||||
);
|
||||
|
||||
di.override(rendererExtensionsInjectable, () =>
|
||||
computed(() => [someTestExtension]),
|
||||
);
|
||||
});
|
||||
|
||||
it("given no statuses, when rendered, renders as empty", () => {
|
||||
@ -48,14 +39,14 @@ describe("kube-object-status-icon", () => {
|
||||
it('given level "critical" status, when rendered, renders with status', () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = getStatusRegistration(
|
||||
const statusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.CRITICAL,
|
||||
"critical",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -67,14 +58,14 @@ describe("kube-object-status-icon", () => {
|
||||
it('given level "info" status, when rendered, renders with status', () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = getStatusRegistration(
|
||||
const statusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.INFO,
|
||||
"info",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -86,14 +77,14 @@ describe("kube-object-status-icon", () => {
|
||||
it('given level "warning" status, when rendered, renders with status', () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = getStatusRegistration(
|
||||
const statusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.WARNING,
|
||||
"warning",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -105,30 +96,32 @@ describe("kube-object-status-icon", () => {
|
||||
it("given status for all levels is present, when rendered, renders with statuses", () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const critical = getStatusRegistration(
|
||||
const criticalStatusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.CRITICAL,
|
||||
"critical",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
const warning = getStatusRegistration(
|
||||
const warningStatusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.WARNING,
|
||||
"warning",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
const info = getStatusRegistration(
|
||||
const infoStatusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.INFO,
|
||||
"info",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(critical);
|
||||
kubeObjectStatusRegistrations.push(warning);
|
||||
kubeObjectStatusRegistrations.push(info);
|
||||
di.register(
|
||||
criticalStatusTextInjectable,
|
||||
warningStatusTextInjectable,
|
||||
infoStatusTextInjectable,
|
||||
);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -140,22 +133,21 @@ describe("kube-object-status-icon", () => {
|
||||
it("given info and warning statuses are present, when rendered, renders with statuses", () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const warning = getStatusRegistration(
|
||||
const warningStatusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.WARNING,
|
||||
"warning",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
const info = getStatusRegistration(
|
||||
const infoStatusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.INFO,
|
||||
"info",
|
||||
"some-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(warning);
|
||||
kubeObjectStatusRegistrations.push(info);
|
||||
di.register(warningStatusTextInjectable, infoStatusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -168,14 +160,14 @@ describe("kube-object-status-icon", () => {
|
||||
it("given registration for wrong api version, when rendered, renders as empty", () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = getStatusRegistration(
|
||||
const statusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.CRITICAL,
|
||||
"irrelevant",
|
||||
"some-kind",
|
||||
["some-other-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -187,14 +179,14 @@ describe("kube-object-status-icon", () => {
|
||||
it("given registration for wrong kind, when rendered, renders as empty", () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = getStatusRegistration(
|
||||
const statusTextInjectable = getStatusTextInjectable(
|
||||
KubeObjectStatusLevel.CRITICAL,
|
||||
"irrelevant",
|
||||
"some-other-kind",
|
||||
["some-api-version"],
|
||||
);
|
||||
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
@ -206,21 +198,25 @@ describe("kube-object-status-icon", () => {
|
||||
it("given registration without status for exact kube object, when rendered, renders as empty", () => {
|
||||
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
|
||||
|
||||
const statusRegistration = {
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-kind",
|
||||
resolve: (): void => {},
|
||||
};
|
||||
const statusTextInjectable = getInjectable({
|
||||
id: "some-id",
|
||||
instantiate: () => ({
|
||||
apiVersions: ["some-api-version"],
|
||||
kind: "some-kind",
|
||||
resolve: () => { return undefined as unknown as KubeObjectStatus; },
|
||||
enabled: computed(() => true),
|
||||
}),
|
||||
|
||||
// @ts-ignore
|
||||
kubeObjectStatusRegistrations.push(statusRegistration);
|
||||
injectionToken: kubeObjectStatusTextInjectionToken,
|
||||
});
|
||||
|
||||
di.register(statusTextInjectable);
|
||||
|
||||
const { baseElement } = render(
|
||||
<KubeObjectStatusIcon object={kubeObject} />,
|
||||
);
|
||||
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -236,28 +232,20 @@ const getKubeObjectStub = (kind: string, apiVersion: string) => KubeObject.creat
|
||||
},
|
||||
});
|
||||
|
||||
const getStatusRegistration = (level: KubeObjectStatusLevel, title: string, kind: string, apiVersions: string[]) => ({
|
||||
apiVersions,
|
||||
kind,
|
||||
resolve: (kubeObject: KubeObject) => ({
|
||||
level,
|
||||
text: `Some ${title} status for ${kubeObject.getName()}`,
|
||||
timestamp: "2015-10-19T07:28:00Z",
|
||||
const getStatusTextInjectable = (level: KubeObjectStatusLevel, title: string, kind: string, apiVersions: string[]) => getInjectable({
|
||||
id: title,
|
||||
instantiate: () => ({
|
||||
apiVersions,
|
||||
kind,
|
||||
|
||||
resolve: (kubeObject: KubeObject) => ({
|
||||
level,
|
||||
text: `Some ${title} status for ${kubeObject.getName()}`,
|
||||
timestamp: "2015-10-19T07:28:00Z",
|
||||
}),
|
||||
|
||||
enabled: computed(() => true),
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectStatusTextInjectionToken,
|
||||
});
|
||||
|
||||
class SomeTestExtension extends LensRendererExtension {
|
||||
constructor(kubeObjectStatusTexts: KubeObjectStatusRegistration[]) {
|
||||
super({
|
||||
id: "some-id",
|
||||
absolutePath: "irrelevant",
|
||||
isBundled: false,
|
||||
isCompatible: false,
|
||||
isEnabled: false,
|
||||
manifest: { name: "some-id", version: "some-version", engines: { lens: "^5.5.0" }},
|
||||
manifestPath: "irrelevant",
|
||||
});
|
||||
|
||||
this.kubeObjectStatusTexts = kubeObjectStatusTexts;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,10 +9,12 @@ import React from "react";
|
||||
import { Icon } from "../icon";
|
||||
import { cssNames, formatDuration, getOrInsert } from "../../utils";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import statusesForKubeObjectInjectable from "./statuses-for-kube-object.injectable";
|
||||
import kubeObjectStatusTextsForObjectInjectable from "./kube-object-status-texts-for-object.injectable";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { KubeObjectStatus } from "../../../common/k8s-api/kube-object-status";
|
||||
import { KubeObjectStatusLevel } from "../../../common/k8s-api/kube-object-status";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
function statusClassName(level: KubeObjectStatusLevel): string {
|
||||
switch (level) {
|
||||
@ -76,9 +78,10 @@ export interface KubeObjectStatusIconProps {
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
statuses: KubeObjectStatus[];
|
||||
statuses: IComputedValue<KubeObjectStatus[]>;
|
||||
}
|
||||
|
||||
@observer
|
||||
class NonInjectedKubeObjectStatusIcon extends React.Component<KubeObjectStatusIconProps & Dependencies> {
|
||||
renderStatuses(statuses: KubeObjectStatus[], level: number) {
|
||||
const filteredStatuses = statuses.filter((item) => item.level == level);
|
||||
@ -104,7 +107,7 @@ class NonInjectedKubeObjectStatusIcon extends React.Component<KubeObjectStatusIc
|
||||
}
|
||||
|
||||
render() {
|
||||
const statuses = this.props.statuses;
|
||||
const statuses = this.props.statuses.get();
|
||||
|
||||
if (statuses.length === 0) {
|
||||
return null;
|
||||
@ -135,7 +138,7 @@ export const KubeObjectStatusIcon = withInjectables<Dependencies, KubeObjectStat
|
||||
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
statuses: di.inject(statusesForKubeObjectInjectable, props.object),
|
||||
statuses: di.inject(kubeObjectStatusTextsForObjectInjectable, props.object),
|
||||
...props,
|
||||
}),
|
||||
},
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { KubeObjectStatus } from "../../../common/k8s-api/kube-object-status";
|
||||
import type { IComputedValue } from "mobx";
|
||||
|
||||
export interface KubeObjectStatusText {
|
||||
kind: string;
|
||||
apiVersions: string[];
|
||||
resolve: (object: KubeObject) => KubeObjectStatus;
|
||||
enabled: IComputedValue<boolean>;
|
||||
}
|
||||
|
||||
export const kubeObjectStatusTextInjectionToken =
|
||||
getInjectionToken<KubeObjectStatusText>({
|
||||
id: "kube-object-status-text-injection-token",
|
||||
});
|
||||
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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 { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable";
|
||||
import { kubeObjectStatusTextInjectionToken } from "./kube-object-status-text-injection-token";
|
||||
import extensionShouldBeEnabledForClusterFrameInjectable from "../../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
|
||||
import { computed } from "mobx";
|
||||
|
||||
const kubeObjectStatusTextRegistratorInjectable = getInjectable({
|
||||
id: "kube-object-status-text-registrator",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getRandomId = di.inject(getRandomIdInjectable);
|
||||
|
||||
const getExtensionShouldBeEnabledForClusterFrame = (
|
||||
extension: LensRendererExtension,
|
||||
) =>
|
||||
di.inject(extensionShouldBeEnabledForClusterFrameInjectable, extension);
|
||||
|
||||
return (ext) => {
|
||||
const extension = ext as LensRendererExtension;
|
||||
|
||||
const extensionShouldBeEnabledForClusterFrame =
|
||||
getExtensionShouldBeEnabledForClusterFrame(extension);
|
||||
|
||||
return extension.kubeObjectStatusTexts.map((registration) => {
|
||||
const id = `kube-object-status-text-registration-from-${
|
||||
extension.sanitizedExtensionId
|
||||
}-${getRandomId()}`;
|
||||
|
||||
return getInjectable({
|
||||
id,
|
||||
|
||||
instantiate: () => ({
|
||||
...registration,
|
||||
|
||||
enabled: computed(() =>
|
||||
extensionShouldBeEnabledForClusterFrame.value.get(),
|
||||
),
|
||||
}),
|
||||
|
||||
injectionToken: kubeObjectStatusTextInjectionToken,
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
export default kubeObjectStatusTextRegistratorInjectable;
|
||||
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 kubeObjectStatusTextsInjectable from "./kube-object-status-texts.injectable";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { conforms, eq, includes } from "lodash/fp";
|
||||
import type { KubeObjectStatusRegistration } from "./kube-object-status-registration";
|
||||
import { computed } from "mobx";
|
||||
|
||||
const kubeObjectStatusTextsForObjectInjectable = getInjectable({
|
||||
id: "kube-object-status-texts-for-object",
|
||||
|
||||
instantiate: (di, kubeObject: KubeObject) => {
|
||||
const allStatusTexts = di.inject(kubeObjectStatusTextsInjectable);
|
||||
|
||||
return computed(() =>
|
||||
allStatusTexts
|
||||
.get()
|
||||
.filter(toKubeObjectRelated(kubeObject))
|
||||
.map(toStatus(kubeObject))
|
||||
.filter(Boolean),
|
||||
);
|
||||
},
|
||||
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, kubeObject: KubeObject) => kubeObject.getId(),
|
||||
}),
|
||||
});
|
||||
|
||||
const toKubeObjectRelated = (kubeObject: KubeObject) =>
|
||||
conforms({
|
||||
kind: eq(kubeObject.kind),
|
||||
apiVersions: includes(kubeObject.apiVersion),
|
||||
});
|
||||
|
||||
const toStatus =
|
||||
(kubeObject: KubeObject) => (item: KubeObjectStatusRegistration) =>
|
||||
item.resolve(kubeObject);
|
||||
|
||||
export default kubeObjectStatusTextsForObjectInjectable;
|
||||
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import { computed } from "mobx";
|
||||
import { kubeObjectStatusTextInjectionToken } from "./kube-object-status-text-injection-token";
|
||||
|
||||
const kubeObjectStatusTextsInjectable = getInjectable({
|
||||
id: "kube-object-status-texts",
|
||||
|
||||
instantiate: (di) => {
|
||||
const computedInjectMany = di.inject(computedInjectManyInjectable);
|
||||
const statusTexts = computedInjectMany(kubeObjectStatusTextInjectionToken);
|
||||
|
||||
return computed(() =>
|
||||
statusTexts.get().filter((statusText) => statusText.enabled.get()),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default kubeObjectStatusTextsInjectable;
|
||||
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
|
||||
const statusRegistrationsInjectable = getInjectable({
|
||||
id: "status-registrations",
|
||||
|
||||
instantiate: (di) => {
|
||||
const extensions = di.inject(rendererExtensionsInjectable);
|
||||
|
||||
return computed(() =>
|
||||
extensions.get().flatMap((extension) => extension.kubeObjectStatusTexts),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default statusRegistrationsInjectable;
|
||||
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* 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 statusRegistrationsInjectable from "./status-registrations.injectable";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import { conforms, eq, includes } from "lodash/fp";
|
||||
import type { KubeObjectStatusRegistration } from "./kube-object-status-registration";
|
||||
|
||||
const statusesForKubeObjectInjectable = getInjectable({
|
||||
id: "statuses-for-kube-object",
|
||||
|
||||
instantiate: (di, kubeObject: KubeObject) =>
|
||||
di
|
||||
.inject(statusRegistrationsInjectable)
|
||||
.get()
|
||||
.filter(toKubeObjectRelated(kubeObject))
|
||||
.map(toStatus(kubeObject))
|
||||
.filter(Boolean),
|
||||
|
||||
lifecycle: lifecycleEnum.transient,
|
||||
});
|
||||
|
||||
const toKubeObjectRelated = (kubeObject: KubeObject) =>
|
||||
conforms({
|
||||
kind: eq(kubeObject.kind),
|
||||
apiVersions: includes(kubeObject.apiVersion),
|
||||
});
|
||||
|
||||
const toStatus =
|
||||
(kubeObject: KubeObject) => (item: KubeObjectStatusRegistration) =>
|
||||
item.resolve(kubeObject);
|
||||
|
||||
export default statusesForKubeObjectInjectable;
|
||||
@ -14,6 +14,7 @@ import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
|
||||
import { navigateToRouteInjectionToken } from "../../../common/front-end-routing/navigate-to-route-injection-token";
|
||||
import { getExtensionRoutePath } from "../../routes/for-extension";
|
||||
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import extensionShouldBeEnabledForClusterFrameInjectable from "../../extension-loader/extension-should-be-enabled-for-cluster-frame.injectable";
|
||||
|
||||
const extensionSidebarItemRegistratorInjectable = getInjectable({
|
||||
id: "extension-sidebar-item-registrator",
|
||||
@ -22,6 +23,7 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
|
||||
const extension = ext as LensRendererExtension;
|
||||
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
||||
const routes = di.inject(routesInjectable);
|
||||
const extensionShouldBeEnabledForClusterFrame = di.inject(extensionShouldBeEnabledForClusterFrameInjectable, extension);
|
||||
|
||||
const sidebarItemsForExtensionInjectable = getInjectable({
|
||||
id: `sidebar-items-for-extension-${extension.sanitizedExtensionId}`,
|
||||
@ -41,6 +43,18 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
|
||||
matches({ path: targetRoutePath }),
|
||||
);
|
||||
|
||||
const isVisible = computed(() => {
|
||||
if (!extensionShouldBeEnabledForClusterFrame.value.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!registration.visible) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return registration.visible.get();
|
||||
});
|
||||
|
||||
const res: SidebarItemRegistration = {
|
||||
id: `${extension.sanitizedExtensionId}-${registration.id}`,
|
||||
orderNumber: 9999,
|
||||
@ -49,7 +63,7 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
|
||||
? `${extension.sanitizedExtensionId}-${registration.parentId}`
|
||||
: null,
|
||||
|
||||
...(registration.visible ? { isVisible: registration.visible } : {}),
|
||||
isVisible,
|
||||
|
||||
title: registration.title,
|
||||
getIcon: registration.components.Icon
|
||||
|
||||
Loading…
Reference in New Issue
Block a user