From 4240d3afbcc75dabd38bb93f1356a63931494ef3 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 5 Feb 2021 17:34:36 +0200 Subject: [PATCH] refactoring & fixes Signed-off-by: Roman --- src/renderer/api/kube-watch-api.ts | 25 +++++++-------- .../+workloads-overview/overview.tsx | 2 ++ .../item-object-list/item-list-layout.tsx | 4 ++- .../kube-object/kube-object-list-layout.tsx | 6 +++- src/renderer/kube-object.store.ts | 32 ++++++++++++------- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/renderer/api/kube-watch-api.ts b/src/renderer/api/kube-watch-api.ts index 4d68073e78..c6ca50eaf5 100644 --- a/src/renderer/api/kube-watch-api.ts +++ b/src/renderer/api/kube-watch-api.ts @@ -8,7 +8,7 @@ import type { ClusterContext } from "../components/context"; import plimit from "p-limit"; import debounce from "lodash/debounce"; -import { comparer, computed, IReactionDisposer, observable, reaction, when } from "mobx"; +import { comparer, computed, IReactionDisposer, observable, reaction, toJS, when } from "mobx"; import { autobind, EventEmitter, noop } from "../utils"; import { ensureObjectSelfLink, KubeApi, parseKubeApi } from "./kube-api"; import { KubeJsonApiData, KubeJsonApiError } from "./kube-json-api"; @@ -26,7 +26,7 @@ export interface IKubeWatchMessage { } export interface IKubeWatchSubscribeStoreOptions { - namespaces?: string[]; // todo: support custom namespaces to subscribe + namespaces?: string[]; // default: all accessible namespaces preload?: boolean; // preload store items, default: true waitUntilLoaded?: boolean; // subscribe only after loading all stores, default: true loadOnce?: boolean; // check store.isLoaded to skip loading if done already, default: false @@ -134,9 +134,7 @@ export class KubeWatchApi { preloading.push(limitRequests(async () => { if (store.isLoaded && opts.loadOnce) return; // skip - return store.loadAll({ - namespaces: opts.namespaces ?? this.context?.contextNamespaces, - }); + return store.loadAll({ namespaces: opts.namespaces }); })); } @@ -146,13 +144,14 @@ export class KubeWatchApi { }; } - subscribeStores(stores: KubeObjectStore[], options: IKubeWatchSubscribeStoreOptions = {}): () => void { - const { preload = true, waitUntilLoaded = true, loadOnce = false } = options; + subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): () => void { + const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts; const apis = new Set(stores.map(store => store.getSubscribeApis()).flat()); - const unsubscribeList: (() => void)[] = []; + const unsubscribeList: Function[] = []; let isUnsubscribed = false; - const load = (namespaces?: string[]) => this.preloadStores(stores, { namespaces, loadOnce }); + const namespaces = opts.namespaces ?? this.context?.allNamespaces ?? []; + const load = () => this.preloadStores(stores, { namespaces, loadOnce }); let preloading = preload && load(); let cancelReloading: IReactionDisposer = noop; @@ -166,17 +165,17 @@ export class KubeWatchApi { preloading.loading.then(subscribe, error => { this.log({ message: new Error("Loading stores has failed"), - meta: { stores, error, options }, + meta: { stores, error, options: opts }, }); }); } else { subscribe(); } - // partial reload only selected namespaces - cancelReloading = reaction(() => this.context.contextNamespaces, namespaces => { + // reload stores for requested namespaces + cancelReloading = reaction(() => toJS(namespaces), () => { preloading?.cancelLoading(); - preloading = load(namespaces); + preloading = load(); }, { equals: comparer.shallow, }); diff --git a/src/renderer/components/+workloads-overview/overview.tsx b/src/renderer/components/+workloads-overview/overview.tsx index 50a25ef87c..92bc569307 100644 --- a/src/renderer/components/+workloads-overview/overview.tsx +++ b/src/renderer/components/+workloads-overview/overview.tsx @@ -16,6 +16,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import { Events } from "../+events"; import { isAllowedResource } from "../../../common/rbac"; import { kubeWatchApi } from "../../api/kube-watch-api"; +import { clusterContext } from "../context"; interface Props extends RouteComponentProps { } @@ -29,6 +30,7 @@ export class WorkloadsOverview extends React.Component { jobStore, cronJobStore, eventStore, ], { preload: true, + namespaces: clusterContext.contextNamespaces, }), ]); } 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 ddee6638b9..42331990da 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -38,6 +38,7 @@ interface IHeaderPlaceholders { export interface ItemListLayoutProps { tableId?: string; className: IClassName; + items?: T[]; store: ItemStore; dependentStores?: ItemStore[]; preloadStores?: boolean; @@ -218,7 +219,8 @@ export class ItemListLayout extends React.Component { } }); - return this.applyFilters(filterItems, allItems); + const items = this.props.items ?? allItems; + return this.applyFilters(filterItems, items); } @autobind() 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 226023fc8d..d8e78aa69d 100644 --- a/src/renderer/components/kube-object/kube-object-list-layout.tsx +++ b/src/renderer/components/kube-object/kube-object-list-layout.tsx @@ -8,6 +8,7 @@ import { KubeObjectStore } from "../../kube-object.store"; import { KubeObjectMenu } from "./kube-object-menu"; import { kubeSelectedUrlParam, showDetails } from "./kube-object-details"; import { kubeWatchApi } from "../../api/kube-watch-api"; +import { clusterContext } from "../context"; export interface KubeObjectListLayoutProps extends ItemListLayoutProps { store: KubeObjectStore; @@ -26,7 +27,8 @@ export class KubeObjectListLayout extends React.Component extends ItemSt contextReady = when(() => Boolean(this.context)); + constructor() { + super(); + this.bindWatchEventsUpdater(); + } + get context(): ClusterContext { return KubeObjectStore.defaultContext; } - constructor() { - super(); - this.bindWatchEventsUpdater(); + @computed get contextItems(): T[] { + const namespaces = this.context?.contextNamespaces ?? []; + + return this.items.filter(item => { + const itemNamespace = item.getNs(); + return !itemNamespace /* cluster-wide */ || namespaces.includes(itemNamespace); + }); } get query(): IKubeApiQueryParams { @@ -107,21 +116,22 @@ export abstract class KubeObjectStore extends ItemSt } @action - async loadAll({ namespaces = [], merge = true } = {}): Promise { + async loadAll(options: { namespaces?: string[], merge?: boolean } = {}): Promise { await this.contextReady; this.isLoading = true; try { - if (!namespaces.length) { - namespaces = this.context.allNamespaces; // load all available namespaces by default - } + const { + namespaces = this.context.allNamespaces, // load all namespaces by default + merge = true, // merge loaded items or return as result + } = options; const items = await this.loadItems({ namespaces, api: this.api }); this.isLoaded = true; if (merge) { - this.mergeItems(items); + this.mergeItems(items, { replace: false }); } else { return items; } @@ -134,7 +144,7 @@ export abstract class KubeObjectStore extends ItemSt } @action - reloadAll(opts: { namespaces?: string[], merge?: boolean, force?: boolean } = {}) { + reloadAll(opts: { force?: boolean, namespaces?: string[], merge?: boolean } = {}) { const { force = false, ...loadingOptions } = opts; if (this.isLoading || (this.isLoaded && !force)) { @@ -145,7 +155,7 @@ export abstract class KubeObjectStore extends ItemSt } @action - mergeItems(partialItems: T[], { replace = true, updateStore = true, sort = true, filter = true } = {}): T[] { + mergeItems(partialItems: T[], { replace = false, updateStore = true, sort = true, filter = true } = {}): T[] { let items = partialItems; // update existing items