diff --git a/src/renderer/api/__tests__/api-manager.test.ts b/src/renderer/api/__tests__/api-manager.test.ts index 8c7beb12c9..83034d20b6 100644 --- a/src/renderer/api/__tests__/api-manager.test.ts +++ b/src/renderer/api/__tests__/api-manager.test.ts @@ -22,8 +22,9 @@ import { ingressStore } from "../../components/+network-ingresses/ingress.store"; import { apiManager } from "../api-manager"; import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; -class TestApi extends KubeApi { +class TestApi extends KubeApi { protected async checkPreferredVersion() { return; @@ -36,6 +37,7 @@ describe("ApiManager", () => { const apiBase = "apis/v1/foo"; const fallbackApiBase = "/apis/extensions/v1beta1/foo"; const kubeApi = new TestApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, diff --git a/src/renderer/api/__tests__/kube-api.test.ts b/src/renderer/api/__tests__/kube-api.test.ts index c7415cba73..1eb0199f89 100644 --- a/src/renderer/api/__tests__/kube-api.test.ts +++ b/src/renderer/api/__tests__/kube-api.test.ts @@ -20,6 +20,7 @@ */ import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; describe("KubeApi", () => { it("uses url from apiBase if apiBase contains the resource", async () => { @@ -29,7 +30,7 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else if (request.url === "/api-kube/apis/extensions/v1beta1") { @@ -38,13 +39,13 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } @@ -53,6 +54,7 @@ describe("KubeApi", () => { const apiBase = "/apis/networking.k8s.io/v1/ingresses"; const fallbackApiBase = "/apis/extensions/v1beta1/ingresses"; const kubeApi = new KubeApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, @@ -68,7 +70,7 @@ describe("KubeApi", () => { if (request.url === "/api-kube/apis/networking.k8s.io/v1") { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } else if (request.url === "/api-kube/apis/extensions/v1beta1") { @@ -76,13 +78,13 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } @@ -91,6 +93,7 @@ describe("KubeApi", () => { const apiBase = "apis/networking.k8s.io/v1/ingresses"; const fallbackApiBase = "/apis/extensions/v1beta1/ingresses"; const kubeApi = new KubeApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index eafcf0d8ee..adfdb42572 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -24,17 +24,18 @@ import type { KubeObjectStore } from "../kube-object.store"; import { action, observable, makeObservable } from "mobx"; import { autoBind } from "../utils"; import { KubeApi, parseKubeApi } from "./kube-api"; +import type { KubeObject } from "./kube-object"; export class ApiManager { - private apis = observable.map(); - private stores = observable.map(); + private apis = observable.map>(); + private stores = observable.map>(); constructor() { makeObservable(this); autoBind(this); } - getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { + getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { if (typeof pathOrCallback === "string") { return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase); } @@ -46,10 +47,10 @@ export class ApiManager { return Array.from(this.apis.values()).find((api) => api.kind === kind && api.apiVersionWithGroup === apiVersion); } - registerApi(apiBase: string, api: KubeApi) { + registerApi(apiBase: string, api: KubeApi) { if (!this.apis.has(apiBase)) { this.stores.forEach((store) => { - if(store.api === api) { + if (store.api === api) { this.stores.set(apiBase, store); } }); @@ -58,13 +59,13 @@ export class ApiManager { } } - protected resolveApi(api: string | KubeApi): KubeApi { - if (typeof api === "string") return this.getApi(api); + protected resolveApi(api: string | KubeApi): KubeApi { + if (typeof api === "string") return this.getApi(api) as KubeApi; return api; } - unregisterApi(api: string | KubeApi) { + unregisterApi(api: string | KubeApi) { if (typeof api === "string") this.apis.delete(api); else { const apis = Array.from(this.apis.entries()); @@ -75,13 +76,13 @@ export class ApiManager { } @action - registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) { + registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) { apis.forEach(api => { this.stores.set(api.apiBase, store); }); } - getStore(api: string | KubeApi): S { + getStore>(api: string | KubeApi): S { return this.stores.get(this.resolveApi(api)?.apiBase) as S; } } diff --git a/src/renderer/api/endpoints/resource-applier.api.ts b/src/renderer/api/endpoints/resource-applier.api.ts index 1756977c95..b10c10f7db 100644 --- a/src/renderer/api/endpoints/resource-applier.api.ts +++ b/src/renderer/api/endpoints/resource-applier.api.ts @@ -30,7 +30,7 @@ export const resourceApplierApi = { "kubectl.kubernetes.io/last-applied-configuration" ], - async update(resource: object | string): Promise { + async update(resource: object | string): Promise { if (typeof resource === "string") { resource = jsYaml.safeLoad(resource); } @@ -48,7 +48,7 @@ export const resourceApplierApi = { } }); - return items.length === 1 ? items[0] : items; + return items[0] as K ?? null; }); } }; diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 21e370a207..ff023f51f2 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -49,7 +49,7 @@ export interface IKubeApiOptions { */ fallbackApiBases?: string[]; - objectConstructor?: IKubeObjectConstructor; + objectConstructor: IKubeObjectConstructor; request?: KubeJsonApi; isNamespaced?: boolean; kind?: string; @@ -115,7 +115,7 @@ export function forCluster(cluster: IKubeApiCluster, kubeC }); } -export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { +export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { if (!object.metadata.selfLink) { object.metadata.selfLink = createKubeApiURL({ apiPrefix: api.apiPrefix, @@ -127,7 +127,7 @@ export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { } } -export type KubeApiWatchCallback = (data: IKubeWatchEvent, error: any) => void; +export type KubeApiWatchCallback = (data: IKubeWatchEvent, error: any) => void; export type KubeApiWatchOptions = { namespace: string; @@ -135,7 +135,7 @@ export type KubeApiWatchOptions = { abortController?: AbortController }; -export class KubeApi { +export class KubeApi { readonly kind: string; readonly apiBase: string; readonly apiPrefix: string; @@ -152,7 +152,7 @@ export class KubeApi { constructor(protected options: IKubeApiOptions) { const { - objectConstructor = KubeObject as IKubeObjectConstructor, + objectConstructor, request = apiKube, kind = options.objectConstructor?.kind, isNamespaced = options.objectConstructor?.namespaced @@ -456,14 +456,14 @@ export class KubeApi { clearTimeout(timedRetry); timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry - this.watch({...opts, namespace, callback}); + this.watch({ ...opts, namespace, callback }); }, 1000); }); }); byline(nodeStream).on("data", (line) => { try { - const event: IKubeWatchEvent = JSON.parse(line); + const event: IKubeWatchEvent = JSON.parse(line); if (event.type === "ERROR" && event.object.kind === "Status") { errorReceived = true; @@ -474,7 +474,7 @@ export class KubeApi { this.modifyWatchEvent(event); callback(event, null); } catch (ignore) { - // ignore parse errors + // ignore parse errors } }); }) @@ -487,7 +487,7 @@ export class KubeApi { return abort; } - protected modifyWatchEvent(event: IKubeWatchEvent) { + protected modifyWatchEvent(event: IKubeWatchEvent) { switch (event.type) { case "ADDED": diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts index b72beb9383..ffba489651 100644 --- a/src/renderer/api/kube-object.ts +++ b/src/renderer/api/kube-object.ts @@ -30,7 +30,7 @@ import type { JsonApiParams } from "./json-api"; import { resourceApplierApi } from "./endpoints/resource-applier.api"; import { hasOptionalProperty, hasTypedProperty, isObject, isString, bindPredicate, isTypedArray, isRecord } from "../../common/utils/type-narrowing"; -export type IKubeObjectConstructor = (new (data: KubeJsonApiData | any) => T) & { +export type IKubeObjectConstructor = (new (data: KubeJsonApiData | any) => K) & { kind?: string; namespaced?: boolean; apiBase?: string; @@ -266,8 +266,8 @@ export class KubeObject(data: Partial): Promise { - return resourceApplierApi.update({ + async update(data: Partial): Promise { + return resourceApplierApi.update({ ...this.toPlainObject(), ...data, }); diff --git a/src/renderer/api/kube-watch-api.ts b/src/renderer/api/kube-watch-api.ts index 1a0f7e0b17..4d316b5caa 100644 --- a/src/renderer/api/kube-watch-api.ts +++ b/src/renderer/api/kube-watch-api.ts @@ -31,8 +31,9 @@ import { autoBind, Disposer, noop } from "../utils"; import type { KubeApi } from "./kube-api"; import type { KubeJsonApiData } from "./kube-json-api"; import { isDebugging, isProduction } from "../../common/vars"; +import type { KubeObject } from "./kube-object"; -export interface IKubeWatchEvent { +export interface IKubeWatchEvent { type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR"; object?: T; } @@ -58,11 +59,11 @@ export class KubeWatchApi { autoBind(this); } - isAllowedApi(api: KubeApi): boolean { + isAllowedApi(api: KubeApi): boolean { return Boolean(this.context?.cluster.isAllowedResource(api.kind)); } - preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) { + preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) { const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages const preloading: Promise[] = []; @@ -80,7 +81,7 @@ export class KubeWatchApi { }; } - subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer { + subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer { const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts; const subscribingNamespaces = opts.namespaces ?? this.context?.allNamespaces ?? []; const unsubscribeList: Function[] = []; diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index d8cb43a98d..0926130cb5 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -83,14 +83,14 @@ export class HelmCharts extends Component { store={helmChartStore} isSelectable={false} sortingCallbacks={{ - [columnId.name]: (chart: HelmChart) => chart.getName(), - [columnId.repo]: (chart: HelmChart) => chart.getRepository(), + [columnId.name]: chart => chart.getName(), + [columnId.repo]: chart => chart.getRepository(), }} searchFilters={[ - (chart: HelmChart) => chart.getName(), - (chart: HelmChart) => chart.getVersion(), - (chart: HelmChart) => chart.getAppVersion(), - (chart: HelmChart) => chart.getKeywords(), + chart => chart.getName(), + chart => chart.getVersion(), + chart => chart.getAppVersion(), + chart => chart.getKeywords(), ]} customizeHeader={() => ( @@ -103,7 +103,7 @@ export class HelmCharts extends Component { { title: "App Version", className: "app-version", id: columnId.appVersion }, { title: "Repository", className: "repository", sortBy: columnId.repo, id: columnId.repo }, ]} - renderTableContents={(chart: HelmChart) => [ + renderTableContents={chart => [
{ store={releaseStore} dependentStores={[secretsStore]} sortingCallbacks={{ - [columnId.name]: (release: HelmRelease) => release.getName(), - [columnId.namespace]: (release: HelmRelease) => release.getNs(), - [columnId.revision]: (release: HelmRelease) => release.getRevision(), - [columnId.chart]: (release: HelmRelease) => release.getChart(), - [columnId.status]: (release: HelmRelease) => release.getStatus(), - [columnId.updated]: (release: HelmRelease) => release.getUpdated(false, false), + [columnId.name]: release => release.getName(), + [columnId.namespace]: release => release.getNs(), + [columnId.revision]: release => release.getRevision(), + [columnId.chart]: release => release.getChart(), + [columnId.status]: release => release.getStatus(), + [columnId.updated]: release => release.getUpdated(false, false), }} searchFilters={[ - (release: HelmRelease) => release.getName(), - (release: HelmRelease) => release.getNs(), - (release: HelmRelease) => release.getChart(), - (release: HelmRelease) => release.getStatus(), - (release: HelmRelease) => release.getVersion(), + release => release.getName(), + release => release.getNs(), + release => release.getChart(), + release => release.getStatus(), + release => release.getVersion(), ]} renderHeaderTitle="Releases" customizeHeader={({ filters, ...headerPlaceholders }) => ({ @@ -137,7 +137,7 @@ export class HelmReleases extends Component { { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, { title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated }, ]} - renderTableContents={(release: HelmRelease) => [ + renderTableContents={release => [ release.getName(), release.getNs(), release.getChart(), @@ -147,13 +147,13 @@ export class HelmReleases extends Component { { title: release.getStatus(), className: kebabCase(release.getStatus()) }, release.getUpdated(), ]} - renderItemMenu={(release: HelmRelease) => ( + renderItemMenu={release => ( )} - customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({ + customizeRemoveDialog={selectedItems => ({ message: this.renderRemoveDialogMessage(selectedItems) })} detailsItem={this.selectedRelease} diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index c2d180d2e2..7e676c37d4 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -55,7 +55,7 @@ enum sortBy { status = "status" } -interface Props extends RouteComponentProps {} +interface Props extends RouteComponentProps { } @observer export class Catalog extends React.Component { @@ -182,7 +182,7 @@ export class Catalog extends React.Component { )) } - this.addToHotbar(item) }> + this.addToHotbar(item)}> Pin to Hotbar @@ -211,12 +211,12 @@ export class Catalog extends React.Component { store={this.catalogEntityStore} tableId="catalog-items" sortingCallbacks={{ - [sortBy.name]: (item: CatalogEntityItem) => item.name, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, + [sortBy.name]: entity => entity.name, + [sortBy.source]: entity => entity.source, + [sortBy.status]: entity => entity.phase, }} searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, + entity => entity.searchFields, ]} renderTableHeader={[ { title: "", className: styles.iconCell }, @@ -225,14 +225,14 @@ export class Catalog extends React.Component { { title: "Labels", className: styles.labelsCell }, { title: "Status", className: styles.statusCell, sortBy: sortBy.status }, ]} - renderTableContents={(item: CatalogEntityItem) => [ - this.renderIcon(item), - item.name, - item.source, - item.labels.map((label) => ), - { title: item.phase, className: kebabCase(item.phase) } + renderTableContents={entity => [ + this.renderIcon(entity), + entity.name, + entity.source, + entity.labels.map((label) => ), + { title: entity.phase, className: kebabCase(entity.phase) } ]} - onDetails={(item: CatalogEntityItem) => this.onDetails(item) } + onDetails={entity => this.onDetails(entity) } renderItemMenu={this.renderItemMenu} /> ); @@ -248,13 +248,13 @@ export class Catalog extends React.Component { store={this.catalogEntityStore} tableId="catalog-items" sortingCallbacks={{ - [sortBy.name]: (item: CatalogEntityItem) => item.name, - [sortBy.kind]: (item: CatalogEntityItem) => item.kind, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, + [sortBy.name]: entity => entity.name, + [sortBy.kind]: entity => entity.kind, + [sortBy.source]: entity => entity.source, + [sortBy.status]: entity => entity.phase, }} searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, + entity => entity.searchFields, ]} renderTableHeader={[ { title: "", className: styles.iconCell }, @@ -263,16 +263,16 @@ export class Catalog extends React.Component { { title: "Labels", className: styles.labelsCell }, { title: "Status", className: styles.statusCell, sortBy: sortBy.status }, ]} - renderTableContents={(item: CatalogEntityItem) => [ - this.renderIcon(item), - item.name, - item.kind, - item.source, - item.labels.map((label) => ), - { title: item.phase, className: kebabCase(item.phase) } + renderTableContents={entity => [ + this.renderIcon(entity), + entity.name, + entity.kind, + entity.source, + entity.labels.map((label) => ), + { title: entity.phase, className: kebabCase(entity.phase) } ]} detailsItem={this.selectedItem} - onDetails={(item: CatalogEntityItem) => this.onDetails(item) } + onDetails={entity => this.onDetails(entity) } renderItemMenu={this.renderItemMenu} /> ); diff --git a/src/renderer/components/+config-autoscalers/hpa.tsx b/src/renderer/components/+config-autoscalers/hpa.tsx index 54eb0b67a8..e418a133f6 100644 --- a/src/renderer/components/+config-autoscalers/hpa.tsx +++ b/src/renderer/components/+config-autoscalers/hpa.tsx @@ -64,15 +64,15 @@ export class HorizontalPodAutoscalers extends React.Component { tableId="configuration_hpa" className="HorizontalPodAutoscalers" store={hpaStore} sortingCallbacks={{ - [columnId.name]: (item: HorizontalPodAutoscaler) => item.getName(), - [columnId.namespace]: (item: HorizontalPodAutoscaler) => item.getNs(), - [columnId.minPods]: (item: HorizontalPodAutoscaler) => item.getMinPods(), - [columnId.maxPods]: (item: HorizontalPodAutoscaler) => item.getMaxPods(), - [columnId.replicas]: (item: HorizontalPodAutoscaler) => item.getReplicas(), - [columnId.age]: (item: HorizontalPodAutoscaler) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.minPods]: item => item.getMinPods(), + [columnId.maxPods]: item => item.getMaxPods(), + [columnId.replicas]: item => item.getReplicas(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: HorizontalPodAutoscaler) => item.getSearchFields() + item => item.getSearchFields() ]} renderHeaderTitle="Horizontal Pod Autoscalers" renderTableHeader={[ @@ -86,7 +86,7 @@ export class HorizontalPodAutoscalers extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", id: columnId.status }, ]} - renderTableContents={(hpa: HorizontalPodAutoscaler) => [ + renderTableContents={hpa => [ hpa.getName(), , hpa.getNs(), diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx b/src/renderer/components/+config-limit-ranges/limit-ranges.tsx index 6784e6f7d6..054b5363f0 100644 --- a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-ranges.tsx @@ -28,7 +28,6 @@ import { limitRangeStore } from "./limit-ranges.store"; import type { LimitRangeRouteParams } from "./limit-ranges.route"; import React from "react"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { LimitRange } from "../../api/endpoints/limit-range.api"; enum columnId { name = "name", @@ -49,22 +48,22 @@ export class LimitRanges extends React.Component { className="LimitRanges" store={limitRangeStore} sortingCallbacks={{ - [columnId.name]: (item: LimitRange) => item.getName(), - [columnId.namespace]: (item: LimitRange) => item.getNs(), - [columnId.age]: (item: LimitRange) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: LimitRange) => item.getName(), - (item: LimitRange) => item.getNs(), + item => item.getName(), + item => item.getNs(), ]} - renderHeaderTitle={"Limit Ranges"} + renderHeaderTitle="Limit Ranges" renderTableHeader={[ { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { className: "warning", showWithColumn: columnId.name }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(limitRange: LimitRange) => [ + renderTableContents={limitRange => [ limitRange.getName(), , limitRange.getNs(), diff --git a/src/renderer/components/+config-maps/config-maps.tsx b/src/renderer/components/+config-maps/config-maps.tsx index b1997fa792..c2ff98e2d2 100644 --- a/src/renderer/components/+config-maps/config-maps.tsx +++ b/src/renderer/components/+config-maps/config-maps.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import { configMapsStore } from "./config-maps.store"; -import type { ConfigMap } from "../../api/endpoints/configmap.api"; import { KubeObjectListLayout } from "../kube-object"; import type { IConfigMapsRouteParams } from "./config-maps.route"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -49,14 +48,14 @@ export class ConfigMaps extends React.Component { tableId="configuration_configmaps" className="ConfigMaps" store={configMapsStore} sortingCallbacks={{ - [columnId.name]: (item: ConfigMap) => item.getName(), - [columnId.namespace]: (item: ConfigMap) => item.getNs(), - [columnId.keys]: (item: ConfigMap) => item.getKeys(), - [columnId.age]: (item: ConfigMap) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.keys]: item => item.getKeys(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: ConfigMap) => item.getSearchFields(), - (item: ConfigMap) => item.getKeys() + item => item.getSearchFields(), + item => item.getKeys() ]} renderHeaderTitle="Config Maps" renderTableHeader={[ @@ -66,7 +65,7 @@ export class ConfigMaps extends React.Component { { title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(configMap: ConfigMap) => [ + renderTableContents={configMap => [ configMap.getName(), , configMap.getNs(), diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx index abc87a6a4d..f662c96ab3 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx @@ -51,16 +51,16 @@ export class PodDisruptionBudgets extends React.Component { className="PodDisruptionBudgets" store={podDisruptionBudgetsStore} sortingCallbacks={{ - [columnId.name]: (pdb: PodDisruptionBudget) => pdb.getName(), - [columnId.namespace]: (pdb: PodDisruptionBudget) => pdb.getNs(), - [columnId.minAvailable]: (pdb: PodDisruptionBudget) => pdb.getMinAvailable(), - [columnId.maxUnavailable]: (pdb: PodDisruptionBudget) => pdb.getMaxUnavailable(), - [columnId.currentHealthy]: (pdb: PodDisruptionBudget) => pdb.getCurrentHealthy(), - [columnId.desiredHealthy]: (pdb: PodDisruptionBudget) => pdb.getDesiredHealthy(), - [columnId.age]: (pdb: PodDisruptionBudget) => pdb.getAge(), + [columnId.name]: pdb => pdb.getName(), + [columnId.namespace]: pdb => pdb.getNs(), + [columnId.minAvailable]: pdb => pdb.getMinAvailable(), + [columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(), + [columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(), + [columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(), + [columnId.age]: pdb => pdb.getAge(), }} searchFilters={[ - (pdb: PodDisruptionBudget) => pdb.getSearchFields(), + pdb => pdb.getSearchFields(), ]} renderHeaderTitle="Pod Disruption Budgets" renderTableHeader={[ @@ -73,7 +73,7 @@ export class PodDisruptionBudgets extends React.Component { { title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(pdb: PodDisruptionBudget) => { + renderTableContents={pdb => { return [ pdb.getName(), , diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx index 59be708d6f..88da84f213 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import { KubeObjectListLayout } from "../kube-object"; -import type { ResourceQuota } from "../../api/endpoints/resource-quota.api"; import { AddQuotaDialog } from "./add-quota-dialog"; import { resourceQuotaStore } from "./resource-quotas.store"; import type { IResourceQuotaRouteParams } from "./resource-quotas.route"; @@ -50,13 +49,13 @@ export class ResourceQuotas extends React.Component { tableId="configuration_quotas" className="ResourceQuotas" store={resourceQuotaStore} sortingCallbacks={{ - [columnId.name]: (item: ResourceQuota) => item.getName(), - [columnId.namespace]: (item: ResourceQuota) => item.getNs(), - [columnId.age]: (item: ResourceQuota) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: ResourceQuota) => item.getSearchFields(), - (item: ResourceQuota) => item.getName(), + item => item.getSearchFields(), + item => item.getName(), ]} renderHeaderTitle="Resource Quotas" renderTableHeader={[ @@ -65,7 +64,7 @@ export class ResourceQuotas extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(resourceQuota: ResourceQuota) => [ + renderTableContents={resourceQuota => [ resourceQuota.getName(), , resourceQuota.getNs(), diff --git a/src/renderer/components/+config-secrets/secrets.tsx b/src/renderer/components/+config-secrets/secrets.tsx index b87c6a51ed..ec276d4e3c 100644 --- a/src/renderer/components/+config-secrets/secrets.tsx +++ b/src/renderer/components/+config-secrets/secrets.tsx @@ -24,7 +24,6 @@ import "./secrets.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; -import type { Secret } from "../../api/endpoints"; import { AddSecretDialog } from "./add-secret-dialog"; import type { ISecretsRouteParams } from "./secrets.route"; import { KubeObjectListLayout } from "../kube-object"; @@ -54,16 +53,16 @@ export class Secrets extends React.Component { tableId="configuration_secrets" className="Secrets" store={secretsStore} sortingCallbacks={{ - [columnId.name]: (item: Secret) => item.getName(), - [columnId.namespace]: (item: Secret) => item.getNs(), - [columnId.labels]: (item: Secret) => item.getLabels(), - [columnId.keys]: (item: Secret) => item.getKeys(), - [columnId.type]: (item: Secret) => item.type, - [columnId.age]: (item: Secret) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.labels]: item => item.getLabels(), + [columnId.keys]: item => item.getKeys(), + [columnId.type]: item => item.type, + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: Secret) => item.getSearchFields(), - (item: Secret) => item.getKeys(), + item => item.getSearchFields(), + item => item.getKeys(), ]} renderHeaderTitle="Secrets" renderTableHeader={[ @@ -75,7 +74,7 @@ export class Secrets extends React.Component { { title: "Type", className: "type", sortBy: columnId.type, id: columnId.type }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(secret: Secret) => [ + renderTableContents={secret => [ secret.getName(), , secret.getNs(), diff --git a/src/renderer/components/+custom-resources/crd-list.tsx b/src/renderer/components/+custom-resources/crd-list.tsx index 5e44782476..60b05a6453 100644 --- a/src/renderer/components/+custom-resources/crd-list.tsx +++ b/src/renderer/components/+custom-resources/crd-list.tsx @@ -32,6 +32,7 @@ import type { CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { Select, SelectOption } from "../select"; import { createPageParam } from "../../navigation"; import { Icon } from "../icon"; +import type { TableSortCallbacks } from "../table"; export const crdGroupsUrlParam = createPageParam({ name: "groups", @@ -78,11 +79,11 @@ export class CrdList extends React.Component { render() { const { items, selectedGroups } = this; - const sortingCallbacks = { - [columnId.kind]: (crd: CustomResourceDefinition) => crd.getResourceKind(), - [columnId.group]: (crd: CustomResourceDefinition) => crd.getGroup(), - [columnId.version]: (crd: CustomResourceDefinition) => crd.getVersion(), - [columnId.scope]: (crd: CustomResourceDefinition) => crd.getScope(), + const sortingCallbacks: TableSortCallbacks = { + [columnId.kind]: crd => crd.getResourceKind(), + [columnId.group]: crd => crd.getGroup(), + [columnId.version]: crd => crd.getVersion(), + [columnId.scope]: crd => crd.getScope(), }; return ( @@ -133,7 +134,7 @@ export class CrdList extends React.Component { { title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(crd: CustomResourceDefinition) => [ + renderTableContents={crd => [ {crd.getResourceTitle()} , diff --git a/src/renderer/components/+custom-resources/crd-resource.store.ts b/src/renderer/components/+custom-resources/crd-resource.store.ts index 8717884785..335a015aa7 100644 --- a/src/renderer/components/+custom-resources/crd-resource.store.ts +++ b/src/renderer/components/+custom-resources/crd-resource.store.ts @@ -23,10 +23,10 @@ import type { KubeApi } from "../../api/kube-api"; import { KubeObjectStore } from "../../kube-object.store"; import type { KubeObject } from "../../api/kube-object"; -export class CRDResourceStore extends KubeObjectStore { - api: KubeApi; +export class CRDResourceStore extends KubeObjectStore { + api: KubeApi; - constructor(api: KubeApi) { + constructor(api: KubeApi) { super(); this.api = api; } diff --git a/src/renderer/components/+custom-resources/crd-resources.tsx b/src/renderer/components/+custom-resources/crd-resources.tsx index b42703aa33..4037494a86 100644 --- a/src/renderer/components/+custom-resources/crd-resources.tsx +++ b/src/renderer/components/+custom-resources/crd-resources.tsx @@ -30,7 +30,7 @@ import type { KubeObject } from "../../api/kube-object"; import type { ICRDRouteParams } from "./crd.route"; import { autorun, computed, makeObservable } from "mobx"; import { crdStore } from "./crd.store"; -import type { TableSortCallback } from "../table"; +import type { TableSortCallbacks } from "../table"; import { apiManager } from "../../api/api-manager"; import { parseJsonPath } from "../../utils/jsonPath"; @@ -80,14 +80,14 @@ export class CrdResources extends React.Component { if (!crd) return null; const isNamespaced = crd.isNamespaced(); const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details - const sortingCallbacks: { [sortBy: string]: TableSortCallback } = { - [columnId.name]: (item: KubeObject) => item.getName(), - [columnId.namespace]: (item: KubeObject) => item.getNs(), - [columnId.age]: (item: KubeObject) => item.getTimeDiffFromNow(), + const sortingCallbacks: TableSortCallbacks = { + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }; extraColumns.forEach(column => { - sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1))); + sortingCallbacks[column.name] = item => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1))); }); return ( @@ -98,7 +98,7 @@ export class CrdResources extends React.Component { store={store} sortingCallbacks={sortingCallbacks} searchFilters={[ - (item: KubeObject) => item.getSearchFields(), + item => item.getSearchFields(), ]} renderHeaderTitle={crd.getResourceTitle()} renderTableHeader={[ @@ -116,7 +116,7 @@ export class CrdResources extends React.Component { }), { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(crdInstance: KubeObject) => [ + renderTableContents={crdInstance => [ crdInstance.getName(), isNamespaced && crdInstance.getNs(), ...extraColumns.map((column) => { diff --git a/src/renderer/components/+custom-resources/crd.store.ts b/src/renderer/components/+custom-resources/crd.store.ts index 613438bca7..25415e5ab3 100644 --- a/src/renderer/components/+custom-resources/crd.store.ts +++ b/src/renderer/components/+custom-resources/crd.store.ts @@ -26,13 +26,18 @@ import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { apiManager } from "../../api/api-manager"; import { KubeApi } from "../../api/kube-api"; import { CRDResourceStore } from "./crd-resource.store"; -import type { KubeObject } from "../../api/kube-object"; +import { KubeObject } from "../../api/kube-object"; function initStore(crd: CustomResourceDefinition) { const apiBase = crd.getResourceApiBase(); const kind = crd.getResourceKind(); const isNamespaced = crd.isNamespaced(); - const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced }); + const api = apiManager.getApi(apiBase) ?? new KubeApi({ + objectConstructor: KubeObject, + apiBase, + kind, + isNamespaced, + }); if (!apiManager.getStore(api)) { apiManager.registerStore(new CRDResourceStore(api)); diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index adf7b2d105..a7512ce62b 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -29,7 +29,7 @@ import { TabLayout } from "../layout/tab-layout"; import { EventStore, eventStore } from "./event.store"; import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object"; import type { KubeEvent } from "../../api/endpoints/events.api"; -import type { TableSortCallbacks, TableSortParams, TableProps } from "../table"; +import type { TableSortCallbacks, TableSortParams } from "../table"; import type { IHeaderPlaceholders } from "../item-object-list"; import { Tooltip } from "../tooltip"; import { Link } from "react-router-dom"; @@ -49,7 +49,7 @@ enum columnId { lastSeen = "last-seen", } -interface Props extends Partial { +interface Props extends Partial> { className?: IClassName; compact?: boolean; compactLimit?: number; @@ -69,19 +69,13 @@ export class Events extends React.Component { orderBy: "asc", }; - private sortingCallbacks: TableSortCallbacks = { - [columnId.namespace]: (event: KubeEvent) => event.getNs(), - [columnId.type]: (event: KubeEvent) => event.type, - [columnId.object]: (event: KubeEvent) => event.involvedObject.name, - [columnId.count]: (event: KubeEvent) => event.count, - [columnId.age]: (event: KubeEvent) => event.getTimeDiffFromNow(), - [columnId.lastSeen]: (event: KubeEvent) => this.now - new Date(event.lastTimestamp).getTime(), - }; - - private tableConfiguration: TableProps = { - sortSyncWithUrl: false, - sortByDefault: this.sorting, - onSort: params => this.sorting = params, + private sortingCallbacks: TableSortCallbacks = { + [columnId.namespace]: event => event.getNs(), + [columnId.type]: event => event.type, + [columnId.object]: event => event.involvedObject.name, + [columnId.count]: event => event.count, + [columnId.age]: event => event.getTimeDiffFromNow(), + [columnId.lastSeen]: event => this.now - new Date(event.lastTimestamp).getTime(), }; constructor(props: Props) { @@ -156,13 +150,17 @@ export class Events extends React.Component { isSelectable={false} items={visibleItems} virtual={!compact} - tableProps={this.tableConfiguration} + tableProps={{ + sortSyncWithUrl: false, + sortByDefault: this.sorting, + onSort: params => this.sorting = params, + }} sortingCallbacks={this.sortingCallbacks} searchFilters={[ - (event: KubeEvent) => event.getSearchFields(), - (event: KubeEvent) => event.message, - (event: KubeEvent) => event.getSource(), - (event: KubeEvent) => event.involvedObject.name, + event => event.getSearchFields(), + event => event.message, + event => event.getSource(), + event => event.involvedObject.name, ]} renderTableHeader={[ { title: "Type", className: "type", sortBy: columnId.type, id: columnId.type }, @@ -174,7 +172,7 @@ export class Events extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Last Seen", className: "last-seen", sortBy: columnId.lastSeen, id: columnId.lastSeen }, ]} - renderTableContents={(event: KubeEvent) => { + renderTableContents={event => { const { involvedObject, type, message } = event; const tooltipId = `message-${event.getId()}`; const isWarning = event.isWarning(); diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index 29098c8a49..18876c4aa6 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -106,7 +106,7 @@ export class NamespaceStore extends KubeObjectStore { return super.getSubscribeApis(); } - protected async loadItems(params: KubeObjectStoreLoadingParams) { + protected async loadItems(params: KubeObjectStoreLoadingParams) { const { allowedNamespaces } = this; let namespaces = (await super.loadItems(params)) || []; diff --git a/src/renderer/components/+namespaces/namespaces.tsx b/src/renderer/components/+namespaces/namespaces.tsx index 60ecc2f3e5..86bd0080a7 100644 --- a/src/renderer/components/+namespaces/namespaces.tsx +++ b/src/renderer/components/+namespaces/namespaces.tsx @@ -22,7 +22,7 @@ import "./namespaces.scss"; import React from "react"; -import { Namespace, NamespaceStatus } from "../../api/endpoints"; +import { NamespaceStatus } from "../../api/endpoints"; import { AddNamespaceDialog } from "./add-namespace-dialog"; import { TabLayout } from "../layout/tab-layout"; import { Badge } from "../badge"; @@ -51,14 +51,14 @@ export class Namespaces extends React.Component { tableId="namespaces" className="Namespaces" store={namespaceStore} sortingCallbacks={{ - [columnId.name]: (ns: Namespace) => ns.getName(), - [columnId.labels]: (ns: Namespace) => ns.getLabels(), - [columnId.age]: (ns: Namespace) => ns.getTimeDiffFromNow(), - [columnId.status]: (ns: Namespace) => ns.getStatus(), + [columnId.name]: ns => ns.getName(), + [columnId.labels]: ns => ns.getLabels(), + [columnId.age]: ns => ns.getTimeDiffFromNow(), + [columnId.status]: ns => ns.getStatus(), }} searchFilters={[ - (item: Namespace) => item.getSearchFields(), - (item: Namespace) => item.getStatus() + item => item.getSearchFields(), + item => item.getStatus() ]} renderHeaderTitle="Namespaces" renderTableHeader={[ @@ -68,7 +68,7 @@ export class Namespaces extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(item: Namespace) => [ + renderTableContents={item => [ item.getName(), , item.getLabels().map(label => ), @@ -79,7 +79,7 @@ export class Namespaces extends React.Component { addTooltip: "Add Namespace", onAdd: () => AddNamespaceDialog.open(), }} - customizeTableRowProps={(item: Namespace) => ({ + customizeTableRowProps={item => ({ disabled: item.getStatus() === NamespaceStatus.TERMINATING, })} /> diff --git a/src/renderer/components/+network-endpoints/endpoints.tsx b/src/renderer/components/+network-endpoints/endpoints.tsx index 7cc74427fa..0d9a7e125c 100644 --- a/src/renderer/components/+network-endpoints/endpoints.tsx +++ b/src/renderer/components/+network-endpoints/endpoints.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; import type { EndpointRouteParams } from "./endpoints.route"; -import type { Endpoint } from "../../api/endpoints/endpoint.api"; import { endpointStore } from "./endpoints.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -49,12 +48,12 @@ export class Endpoints extends React.Component { tableId="network_endpoints" className="Endpoints" store={endpointStore} sortingCallbacks={{ - [columnId.name]: (endpoint: Endpoint) => endpoint.getName(), - [columnId.namespace]: (endpoint: Endpoint) => endpoint.getNs(), - [columnId.age]: (endpoint: Endpoint) => endpoint.getTimeDiffFromNow(), + [columnId.name]: endpoint => endpoint.getName(), + [columnId.namespace]: endpoint => endpoint.getNs(), + [columnId.age]: endpoint => endpoint.getTimeDiffFromNow(), }} searchFilters={[ - (endpoint: Endpoint) => endpoint.getSearchFields() + endpoint => endpoint.getSearchFields() ]} renderHeaderTitle="Endpoints" renderTableHeader={[ @@ -64,7 +63,7 @@ export class Endpoints extends React.Component { { title: "Endpoints", className: "endpoints", id: columnId.endpoints }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(endpoint: Endpoint) => [ + renderTableContents={endpoint => [ endpoint.getName(), , endpoint.getNs(), @@ -72,7 +71,7 @@ export class Endpoints extends React.Component { endpoint.getAge(), ]} tableProps={{ - customRowHeights: (item: Endpoint, lineHeight, paddings) => { + customRowHeights: (item, lineHeight, paddings) => { const lines = item.getEndpointSubsets().length || 1; return lines * lineHeight + paddings; diff --git a/src/renderer/components/+network-ingresses/ingresses.tsx b/src/renderer/components/+network-ingresses/ingresses.tsx index 1ee39d4010..7b0cc9f608 100644 --- a/src/renderer/components/+network-ingresses/ingresses.tsx +++ b/src/renderer/components/+network-ingresses/ingresses.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; import type { IngressRouteParams } from "./ingresses.route"; -import type { Ingress } from "../../api/endpoints/ingress.api"; import { ingressStore } from "./ingress.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -50,13 +49,13 @@ export class Ingresses extends React.Component { tableId="network_ingresses" className="Ingresses" store={ingressStore} sortingCallbacks={{ - [columnId.name]: (ingress: Ingress) => ingress.getName(), - [columnId.namespace]: (ingress: Ingress) => ingress.getNs(), - [columnId.age]: (ingress: Ingress) => ingress.getTimeDiffFromNow(), + [columnId.name]: ingress => ingress.getName(), + [columnId.namespace]: ingress => ingress.getNs(), + [columnId.age]: ingress => ingress.getTimeDiffFromNow(), }} searchFilters={[ - (ingress: Ingress) => ingress.getSearchFields(), - (ingress: Ingress) => ingress.getPorts(), + ingress => ingress.getSearchFields(), + ingress => ingress.getPorts(), ]} renderHeaderTitle="Ingresses" renderTableHeader={[ @@ -67,7 +66,7 @@ export class Ingresses extends React.Component { { title: "Rules", className: "rules", id: columnId.rules }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(ingress: Ingress) => [ + renderTableContents={ingress => [ ingress.getName(), , ingress.getNs(), @@ -76,7 +75,7 @@ export class Ingresses extends React.Component { ingress.getAge(), ]} tableProps={{ - customRowHeights: (item: Ingress, lineHeight, paddings) => { + customRowHeights: (item, lineHeight, paddings) => { const lines = item.getRoutes().length || 1; return lines * lineHeight + paddings; diff --git a/src/renderer/components/+network-policies/network-policies.tsx b/src/renderer/components/+network-policies/network-policies.tsx index 1010c07175..e10430af1e 100644 --- a/src/renderer/components/+network-policies/network-policies.tsx +++ b/src/renderer/components/+network-policies/network-policies.tsx @@ -24,7 +24,6 @@ import "./network-policies.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; -import type { NetworkPolicy } from "../../api/endpoints/network-policy.api"; import { KubeObjectListLayout } from "../kube-object"; import type { INetworkPoliciesRouteParams } from "./network-policies.route"; import { networkPolicyStore } from "./network-policy.store"; @@ -49,12 +48,12 @@ export class NetworkPolicies extends React.Component { tableId="network_policies" className="NetworkPolicies" store={networkPolicyStore} sortingCallbacks={{ - [columnId.name]: (item: NetworkPolicy) => item.getName(), - [columnId.namespace]: (item: NetworkPolicy) => item.getNs(), - [columnId.age]: (item: NetworkPolicy) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: NetworkPolicy) => item.getSearchFields(), + item => item.getSearchFields(), ]} renderHeaderTitle="Network Policies" renderTableHeader={[ @@ -64,7 +63,7 @@ export class NetworkPolicies extends React.Component { { title: "Policy Types", className: "type", id: columnId.types }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(item: NetworkPolicy) => [ + renderTableContents={item => [ item.getName(), , item.getNs(), diff --git a/src/renderer/components/+network-services/services.tsx b/src/renderer/components/+network-services/services.tsx index 33f9b73e08..9260d4005d 100644 --- a/src/renderer/components/+network-services/services.tsx +++ b/src/renderer/components/+network-services/services.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import type { IServicesRouteParams } from "./services.route"; -import type { Service } from "../../api/endpoints/service.api"; import { KubeObjectListLayout } from "../kube-object"; import { Badge } from "../badge"; import { serviceStore } from "./services.store"; @@ -55,19 +54,19 @@ export class Services extends React.Component { tableId="network_services" className="Services" store={serviceStore} sortingCallbacks={{ - [columnId.name]: (service: Service) => service.getName(), - [columnId.namespace]: (service: Service) => service.getNs(), - [columnId.selector]: (service: Service) => service.getSelector(), - [columnId.ports]: (service: Service) => (service.spec.ports || []).map(({ port }) => port)[0], - [columnId.clusterIp]: (service: Service) => service.getClusterIp(), - [columnId.type]: (service: Service) => service.getType(), - [columnId.age]: (service: Service) => service.getTimeDiffFromNow(), - [columnId.status]: (service: Service) => service.getStatus(), + [columnId.name]: service => service.getName(), + [columnId.namespace]: service => service.getNs(), + [columnId.selector]: service => service.getSelector(), + [columnId.ports]: service => (service.spec.ports || []).map(({ port }) => port)[0], + [columnId.clusterIp]: service => service.getClusterIp(), + [columnId.type]: service => service.getType(), + [columnId.age]: service => service.getTimeDiffFromNow(), + [columnId.status]: service => service.getStatus(), }} searchFilters={[ - (service: Service) => service.getSearchFields(), - (service: Service) => service.getSelector().join(" "), - (service: Service) => service.getPorts().join(" "), + service => service.getSearchFields(), + service => service.getSelector().join(" "), + service => service.getPorts().join(" "), ]} renderHeaderTitle="Services" renderTableHeader={[ @@ -82,7 +81,7 @@ export class Services extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(service: Service) => [ + renderTableContents={service => [ service.getName(), , service.getNs(), diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index 834ae80a0e..8ef5f027f7 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -171,21 +171,21 @@ export class Nodes extends React.Component { dependentStores={[podsStore]} isSelectable={false} sortingCallbacks={{ - [columnId.name]: (node: Node) => node.getName(), - [columnId.cpu]: (node: Node) => nodesStore.getLastMetricValues(node, ["cpuUsage"]), - [columnId.memory]: (node: Node) => nodesStore.getLastMetricValues(node, ["memoryUsage"]), - [columnId.disk]: (node: Node) => nodesStore.getLastMetricValues(node, ["fsUsage"]), - [columnId.conditions]: (node: Node) => node.getNodeConditionText(), - [columnId.taints]: (node: Node) => node.getTaints().length, - [columnId.roles]: (node: Node) => node.getRoleLabels(), - [columnId.age]: (node: Node) => node.getTimeDiffFromNow(), - [columnId.version]: (node: Node) => node.getKubeletVersion(), + [columnId.name]: node => node.getName(), + [columnId.cpu]: node => nodesStore.getLastMetricValues(node, ["cpuUsage"]), + [columnId.memory]: node => nodesStore.getLastMetricValues(node, ["memoryUsage"]), + [columnId.disk]: node => nodesStore.getLastMetricValues(node, ["fsUsage"]), + [columnId.conditions]: node => node.getNodeConditionText(), + [columnId.taints]: node => node.getTaints().length, + [columnId.roles]: node => node.getRoleLabels(), + [columnId.age]: node => node.getTimeDiffFromNow(), + [columnId.version]: node => node.getKubeletVersion(), }} searchFilters={[ - (node: Node) => node.getSearchFields(), - (node: Node) => node.getRoleLabels(), - (node: Node) => node.getKubeletVersion(), - (node: Node) => node.getNodeConditionText(), + node => node.getSearchFields(), + node => node.getRoleLabels(), + node => node.getKubeletVersion(), + node => node.getNodeConditionText(), ]} renderHeaderTitle="Nodes" renderTableHeader={[ @@ -200,7 +200,7 @@ export class Nodes extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} - renderTableContents={(node: Node) => { + renderTableContents={node => { const tooltipId = `node-taints-${node.getId()}`; return [ diff --git a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx index a74a5e8623..c578bf8e34 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import { KubeObjectListLayout } from "../kube-object"; import { podSecurityPoliciesStore } from "./pod-security-policies.store"; -import type { PodSecurityPolicy } from "../../api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; enum columnId { @@ -45,15 +44,15 @@ export class PodSecurityPolicies extends React.Component { className="PodSecurityPolicies" store={podSecurityPoliciesStore} sortingCallbacks={{ - [columnId.name]: (item: PodSecurityPolicy) => item.getName(), - [columnId.volumes]: (item: PodSecurityPolicy) => item.getVolumes(), - [columnId.privileged]: (item: PodSecurityPolicy) => +item.isPrivileged(), - [columnId.age]: (item: PodSecurityPolicy) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.volumes]: item => item.getVolumes(), + [columnId.privileged]: item => +item.isPrivileged(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: PodSecurityPolicy) => item.getSearchFields(), - (item: PodSecurityPolicy) => item.getVolumes(), - (item: PodSecurityPolicy) => Object.values(item.getRules()), + item => item.getSearchFields(), + item => item.getVolumes(), + item => Object.values(item.getRules()), ]} renderHeaderTitle="Pod Security Policies" renderTableHeader={[ @@ -63,7 +62,7 @@ export class PodSecurityPolicies extends React.Component { { title: "Volumes", className: "volumes", sortBy: columnId.volumes, id: columnId.volumes }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(item: PodSecurityPolicy) => { + renderTableContents={item => { return [ item.getName(), , diff --git a/src/renderer/components/+storage-classes/storage-classes.tsx b/src/renderer/components/+storage-classes/storage-classes.tsx index f052e3e8ef..dc90e48563 100644 --- a/src/renderer/components/+storage-classes/storage-classes.tsx +++ b/src/renderer/components/+storage-classes/storage-classes.tsx @@ -24,7 +24,6 @@ import "./storage-classes.scss"; import React from "react"; import type { RouteComponentProps } from "react-router-dom"; import { observer } from "mobx-react"; -import type { StorageClass } from "../../api/endpoints/storage-class.api"; import { KubeObjectListLayout } from "../kube-object"; import type { IStorageClassesRouteParams } from "./storage-classes.route"; import { storageClassStore } from "./storage-class.store"; @@ -51,14 +50,14 @@ export class StorageClasses extends React.Component { className="StorageClasses" store={storageClassStore} sortingCallbacks={{ - [columnId.name]: (item: StorageClass) => item.getName(), - [columnId.age]: (item: StorageClass) => item.getTimeDiffFromNow(), - [columnId.provisioner]: (item: StorageClass) => item.provisioner, - [columnId.reclaimPolicy]: (item: StorageClass) => item.reclaimPolicy, + [columnId.name]: item => item.getName(), + [columnId.age]: item => item.getTimeDiffFromNow(), + [columnId.provisioner]: item => item.provisioner, + [columnId.reclaimPolicy]: item => item.reclaimPolicy, }} searchFilters={[ - (item: StorageClass) => item.getSearchFields(), - (item: StorageClass) => item.provisioner, + item => item.getSearchFields(), + item => item.provisioner, ]} renderHeaderTitle="Storage Classes" renderTableHeader={[ @@ -69,7 +68,7 @@ export class StorageClasses extends React.Component { { title: "Default", className: "is-default", id: columnId.default }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(storageClass: StorageClass) => [ + renderTableContents={storageClass => [ storageClass.getName(), , storageClass.provisioner, diff --git a/src/renderer/components/+storage-volume-claims/volume-claims.tsx b/src/renderer/components/+storage-volume-claims/volume-claims.tsx index 34309b43ec..8bdf6c3c18 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claims.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claims.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import { Link, RouteComponentProps } from "react-router-dom"; import { volumeClaimStore } from "./volume-claim.store"; -import type { PersistentVolumeClaim } from "../../api/endpoints/persistent-volume-claims.api"; import { podsStore } from "../+workloads-pods/pods.store"; import { getDetailsUrl, KubeObjectListLayout } from "../kube-object"; import type { IVolumeClaimsRouteParams } from "./volume-claims.route"; @@ -58,17 +57,17 @@ export class PersistentVolumeClaims extends React.Component { store={volumeClaimStore} dependentStores={[podsStore]} sortingCallbacks={{ - [columnId.name]: (pvc: PersistentVolumeClaim) => pvc.getName(), - [columnId.namespace]: (pvc: PersistentVolumeClaim) => pvc.getNs(), - [columnId.pods]: (pvc: PersistentVolumeClaim) => pvc.getPods(podsStore.items).map(pod => pod.getName()), - [columnId.status]: (pvc: PersistentVolumeClaim) => pvc.getStatus(), - [columnId.size]: (pvc: PersistentVolumeClaim) => unitsToBytes(pvc.getStorage()), - [columnId.storageClass]: (pvc: PersistentVolumeClaim) => pvc.spec.storageClassName, - [columnId.age]: (pvc: PersistentVolumeClaim) => pvc.getTimeDiffFromNow(), + [columnId.name]: pvc => pvc.getName(), + [columnId.namespace]: pvc => pvc.getNs(), + [columnId.pods]: pvc => pvc.getPods(podsStore.items).map(pod => pod.getName()), + [columnId.status]: pvc => pvc.getStatus(), + [columnId.size]: pvc => unitsToBytes(pvc.getStorage()), + [columnId.storageClass]: pvc => pvc.spec.storageClassName, + [columnId.age]: pvc => pvc.getTimeDiffFromNow(), }} searchFilters={[ - (item: PersistentVolumeClaim) => item.getSearchFields(), - (item: PersistentVolumeClaim) => item.getPods(podsStore.items).map(pod => pod.getName()), + item => item.getSearchFields(), + item => item.getPods(podsStore.items).map(pod => pod.getName()), ]} renderHeaderTitle="Persistent Volume Claims" renderTableHeader={[ @@ -81,7 +80,7 @@ export class PersistentVolumeClaims extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(pvc: PersistentVolumeClaim) => { + renderTableContents={pvc => { const pods = pvc.getPods(podsStore.items); const { storageClassName } = pvc.spec; const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ diff --git a/src/renderer/components/+storage-volumes/volumes.tsx b/src/renderer/components/+storage-volumes/volumes.tsx index 2eb25dbdb3..c9c3c898a4 100644 --- a/src/renderer/components/+storage-volumes/volumes.tsx +++ b/src/renderer/components/+storage-volumes/volumes.tsx @@ -24,7 +24,6 @@ import "./volumes.scss"; import React from "react"; import { observer } from "mobx-react"; import { Link, RouteComponentProps } from "react-router-dom"; -import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api"; import { getDetailsUrl, KubeObjectListLayout } from "../kube-object"; import type { IVolumesRouteParams } from "./volumes.route"; import { stopPropagation } from "../../utils"; @@ -54,15 +53,15 @@ export class PersistentVolumes extends React.Component { className="PersistentVolumes" store={volumesStore} sortingCallbacks={{ - [columnId.name]: (item: PersistentVolume) => item.getName(), - [columnId.storageClass]: (item: PersistentVolume) => item.getStorageClass(), - [columnId.capacity]: (item: PersistentVolume) => item.getCapacity(true), - [columnId.status]: (item: PersistentVolume) => item.getStatus(), - [columnId.age]: (item: PersistentVolume) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.storageClass]: item => item.getStorageClass(), + [columnId.capacity]: item => item.getCapacity(true), + [columnId.status]: item => item.getStatus(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: PersistentVolume) => item.getSearchFields(), - (item: PersistentVolume) => item.getClaimRefName(), + item => item.getSearchFields(), + item => item.getClaimRefName(), ]} renderHeaderTitle="Persistent Volumes" renderTableHeader={[ @@ -74,7 +73,7 @@ export class PersistentVolumes extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(volume: PersistentVolume) => { + renderTableContents={volume => { const { claimRef, storageClassName } = volume.spec; const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ name: storageClassName diff --git a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx index 273a3c0d51..63ad039d22 100644 --- a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx +++ b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx @@ -40,7 +40,6 @@ import { namespaceStore } from "../+namespaces/namespace.store"; import { serviceAccountsStore } from "../+user-management-service-accounts/service-accounts.store"; import { roleBindingsStore } from "./role-bindings.store"; import { showDetails } from "../kube-object"; -import type { KubeObjectStore } from "../../kube-object.store"; interface BindingSelectOption extends SelectOption { value: string; // binding name @@ -102,14 +101,12 @@ export class AddRoleBindingDialog extends React.Component { }; async loadData() { - const stores: KubeObjectStore[] = [ - namespaceStore, - rolesStore, - serviceAccountsStore, - ]; - this.isLoading = true; - await Promise.all(stores.map(store => store.reloadAll())); + await Promise.all([ + namespaceStore.reloadAll(), + rolesStore.reloadAll(), + serviceAccountsStore.reloadAll(), + ]); this.isLoading = false; } diff --git a/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts b/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts index 25c4285fef..d6149aeb87 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts +++ b/src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts @@ -51,7 +51,7 @@ export class RoleBindingsStore extends KubeObjectStore { return clusterRoleBindingApi.get(params); } - protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { + protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { const items = await Promise.all([ super.loadItems({ ...params, api: clusterRoleBindingApi }), super.loadItems({ ...params, api: roleBindingApi }), diff --git a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx index db86d37cd6..885de84ade 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx +++ b/src/renderer/components/+user-management-roles-bindings/role-bindings.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import type { IRoleBindingsRouteParams } from "../+user-management/user-management.route"; -import type { RoleBinding } from "../../api/endpoints"; import { roleBindingsStore } from "./role-bindings.store"; import { KubeObjectListLayout } from "../kube-object"; import { AddRoleBindingDialog } from "./add-role-binding-dialog"; @@ -51,14 +50,14 @@ export class RoleBindings extends React.Component { className="RoleBindings" store={roleBindingsStore} sortingCallbacks={{ - [columnId.name]: (binding: RoleBinding) => binding.getName(), - [columnId.namespace]: (binding: RoleBinding) => binding.getNs(), - [columnId.bindings]: (binding: RoleBinding) => binding.getSubjectNames(), - [columnId.age]: (binding: RoleBinding) => binding.getTimeDiffFromNow(), + [columnId.name]: binding => binding.getName(), + [columnId.namespace]: binding => binding.getNs(), + [columnId.bindings]: binding => binding.getSubjectNames(), + [columnId.age]: binding => binding.getTimeDiffFromNow(), }} searchFilters={[ - (binding: RoleBinding) => binding.getSearchFields(), - (binding: RoleBinding) => binding.getSubjectNames(), + binding => binding.getSearchFields(), + binding => binding.getSubjectNames(), ]} renderHeaderTitle="Role Bindings" renderTableHeader={[ @@ -68,7 +67,7 @@ export class RoleBindings extends React.Component { { title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(binding: RoleBinding) => [ + renderTableContents={binding => [ binding.getName(), , binding.getNs() || "-", diff --git a/src/renderer/components/+user-management-roles/roles.store.ts b/src/renderer/components/+user-management-roles/roles.store.ts index a70d3e8abe..58b99287b0 100644 --- a/src/renderer/components/+user-management-roles/roles.store.ts +++ b/src/renderer/components/+user-management-roles/roles.store.ts @@ -49,7 +49,7 @@ export class RolesStore extends KubeObjectStore { return clusterRoleApi.get(params); } - protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { + protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { const items = await Promise.all([ super.loadItems({ ...params, api: clusterRoleApi }), super.loadItems({ ...params, api: roleApi }), diff --git a/src/renderer/components/+user-management-roles/roles.tsx b/src/renderer/components/+user-management-roles/roles.tsx index ac7385cb2c..871eb4c515 100644 --- a/src/renderer/components/+user-management-roles/roles.tsx +++ b/src/renderer/components/+user-management-roles/roles.tsx @@ -26,7 +26,6 @@ import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import type { IRolesRouteParams } from "../+user-management/user-management.route"; import { rolesStore } from "./roles.store"; -import type { Role } from "../../api/endpoints"; import { KubeObjectListLayout } from "../kube-object"; import { AddRoleDialog } from "./add-role-dialog"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -51,12 +50,12 @@ export class Roles extends React.Component { className="Roles" store={rolesStore} sortingCallbacks={{ - [columnId.name]: (role: Role) => role.getName(), - [columnId.namespace]: (role: Role) => role.getNs(), - [columnId.age]: (role: Role) => role.getTimeDiffFromNow(), + [columnId.name]: role => role.getName(), + [columnId.namespace]: role => role.getNs(), + [columnId.age]: role => role.getTimeDiffFromNow(), }} searchFilters={[ - (role: Role) => role.getSearchFields(), + role => role.getSearchFields(), ]} renderHeaderTitle="Roles" renderTableHeader={[ @@ -65,7 +64,7 @@ export class Roles extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(role: Role) => [ + renderTableContents={role => [ role.getName(), , role.getNs() || "-", diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx index dff4d1fe58..f8d0cbf951 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx +++ b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx @@ -55,12 +55,12 @@ export class ServiceAccounts extends React.Component { tableId="access_service_accounts" className="ServiceAccounts" store={serviceAccountsStore} sortingCallbacks={{ - [columnId.name]: (account: ServiceAccount) => account.getName(), - [columnId.namespace]: (account: ServiceAccount) => account.getNs(), - [columnId.age]: (account: ServiceAccount) => account.getTimeDiffFromNow(), + [columnId.name]: account => account.getName(), + [columnId.namespace]: account => account.getNs(), + [columnId.age]: account => account.getTimeDiffFromNow(), }} searchFilters={[ - (account: ServiceAccount) => account.getSearchFields(), + account => account.getSearchFields(), ]} renderHeaderTitle="Service Accounts" renderTableHeader={[ @@ -69,7 +69,7 @@ export class ServiceAccounts extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(account: ServiceAccount) => [ + renderTableContents={account => [ account.getName(), , account.getNs(), diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index 04560aa197..e421e0a82a 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -62,16 +62,16 @@ export class CronJobs extends React.Component { className="CronJobs" store={cronJobStore} dependentStores={[jobStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (cronJob: CronJob) => cronJob.getName(), - [columnId.namespace]: (cronJob: CronJob) => cronJob.getNs(), - [columnId.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(), - [columnId.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob), - [columnId.lastSchedule]: (cronJob: CronJob) => cronJob.getLastScheduleTime(), - [columnId.age]: (cronJob: CronJob) => cronJob.getTimeDiffFromNow(), + [columnId.name]: cronJob => cronJob.getName(), + [columnId.namespace]: cronJob => cronJob.getNs(), + [columnId.suspend]: cronJob => cronJob.getSuspendFlag(), + [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), + [columnId.lastSchedule]: cronJob => cronJob.getLastScheduleTime(), + [columnId.age]: cronJob => cronJob.getTimeDiffFromNow(), }} searchFilters={[ - (cronJob: CronJob) => cronJob.getSearchFields(), - (cronJob: CronJob) => cronJob.getSchedule(), + cronJob => cronJob.getSearchFields(), + cronJob => cronJob.getSchedule(), ]} renderHeaderTitle="Cron Jobs" renderTableHeader={[ @@ -84,7 +84,7 @@ export class CronJobs extends React.Component { { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(cronJob: CronJob) => [ + renderTableContents={cronJob => [ cronJob.getName(), , cronJob.getNs(), diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx index b256ac1f62..6eb3744149 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx @@ -65,14 +65,14 @@ export class DaemonSets extends React.Component { className="DaemonSets" store={daemonSetStore} dependentStores={[podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (daemonSet: DaemonSet) => daemonSet.getName(), - [columnId.namespace]: (daemonSet: DaemonSet) => daemonSet.getNs(), - [columnId.pods]: (daemonSet: DaemonSet) => this.getPodsLength(daemonSet), - [columnId.age]: (daemonSet: DaemonSet) => daemonSet.getTimeDiffFromNow(), + [columnId.name]: daemonSet => daemonSet.getName(), + [columnId.namespace]: daemonSet => daemonSet.getNs(), + [columnId.pods]: daemonSet => this.getPodsLength(daemonSet), + [columnId.age]: daemonSet => daemonSet.getTimeDiffFromNow(), }} searchFilters={[ - (daemonSet: DaemonSet) => daemonSet.getSearchFields(), - (daemonSet: DaemonSet) => daemonSet.getLabels(), + daemonSet => daemonSet.getSearchFields(), + daemonSet => daemonSet.getLabels(), ]} renderHeaderTitle="Daemon Sets" renderTableHeader={[ @@ -83,7 +83,7 @@ export class DaemonSets extends React.Component { { title: "Node Selector", className: "labels", id: columnId.labels }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(daemonSet: DaemonSet) => [ + renderTableContents={daemonSet => [ daemonSet.getName(), daemonSet.getNs(), this.getPodsLength(daemonSet), diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index 74ddb08fcb..ba9eaeca8e 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -82,15 +82,15 @@ export class Deployments extends React.Component { className="Deployments" store={deploymentStore} dependentStores={[replicaSetStore, podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (deployment: Deployment) => deployment.getName(), - [columnId.namespace]: (deployment: Deployment) => deployment.getNs(), - [columnId.replicas]: (deployment: Deployment) => deployment.getReplicas(), - [columnId.age]: (deployment: Deployment) => deployment.getTimeDiffFromNow(), - [columnId.condition]: (deployment: Deployment) => deployment.getConditionsText(), + [columnId.name]: deployment => deployment.getName(), + [columnId.namespace]: deployment => deployment.getNs(), + [columnId.replicas]: deployment => deployment.getReplicas(), + [columnId.age]: deployment => deployment.getTimeDiffFromNow(), + [columnId.condition]: deployment => deployment.getConditionsText(), }} searchFilters={[ - (deployment: Deployment) => deployment.getSearchFields(), - (deployment: Deployment) => deployment.getConditionsText(), + deployment => deployment.getSearchFields(), + deployment => deployment.getConditionsText(), ]} renderHeaderTitle="Deployments" renderTableHeader={[ @@ -102,7 +102,7 @@ export class Deployments extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.condition, id: columnId.condition }, ]} - renderTableContents={(deployment: Deployment) => [ + renderTableContents={deployment => [ deployment.getName(), , deployment.getNs(), @@ -111,9 +111,7 @@ export class Deployments extends React.Component { deployment.getAge(), this.renderConditions(deployment), ]} - renderItemMenu={(item: Deployment) => { - return ; - }} + renderItemMenu={item => } /> ); } diff --git a/src/renderer/components/+workloads-jobs/jobs.tsx b/src/renderer/components/+workloads-jobs/jobs.tsx index bcc9b85efe..9ecb6d08ab 100644 --- a/src/renderer/components/+workloads-jobs/jobs.tsx +++ b/src/renderer/components/+workloads-jobs/jobs.tsx @@ -27,7 +27,6 @@ import type { RouteComponentProps } from "react-router"; import { podsStore } from "../+workloads-pods/pods.store"; import { jobStore } from "./job.store"; import { eventStore } from "../+events/event.store"; -import type { Job } from "../../api/endpoints/job.api"; import { KubeObjectListLayout } from "../kube-object"; import type { IJobsRouteParams } from "../+workloads"; import kebabCase from "lodash/kebabCase"; @@ -54,13 +53,13 @@ export class Jobs extends React.Component { className="Jobs" store={jobStore} dependentStores={[podsStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (job: Job) => job.getName(), - [columnId.namespace]: (job: Job) => job.getNs(), - [columnId.conditions]: (job: Job) => job.getCondition() != null ? job.getCondition().type : "", - [columnId.age]: (job: Job) => job.getTimeDiffFromNow(), + [columnId.name]: job => job.getName(), + [columnId.namespace]: job => job.getNs(), + [columnId.conditions]: job => job.getCondition() != null ? job.getCondition().type : "", + [columnId.age]: job => job.getTimeDiffFromNow(), }} searchFilters={[ - (job: Job) => job.getSearchFields(), + job => job.getSearchFields(), ]} renderHeaderTitle="Jobs" renderTableHeader={[ @@ -71,7 +70,7 @@ export class Jobs extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} - renderTableContents={(job: Job) => { + renderTableContents={job => { const condition = job.getCondition(); return [ diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index 21ae379b69..89194c2d96 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -46,7 +46,7 @@ const resources: KubeResource[] = [ export class OverviewStatuses extends React.Component { @boundMethod renderWorkload(resource: KubeResource): React.ReactElement { - const store = workloadStores[resource]; + const store = workloadStores.get(resource); const items = store.getAllByNs(namespaceStore.contextNamespaces); return ( diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-tolerations.tsx index 5ca65cc2b3..306ebf9a25 100644 --- a/src/renderer/components/+workloads-pods/pod-tolerations.tsx +++ b/src/renderer/components/+workloads-pods/pod-tolerations.tsx @@ -37,13 +37,6 @@ enum sortBy { Seconds = "seconds", } -const sortingCallbacks = { - [sortBy.Key]: (toleration: IToleration) => toleration.key, - [sortBy.Operator]: (toleration: IToleration) => toleration.operator, - [sortBy.Effect]: (toleration: IToleration) => toleration.effect, - [sortBy.Seconds]: (toleration: IToleration) => toleration.tolerationSeconds, -}; - const getTableRow = (toleration: IToleration) => { const { key, operator, effect, tolerationSeconds } = toleration; @@ -66,10 +59,17 @@ export function PodTolerations({ tolerations }: Props) { toleration.key, + [sortBy.Operator]: toleration => toleration.operator, + [sortBy.Effect]: toleration => toleration.effect, + [sortBy.Seconds]: toleration => toleration.tolerationSeconds, + }} sortSyncWithUrl={false} className="PodTolerations" + renderRow={getTableRow} > Key @@ -77,9 +77,6 @@ export function PodTolerations({ tolerations }: Props) { Effect Seconds - { - tolerations.map(getTableRow) - }
); } diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index be5cbf8852..e9ccdbe181 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -97,21 +97,21 @@ export class Pods extends React.Component { tableId = "workloads_pods" isConfigurable sortingCallbacks={{ - [columnId.name]: (pod: Pod) => pod.getName(), - [columnId.namespace]: (pod: Pod) => pod.getNs(), - [columnId.containers]: (pod: Pod) => pod.getContainers().length, - [columnId.restarts]: (pod: Pod) => pod.getRestartsCount(), - [columnId.owners]: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind), - [columnId.qos]: (pod: Pod) => pod.getQosClass(), - [columnId.node]: (pod: Pod) => pod.getNodeName(), - [columnId.age]: (pod: Pod) => pod.getTimeDiffFromNow(), - [columnId.status]: (pod: Pod) => pod.getStatusMessage(), + [columnId.name]: pod => pod.getName(), + [columnId.namespace]: pod => pod.getNs(), + [columnId.containers]: pod => pod.getContainers().length, + [columnId.restarts]: pod => pod.getRestartsCount(), + [columnId.owners]: pod => pod.getOwnerRefs().map(ref => ref.kind), + [columnId.qos]: pod => pod.getQosClass(), + [columnId.node]: pod => pod.getNodeName(), + [columnId.age]: pod => pod.getTimeDiffFromNow(), + [columnId.status]: pod => pod.getStatusMessage(), }} searchFilters={[ - (pod: Pod) => pod.getSearchFields(), - (pod: Pod) => pod.getStatusMessage(), - (pod: Pod) => pod.status.podIP, - (pod: Pod) => pod.getNodeName(), + pod => pod.getSearchFields(), + pod => pod.getStatusMessage(), + pod => pod.status.podIP, + pod => pod.getNodeName(), ]} renderHeaderTitle="Pods" renderTableHeader={[ @@ -126,7 +126,7 @@ export class Pods extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(pod: Pod) => [ + renderTableContents={pod => [ , , pod.getNs(), diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx index ffc75ead0e..2556e56c3e 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx @@ -56,15 +56,15 @@ export class ReplicaSets extends React.Component { tableId="workload_replicasets" className="ReplicaSets" store={replicaSetStore} sortingCallbacks={{ - [columnId.name]: (replicaSet: ReplicaSet) => replicaSet.getName(), - [columnId.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(), - [columnId.desired]: (replicaSet: ReplicaSet) => replicaSet.getDesired(), - [columnId.current]: (replicaSet: ReplicaSet) => replicaSet.getCurrent(), - [columnId.ready]: (replicaSet: ReplicaSet) => replicaSet.getReady(), - [columnId.age]: (replicaSet: ReplicaSet) => replicaSet.getTimeDiffFromNow(), + [columnId.name]: replicaSet => replicaSet.getName(), + [columnId.namespace]: replicaSet => replicaSet.getNs(), + [columnId.desired]: replicaSet => replicaSet.getDesired(), + [columnId.current]: replicaSet => replicaSet.getCurrent(), + [columnId.ready]: replicaSet => replicaSet.getReady(), + [columnId.age]: replicaSet => replicaSet.getTimeDiffFromNow(), }} searchFilters={[ - (replicaSet: ReplicaSet) => replicaSet.getSearchFields(), + replicaSet => replicaSet.getSearchFields(), ]} renderHeaderTitle="Replica Sets" renderTableHeader={[ @@ -76,7 +76,7 @@ export class ReplicaSets extends React.Component { { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(replicaSet: ReplicaSet) => [ + renderTableContents={replicaSet => [ replicaSet.getName(), , replicaSet.getNs(), diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx index 59b208cccf..27632e0d54 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx @@ -65,13 +65,13 @@ export class StatefulSets extends React.Component { className="StatefulSets" store={statefulSetStore} dependentStores={[podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (statefulSet: StatefulSet) => statefulSet.getName(), - [columnId.namespace]: (statefulSet: StatefulSet) => statefulSet.getNs(), - [columnId.age]: (statefulSet: StatefulSet) => statefulSet.getTimeDiffFromNow(), - [columnId.replicas]: (statefulSet: StatefulSet) => statefulSet.getReplicas(), + [columnId.name]: statefulSet => statefulSet.getName(), + [columnId.namespace]: statefulSet => statefulSet.getNs(), + [columnId.age]: statefulSet => statefulSet.getTimeDiffFromNow(), + [columnId.replicas]: statefulSet => statefulSet.getReplicas(), }} searchFilters={[ - (statefulSet: StatefulSet) => statefulSet.getSearchFields(), + statefulSet => statefulSet.getSearchFields(), ]} renderHeaderTitle="Stateful Sets" renderTableHeader={[ @@ -82,7 +82,7 @@ export class StatefulSets extends React.Component { { className: "warning", showWithColumn: columnId.replicas }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(statefulSet: StatefulSet) => [ + renderTableContents={statefulSet => [ statefulSet.getName(), statefulSet.getNs(), this.renderPods(statefulSet), @@ -90,9 +90,7 @@ export class StatefulSets extends React.Component { , statefulSet.getAge(), ]} - renderItemMenu={(item: StatefulSet) => { - return ; - }} + renderItemMenu={item => } /> ); } diff --git a/src/renderer/components/+workloads/workloads.stores.ts b/src/renderer/components/+workloads/workloads.stores.ts index a06c17200d..e53ea7cb03 100644 --- a/src/renderer/components/+workloads/workloads.stores.ts +++ b/src/renderer/components/+workloads/workloads.stores.ts @@ -28,13 +28,14 @@ import { jobStore } from "../+workloads-jobs/job.store"; import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import type { KubeResource } from "../../../common/rbac"; import { replicaSetStore } from "../+workloads-replicasets/replicasets.store"; +import type { KubeObject } from "../../api/kube-object"; -export const workloadStores: Partial> = { - "pods": podsStore, - "deployments": deploymentStore, - "daemonsets": daemonSetStore, - "statefulsets": statefulSetStore, - "replicasets": replicaSetStore, - "jobs": jobStore, - "cronjobs": cronJobStore, -}; +export const workloadStores = new Map>([ + ["pods", podsStore], + ["deployments", deploymentStore], + ["daemonsets", daemonSetStore], + ["statefulsets", statefulSetStore], + ["replicasets", replicaSetStore], + ["jobs", jobStore], + ["cronjobs", cronJobStore], +]); diff --git a/src/renderer/components/dock/edit-resource.store.ts b/src/renderer/components/dock/edit-resource.store.ts index 9b10491901..0630176927 100644 --- a/src/renderer/components/dock/edit-resource.store.ts +++ b/src/renderer/components/dock/edit-resource.store.ts @@ -84,7 +84,7 @@ export class EditResourceStore extends DockTabStore { return Boolean(tabDataReady && this.getResource(tabId)); // ready to edit resource } - getStore(tabId: TabId): KubeObjectStore | undefined { + getStore(tabId: TabId): KubeObjectStore | undefined { return apiManager.getStore(this.getResourcePath(tabId)); } diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index 647cbcaec9..76aa78315b 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -26,7 +26,7 @@ import React, { ReactNode } from "react"; import { computed, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog"; -import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallback } from "../table"; +import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table"; import { boundMethod, createStorage, cssNames, IClassName, isReactNode, noop, ObservableToggleSet, prevDefault, stopPropagation } from "../../utils"; import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons"; import { NoItems } from "../no-items"; @@ -46,8 +46,10 @@ import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; // todo: refactor, split to small re-usable components -export type SearchFilter = (item: T) => string | number | (string | number)[]; -export type ItemsFilter = (items: T[]) => T[]; +export type SearchFilter = (item: Item) => string | number | (string | number)[]; +export type SearchFilters = Record>; +export type ItemsFilter = (items: Item[]) => Item[]; +export type ItemsFilters = Record>; export interface IHeaderPlaceholders { title: ReactNode; @@ -56,22 +58,22 @@ export interface IHeaderPlaceholders { info: ReactNode; } -export interface ItemListLayoutProps { +export interface ItemListLayoutProps { tableId?: string; className: IClassName; - items?: T[]; - store: ItemStore; - dependentStores?: ItemStore[]; + items?: Item[]; + store: ItemStore; + dependentStores?: ItemStore[]; preloadStores?: boolean; hideFilters?: boolean; - searchFilters?: SearchFilter[]; + searchFilters?: SearchFilter[]; /** @deprecated */ - filterItems?: ItemsFilter[]; + filterItems?: ItemsFilter[]; // header (title, filtering, searching, etc.) showHeader?: boolean; headerClassName?: IClassName; - renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode); + renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode); customizeHeader?: (placeholders: IHeaderPlaceholders, content: ReactNode) => Partial | ReactNode; // items list configuration @@ -80,26 +82,28 @@ export interface ItemListLayoutProps { isSearchable?: boolean; // apply search-filter & add search-input isConfigurable?: boolean; copyClassNameFromHeadCells?: boolean; - sortingCallbacks?: { [sortBy: string]: TableSortCallback }; - tableProps?: Partial; // low-level table configuration + sortingCallbacks?: TableSortCallbacks; + tableProps?: Partial>; // low-level table configuration renderTableHeader: TableCellProps[] | null; - renderTableContents: (item: T) => (ReactNode | TableCellProps)[]; - renderItemMenu?: (item: T, store: ItemStore) => ReactNode; - customizeTableRowProps?: (item: T) => Partial; + renderTableContents: (item: Item) => (ReactNode | TableCellProps)[]; + renderItemMenu?: (item: Item, store: ItemStore) => ReactNode; + customizeTableRowProps?: (item: Item) => Partial; addRemoveButtons?: Partial; virtual?: boolean; // item details view hasDetailsView?: boolean; - detailsItem?: T; - onDetails?: (item: T) => void; + detailsItem?: Item; + onDetails?: (item: Item) => void; // other - customizeRemoveDialog?: (selectedItems: T[]) => Partial; - renderFooter?: (parent: ItemListLayout) => React.ReactNode; + customizeRemoveDialog?: (selectedItems: Item[]) => Partial; + renderFooter?: (parent: ItemListLayout) => React.ReactNode; + + filterCallbacks?: ItemsFilters; } -const defaultProps: Partial = { +const defaultProps: Partial> = { showHeader: true, isSearchable: true, isSelectable: true, @@ -115,14 +119,14 @@ const defaultProps: Partial = { }; @observer -export class ItemListLayout extends React.Component { +export class ItemListLayout extends React.Component> { static defaultProps = defaultProps as object; private storage = createStorage("item_list_layout", { showFilters: false, // setup defaults }); - constructor(props: ItemListLayoutProps) { + constructor(props: ItemListLayoutProps) { super(props); makeObservable(this); } @@ -158,7 +162,7 @@ export class ItemListLayout extends React.Component { stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces)); } - private filterCallbacks: { [type: string]: ItemsFilter } = { + private filterCallbacks: ItemsFilters = { [FilterType.SEARCH]: items => { const { searchFilters, isSearchable } = this.props; const search = pageFilters.getValues(FilterType.SEARCH)[0] || ""; @@ -199,20 +203,20 @@ export class ItemListLayout extends React.Component { return activeFilters; } - applyFilters(filters: ItemsFilter[], items: T[]): T[] { + applyFilters(filters: ItemsFilter[], items: Item[]): Item[] { if (!filters || !filters.length) return items; return filters.reduce((items, filter) => filter(items), items); } @computed get items() { - const { filters, filterCallbacks } = this; + const { filters, filterCallbacks, props } = this; const filterGroups = groupBy(filters, ({ type }) => type); - const filterItems: ItemsFilter[] = []; + const filterItems: ItemsFilter[] = []; Object.entries(filterGroups).forEach(([type, filtersGroup]) => { - const filterCallback = filterCallbacks[type]; + const filterCallback = filterCallbacks[type] ?? props.filterCallbacks?.[type]; if (filterCallback && filtersGroup.length > 0) { filterItems.push(filterCallback); diff --git a/src/renderer/components/kube-object/kube-object-details.tsx b/src/renderer/components/kube-object/kube-object-details.tsx index 217c7c25c8..44c8b42545 100644 --- a/src/renderer/components/kube-object/kube-object-details.tsx +++ b/src/renderer/components/kube-object/kube-object-details.tsx @@ -33,6 +33,7 @@ import { crdStore } from "../+custom-resources/crd.store"; import { CrdResourceDetails } from "../+custom-resources"; import { KubeObjectMenu } from "./kube-object-menu"; import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; +import { CustomResourceDefinition } from "../../api/endpoints"; /** * Used to store `object.selfLink` to show more info about resource in the details panel. @@ -99,15 +100,7 @@ export class KubeObjectDetails extends React.Component { } @computed get object() { - const store = apiManager.getStore(this.path); - - if (store) { - return store.getByPath(this.path); - } - } - - @computed get isCrdInstance() { - return !!crdStore.getByObject(this.object); + return apiManager.getStore(this.path)?.getByPath(this.path); } @disposeOnUnmount @@ -137,7 +130,7 @@ export class KubeObjectDetails extends React.Component { }); render() { - const { object, isLoading, loadingError, isCrdInstance } = this; + const { object, isLoading, loadingError } = this; const isOpen = !!(object || isLoading || loadingError); let title = ""; let details: React.ReactNode[]; @@ -150,7 +143,7 @@ export class KubeObjectDetails extends React.Component { return ; }); - if (isCrdInstance && details.length === 0) { + if (object instanceof CustomResourceDefinition && details.length === 0) { details.push(); } } diff --git a/src/renderer/components/kube-object/kube-object-list-layout.tsx b/src/renderer/components/kube-object/kube-object-list-layout.tsx index 52c886f6a2..cfd79adaed 100644 --- a/src/renderer/components/kube-object/kube-object-list-layout.tsx +++ b/src/renderer/components/kube-object/kube-object-list-layout.tsx @@ -30,21 +30,22 @@ import { KubeObjectMenu } from "./kube-object-menu"; import { kubeSelectedUrlParam, showDetails } from "./kube-object-details"; import { kubeWatchApi } from "../../api/kube-watch-api"; import { clusterContext } from "../context"; +import { FilterType, pageFilters } from "../item-object-list/page-filters.store"; -export interface KubeObjectListLayoutProps extends ItemListLayoutProps { - store: KubeObjectStore; - dependentStores?: KubeObjectStore[]; +export interface KubeObjectListLayoutProps extends ItemListLayoutProps { + store: KubeObjectStore; + dependentStores?: KubeObjectStore[]; } -const defaultProps: Partial = { +const defaultProps: Partial> = { onDetails: (item: KubeObject) => showDetails(item.selfLink), }; @observer -export class KubeObjectListLayout extends React.Component { +export class KubeObjectListLayout extends React.Component> { static defaultProps = defaultProps as object; - constructor(props: KubeObjectListLayoutProps) { + constructor(props: KubeObjectListLayoutProps) { super(props); makeObservable(this); } @@ -76,7 +77,18 @@ export class KubeObjectListLayout extends React.Component } // safe because we are dealing with KubeObjects here + renderItemMenu={(item: K) => } // safe because we are dealing with KubeObjects here + filterCallbacks={{ + [FilterType.NAMESPACE]: items => { + const filterValues = pageFilters.getValues(FilterType.NAMESPACE); + + if (filterValues.length > 0) { + return items.filter(item => filterValues.includes(item.getNs())); + } + + return items; + }, + }} /> ); } diff --git a/src/renderer/components/table/table.tsx b/src/renderer/components/table/table.tsx index c1cc7f0cd6..bf6b6d3197 100644 --- a/src/renderer/components/table/table.tsx +++ b/src/renderer/components/table/table.tsx @@ -30,19 +30,18 @@ import { TableHead, TableHeadElem, TableHeadProps } from "./table-head"; import type { TableCellElem } from "./table-cell"; import { VirtualList } from "../virtual-list"; import { createPageParam } from "../../navigation"; -import type { ItemObject } from "../../item.store"; import { getSortParams, setSortParams } from "./table.storage"; import { computed, makeObservable } from "mobx"; export type TableSortBy = string; export type TableOrderBy = "asc" | "desc" | string; export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy }; -export type TableSortCallback = (data: D) => string | number | (string | number)[]; -export type TableSortCallbacks = { [columnId: string]: TableSortCallback }; +export type TableSortCallback = (data: Item) => string | number | (string | number)[]; +export type TableSortCallbacks = Record>; -export interface TableProps extends React.DOMAttributes { +export interface TableProps extends React.DOMAttributes { tableId?: string; - items?: ItemObject[]; // Raw items data + items?: Item[]; // Raw items data className?: string; autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0) selectable?: boolean; // Highlight rows on hover @@ -52,7 +51,7 @@ export interface TableProps extends React.DOMAttributes { * Define sortable callbacks for every column in * @sortItem argument in the callback is an object, provided in */ - sortable?: TableSortCallbacks; + sortable?: TableSortCallbacks; sortSyncWithUrl?: boolean; // sorting state is managed globally from url params sortByDefault?: Partial; // default sorting params onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url @@ -61,8 +60,9 @@ export interface TableProps extends React.DOMAttributes { virtual?: boolean; // Use virtual list component to render only visible rows rowPadding?: string; rowLineHeight?: string; - customRowHeights?: (item: object, lineHeight: number, paddings: number) => number; + customRowHeights?: (item: Item, lineHeight: number, paddings: number) => number; getTableRow?: (uid: string) => React.ReactElement; + renderRow?: (item: Item) => React.ReactElement; } export const sortByUrlParam = createPageParam({ @@ -74,8 +74,8 @@ export const orderByUrlParam = createPageParam({ }); @observer -export class Table extends React.Component { - static defaultProps: TableProps = { +export class Table extends React.Component> { + static defaultProps: TableProps = { scrollable: true, autoSize: true, rowPadding: "8px", @@ -83,7 +83,7 @@ export class Table extends React.Component { sortSyncWithUrl: true, }; - constructor(props: TableProps) { + constructor(props: TableProps) { super(props); makeObservable(this); } @@ -171,9 +171,20 @@ export class Table extends React.Component { }); } - renderRows() { - const { sortable, noItems, children, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props; + getContent() { + const { items, renderRow, children } = this.props; const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[]; + + if (renderRow) { + content.push(...items.map(renderRow)); + } + + return content; + } + + renderRows() { + const { sortable, noItems, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props; + const content = this.getContent(); let rows: React.ReactElement[] = content.filter(elem => elem.type === TableRow); let sortedItems = rows.length ? rows.map(row => row.props.sortItem) : [...items]; diff --git a/src/renderer/item.store.ts b/src/renderer/item.store.ts index 83733dea41..c8aa656d81 100644 --- a/src/renderer/item.store.ts +++ b/src/renderer/item.store.ts @@ -28,15 +28,15 @@ export interface ItemObject { getName(): string; } -export abstract class ItemStore { - abstract loadAll(...args: any[]): Promise; +export abstract class ItemStore { + abstract loadAll(...args: any[]): Promise; - protected defaultSorting = (item: T) => item.getName(); + protected defaultSorting = (item: Item) => item.getName(); @observable failedLoading = false; @observable isLoading = false; @observable isLoaded = false; - @observable items = observable.array([], { deep: false }); + @observable items = observable.array([], { deep: false }); @observable selectedItemsIds = observable.map(); constructor() { @@ -44,11 +44,11 @@ export abstract class ItemStore { autoBind(this); } - @computed get selectedItems(): T[] { + @computed get selectedItems(): Item[] { return this.items.filter(item => this.selectedItemsIds.get(item.getId())); } - public getItems(): T[] { + public getItems(): Item[] { return Array.from(this.items); } @@ -56,8 +56,8 @@ export abstract class ItemStore { return this.items.length; } - getByName(name: string, ...args: any[]): T; - getByName(name: string): T { + getByName(name: string, ...args: any[]): Item; + getByName(name: string): Item { return this.items.find(item => item.getName() === name); } @@ -75,13 +75,13 @@ export abstract class ItemStore { * @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`) */ @action - protected sortItems(items: T[] = this.items, sorting: ((item: T) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): T[] { + protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] { return orderBy(items, sorting, order); } protected async createItem(...args: any[]): Promise; @action - protected async createItem(request: () => Promise) { + protected async createItem(request: () => Promise) { const newItem = await request(); const item = this.items.find(item => item.getId() === newItem.getId()); @@ -98,7 +98,7 @@ export abstract class ItemStore { protected async loadItems(...args: any[]): Promise; @action - protected async loadItems(request: () => Promise, sortItems = true) { + protected async loadItems(request: () => Promise, sortItems = true) { if (this.isLoading) { await when(() => !this.isLoading); @@ -117,9 +117,9 @@ export abstract class ItemStore { } } - protected async loadItem(...args: any[]): Promise + protected async loadItem(...args: any[]): Promise @action - protected async loadItem(request: () => Promise, sortItems = true) { + protected async loadItem(request: () => Promise, sortItems = true) { const item = await Promise.resolve(request()).catch(() => null); if (item) { @@ -141,7 +141,7 @@ export abstract class ItemStore { } @action - protected async updateItem(item: T, request: () => Promise) { + protected async updateItem(item: Item, request: () => Promise) { const updatedItem = await request(); const index = this.items.findIndex(i => i.getId() === item.getId()); @@ -151,28 +151,28 @@ export abstract class ItemStore { } @action - protected async removeItem(item: T, request: () => Promise) { + protected async removeItem(item: Item, request: () => Promise) { await request(); this.items.remove(item); this.selectedItemsIds.delete(item.getId()); } - isSelected(item: T) { + isSelected(item: Item) { return !!this.selectedItemsIds.get(item.getId()); } @action - select(item: T) { + select(item: Item) { this.selectedItemsIds.set(item.getId(), true); } @action - unselect(item: T) { + unselect(item: Item) { this.selectedItemsIds.delete(item.getId()); } @action - toggleSelection(item: T) { + toggleSelection(item: Item) { if (this.isSelected(item)) { this.unselect(item); } else { @@ -181,7 +181,7 @@ export abstract class ItemStore { } @action - toggleSelectionAll(visibleItems: T[] = this.items) { + toggleSelectionAll(visibleItems: Item[] = this.items) { const allSelected = visibleItems.every(this.isSelected); if (allSelected) { @@ -191,7 +191,7 @@ export abstract class ItemStore { } } - isSelectedAll(visibleItems: T[] = this.items) { + isSelectedAll(visibleItems: Item[] = this.items) { if (!visibleItems.length) return false; return visibleItems.every(this.isSelected); @@ -218,7 +218,7 @@ export abstract class ItemStore { return noop; } - * [Symbol.iterator]() { + *[Symbol.iterator]() { yield* this.items; } } diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index f1b9cfba96..0172a1bc83 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -31,16 +31,16 @@ import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from import type { KubeJsonApiData } from "./api/kube-json-api"; import { Notifications } from "./components/notifications"; -export interface KubeObjectStoreLoadingParams { +export interface KubeObjectStoreLoadingParams { namespaces: string[]; - api?: KubeApi; + api?: KubeApi; reqInit?: RequestInit; } -export abstract class KubeObjectStore extends ItemStore { +export abstract class KubeObjectStore extends ItemStore { static defaultContext = observable.box(); // TODO: support multiple cluster contexts - abstract api: KubeApi; + abstract api: KubeApi; public readonly limit?: number; public readonly bufferSize: number = 50000; @observable private loadedNamespaces?: string[]; @@ -64,7 +64,7 @@ export abstract class KubeObjectStore extends ItemSt return KubeObjectStore.defaultContext.get(); } - @computed get contextItems(): T[] { + @computed get contextItems(): K[] { const namespaces = this.context?.contextNamespaces ?? []; return this.items.filter(item => { @@ -88,9 +88,9 @@ export abstract class KubeObjectStore extends ItemSt return { limit }; } - getStatuses?(items: T[]): Record; + getStatuses?(items: K[]): Record; - getAllByNs(namespace: string | string[], strict = false): T[] { + getAllByNs(namespace: string | string[], strict = false): K[] { const namespaces: string[] = [].concat(namespace); if (namespaces.length) { @@ -108,7 +108,7 @@ export abstract class KubeObjectStore extends ItemSt return this.items.find(item => item.getId() === id); } - getByName(name: string, namespace?: string): T { + getByName(name: string, namespace?: string): K { return this.items.find(item => { return item.getName() === name && ( namespace ? item.getNs() === namespace : true @@ -116,19 +116,19 @@ export abstract class KubeObjectStore extends ItemSt }); } - getByPath(path: string): T { + getByPath(path: string): K { return this.items.find(item => item.selfLink === path); } - getByLabel(labels: string[] | { [label: string]: string }): T[] { + getByLabel(labels: string[] | { [label: string]: string }): K[] { if (Array.isArray(labels)) { - return this.items.filter((item: T) => { + return this.items.filter((item: K) => { const itemLabels = item.getLabels(); return labels.every(label => itemLabels.includes(label)); }); } else { - return this.items.filter((item: T) => { + return this.items.filter((item: K) => { const itemLabels = item.metadata.labels || {}; return Object.entries(labels) @@ -137,7 +137,7 @@ export abstract class KubeObjectStore extends ItemSt } } - protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise { + protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise { if (this.context?.cluster.isAllowedResource(api.kind)) { if (!api.isNamespaced) { return api.list({ reqInit }, this.query); @@ -163,12 +163,12 @@ export abstract class KubeObjectStore extends ItemSt return []; } - protected filterItemsOnLoad(items: T[]) { + protected filterItemsOnLoad(items: K[]) { return items; } @action - async loadAll(options: { namespaces?: string[], merge?: boolean, reqInit?: RequestInit } = {}): Promise { + async loadAll(options: { namespaces?: string[], merge?: boolean, reqInit?: RequestInit } = {}): Promise { await this.contextReady; this.isLoading = true; @@ -215,7 +215,7 @@ export abstract class KubeObjectStore extends ItemSt } @action - protected mergeItems(partialItems: T[], { replace = false, updateStore = true, sort = true, filter = true } = {}): T[] { + protected mergeItems(partialItems: K[], { replace = false, updateStore = true, sort = true, filter = true } = {}): K[] { let items = partialItems; // update existing items @@ -239,12 +239,12 @@ export abstract class KubeObjectStore extends ItemSt if (error) this.reset(); } - protected async loadItem(params: { name: string; namespace?: string }): Promise { + protected async loadItem(params: { name: string; namespace?: string }): Promise { return this.api.get(params); } @action - async load(params: { name: string; namespace?: string }): Promise { + async load(params: { name: string; namespace?: string }): Promise { const { name, namespace } = params; let item = this.getByName(name, namespace); @@ -265,11 +265,11 @@ export abstract class KubeObjectStore extends ItemSt return this.load({ name, namespace }); } - protected async createItem(params: { name: string; namespace?: string }, data?: Partial): Promise { + protected async createItem(params: { name: string; namespace?: string }, data?: Partial): Promise { return this.api.create(params, data); } - async create(params: { name: string; namespace?: string }, data?: Partial): Promise { + async create(params: { name: string; namespace?: string }, data?: Partial): Promise { const newItem = await this.createItem(params, data); const items = this.sortItems([...this.items, newItem]); @@ -278,9 +278,9 @@ export abstract class KubeObjectStore extends ItemSt return newItem; } - async update(item: T, data: Partial): Promise { - const newItem = await item.update(data); - + async update(item: K, data: Partial): Promise { + const newItem = await item.update(data); + ensureObjectSelfLink(this.api, newItem); const index = this.items.findIndex(item => item.getId() === newItem.getId()); @@ -290,7 +290,7 @@ export abstract class KubeObjectStore extends ItemSt return newItem; } - async remove(item: T) { + async remove(item: K) { await item.delete(); this.items.remove(item); this.selectedItemsIds.delete(item.getId()); @@ -309,7 +309,8 @@ export abstract class KubeObjectStore extends ItemSt }); } - getSubscribeApis(): KubeApi[] { + getSubscribeApis(): KubeApi[] { + // TODO remove this function, each Store should only be a single API return [this.api]; } @@ -351,7 +352,7 @@ export abstract class KubeObjectStore extends ItemSt }; } - private watchNamespace(api: KubeApi, namespace: string, abortController: AbortController) { + private watchNamespace(api: KubeApi, namespace: string, abortController: AbortController) { let timedRetry: NodeJS.Timeout; const watch = () => api.watch({ namespace, @@ -361,7 +362,7 @@ export abstract class KubeObjectStore extends ItemSt const { signal } = abortController; - const callback = (data: IKubeWatchEvent, error: any) => { + const callback = (data: IKubeWatchEvent, error: any) => { if (!this.isLoaded || error instanceof DOMException) return; if (error instanceof Response) { @@ -409,7 +410,7 @@ export abstract class KubeObjectStore extends ItemSt switch (type) { case "ADDED": case "MODIFIED": - const newItem = new api.objectConstructor(object); + const newItem = new api.objectConstructor(object) as K; if (!item) { items.push(newItem);