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

Support Events on CustomResourece details panel

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-10-24 07:49:14 -04:00
parent 33e6771da3
commit 718834212e
43 changed files with 156 additions and 241 deletions

View File

@ -16,7 +16,6 @@ import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTa
import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api"; import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import type { ApiManager } from "../../../common/k8s-api/api-manager"; import type { ApiManager } from "../../../common/k8s-api/api-manager";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable"; import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
@ -121,8 +120,6 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
return ( return (
<div className="HpaDetails"> <div className="HpaDetails">
<KubeObjectMeta object={hpa}/>
<DrawerItem name="Reference"> <DrawerItem name="Reference">
{scaleTargetRef && ( {scaleTargetRef && (
<Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}> <Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>

View File

@ -11,7 +11,6 @@ import { cssNames } from "../../utils";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
import { Input } from "../input"; import { Input } from "../input";
import type { AdditionalPrinterColumnsV1 } from "../../../common/k8s-api/endpoints/custom-resource-definition.api"; import type { AdditionalPrinterColumnsV1 } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/custom-resource-definition.api"; import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
@ -21,7 +20,7 @@ import { KubeObject } from "../../../common/k8s-api/kube-object";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> { export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> {
crd: CustomResourceDefinition; crd?: CustomResourceDefinition;
} }
function convertSpecValue(value: unknown): React.ReactNode { function convertSpecValue(value: unknown): React.ReactNode {
@ -129,7 +128,6 @@ export class CustomResourceDetails extends React.Component<CustomResourceDetails
return ( return (
<div className={cssNames("CrdResourceDetails", crd.getResourceKind())}> <div className={cssNames("CrdResourceDetails", crd.getResourceKind())}>
<KubeObjectMeta object={object} />
{this.renderAdditionalColumns(object, extraColumns)} {this.renderAdditionalColumns(object, extraColumns)}
{this.renderStatus(object, extraColumns)} {this.renderStatus(object, extraColumns)}
</div> </div>

View File

@ -58,7 +58,7 @@ export class CustomResourceDefinitionStore extends KubeObjectStore<CustomResourc
} }
getByObject(obj: KubeObject) { getByObject(obj: KubeObject) {
if (!obj) return null; if (!obj) return undefined;
const { kind, apiVersion } = obj; const { kind, apiVersion } = obj;
return this.items.find(crd => ( return this.items.find(crd => (

View File

@ -17,7 +17,6 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { getDetailsUrl } from "../kube-detail-params"; import { getDetailsUrl } from "../kube-detail-params";
import type { Job } from "../../../common/k8s-api/endpoints"; import type { Job } from "../../../common/k8s-api/endpoints";
import { CronJob } from "../../../common/k8s-api/endpoints"; import { CronJob } from "../../../common/k8s-api/endpoints";
import { KubeObjectMeta } from "../kube-object-meta";
import logger from "../../../common/logger"; import logger from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api"; import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
@ -61,7 +60,6 @@ class NonInjectedCronJobDetails extends React.Component<CronJobDetailsProps & De
return ( return (
<div className="CronJobDetails"> <div className="CronJobDetails">
<KubeObjectMeta object={cronJob}/>
<DrawerItem name="Schedule"> <DrawerItem name="Schedule">
{ {
cronJob.isNeverRun() cronJob.isNeverRun()

View File

@ -3,10 +3,15 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable"; import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import { asyncComputed } from "@ogre-tools/injectable-react";
import type { KubeObject } from "../../../common/k8s-api/kube-object";
export type CurrentKubeObject =
| undefined
| { object: KubeObject; error?: undefined }
| { object?: undefined; error: string };
const currentKubeObjectInDetailsInjectable = getInjectable({ const currentKubeObjectInDetailsInjectable = getInjectable({
id: "current-kube-object-in-details", id: "current-kube-object-in-details",
@ -14,19 +19,21 @@ const currentKubeObjectInDetailsInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const urlParam = di.inject(kubeDetailsUrlParamInjectable); const urlParam = di.inject(kubeDetailsUrlParamInjectable);
const apiManager = di.inject(apiManagerInjectable); const apiManager = di.inject(apiManagerInjectable);
const logger = di.inject(loggerInjectable);
return computed(() => { return asyncComputed(async (): Promise<CurrentKubeObject> => {
const path = urlParam.get(); const path = urlParam.get();
const store = apiManager.getStore(path);
if (!store) {
return undefined;
}
try { try {
return apiManager.getStore(path)?.getByPath(path); const object = await store.loadFromPath(path);
} catch (error) {
logger.error(
`[KUBE-OBJECT-DETAILS]: failed to get store or object ${path}: ${error}`,
);
return undefined; return { object };
} catch (error) {
return { error: String(error) };
} }
}); });
}, },

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import React from "react";
import { CustomResourceDetails } from "../+custom-resources";
import customResourceDefinitionStoreInjectable from "../+custom-resources/definition.store.injectable";
import currentKubeObjectInDetailsInjectable from "./current-kube-object-in-details.injectable";
import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-items/kube-object-detail-item-injection-token";
const customResourceDetailItemInjectable = getInjectable({
id: "custom-resource-detail-item",
instantiate: (di) => {
const customResourceDefinitionStore = di.inject(customResourceDefinitionStoreInjectable);
const currentKubeObjectInDetails = di.inject(currentKubeObjectInDetailsInjectable);
const currentCustomResourceDefinition = computed(() => {
const { object } = currentKubeObjectInDetails.value.get() ?? {};
if (!object) {
return undefined;
}
return customResourceDefinitionStore.getByObject(object);
});
return {
Component: ({ object }) => <CustomResourceDetails object={object} crd={currentCustomResourceDefinition.get()} />,
enabled: computed(() => Boolean(currentCustomResourceDefinition.get())),
orderNumber: 100,
};
},
injectionToken: kubeObjectDetailItemInjectionToken,
});
export default customResourceDetailItemInjectable;

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { KubeObjectMeta } from "../kube-object-meta";
import { kubeObjectDetailItemInjectionToken } from "./kube-object-detail-items/kube-object-detail-item-injection-token";
const defaultKubeObjectMetaDetailsItemInjectable = getInjectable({
id: "default-kube-object-meta-details-item",
instantiate: () => ({
Component: KubeObjectMeta,
enabled: computed(() => true),
orderNumber: -Infinity,
}),
injectionToken: kubeObjectDetailItemInjectionToken,
});
export default defaultKubeObjectMetaDetailsItemInjectable;

View File

@ -17,7 +17,7 @@ const clusterRoleBindingDetailItemInjectable = getInjectable({
return { return {
Component: ClusterRoleBindingDetails, Component: ClusterRoleBindingDetails,
enabled: computed(() => isClusterRoleBinding(kubeObject.get())), enabled: computed(() => isClusterRoleBinding(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const clusterRoleDetailItemInjectable = getInjectable({
return { return {
Component: ClusterRoleDetails, Component: ClusterRoleDetails,
enabled: computed(() => isClusterRole(kubeObject.get())), enabled: computed(() => isClusterRole(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const configMapDetailItemInjectable = getInjectable({
return { return {
Component: ConfigMapDetails, Component: ConfigMapDetails,
enabled: computed(() => isConfigMap(kubeObject.get())), enabled: computed(() => isConfigMap(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const cronJobDetailItemInjectable = getInjectable({
return { return {
Component: CronJobDetails, Component: CronJobDetails,
enabled: computed(() => isCronJob(kubeObject.get())), enabled: computed(() => isCronJob(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const customResourceDefinitionsDetailItemInjectable = getInjectable({
return { return {
Component: CRDDetails, Component: CRDDetails,
enabled: computed(() => isCustomResourceDefinition(kubeObject.get())), enabled: computed(() => isCustomResourceDefinition(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const daemonSetDetailItemInjectable = getInjectable({
return { return {
Component: DaemonSetDetails, Component: DaemonSetDetails,
enabled: computed(() => isDaemonSet(kubeObject.get())), enabled: computed(() => isDaemonSet(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const deploymentDetailItemInjectable = getInjectable({
return { return {
Component: DeploymentDetails, Component: DeploymentDetails,
enabled: computed(() => isDeployment(kubeObject.get())), enabled: computed(() => isDeployment(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const endpointsDetailItemInjectable = getInjectable({
return { return {
Component: EndpointsDetails, Component: EndpointsDetails,
enabled: computed(() => isEndpoint(kubeObject.get())), enabled: computed(() => isEndpoint(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const eventsDetailItemInjectable = getInjectable({
return { return {
Component: EventDetails, Component: EventDetails,
enabled: computed(() => isEvent(kubeObject.get())), enabled: computed(() => isEvent(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({
return { return {
Component: HpaDetails, Component: HpaDetails,
enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.get())), enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const ingressDetailItemInjectable = getInjectable({
return { return {
Component: IngressDetails, Component: IngressDetails,
enabled: computed(() => isIngress(kubeObject.get())), enabled: computed(() => isIngress(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const jobDetailItemInjectable = getInjectable({
return { return {
Component: JobDetails, Component: JobDetails,
enabled: computed(() => isJob(kubeObject.get())), enabled: computed(() => isJob(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -6,77 +6,15 @@ import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token"; import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token";
import { KubeEventDetails } from "../../../+events/kube-event-details"; import { KubeEventDetails } from "../../../+events/kube-event-details";
import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable";
import { isClusterRole } from "./cluster-role-detail-item.injectable";
import { isClusterRoleBinding } from "./cluster-role-binding-detail-item.injectable";
import { isCronJob } from "./cron-job-detail-item.injectable";
import { isDaemonSet } from "./daemon-set-detail-item.injectable";
import { isDeployment } from "./deployment-detail-item.injectable";
import { isEndpoint } from "./endpoints-detail-item.injectable";
import { isHorizontalPodAutoscaler } from "./horizontal-pod-autoscaler-detail-item.injectable";
import { isIngress } from "./ingress-detail-item.injectable";
import { isJob } from "./job-detail-item.injectable";
import { isNetworkPolicy } from "./network-policy-detail-item.injectable";
import { isPersistentVolume } from "./persistent-volume-detail-item.injectable";
import { isPersistentVolumeClaim } from "./persistent-volume-claim-detail-item.injectable";
import { isNode } from "./node-detail-item.injectable";
import { isPod } from "./pod-detail-item.injectable";
import { isReplicaSet } from "./replica-set-detail-item.injectable";
import { isRole } from "./role-detail-item.injectable";
import { isRoleBinding } from "./role-binding-detail-item.injectable";
import { isService } from "./service-detail-item.injectable";
import { isServiceAccount } from "./service-account-detail-item.injectable";
import { isStatefulSet } from "./stateful-set-detail-item.injectable";
import { isStorageClass } from "./storage-class-detail-item.injectable";
const kubeEventDetailItemInjectable = getInjectable({ const kubeEventDetailItemInjectable = getInjectable({
id: "kube-event-detail-item", id: "kube-event-detail-item",
instantiate: (di) => { instantiate: () => ({
const currentKubeObjectInDetails = di.inject( Component: KubeEventDetails,
currentKubeObjectInDetailsInjectable, enabled: computed(() => true),
); orderNumber: Infinity,
}),
return {
Component: KubeEventDetails,
enabled: computed(() => {
const kubeObject = currentKubeObjectInDetails.get();
if (!kubeObject) {
return false;
}
const predicates = [
isClusterRole,
isClusterRoleBinding,
isCronJob,
isDaemonSet,
isDeployment,
isEndpoint,
isHorizontalPodAutoscaler,
isIngress,
isJob,
isNetworkPolicy,
isNode,
isPersistentVolume,
isPersistentVolumeClaim,
isPod,
isReplicaSet,
isRole,
isRoleBinding,
isService,
isServiceAccount,
isStatefulSet,
isStorageClass,
];
return predicates.some((predicate) => predicate(kubeObject));
}),
orderNumber: 355,
};
},
injectionToken: kubeObjectDetailItemInjectionToken, injectionToken: kubeObjectDetailItemInjectionToken,
}); });

View File

@ -17,7 +17,7 @@ const leaseDetailItemInjectable = getInjectable({
return { return {
Component: LeaseDetails, Component: LeaseDetails,
enabled: computed(() => isLease(kubeObject.get())), enabled: computed(() => isLease(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const limitRangeDetailItemInjectable = getInjectable({
return { return {
Component: LimitRangeDetails, Component: LimitRangeDetails,
enabled: computed(() => isLimitRange(kubeObject.get())), enabled: computed(() => isLimitRange(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const namespacesDetailItemInjectable = getInjectable({
return { return {
Component: NamespaceDetails, Component: NamespaceDetails,
enabled: computed(() => isNamespace(kubeObject.get())), enabled: computed(() => isNamespace(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const networkPolicyDetailItemInjectable = getInjectable({
return { return {
Component: NetworkPolicyDetails, Component: NetworkPolicyDetails,
enabled: computed(() => isNetworkPolicy(kubeObject.get())), enabled: computed(() => isNetworkPolicy(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const nodeDetailItemInjectable = getInjectable({
return { return {
Component: NodeDetails, Component: NodeDetails,
enabled: computed(() => isNode(kubeObject.get())), enabled: computed(() => isNode(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const persistentVolumeClaimDetailItemInjectable = getInjectable({
return { return {
Component: PersistentVolumeClaimDetails, Component: PersistentVolumeClaimDetails,
enabled: computed(() => isPersistentVolumeClaim(kubeObject.get())), enabled: computed(() => isPersistentVolumeClaim(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const persistentVolumeDetailItemInjectable = getInjectable({
return { return {
Component: PersistentVolumeDetails, Component: PersistentVolumeDetails,
enabled: computed(() => isPersistentVolume(kubeObject.get())), enabled: computed(() => isPersistentVolume(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const podDetailItemInjectable = getInjectable({
return { return {
Component: PodDetails, Component: PodDetails,
enabled: computed(() => isPod(kubeObject.get())), enabled: computed(() => isPod(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const podDisruptionBudgetDetailItemInjectable = getInjectable({
return { return {
Component: PodDisruptionBudgetDetails, Component: PodDisruptionBudgetDetails,
enabled: computed(() => isPodDisruptionBudget(kubeObject.get())), enabled: computed(() => isPodDisruptionBudget(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const podSecurityPolicyDetailItemInjectable = getInjectable({
return { return {
Component: PodSecurityPolicyDetails, Component: PodSecurityPolicyDetails,
enabled: computed(() => isPodSecurityPolicy(kubeObject.get())), enabled: computed(() => isPodSecurityPolicy(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const priorityClassDetailItemInjectable = getInjectable({
return { return {
Component: PriorityClassesDetails, Component: PriorityClassesDetails,
enabled: computed(() => isPriorityClass(kubeObject.get())), enabled: computed(() => isPriorityClass(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const replicaSetDetailItemInjectable = getInjectable({
return { return {
Component: ReplicaSetDetails, Component: ReplicaSetDetails,
enabled: computed(() => isReplicaSet(kubeObject.get())), enabled: computed(() => isReplicaSet(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const resourceQuotaDetailItemInjectable = getInjectable({
return { return {
Component: ResourceQuotaDetails, Component: ResourceQuotaDetails,
enabled: computed(() => isResourceQuota(kubeObject.get())), enabled: computed(() => isResourceQuota(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const roleBindingDetailItemInjectable = getInjectable({
return { return {
Component: RoleBindingDetails, Component: RoleBindingDetails,
enabled: computed(() => isRoleBinding(kubeObject.get())), enabled: computed(() => isRoleBinding(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const roleDetailItemInjectable = getInjectable({
return { return {
Component: RoleDetails, Component: RoleDetails,
enabled: computed(() => isRole(kubeObject.get())), enabled: computed(() => isRole(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const runtimeClassDetailItemInjectable = getInjectable({
return { return {
Component: RuntimeClassesDetails, Component: RuntimeClassesDetails,
enabled: computed(() => isRuntimeClass(kubeObject.get())), enabled: computed(() => isRuntimeClass(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const secretsDetailItemInjectable = getInjectable({
return { return {
Component: SecretDetails, Component: SecretDetails,
enabled: computed(() => isSecret(kubeObject.get())), enabled: computed(() => isSecret(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const serviceAccountDetailItemInjectable = getInjectable({
return { return {
Component: ServiceAccountsDetails, Component: ServiceAccountsDetails,
enabled: computed(() => isServiceAccount(kubeObject.get())), enabled: computed(() => isServiceAccount(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const serviceDetailItemInjectable = getInjectable({
return { return {
Component: ServiceDetails, Component: ServiceDetails,
enabled: computed(() => isService(kubeObject.get())), enabled: computed(() => isService(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const statefulSetDetailItemInjectable = getInjectable({
return { return {
Component: StatefulSetDetails, Component: StatefulSetDetails,
enabled: computed(() => isStatefulSet(kubeObject.get())), enabled: computed(() => isStatefulSet(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -17,7 +17,7 @@ const storageClassDetailItemInjectable = getInjectable({
return { return {
Component: StorageClassDetails, Component: StorageClassDetails,
enabled: computed(() => isStorageClass(kubeObject.get())), enabled: computed(() => isStorageClass(kubeObject.value.get()?.object)),
orderNumber: 10, orderNumber: 10,
}; };
}, },

View File

@ -55,7 +55,7 @@ const kubeObjectDetailItemRegistratorInjectable = getInjectable({
return false; return false;
} }
if (!isRelevantKubeObject(kubeObject.get())) { if (!isRelevantKubeObject(kubeObject.value.get()?.object)) {
return false; return false;
} }

View File

@ -6,25 +6,18 @@
import "./kube-object-details.scss"; import "./kube-object-details.scss";
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { observer } from "mobx-react";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import { observable, reaction, makeObservable } from "mobx";
import { Drawer } from "../drawer"; import { Drawer } from "../drawer";
import type { KubeObject } from "../../../common/k8s-api/kube-object"; import type { KubeObject } from "../../../common/k8s-api/kube-object";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import type { ApiManager } from "../../../common/k8s-api/api-manager";
import { KubeObjectMenu } from "../kube-object-menu"; import { KubeObjectMenu } from "../kube-object-menu";
import { CustomResourceDetails } from "../+custom-resources";
import { KubeObjectMeta } from "../kube-object-meta";
import type { PageParam } from "../../navigation";
import type { HideDetails } from "../kube-detail-params/hide-details.injectable"; import type { HideDetails } from "../kube-detail-params/hide-details.injectable";
import type { CustomResourceDefinitionStore } from "../+custom-resources/definition.store"; import type { IAsyncComputed } from "@ogre-tools/injectable-react";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import customResourceDefinitionStoreInjectable from "../+custom-resources/definition.store.injectable";
import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable"; import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable";
import kubeDetailsUrlParamInjectable from "../kube-detail-params/kube-details-url.injectable";
import kubeObjectDetailItemsInjectable from "./kube-object-detail-items/kube-object-detail-items.injectable"; import kubeObjectDetailItemsInjectable from "./kube-object-detail-items/kube-object-detail-items.injectable";
import type { CurrentKubeObject } from "./current-kube-object-in-details.injectable";
import currentKubeObjectInDetailsInjectable from "./current-kube-object-in-details.injectable"; import currentKubeObjectInDetailsInjectable from "./current-kube-object-in-details.injectable";
export interface KubeObjectDetailsProps<Kube extends KubeObject = KubeObject> { export interface KubeObjectDetailsProps<Kube extends KubeObject = KubeObject> {
@ -34,128 +27,55 @@ export interface KubeObjectDetailsProps<Kube extends KubeObject = KubeObject> {
interface Dependencies { interface Dependencies {
detailComponents: IComputedValue<React.ElementType[]>; detailComponents: IComputedValue<React.ElementType[]>;
kubeObject: IComputedValue<KubeObject | undefined>; kubeObject: IAsyncComputed<CurrentKubeObject>;
kubeDetailsUrlParam: PageParam<string>;
apiManager: ApiManager;
hideDetails: HideDetails; hideDetails: HideDetails;
customResourceDefinitionStore: CustomResourceDefinitionStore;
} }
@observer const NonInjectedKubeObjectDetails = observer((props: Dependencies) => {
class NonInjectedKubeObjectDetails extends React.Component<Dependencies> { const {
@observable isLoading = false; detailComponents,
@observable.ref loadingError: React.ReactNode; hideDetails,
kubeObject,
} = props;
constructor(props: Dependencies) { const currentKubeObject = kubeObject.value.get();
super(props); const isLoading = kubeObject.pending.get();
makeObservable(this);
}
get path() { return (
return this.props.kubeDetailsUrlParam.get(); <Drawer
} className="KubeObjectDetails flex column"
open={Boolean(isLoading || currentKubeObject)}
get object() { title={(
return this.props.kubeObject.get(); currentKubeObject?.object
} ? `${currentKubeObject.object.kind}: ${currentKubeObject.object.getName()}`
: ""
componentDidMount(): void { )}
disposeOnUnmount(this, [ toolbar={currentKubeObject?.object && <KubeObjectMenu object={currentKubeObject.object} toolbar={true}/>}
reaction(() => [ onClose={hideDetails}
this.path, >
this.object, // resource might be updated via watch-event or from already opened details {isLoading && <Spinner center/>}
this.props.customResourceDefinitionStore.items.length, // crd stores initialized after loading {currentKubeObject?.error && (
], async () => { <div className="box center">
this.loadingError = ""; Resource loading has failed:
const { path, object } = this; <b>{currentKubeObject.error}</b>
</div>
if (!object) { )}
const store = this.props.apiManager.getStore(path); {currentKubeObject?.object && (
<>
if (store) { {
this.isLoading = true; detailComponents.get()
.map((Component, index) => <Component key={index} object={currentKubeObject.object} />)
try {
await store.loadFromPath(path);
} catch (err) {
this.loadingError = (
<>
Resource loading has failed:
<b>{String(err)}</b>
</>
);
} finally {
this.isLoading = false;
}
} }
} </>
}), )}
]); </Drawer>
} );
});
renderTitle(object: KubeObject | null | undefined) {
if (!object) {
return "";
}
return `${object.kind}: ${object.getName()}`;
}
renderContents(object: KubeObject) {
const details = this.props.detailComponents.get();
if (details.length === 0) {
const crd = this.props.customResourceDefinitionStore.getByObject(object);
/**
* This is a fallback so that if a custom resource object doesn't have
* any defined details we should try and display at least some details
*/
if (crd) {
return (
<CustomResourceDetails
key={object.getId()}
object={object}
crd={crd}
/>
);
} else {
// if we still don't have any details to show, just show the standard object metadata
return <KubeObjectMeta key={object.getId()} object={object} />;
}
}
return details.map((DetailComponent, index) => (
<DetailComponent key={index} object={object} />
));
}
render() {
const { object, isLoading, loadingError } = this;
return (
<Drawer
className="KubeObjectDetails flex column"
open={!!(object || isLoading || loadingError)}
title={this.renderTitle(object)}
toolbar={object && <KubeObjectMenu object={object} toolbar={true}/>}
onClose={this.props.hideDetails}
>
{isLoading && <Spinner center/>}
{loadingError && <div className="box center">{loadingError}</div>}
{object && this.renderContents(object)}
</Drawer>
);
}
}
export const KubeObjectDetails = withInjectables<Dependencies>(NonInjectedKubeObjectDetails, { export const KubeObjectDetails = withInjectables<Dependencies>(NonInjectedKubeObjectDetails, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
apiManager: di.inject(apiManagerInjectable),
customResourceDefinitionStore: di.inject(customResourceDefinitionStoreInjectable),
hideDetails: di.inject(hideDetailsInjectable), hideDetails: di.inject(hideDetailsInjectable),
kubeDetailsUrlParam: di.inject(kubeDetailsUrlParamInjectable),
detailComponents: di.inject(kubeObjectDetailItemsInjectable), detailComponents: di.inject(kubeObjectDetailItemsInjectable),
kubeObject: di.inject(currentKubeObjectInDetailsInjectable), kubeObject: di.inject(currentKubeObjectInDetailsInjectable),
}), }),