From 052d12fc197f48c479705f9cfa8dc1a10e182f61 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 15 Oct 2021 12:11:46 -0400 Subject: [PATCH] Check object instanceof on all detail panels (#4054) * Check object instanceof on all detail panels Signed-off-by: Sebastian Malton * Fix unit tests - Remove prettier as snapShot testing is too tight of a bound Signed-off-by: Sebastian Malton --- package.json | 1 - .../+catalog/catalog-entity-item.tsx | 8 +- .../components/+catalog/catalog.test.tsx | 112 ++++++------------ .../+config-autoscalers/hpa-details.tsx | 26 ++-- .../limit-range-details.tsx | 20 +++- .../+config-maps/config-map-details.tsx | 42 ++++--- .../pod-disruption-budgets-details.tsx | 14 ++- .../resource-quota-details.tsx | 13 +- .../+config-secrets/secret-details.tsx | 13 +- .../+custom-resources/crd-details.tsx | 14 ++- .../crd-resource-details.tsx | 17 ++- .../components/+events/event-details.tsx | 14 ++- .../components/+events/kube-event-details.tsx | 14 ++- .../+namespaces/namespace-details.tsx | 12 +- .../+network-endpoints/endpoint-details.tsx | 13 +- .../+network-ingresses/ingress-details.tsx | 9 +- .../network-policy-details.tsx | 10 +- .../+network-services/service-details.tsx | 14 ++- .../components/+nodes/node-details.tsx | 12 +- .../pod-security-policy-details.tsx | 35 ++++-- .../storage-class-details.tsx | 16 ++- .../volume-claim-details.tsx | 8 ++ .../+storage-volumes/volume-details.tsx | 8 ++ .../+workloads-cronjobs/cronjob-details.tsx | 24 ++-- .../daemonset-details.tsx | 12 +- .../deployment-details.tsx | 12 +- .../+workloads-jobs/job-details.tsx | 12 +- .../+workloads-pods/pod-details.tsx | 12 +- .../replicaset-details.tsx | 12 +- .../statefulset-details.tsx | 12 +- .../kube-object-meta/kube-object-meta.tsx | 14 ++- yarn.lock | 5 - 32 files changed, 392 insertions(+), 168 deletions(-) diff --git a/package.json b/package.json index 5a452a19d3..bb426467d9 100644 --- a/package.json +++ b/package.json @@ -357,7 +357,6 @@ "postcss": "^8.3.6", "postcss-loader": "4.3.0", "postinstall-postinstall": "^2.1.0", - "prettier": "^2.4.1", "progress-bar-webpack-plugin": "^2.1.0", "randomcolor": "^0.6.2", "raw-loader": "^4.0.2", diff --git a/src/renderer/components/+catalog/catalog-entity-item.tsx b/src/renderer/components/+catalog/catalog-entity-item.tsx index b16406758f..1489ef9efc 100644 --- a/src/renderer/components/+catalog/catalog-entity-item.tsx +++ b/src/renderer/components/+catalog/catalog-entity-item.tsx @@ -21,7 +21,7 @@ import styles from "./catalog.module.css"; import React from "react"; import { action, computed } from "mobx"; -import type { CatalogEntity } from "../../api/catalog-entity"; +import { CatalogEntity } from "../../api/catalog-entity"; import type { ItemObject } from "../../../common/item.store"; import { Badge } from "../badge"; import { navigation } from "../../navigation"; @@ -33,7 +33,11 @@ import type { CatalogEntityRegistry } from "../../api/catalog-entity-registry"; const css = makeCss(styles); export class CatalogEntityItem implements ItemObject { - constructor(public entity: T, private registry: CatalogEntityRegistry) {} + constructor(public entity: T, private registry: CatalogEntityRegistry) { + if (!(entity instanceof CatalogEntity)) { + throw Object.assign(new TypeError("CatalogEntityItem cannot wrap a non-CatalogEntity type"), { typeof: typeof entity, prototype: Object.getPrototypeOf(entity) }); + } + } get kind() { return this.entity.kind; diff --git a/src/renderer/components/+catalog/catalog.test.tsx b/src/renderer/components/+catalog/catalog.test.tsx index bb1ce35a5b..417496a038 100644 --- a/src/renderer/components/+catalog/catalog.test.tsx +++ b/src/renderer/components/+catalog/catalog.test.tsx @@ -26,7 +26,7 @@ import { Catalog } from "./catalog"; import { createMemoryHistory } from "history"; import { mockWindow } from "../../../../__mocks__/windowMock"; import { kubernetesClusterCategory } from "../../../common/catalog-entities/kubernetes-cluster"; -import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../../common/catalog"; +import { catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntity, CatalogEntityActionContext, CatalogEntityData } from "../../../common/catalog"; import { CatalogEntityRegistry } from "../../../renderer/api/catalog-entity-registry"; import { CatalogEntityDetailRegistry } from "../../../extensions/registries"; import { CatalogEntityItem } from "./catalog-entity-item"; @@ -46,6 +46,18 @@ jest.mock("@electron/remote", () => { }; }); +class MockCatalogEntity extends CatalogEntity { + public apiVersion = "api"; + public kind = "kind"; + + constructor(data: CatalogEntityData, public onRun: (context: CatalogEntityActionContext) => void | Promise) { + super(data); + } + + public onContextMenuOpen(): void | Promise {} + public onSettingsOpen(): void | Promise {} +} + describe("", () => { const history = createMemoryHistory(); const mockLocation = { @@ -66,30 +78,21 @@ describe("", () => { url: "", }; - const catalogEntityUid = "a_catalogEntity_uid"; - const catalogEntity = { - enabled: true, - apiVersion: "api", - kind: "kind", - metadata: { - uid: catalogEntityUid, - name: "a catalog entity", - labels: { - test: "label", + function createMockCatalogEntity(onRun: (context: CatalogEntityActionContext) => void | Promise) { + return new MockCatalogEntity({ + metadata: { + uid: "a_catalogEntity_uid", + name: "a catalog entity", + labels: { + test: "label", + }, }, - }, - status: { - phase: "", - }, - spec: {}, - }; - const catalogEntityItemMethods = { - getId: () => catalogEntity.metadata.uid, - getName: () => catalogEntity.metadata.name, - onContextMenuOpen: () => {}, - onSettingsOpen: () => {}, - onRun: () => {}, - }; + status: { + phase: "", + }, + spec: {}, + }, onRun); + } beforeEach(() => { CatalogEntityDetailRegistry.createInstance(); @@ -117,11 +120,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest @@ -130,29 +129,8 @@ describe("", () => { catalogEntityRegistry.addOnBeforeRun( (event) => { - expect(event.target).toMatchInlineSnapshot(` - Object { - "apiVersion": "api", - "enabled": true, - "getId": [Function], - "getName": [Function], - "kind": "kind", - "metadata": Object { - "labels": Object { - "test": "label", - }, - "name": "a catalog entity", - "uid": "a_catalogEntity_uid", - }, - "onContextMenuOpen": [Function], - "onRun": [MockFunction], - "onSettingsOpen": [Function], - "spec": Object {}, - "status": Object { - "phase": "", - }, - } - `); + expect(event.target.getId()).toBe("a_catalogEntity_uid"); + expect(event.target.getName()).toBe("a catalog entity"); setTimeout(() => { expect(onRun).toHaveBeenCalled(); @@ -178,11 +156,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest @@ -216,11 +190,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest @@ -255,11 +225,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(() => done()); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest @@ -289,11 +255,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest @@ -330,11 +292,7 @@ describe("", () => { const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); const onRun = jest.fn(); - const catalogEntityItem = new CatalogEntityItem({ - ...catalogEntity, - ...catalogEntityItemMethods, - onRun, - }, catalogEntityRegistry); + const catalogEntityItem = new CatalogEntityItem(createMockCatalogEntity(onRun), catalogEntityRegistry); // mock as if there is a selected item > the detail panel opens jest diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index 4dd544e5b1..3e6c74b9bb 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -33,6 +33,7 @@ import { Table, TableCell, TableHead, TableRow } from "../table"; import { apiManager } from "../../../common/k8s-api/api-manager"; import { KubeObjectMeta } from "../kube-object-meta"; import { getDetailsUrl } from "../kube-detail-params"; +import logger from "../../../common/logger"; export interface HpaDetailsProps extends KubeObjectDetailsProps { } @@ -80,17 +81,13 @@ export class HpaDetails extends React.Component { Current / Target { - hpa.getMetrics().map((metric, index) => { - const name = renderName(metric); - const values = hpa.getMetricValues(metric); - - return ( + hpa.getMetrics() + .map((metric, index) => ( - {name} - {values} + {renderName(metric)} + {hpa.getMetricValues(metric)} - ); - }) + )) } ); @@ -99,7 +96,16 @@ export class HpaDetails extends React.Component { render() { const { object: hpa } = this.props; - if (!hpa) return null; + if (!hpa) { + return null; + } + + if (!(hpa instanceof HorizontalPodAutoscaler)) { + logger.error("[HpaDetails]: passed object that is not an instanceof HorizontalPodAutoscaler", hpa); + + return null; + } + const { scaleTargetRef } = hpa.spec; return ( diff --git a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx index e9acbd0a22..14ecbec7ed 100644 --- a/src/renderer/components/+config-limit-ranges/limit-range-details.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-range-details.tsx @@ -28,6 +28,7 @@ import { LimitPart, LimitRange, LimitRangeItem, Resource } from "../../../common import { KubeObjectMeta } from "../kube-object-meta"; import { DrawerItem } from "../drawer/drawer-item"; import { Badge } from "../badge"; +import logger from "../../../common/logger"; interface Props extends KubeObjectDetailsProps { } @@ -57,15 +58,13 @@ function renderResourceLimits(limit: LimitRangeItem, resource: Resource) { function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) { - return resources.map(resource => + return resources.map(resource => ( { - limits.map(limit => - renderResourceLimits(limit, resource) - ) + limits.map(limit => renderResourceLimits(limit, resource)) } - ); + )); } @observer @@ -73,7 +72,16 @@ export class LimitRangeDetails extends React.Component { render() { const { object: limitRange } = this.props; - if (!limitRange) return null; + if (!limitRange) { + return null; + } + + if (!(limitRange instanceof LimitRange)) { + logger.error("[LimitRangeDetails]: passed object that is not an instanceof LimitRange", limitRange); + + return null; + } + const containerLimits = limitRange.getContainerLimits(); const podLimits = limitRange.getPodLimits(); const pvcLimits = limitRange.getPVCLimits(); diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx index 6150aa1ad9..082a340a88 100644 --- a/src/renderer/components/+config-maps/config-map-details.tsx +++ b/src/renderer/components/+config-maps/config-map-details.tsx @@ -30,8 +30,9 @@ import { Input } from "../input"; import { Button } from "../button"; import { configMapsStore } from "./config-maps.store"; import type { KubeObjectDetailsProps } from "../kube-object-details"; -import type { ConfigMap } from "../../../common/k8s-api/endpoints"; +import { ConfigMap } from "../../../common/k8s-api/endpoints"; import { KubeObjectMeta } from "../kube-object-meta"; +import logger from "../../../common/logger"; interface Props extends KubeObjectDetailsProps { } @@ -82,7 +83,16 @@ export class ConfigMapDetails extends React.Component { render() { const { object: configMap } = this.props; - if (!configMap) return null; + if (!configMap) { + return null; + } + + if (!(configMap instanceof ConfigMap)) { + logger.error("[ConfigMapDetails]: passed object that is not an instanceof ConfigMap", configMap); + + return null; + } + const data = Array.from(this.data.entries()); return ( @@ -93,22 +103,20 @@ export class ConfigMapDetails extends React.Component { <> { - data.map(([name, value]) => { - return ( -
-
{name}
-
- this.data.set(name, v)} - /> -
+ data.map(([name, value]) => ( +
+
{name}
+
+ this.data.set(name, v)} + />
- ); - }) +
+ )) }