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

refactoring & fixes

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-02-05 17:34:36 +02:00
parent 36d54c64fc
commit 4240d3afbc
5 changed files with 43 additions and 26 deletions

View File

@ -8,7 +8,7 @@ import type { ClusterContext } from "../components/context";
import plimit from "p-limit"; import plimit from "p-limit";
import debounce from "lodash/debounce"; 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 { autobind, EventEmitter, noop } from "../utils";
import { ensureObjectSelfLink, KubeApi, parseKubeApi } from "./kube-api"; import { ensureObjectSelfLink, KubeApi, parseKubeApi } from "./kube-api";
import { KubeJsonApiData, KubeJsonApiError } from "./kube-json-api"; import { KubeJsonApiData, KubeJsonApiError } from "./kube-json-api";
@ -26,7 +26,7 @@ export interface IKubeWatchMessage<T extends KubeObject = any> {
} }
export interface IKubeWatchSubscribeStoreOptions { export interface IKubeWatchSubscribeStoreOptions {
namespaces?: string[]; // todo: support custom namespaces to subscribe namespaces?: string[]; // default: all accessible namespaces
preload?: boolean; // preload store items, default: true preload?: boolean; // preload store items, default: true
waitUntilLoaded?: boolean; // subscribe only after loading all stores, 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 loadOnce?: boolean; // check store.isLoaded to skip loading if done already, default: false
@ -134,9 +134,7 @@ export class KubeWatchApi {
preloading.push(limitRequests(async () => { preloading.push(limitRequests(async () => {
if (store.isLoaded && opts.loadOnce) return; // skip if (store.isLoaded && opts.loadOnce) return; // skip
return store.loadAll({ return store.loadAll({ namespaces: opts.namespaces });
namespaces: opts.namespaces ?? this.context?.contextNamespaces,
});
})); }));
} }
@ -146,13 +144,14 @@ export class KubeWatchApi {
}; };
} }
subscribeStores(stores: KubeObjectStore[], options: IKubeWatchSubscribeStoreOptions = {}): () => void { subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): () => void {
const { preload = true, waitUntilLoaded = true, loadOnce = false } = options; const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts;
const apis = new Set(stores.map(store => store.getSubscribeApis()).flat()); const apis = new Set(stores.map(store => store.getSubscribeApis()).flat());
const unsubscribeList: (() => void)[] = []; const unsubscribeList: Function[] = [];
let isUnsubscribed = false; 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 preloading = preload && load();
let cancelReloading: IReactionDisposer = noop; let cancelReloading: IReactionDisposer = noop;
@ -166,17 +165,17 @@ export class KubeWatchApi {
preloading.loading.then(subscribe, error => { preloading.loading.then(subscribe, error => {
this.log({ this.log({
message: new Error("Loading stores has failed"), message: new Error("Loading stores has failed"),
meta: { stores, error, options }, meta: { stores, error, options: opts },
}); });
}); });
} else { } else {
subscribe(); subscribe();
} }
// partial reload only selected namespaces // reload stores for requested namespaces
cancelReloading = reaction(() => this.context.contextNamespaces, namespaces => { cancelReloading = reaction(() => toJS(namespaces), () => {
preloading?.cancelLoading(); preloading?.cancelLoading();
preloading = load(namespaces); preloading = load();
}, { }, {
equals: comparer.shallow, equals: comparer.shallow,
}); });

View File

@ -16,6 +16,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
import { Events } from "../+events"; import { Events } from "../+events";
import { isAllowedResource } from "../../../common/rbac"; import { isAllowedResource } from "../../../common/rbac";
import { kubeWatchApi } from "../../api/kube-watch-api"; import { kubeWatchApi } from "../../api/kube-watch-api";
import { clusterContext } from "../context";
interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> { interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> {
} }
@ -29,6 +30,7 @@ export class WorkloadsOverview extends React.Component<Props> {
jobStore, cronJobStore, eventStore, jobStore, cronJobStore, eventStore,
], { ], {
preload: true, preload: true,
namespaces: clusterContext.contextNamespaces,
}), }),
]); ]);
} }

View File

@ -38,6 +38,7 @@ interface IHeaderPlaceholders {
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> { export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
tableId?: string; tableId?: string;
className: IClassName; className: IClassName;
items?: T[];
store: ItemStore<T>; store: ItemStore<T>;
dependentStores?: ItemStore[]; dependentStores?: ItemStore[];
preloadStores?: boolean; preloadStores?: boolean;
@ -218,7 +219,8 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
} }
}); });
return this.applyFilters(filterItems, allItems); const items = this.props.items ?? allItems;
return this.applyFilters(filterItems, items);
} }
@autobind() @autobind()

