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 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<T extends KubeObject = any> {
}
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,
});

View File

@ -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<IWorkloadsOverviewRouteParams> {
}
@ -29,6 +30,7 @@ export class WorkloadsOverview extends React.Component<Props> {
jobStore, cronJobStore, eventStore,
], {
preload: true,
namespaces: clusterContext.contextNamespaces,
}),
]);
}

View File

@ -38,6 +38,7 @@ interface IHeaderPlaceholders {
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
tableId?: string;
className: IClassName;
items?: T[];
store: ItemStore<T>;
dependentStores?: ItemStore[];
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()

View File

@ -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<KubeObjectListLayoutPr
disposeOnUnmount(this, [
kubeWatchApi.subscribeStores(stores, {
preload: true
preload: true,
namespaces: clusterContext.contextNamespaces,
})
]);
}
@ -40,12 +42,14 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
};
render() {
const items = this.props.store.contextItems;
const { className, ...layoutProps } = this.props;
return (
<ItemListLayout
{...layoutProps}
className={cssNames("KubeObjectListLayout", className)}
items={items}
preloadStores={false} // loading handled in kubeWatchApi.subscribeStores()
detailsItem={this.selectedItem}
onDetails={this.onDetails}

View File

@ -1,6 +1,6 @@
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 { KubeObject } from "./api/kube-object";
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));
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<T extends KubeObject = any> extends ItemSt
}
@action
async loadAll({ namespaces = [], merge = true } = {}): Promise<void | T[]> {
async loadAll(options: { namespaces?: string[], merge?: boolean } = {}): Promise<void | T[]> {
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<T extends KubeObject = any> 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<T extends KubeObject = any> 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