Replace KubeObjectStatusRegistry with reactive solution (#4815)

This commit is contained in:
Janne Savolainen authored and GitHub committed 2022-03-03 07:55:57 -05:00
1 parent d72ebac5f3
commit 54b87efd89
12 files changed
+687 -37

No files matched your search

@@ -0,0 +1,332 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kube-object-status-icon given info and warning statuses are present, when rendered, renders with statuses 1`] = `
<body>
<div>
<i
class="Icon KubeObjectStatusIcon warning material focusable"
id="tooltip_target_5"
>
<span
class="icon"
data-icon-name="warning"
>
warning
</span>
<div />
</i>
</div>
<div
class="Tooltip narrow formatter"
>
<div
class="KubeObjectStatusTooltip"
>
<div
class="level warning"
>
<span
class="title"
>
Warning
</span>
<div
class="status msg"
>
-
Some warning status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
<div
class="level info"
>
<span
class="title"
>
Info
</span>
<div
class="status msg"
>
-
Some info status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
</div>
</div>
</body>
`;
exports[`kube-object-status-icon given level "critical" status, when rendered, renders with status 1`] = `
<body>
<div>
<i
class="Icon KubeObjectStatusIcon error material focusable"
id="tooltip_target_1"
>
<span
class="icon"
data-icon-name="error"
>
error
</span>
<div />
</i>
</div>
<div
class="Tooltip narrow formatter"
>
<div
class="KubeObjectStatusTooltip"
>
<div
class="level error"
>
<span
class="title"
>
Critical
</span>
<div
class="status msg"
>
-
Some critical status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
</div>
</div>
</body>
`;
exports[`kube-object-status-icon given level "info" status, when rendered, renders with status 1`] = `
<body>
<div>
<i
class="Icon KubeObjectStatusIcon info material focusable"
id="tooltip_target_2"
>
<span
class="icon"
data-icon-name="info"
>
info
</span>
<div />
</i>
</div>
<div
class="Tooltip narrow formatter"
>
<div
class="KubeObjectStatusTooltip"
>
<div
class="level info"
>
<span
class="title"
>
Info
</span>
<div
class="status msg"
>
-
Some info status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
</div>
</div>
</body>
`;
exports[`kube-object-status-icon given level "warning" status, when rendered, renders with status 1`] = `
<body>
<div>
<i
class="Icon KubeObjectStatusIcon warning material focusable"
id="tooltip_target_3"
>
<span
class="icon"
data-icon-name="warning"
>
warning
</span>
<div />
</i>
</div>
<div
class="Tooltip narrow formatter"
>
<div
class="KubeObjectStatusTooltip"
>
<div
class="level warning"
>
<span
class="title"
>
Warning
</span>
<div
class="status msg"
>
-
Some warning status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
</div>
</div>
</body>
`;
exports[`kube-object-status-icon given no statuses, when rendered, renders as empty 1`] = `<div />`;
exports[`kube-object-status-icon given registration for wrong api version, when rendered, renders as empty 1`] = `
<body>
<div />
</body>
`;
exports[`kube-object-status-icon given registration for wrong kind, when rendered, renders as empty 1`] = `
<body>
<div />
</body>
`;
exports[`kube-object-status-icon given registration without status for exact kube object, when rendered, renders as empty 1`] = `
<body>
<div />
</body>
`;
exports[`kube-object-status-icon given status for all levels is present, when rendered, renders with statuses 1`] = `
<body>
<div>
<i
class="Icon KubeObjectStatusIcon error material focusable"
id="tooltip_target_4"
>
<span
class="icon"
data-icon-name="error"
>
error
</span>
<div />
</i>
</div>
<div
class="Tooltip narrow formatter"
>
<div
class="KubeObjectStatusTooltip"
>
<div
class="level error"
>
<span
class="title"
>
Critical
</span>
<div
class="status msg"
>
-
Some critical status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
<div
class="level warning"
>
<span
class="title"
>
Warning
</span>
<div
class="status msg"
>
-
Some warning status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
<div
class="level info"
>
<span
class="title"
>
Info
</span>
<div
class="status msg"
>
-
Some info status for some-name
<span
class="age"
>
·
2d
</span>
</div>
</div>
</div>
</div>
</body>
`;
@@ -0,0 +1,263 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import { DiRender, renderFor } from "../test-utils/renderFor";
import { computed } from "mobx";
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import { KubeObjectStatusLevel } from "../../../extensions/renderer-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";
describe("kube-object-status-icon", () => {
let render: DiRender;
let kubeObjectStatusRegistrations: KubeObjectStatusRegistration[];
beforeEach(async () => {
// TODO: Make mocking of date in unit tests global
global.Date.now = () => new Date("2015-10-21T07:28:00Z").getTime();
const di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
kubeObjectStatusRegistrations = [];
const someTestExtension = new SomeTestExtension(
kubeObjectStatusRegistrations,
);
di.override(rendererExtensionsInjectable, () =>
computed(() => [someTestExtension]),
);
await di.runSetups();
});
it("given no statuses, when rendered, renders as empty", () => {
const kubeObject = getKubeObjectStub("irrelevant", "irrelevant");
const { container } = render(<KubeObjectStatusIcon object={kubeObject} />);
expect(container).toMatchSnapshot();
});
it('given level "critical" status, when rendered, renders with status', () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const statusRegistration = getStatusRegistration(
KubeObjectStatusLevel.CRITICAL,
"critical",
"some-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it('given level "info" status, when rendered, renders with status', () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const statusRegistration = getStatusRegistration(
KubeObjectStatusLevel.INFO,
"info",
"some-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it('given level "warning" status, when rendered, renders with status', () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const statusRegistration = getStatusRegistration(
KubeObjectStatusLevel.WARNING,
"warning",
"some-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it("given status for all levels is present, when rendered, renders with statuses", () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const critical = getStatusRegistration(
KubeObjectStatusLevel.CRITICAL,
"critical",
"some-kind",
["some-api-version"],
);
const warning = getStatusRegistration(
KubeObjectStatusLevel.WARNING,
"warning",
"some-kind",
["some-api-version"],
);
const info = getStatusRegistration(
KubeObjectStatusLevel.INFO,
"info",
"some-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(critical);
kubeObjectStatusRegistrations.push(warning);
kubeObjectStatusRegistrations.push(info);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it("given info and warning statuses are present, when rendered, renders with statuses", () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const warning = getStatusRegistration(
KubeObjectStatusLevel.WARNING,
"warning",
"some-kind",
["some-api-version"],
);
const info = getStatusRegistration(
KubeObjectStatusLevel.INFO,
"info",
"some-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(warning);
kubeObjectStatusRegistrations.push(info);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it("given registration for wrong api version, when rendered, renders as empty", () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const statusRegistration = getStatusRegistration(
KubeObjectStatusLevel.CRITICAL,
"irrelevant",
"some-kind",
["some-other-api-version"],
);
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
it("given registration for wrong kind, when rendered, renders as empty", () => {
const kubeObject = getKubeObjectStub("some-kind", "some-api-version");
const statusRegistration = getStatusRegistration(
KubeObjectStatusLevel.CRITICAL,
"irrelevant",
"some-other-kind",
["some-api-version"],
);
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
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 => {},
};
// @ts-ignore
kubeObjectStatusRegistrations.push(statusRegistration);
const { baseElement } = render(
<KubeObjectStatusIcon object={kubeObject} />,
);
expect(baseElement).toMatchSnapshot();
});
});
const getKubeObjectStub = (kind: string, apiVersion: string) => KubeObject.create({
apiVersion,
kind,
metadata: {
uid: "some-uid",
name: "some-name",
resourceVersion: "some-resource-version",
namespace: "some-namespace",
},
});
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",
}),
});
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" },
manifestPath: "irrelevant",
});
this.kubeObjectStatusTexts = kubeObjectStatusTexts;
}
}
@@ -9,7 +9,8 @@ import React from "react";
import { Icon } from "../icon";
import { cssNames, formatDuration } from "../../utils";
import { KubeObject, KubeObjectStatus, KubeObjectStatusLevel } from "../../..//extensions/renderer-api/k8s-api";
import { KubeObjectStatusRegistry } from "../../../extensions/registries";
import { withInjectables } from "@ogre-tools/injectable-react";
import statusesForKubeObjectInjectable from "./statuses-for-kube-object.injectable";
function statusClassName(level: KubeObjectStatusLevel): string {
switch (level) {
@@ -68,7 +69,11 @@ export interface KubeObjectStatusIconProps {
object: KubeObject;
}
export class KubeObjectStatusIcon extends React.Component<KubeObjectStatusIconProps> {
interface Dependencies {
statuses: KubeObjectStatus[];
}
class NonInjectedKubeObjectStatusIcon extends React.Component<KubeObjectStatusIconProps & Dependencies> {
renderStatuses(statuses: KubeObjectStatus[], level: number) {
const filteredStatuses = statuses.filter((item) => item.level == level);
@@ -89,7 +94,7 @@ export class KubeObjectStatusIcon extends React.Component<KubeObjectStatusIconPr
}
render() {
const statuses = KubeObjectStatusRegistry.getInstance().getItemsForObject(this.props.object);
const statuses = this.props.statuses;
if (statuses.length === 0) {
return null;
@@ -114,3 +119,14 @@ export class KubeObjectStatusIcon extends React.Component<KubeObjectStatusIconPr
);
}
}
export const KubeObjectStatusIcon = withInjectables<Dependencies, KubeObjectStatusIconProps>(
NonInjectedKubeObjectStatusIcon,
{
getProps: (di, props) => ({
statuses: di.inject(statusesForKubeObjectInjectable, props.object),
...props,
}),
},
);
@@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { KubeObject } from "../../../common/k8s-api/kube-object";
import type { KubeObjectStatus } from "../../../extensions/renderer-api/kube-object-status";
export interface KubeObjectStatusRegistration {
kind: string;
apiVersions: string[];
resolve: (object: KubeObject) => KubeObjectStatus;
}
@@ -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, lifecycleEnum } 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),
);
},
lifecycle: lifecycleEnum.singleton,
});
export default statusRegistrationsInjectable;
@@ -0,0 +1,35 @@
/**
* 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;
-1
View File
@@ -12,6 +12,5 @@ export function initRegistries() {
registries.EntitySettingRegistry.createInstance();
registries.GlobalPageRegistry.createInstance();
registries.KubeObjectDetailRegistry.createInstance();
registries.KubeObjectStatusRegistry.createInstance();
registries.WorkloadsOverviewDetailRegistry.createInstance();
}