View File

@ -8,6 +8,7 @@ import { KubeObjectStore } from "../../kube-object.store";
import { KubeObjectMenu } from "./kube-object-menu"; import { KubeObjectMenu } from "./kube-object-menu";
import { kubeSelectedUrlParam, showDetails } from "./kube-object-details"; import { kubeSelectedUrlParam, showDetails } from "./kube-object-details";
import { kubeWatchApi } from "../../api/kube-watch-api"; import { kubeWatchApi } from "../../api/kube-watch-api";
import { clusterContext } from "../context";
export interface KubeObjectListLayoutProps extends ItemListLayoutProps { export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
store: KubeObjectStore; store: KubeObjectStore;
@ -26,7 +27,8 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
disposeOnUnmount(this, [ disposeOnUnmount(this, [
kubeWatchApi.subscribeStores(stores, { kubeWatchApi.subscribeStores(stores, {
preload: true preload: true,
namespaces: clusterContext.contextNamespaces,
}) })
]); ]);
} }
@ -40,12 +42,14 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
}; };
render() { render() {
const items = this.props.store.contextItems;
const { className, ...layoutProps } = this.props; const { className, ...layoutProps } = this.props;
return ( return (
<ItemListLayout <ItemListLayout
{...layoutProps} {...layoutProps}
className={cssNames("KubeObjectListLayout", className)} className={cssNames("KubeObjectListLayout", className)}
items={items}
preloadStores={false} // loading handled in kubeWatchApi.subscribeStores() preloadStores={false} // loading handled in kubeWatchApi.subscribeStores()
detailsItem={this.selectedItem} detailsItem={this.selectedItem}
onDetails={this.onDetails} onDetails={this.onDetails}

View File

@ -1,6 +1,6 @@
import type { ClusterContext } from "./components/context"; import type { ClusterContext } from "./components/context";
import { action, observable, reaction, when } from "mobx"; import { action, computed, observable, reaction, when } from "mobx";
import { autobind } from "./utils"; import { autobind } from "./utils";
import { KubeObject } from "./api/kube-object"; import { KubeObject } from "./api/kube-object";
import { IKubeWatchEvent, IKubeWatchMessage, kubeWatchApi } from "./api/kube-watch-api"; import { IKubeWatchEvent, IKubeWatchMessage, kubeWatchApi } from "./api/kube-watch-api";
@ -24,13 +24,22 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
contextReady = when(() => Boolean(this.context)); contextReady = when(() => Boolean(this.context));
constructor() {
super();
this.bindWatchEventsUpdater();
}
get context(): ClusterContext { get context(): ClusterContext {
return KubeObjectStore.defaultContext; return KubeObjectStore.defaultContext;
} }
constructor() { @computed get contextItems(): T[] {
super(); const namespaces = this.context?.contextNamespaces ?? [];
this.bindWatchEventsUpdater();
return this.items.filter(item => {
const itemNamespace = item.getNs();
return !itemNamespace /* cluster-wide */ || namespaces.includes(itemNamespace);
});
} }
get query(): IKubeApiQueryParams { get query(): IKubeApiQueryParams {
@ -107,21 +116,22 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
@action @action
async loadAll({ namespaces = [], merge = true } = {}): Promise<void | T[]> { async loadAll(options: { namespaces?: string[], merge?: boolean } = {}): Promise<void | T[]> {
await this.contextReady; await this.contextReady;
this.isLoading = true; this.isLoading = true;
try { try {
if (!namespaces.length) { const {
namespaces = this.context.allNamespaces; // load all available namespaces by default 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 }); const items = await this.loadItems({ namespaces, api: this.api });
this.isLoaded = true; this.isLoaded = true;
if (merge) { if (merge) {
this.mergeItems(items); this.mergeItems(items, { replace: false });
} else { } else {
return items; return items;
} }
@ -134,7 +144,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
@action @action
reloadAll(opts: { namespaces?: string[], merge?: boolean, force?: boolean } = {}) { reloadAll(opts: { force?: boolean, namespaces?: string[], merge?: boolean } = {}) {
const { force = false, ...loadingOptions } = opts; const { force = false, ...loadingOptions } = opts;
if (this.isLoading || (this.isLoaded && !force)) { if (this.isLoading || (this.isLoaded && !force)) {
@ -145,7 +155,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
@action @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; let items = partialItems;
// update existing items // update existing items