From 9b55a5fd0a9563cbca9621a5a464148fff5b0cf7 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 11 Nov 2020 17:10:44 +0200 Subject: [PATCH] PageRegistration refactoring -- part 2 Signed-off-by: Roman --- src/extensions/extension-loader.ts | 23 +++-- src/extensions/interfaces/registrations.ts | 1 + src/extensions/lens-extension.ts | 10 +++ src/extensions/lens-renderer-extension.ts | 6 +- src/extensions/registries/base-registry.ts | 36 +++++--- .../registries/kube-object-detail-registry.ts | 2 +- .../registries/kube-object-menu-registry.ts | 2 +- .../registries/kube-object-status-registry.ts | 2 +- .../registries/page-menu-registry.ts | 23 +++-- src/extensions/registries/page-registry.ts | 25 +++--- .../+config-autoscalers/hpa-details.tsx | 14 +-- .../+config-maps/config-map-details.tsx | 14 +-- .../pod-disruption-budgets-details.tsx | 14 +-- .../resource-quota-details.tsx | 16 ++-- .../+config-secrets/secret-details.tsx | 14 +-- .../+custom-resources/crd-details.tsx | 14 +-- .../components/+events/event-details.tsx | 14 +-- .../+namespaces/namespace-details.tsx | 14 +-- .../+network-endpoints/endpoint-details.tsx | 15 ++-- .../+network-ingresses/ingress-details.tsx | 14 +-- .../network-policy-details.tsx | 14 +-- .../+network-services/service-details.tsx | 14 +-- .../components/+nodes/node-details.tsx | 14 +-- .../pod-security-policy-details.tsx | 14 +-- .../storage-class-details.tsx | 14 +-- .../volume-claim-details.tsx | 14 +-- .../+storage-volumes/volume-details.tsx | 14 +-- .../role-binding-details.tsx | 28 +++--- .../+user-management-roles/role-details.tsx | 29 ++++--- .../service-accounts-details.tsx | 14 +-- .../service-accounts.tsx | 14 +-- .../+workloads-cronjobs/cronjob-details.tsx | 14 +-- .../+workloads-cronjobs/cronjobs.tsx | 14 +-- .../daemonset-details.tsx | 14 +-- .../deployment-details.tsx | 16 ++-- .../+workloads-deployments/deployments.tsx | 14 +-- .../+workloads-jobs/job-details.tsx | 14 +-- .../+workloads-pods/pod-details.tsx | 14 +-- .../replicaset-details.tsx | 14 +-- .../statefulset-details.tsx | 14 +-- src/renderer/components/app.tsx | 39 +++++++-- .../cluster-manager/clusters-menu.tsx | 8 +- src/renderer/components/layout/sidebar.tsx | 85 +++++++++---------- src/renderer/navigation.ts | 6 +- 44 files changed, 422 insertions(+), 300 deletions(-) diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index 7f7f4deb45..e32f44e208 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -57,31 +57,30 @@ export class ExtensionLoader { loadOnMain() { logger.info('[EXTENSIONS-LOADER]: load on main') this.autoInitExtensions((extension: LensMainExtension) => [ - registries.menuRegistry.add(...extension.appMenus) + registries.menuRegistry.add(extension, extension.appMenus) ]); } loadOnClusterManagerRenderer() { logger.info('[EXTENSIONS-LOADER]: load on main renderer (cluster manager)') this.autoInitExtensions((extension: LensRendererExtension) => [ - registries.globalPageRegistry.add(...extension.globalPages), - registries.globalPageMenuRegistry.add(...extension.globalPageMenus), - registries.appPreferenceRegistry.add(...extension.appPreferences), - registries.clusterFeatureRegistry.add(...extension.clusterFeatures), - registries.statusBarRegistry.add(...extension.statusBarItems), + registries.globalPageRegistry.add(extension, extension.globalPages), + registries.globalPageMenuRegistry.add(extension, extension.globalPageMenus), + registries.appPreferenceRegistry.add(extension, extension.appPreferences), + registries.clusterFeatureRegistry.add(extension, extension.clusterFeatures), + registries.statusBarRegistry.add(extension, extension.statusBarItems), ]); } loadOnClusterRenderer() { logger.info('[EXTENSIONS-LOADER]: load on cluster renderer (dashboard)') this.autoInitExtensions((extension: LensRendererExtension) => [ - registries.clusterPageRegistry.add(...extension.clusterPages), - registries.clusterPageMenuRegistry.add(...extension.clusterPageMenus), - registries.kubeObjectMenuRegistry.add(...extension.kubeObjectMenuItems), - registries.kubeObjectDetailRegistry.add(...extension.kubeObjectDetailItems), - registries.kubeObjectStatusRegistry.add(...extension.kubeObjectStatusTexts) + registries.clusterPageRegistry.add(extension, extension.clusterPages), + registries.clusterPageMenuRegistry.add(extension, extension.clusterPageMenus), + registries.kubeObjectMenuRegistry.add(extension, extension.kubeObjectMenuItems), + registries.kubeObjectDetailRegistry.add(extension, extension.kubeObjectDetailItems), + registries.kubeObjectStatusRegistry.add(extension, extension.kubeObjectStatusTexts) ]) - } protected autoInitExtensions(register: (ext: LensExtension) => Function[]) { diff --git a/src/extensions/interfaces/registrations.ts b/src/extensions/interfaces/registrations.ts index 1d875127d0..14c9f66c22 100644 --- a/src/extensions/interfaces/registrations.ts +++ b/src/extensions/interfaces/registrations.ts @@ -4,4 +4,5 @@ export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from ". export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../registries/kube-object-menu-registry" export type { KubeObjectStatusRegistration } from "../registries/kube-object-status-registry" export type { PageRegistration, PageComponents } from "../registries/page-registry" +export type { PageMenuRegistration, PageMenuComponents } from "../registries/page-menu-registry" export type { StatusBarRegistration } from "../registries/status-bar-registry" \ No newline at end of file diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index f1ffb9184b..5621fd9ff3 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -1,5 +1,6 @@ import type { InstalledExtension } from "./extension-manager"; import { action, observable, reaction } from "mobx"; +import { compile } from "path-to-regexp" import logger from "../main/logger"; export type LensExtensionId = string; // path to manifest (package.json) @@ -14,6 +15,7 @@ export interface LensExtensionManifest { } export class LensExtension { + readonly routePrefix = "/extensions/:name" readonly manifest: LensExtensionManifest; readonly manifestPath: string; readonly isBundled: boolean; @@ -42,6 +44,14 @@ export class LensExtension { return this.manifest.description } + getPageUrl(baseUrl: string) { + return compile(this.routePrefix)({ name: this.name }) + "/" + baseUrl + } + + getPageRoute(baseRoute: string) { + return this.routePrefix + "/" + baseRoute; + } + @action async enable() { if (this.isEnabled) return; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 369aad9230..018cd254ca 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -1,16 +1,16 @@ import type { AppPreferenceRegistration, ClusterFeatureRegistration, KubeObjectMenuRegistration, KubeObjectDetailRegistration, StatusBarRegistration, KubeObjectStatusRegistration, - PageRegistration, PageMenuRegistration, PageRegistrationCluster, PageMenuRegistrationCluster, + PageRegistration, PageMenuRegistration, } from "./registries" import { observable } from "mobx"; import { LensExtension } from "./lens-extension" export class LensRendererExtension extends LensExtension { @observable.shallow globalPages: PageRegistration[] = [] - @observable.shallow clusterPages: PageRegistrationCluster[] = [] + @observable.shallow clusterPages: PageRegistration[] = [] @observable.shallow globalPageMenus: PageMenuRegistration[] = [] - @observable.shallow clusterPageMenus: PageMenuRegistrationCluster[] = [] + @observable.shallow clusterPageMenus: PageMenuRegistration[] = [] @observable.shallow kubeObjectStatusTexts: KubeObjectStatusRegistration[] = [] @observable.shallow appPreferences: AppPreferenceRegistration[] = [] @observable.shallow clusterFeatures: ClusterFeatureRegistration[] = [] diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts index ff23e36cad..41efa171d6 100644 --- a/src/extensions/registries/base-registry.ts +++ b/src/extensions/registries/base-registry.ts @@ -1,23 +1,37 @@ // Base class for extensions-api registries import { action, observable } from "mobx"; +import { LensExtension } from "../lens-extension"; -export class BaseRegistry { - protected items = observable([], { deep: false }); +export class BaseRegistry { + private items = observable.map([], { deep: false }); - getItems(): T[] { - return this.items.toJS(); + getItems(): (T & { extension?: LensExtension })[] { + return Array.from(this.items).map(([ext, items]) => { + return items.map(item => ({ + ...item, + extension: ext, + })) + }).flat() } @action - add(...items: T[]) { - this.items.push(...items); - return () => this.remove(...items); + add(ext: LensExtension | null, items: T[], merge = true) { + if (merge && this.items.has(ext)) { + const newItems = new Set(this.items.get(ext)); + items.forEach(item => newItems.add(item)) + this.items.set(ext, [...newItems]); + } else { + this.items.set(ext, items); + } + return () => this.remove(ext, items) } @action - remove(...items: T[]) { - items.forEach(item => { - this.items.remove(item); // works because of {deep: false}; - }) + remove(ext: LensExtension | null, items: T[]) { + const storedItems = this.items.get(ext); + if (storedItems) { + const newItems = storedItems.filter(item => !items.includes(item)); // works because of {deep: false}; + this.items.set(ext, newItems); + } } } diff --git a/src/extensions/registries/kube-object-detail-registry.ts b/src/extensions/registries/kube-object-detail-registry.ts index bfa8861ccf..a177da5217 100644 --- a/src/extensions/registries/kube-object-detail-registry.ts +++ b/src/extensions/registries/kube-object-detail-registry.ts @@ -13,7 +13,7 @@ export interface KubeObjectDetailRegistration { export class KubeObjectDetailRegistry extends BaseRegistry { getItemsForKind(kind: string, apiVersion: string) { - return this.items.filter((item) => { + return this.getItems().filter((item) => { return item.kind === kind && item.apiVersions.includes(apiVersion) }) } diff --git a/src/extensions/registries/kube-object-menu-registry.ts b/src/extensions/registries/kube-object-menu-registry.ts index 335b1b7441..8f527d6a3d 100644 --- a/src/extensions/registries/kube-object-menu-registry.ts +++ b/src/extensions/registries/kube-object-menu-registry.ts @@ -13,7 +13,7 @@ export interface KubeObjectMenuRegistration { export class KubeObjectMenuRegistry extends BaseRegistry { getItemsForKind(kind: string, apiVersion: string) { - return this.items.filter((item) => { + return this.getItems().filter((item) => { return item.kind === kind && item.apiVersions.includes(apiVersion) }) } diff --git a/src/extensions/registries/kube-object-status-registry.ts b/src/extensions/registries/kube-object-status-registry.ts index bd3a6e0225..74fd8145d2 100644 --- a/src/extensions/registries/kube-object-status-registry.ts +++ b/src/extensions/registries/kube-object-status-registry.ts @@ -9,7 +9,7 @@ export interface KubeObjectStatusRegistration { export class KubeObjectStatusRegistry extends BaseRegistry { getItemsForKind(kind: string, apiVersion: string) { - return this.items.filter((item) => { + return this.getItems().filter((item) => { return item.kind === kind && item.apiVersions.includes(apiVersion) }) } diff --git a/src/extensions/registries/page-menu-registry.ts b/src/extensions/registries/page-menu-registry.ts index 150a8c459d..41680dabf9 100644 --- a/src/extensions/registries/page-menu-registry.ts +++ b/src/extensions/registries/page-menu-registry.ts @@ -3,14 +3,12 @@ import type React from "react"; import type { IconProps } from "../../renderer/components/icon"; import { BaseRegistry } from "./base-registry"; +import { matchPath } from "react-router"; export interface PageMenuRegistration { url: string; title: React.ReactNode; components: PageMenuComponents; -} - -export interface PageMenuRegistrationCluster extends PageMenuRegistration { subMenus?: Omit[]; } @@ -18,8 +16,21 @@ export interface PageMenuComponents { Icon: React.ComponentType; } -export class PageMenuRegistry extends BaseRegistry { +export class PageMenuRegistry extends BaseRegistry { + getByMatchingRoute(routePath: string | string[], exact?: boolean) { + return this.getItems().find(item => !!matchPath(item.url, { + path: routePath, + exact, + })) + } + + getItems() { + return super.getItems().map(item => { + item.url = item.extension.getPageUrl(item.url) + return item + }); + } } -export const globalPageMenuRegistry = new PageMenuRegistry(); -export const clusterPageMenuRegistry = new PageMenuRegistry(); +export const globalPageMenuRegistry = new PageMenuRegistry(); +export const clusterPageMenuRegistry = new PageMenuRegistry(); diff --git a/src/extensions/registries/page-registry.ts b/src/extensions/registries/page-registry.ts index 8494eeda27..886a00ea6a 100644 --- a/src/extensions/registries/page-registry.ts +++ b/src/extensions/registries/page-registry.ts @@ -1,29 +1,34 @@ // Extensions-api -> Custom page registration -import type React from "react"; +import React from "react"; +import { matchPath } from "react-router"; import { BaseRegistry } from "./base-registry"; export interface PageRegistration { routePath: string; // react-router's path, e.g. "/page/:id" exact?: boolean; // route matching flag, see: https://reactrouter.com/web/api/NavLink/exact-bool components: PageComponents; -} - -export interface PageRegistrationCluster extends PageRegistration { - subPages?: Omit; + subPages?: Omit[]; } export interface PageComponents { Page: React.ComponentType; } -export class PageRegistry extends BaseRegistry { - protected routePrefixPath = "/extensions/:name" // todo: figure out how to provide inside extension +export class PageRegistry extends BaseRegistry { + getByMatchingUrl(baseUrl: string) { + return this.getItems().find(({ routePath: path, exact }) => { + return !!matchPath(baseUrl, { path, exact }); + }) + } getItems() { - return super.getItems(); + return super.getItems().map(item => { + item.routePath = item.extension.getPageRoute(item.routePath) + return item + }); } } -export const globalPageRegistry = new PageRegistry(); -export const clusterPageRegistry = new PageRegistry(); +export const globalPageRegistry = new PageRegistry(); +export const clusterPageRegistry = new PageRegistry(); diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index edd2624ba4..fd2f176301 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -128,10 +128,12 @@ export class HpaDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "HorizontalPodAutoscaler", - apiVersions: ["autoscaling/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "HorizontalPodAutoscaler", + apiVersions: ["autoscaling/v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx index a0d824ae98..6cb5bd47c8 100644 --- a/src/renderer/components/+config-maps/config-map-details.tsx +++ b/src/renderer/components/+config-maps/config-map-details.tsx @@ -94,10 +94,12 @@ export class ConfigMapDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "ConfigMap", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "ConfigMap", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx index 745d588be3..b98cb9e1a6 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets-details.tsx @@ -54,10 +54,12 @@ export class PodDisruptionBudgetDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "PodDisruptionBudget", - apiVersions: ["policy/v1beta1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "PodDisruptionBudget", + apiVersions: ["policy/v1beta1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx index 4a2ff08238..fcde6747cd 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quota-details.tsx @@ -16,8 +16,6 @@ import { ReplicaSetDetails } from "../+workloads-replicasets"; interface Props extends KubeObjectDetailsProps { } -const onlyNumbers = /$[0-9]*^/g; - function transformUnit(name: string, value: string): number { if (name.includes("memory") || name.includes("storage")) { return unitsToBytes(value) @@ -98,10 +96,12 @@ export class ResourceQuotaDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "ResourceQuota", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "ResourceQuota", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+config-secrets/secret-details.tsx b/src/renderer/components/+config-secrets/secret-details.tsx index 5e10eee94c..8785fa9d67 100644 --- a/src/renderer/components/+config-secrets/secret-details.tsx +++ b/src/renderer/components/+config-secrets/secret-details.tsx @@ -113,10 +113,12 @@ export class SecretDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Secret", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Secret", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index b921eb0d04..25f50e7452 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -134,10 +134,12 @@ export class CRDDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "CustomResourceDefinition", - apiVersions: ["apiextensions.k8s.io/v1", "apiextensions.k8s.io/v1beta1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "CustomResourceDefinition", + apiVersions: ["apiextensions.k8s.io/v1", "apiextensions.k8s.io/v1beta1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+events/event-details.tsx b/src/renderer/components/+events/event-details.tsx index b3d80ce318..c11e5230a1 100644 --- a/src/renderer/components/+events/event-details.tsx +++ b/src/renderer/components/+events/event-details.tsx @@ -74,10 +74,12 @@ export class EventDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Event", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Event", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+namespaces/namespace-details.tsx b/src/renderer/components/+namespaces/namespace-details.tsx index a7e2abf2f3..f2ca7600bc 100644 --- a/src/renderer/components/+namespaces/namespace-details.tsx +++ b/src/renderer/components/+namespaces/namespace-details.tsx @@ -56,10 +56,12 @@ export class NamespaceDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Namespace", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Namespace", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+network-endpoints/endpoint-details.tsx b/src/renderer/components/+network-endpoints/endpoint-details.tsx index 27d25956f5..f934b3d4fe 100644 --- a/src/renderer/components/+network-endpoints/endpoint-details.tsx +++ b/src/renderer/components/+network-endpoints/endpoint-details.tsx @@ -7,7 +7,6 @@ import { DrawerTitle } from "../drawer"; import { KubeEventDetails } from "../+events/kube-event-details"; import { KubeObjectDetailsProps } from "../kube-object"; import { Endpoint } from "../../api/endpoints"; -import { _i18n } from "../../i18n"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { EndpointSubsetList } from "./endpoint-subset-list"; import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; @@ -38,10 +37,12 @@ export class EndpointDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Endpoints", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Endpoints", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+network-ingresses/ingress-details.tsx b/src/renderer/components/+network-ingresses/ingress-details.tsx index f87da6fc7b..6a0fc440cf 100644 --- a/src/renderer/components/+network-ingresses/ingress-details.tsx +++ b/src/renderer/components/+network-ingresses/ingress-details.tsx @@ -134,10 +134,12 @@ export class IngressDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Ingress", - apiVersions: ["extensions/v1beta1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Ingress", + apiVersions: ["extensions/v1beta1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+network-policies/network-policy-details.tsx b/src/renderer/components/+network-policies/network-policy-details.tsx index bf936a9477..b1e65469c7 100644 --- a/src/renderer/components/+network-policies/network-policy-details.tsx +++ b/src/renderer/components/+network-policies/network-policy-details.tsx @@ -144,10 +144,12 @@ export class NetworkPolicyDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "NetworkPolicy", - apiVersions: ["networking.k8s.io/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "NetworkPolicy", + apiVersions: ["networking.k8s.io/v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 5f438b498a..f3c9b32c35 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -85,10 +85,12 @@ export class ServiceDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Service", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Service", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+nodes/node-details.tsx b/src/renderer/components/+nodes/node-details.tsx index 9a09242c1f..691c0b54fa 100644 --- a/src/renderer/components/+nodes/node-details.tsx +++ b/src/renderer/components/+nodes/node-details.tsx @@ -155,10 +155,12 @@ export class NodeDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Node", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Node", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx index 2c50a4b668..6bf1e88911 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policy-details.tsx @@ -209,10 +209,12 @@ export class PodSecurityPolicyDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "PodSecurityPolicy", - apiVersions: ["policy/v1beta1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "PodSecurityPolicy", + apiVersions: ["policy/v1beta1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+storage-classes/storage-class-details.tsx b/src/renderer/components/+storage-classes/storage-class-details.tsx index 1d24a61861..bdbb9f89f2 100644 --- a/src/renderer/components/+storage-classes/storage-class-details.tsx +++ b/src/renderer/components/+storage-classes/storage-class-details.tsx @@ -62,10 +62,12 @@ export class StorageClassDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "StorageClass", - apiVersions: ["storage.k8s.io/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "StorageClass", + apiVersions: ["storage.k8s.io/v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx index 8b4e6187f1..b480e06322 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx @@ -95,10 +95,12 @@ export class PersistentVolumeClaimDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "PersistentVolumeClaim", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "PersistentVolumeClaim", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+storage-volumes/volume-details.tsx b/src/renderer/components/+storage-volumes/volume-details.tsx index 158eed4289..db3dbc9092 100644 --- a/src/renderer/components/+storage-volumes/volume-details.tsx +++ b/src/renderer/components/+storage-volumes/volume-details.tsx @@ -103,10 +103,12 @@ export class PersistentVolumeDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "PersistentVolume", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "PersistentVolume", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx b/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx index 0d7de24412..01dd131f96 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx +++ b/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx @@ -125,17 +125,19 @@ export class RoleBindingDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "RoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "RoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: (props) => + } + }, + { + kind: "ClusterRoleBinding", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: (props) => + } } -}) -kubeObjectDetailRegistry.add({ - kind: "ClusterRoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: (props) => - } -}) +]) diff --git a/src/renderer/components/+user-management-roles/role-details.tsx b/src/renderer/components/+user-management-roles/role-details.tsx index 164fbef093..82af634b67 100644 --- a/src/renderer/components/+user-management-roles/role-details.tsx +++ b/src/renderer/components/+user-management-roles/role-details.tsx @@ -66,18 +66,19 @@ export class RoleDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Role", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Role", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: (props) => + } + }, + { + kind: "ClusterRole", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: (props) => + } } -}) - -kubeObjectDetailRegistry.add({ - kind: "ClusterRole", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: (props) => - } -}) +]) \ No newline at end of file diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx b/src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx index 1d2fac6437..4ff5e46bc5 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx +++ b/src/renderer/components/+user-management-service-accounts/service-accounts-details.tsx @@ -132,10 +132,12 @@ export class ServiceAccountsDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "ServiceAccount", - apiVersions: ["v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "ServiceAccount", + apiVersions: ["v1"], + components: { + Details: (props) => + } } -}) +]) 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 0455a5cdc0..b0d139d766 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx +++ b/src/renderer/components/+user-management-service-accounts/service-accounts.tsx @@ -77,10 +77,12 @@ function ServiceAccountMenu(props: KubeObjectMenuProps) { ) } -kubeObjectMenuRegistry.add({ - kind: "ServiceAccount", - apiVersions: ["v1"], - components: { - MenuItem: ServiceAccountMenu +kubeObjectMenuRegistry.add(null, [ + { + kind: "ServiceAccount", + apiVersions: ["v1"], + components: { + MenuItem: ServiceAccountMenu + } } -}) +]) diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx index 477819deb8..be2fb0c9ba 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.tsx @@ -87,10 +87,12 @@ export class CronJobDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "CronJob", - apiVersions: ["batch/v1"], - components: { - Details: (props) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "CronJob", + apiVersions: ["batch/v1"], + components: { + Details: (props) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index ab7609de81..2448e3887c 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -88,10 +88,12 @@ export function CronJobMenu(props: KubeObjectMenuProps) { ) } -kubeObjectMenuRegistry.add({ - kind: "CronJob", - apiVersions: ["batch/v1beta1"], - components: { - MenuItem: CronJobMenu +kubeObjectMenuRegistry.add(null, [ + { + kind: "CronJob", + apiVersions: ["batch/v1beta1"], + components: { + MenuItem: CronJobMenu + } } -}) +]) diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx index 1904bed4f0..7aaee14dfb 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx @@ -97,10 +97,12 @@ export class DaemonSetDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "DaemonSet", - apiVersions: ["apps/v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "DaemonSet", + apiVersions: ["apps/v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx index 8e373cfd52..405eceec36 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx @@ -6,7 +6,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import { t, Trans } from "@lingui/macro"; import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; -import { Deployment, deploymentApi } from "../../api/endpoints"; +import { Deployment } from "../../api/endpoints"; import { cssNames } from "../../utils"; import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; @@ -122,10 +122,12 @@ export class DeploymentDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Deployment", - apiVersions: ["apps/v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Deployment", + apiVersions: ["apps/v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index fdc7348590..47b94aa23f 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -129,10 +129,12 @@ export function DeploymentMenu(props: KubeObjectMenuProps) { ) } -kubeObjectMenuRegistry.add({ - kind: "Deployment", - apiVersions: ["apps/v1"], - components: { - MenuItem: DeploymentMenu +kubeObjectMenuRegistry.add(null, [ + { + kind: "Deployment", + apiVersions: ["apps/v1"], + components: { + MenuItem: DeploymentMenu + } } -}) +]) diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index 7e537bc1f1..32d5826401 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -107,10 +107,12 @@ export class JobDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Job", - apiVersions: ["batch/v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Job", + apiVersions: ["batch/v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx index a029591bbd..1aaffb8c1e 100644 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ b/src/renderer/components/+workloads-pods/pod-details.tsx @@ -221,10 +221,12 @@ export class PodDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "Pod", - apiVersions: ["v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "Pod", + apiVersions: ["v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx index 9e8f9a5a3d..6f3683c522 100644 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx +++ b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx @@ -97,10 +97,12 @@ export class ReplicaSetDetails extends React.Component { } } -kubeObjectDetailRegistry.add({ - kind: "ReplicaSet", - apiVersions: ["apps/v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "ReplicaSet", + apiVersions: ["apps/v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx index 236a4bfe4a..63c2744697 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx @@ -96,10 +96,12 @@ export class StatefulSetDetails extends React.Component { } -kubeObjectDetailRegistry.add({ - kind: "StatefulSet", - apiVersions: ["apps/v1"], - components: { - Details: (props: any) => +kubeObjectDetailRegistry.add(null, [ + { + kind: "StatefulSet", + apiVersions: ["apps/v1"], + components: { + Details: (props: any) => + } } -}) +]) diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 0e388dfb85..e1cdb467a9 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -37,6 +37,7 @@ import logger from "../../main/logger"; import { clusterIpc } from "../../common/cluster-ipc"; import { webFrame } from "electron"; import { clusterPageRegistry } from "../../extensions/registries/page-registry"; +import { clusterPageMenuRegistry } from "../../extensions/registries"; import { extensionLoader } from "../../extensions/extension-loader"; import { appEventBus } from "../../common/event-bus"; import whatInput from 'what-input'; @@ -72,6 +73,34 @@ export class App extends React.Component { return workloadsURL(); } + renderExtensionRoutes() { + return clusterPageRegistry.getItems().map(({ components: { Page }, exact, routePath, subPages }) => { + const Component = () => { + if (subPages) { + const tabs: TabLayoutRoute[] = subPages.map(({ exact, routePath, components: { Page } }) => { + const matchingUrl = clusterPageMenuRegistry.getByMatchingRoute(routePath, exact) + if (!matchingUrl) return; + return { + routePath, exact, + component: Page, + url: matchingUrl.url, + title: matchingUrl.title, + } + }).filter(Boolean); + if (tabs.length > 0) { + return ( + + + + ) + } + } + return + }; + return + }) + } + render() { return ( @@ -90,15 +119,7 @@ export class App extends React.Component { - {clusterPageRegistry.getItems().map(({ components: { Page }, subPages = [], exact, routePath }) => { - // return ( - // ( - // - // - // - // )}/> - // ) - })} + {this.renderExtensionRoutes()} diff --git a/src/renderer/components/cluster-manager/clusters-menu.tsx b/src/renderer/components/cluster-manager/clusters-menu.tsx index 9e5c550f67..25c514254c 100644 --- a/src/renderer/components/cluster-manager/clusters-menu.tsx +++ b/src/renderer/components/cluster-manager/clusters-menu.tsx @@ -23,7 +23,7 @@ import { Tooltip } from "../tooltip"; import { ConfirmDialog } from "../confirm-dialog"; import { clusterIpc } from "../../../common/cluster-ipc"; import { clusterViewURL } from "./cluster-view.route"; -import { globalPageMenuRegistry } from "../../../extensions/registries"; +import { globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries"; interface Props { className?: IClassName; @@ -150,8 +150,10 @@ export class ClustersMenu extends React.Component {
{globalPageMenuRegistry.getItems().map(({ title, url, components: { Icon } }) => { - const routePath = "" // todo: find matching route in page-registry + exact - const isActive = !!matchPath(navigation.location.pathname, { path: routePath/*, exact: false*/ }); + const registeredPage = globalPageRegistry.getByMatchingUrl(url); + if (!registeredPage) return; + const { routePath, exact } = registeredPage; + const isActive = !!matchPath(navigation.location.pathname, { path: routePath, exact }); return ( ({ pinned: false }); type SidebarContextValue = { @@ -52,7 +52,7 @@ export class Sidebar extends React.Component { renderCustomResources() { if (crdStore.isLoading) { - return + return } return Object.entries(crdStore.groups).map(([group, crds]) => { @@ -84,7 +84,7 @@ export class Sidebar extends React.Component {
- +
Lens
{
Cluster} - icon={} + icon={} /> Nodes} - icon={} + icon={} /> Workloads} - icon={} + icon={} /> Configuration} - icon={} + icon={} /> Network} - icon={} + icon={} /> } + icon={} text={Storage} /> } + icon={} text={Namespaces} /> } + icon={} text={Events} /> } + icon={} text={Apps} /> } + icon={} text={Access Control} /> } + icon={} text={Custom Resources} > {this.renderCustomResources()} {clusterPageMenuRegistry.getItems().map(({ title, url, components: { Icon } }) => { - const routePath = "" // todo: find in page-registry + const registeredPage = clusterPageRegistry.getByMatchingUrl(url); + if (!registeredPage) return; + const { routePath, exact } = registeredPage; return ( } + key={url} + url={url} + text={title} + icon={} + isActive={isActiveRoute({ path: routePath, exact })} /> ) })} @@ -200,7 +207,7 @@ interface SidebarNavItemProps { className?: string; icon?: React.ReactNode; isHidden?: boolean; - routePath?: string | string[]; + isActive?: boolean; subMenus?: TabLayoutRoute[]; } @@ -225,27 +232,19 @@ class SidebarNavItem extends React.Component { navItemState.set(this.itemId, !this.isExpanded); }; - isActive = () => { - const { url, routePath = url } = this.props; - return !!matchPath(navigation.location.pathname, { - path: routePath - }); - }; - render() { - const { isHidden, subMenus = [], icon, text, url, children, className } = this.props; + const { isHidden, isActive, subMenus = [], icon, text, url, children, className } = this.props; if (isHidden) { return null; } const extendedView = (subMenus.length > 0 || children) && this.context.pinned; if (extendedView) { - const isActive = this.isActive(); return (
{icon} {text} - +
    {subMenus.map(({ title, url }) => ( @@ -263,7 +262,7 @@ class SidebarNavItem extends React.Component { ); } return ( - + isActive}> {icon} {text} diff --git a/src/renderer/navigation.ts b/src/renderer/navigation.ts index b4e0f383b1..32c58ea148 100644 --- a/src/renderer/navigation.ts +++ b/src/renderer/navigation.ts @@ -1,7 +1,7 @@ // Navigation helpers import { ipcRenderer } from "electron"; -import { matchPath } from "react-router"; +import { matchPath, RouteProps } from "react-router"; import { reaction } from "mobx"; import { createObservableHistory } from "mobx-observable-history"; import { createBrowserHistory, createMemoryHistory, LocationDescriptor } from "history"; @@ -19,6 +19,10 @@ export function navigate(location: LocationDescriptor) { } } +export function isActiveRoute(route: string | string[] | RouteProps): boolean { + return !!matchPath(navigation.location.pathname, route); +} + // common params for all pages export interface IQueryParams { namespaces?: string[]; // selected context namespaces