mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
refactoring, detaching NamespaceStore from KubeObjectStore
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
5b86ae2f5d
commit
77e3a7fb83
@ -1,14 +1,14 @@
|
||||
// Kubernetes watch-api client
|
||||
// API: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
|
||||
|
||||
import type { Cluster } from "../../main/cluster";
|
||||
import type { IKubeWatchEvent, IKubeWatchEventStreamEnd, IWatchRoutePayload } from "../../main/routes/watch-route";
|
||||
import type { KubeObject } from "./kube-object";
|
||||
import type { KubeObjectStore } from "../kube-object.store";
|
||||
import type { ClusterContext } from "../components/context";
|
||||
|
||||
import plimit from "p-limit";
|
||||
import debounce from "lodash/debounce";
|
||||
import { autorun, comparer, computed, IReactionDisposer, observable, reaction } from "mobx";
|
||||
import { comparer, computed, IReactionDisposer, observable, reaction, when } from "mobx";
|
||||
import { autobind, EventEmitter, noop } from "../utils";
|
||||
import { ensureObjectSelfLink, KubeApi, parseKubeApi } from "./kube-api";
|
||||
import { KubeJsonApiData, KubeJsonApiError } from "./kube-json-api";
|
||||
@ -48,21 +48,18 @@ export class KubeWatchApi {
|
||||
private reader: ReadableStreamReader<string>;
|
||||
public onMessage = new EventEmitter<[IKubeWatchMessage]>();
|
||||
|
||||
@observable.ref private cluster: Cluster;
|
||||
@observable.ref private namespaces: string[] = [];
|
||||
@observable context: ClusterContext = null;
|
||||
@observable subscribers = observable.map<KubeApi, number>();
|
||||
@observable isConnected = false;
|
||||
|
||||
@computed get isReady(): boolean {
|
||||
return Boolean(this.cluster && this.namespaces);
|
||||
}
|
||||
contextReady = when(() => Boolean(this.context));
|
||||
|
||||
@computed get isActive(): boolean {
|
||||
return this.apis.length > 0;
|
||||
}
|
||||
|
||||
@computed get apis(): string[] {
|
||||
if (!this.isReady) {
|
||||
if (!this.context) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -72,22 +69,20 @@ export class KubeWatchApi {
|
||||
}
|
||||
|
||||
// TODO: optimize - check when all namespaces are selected and then request all in one
|
||||
if (api.isNamespaced && !this.cluster.isGlobalWatchEnabled) {
|
||||
return this.namespaces.map(namespace => api.getWatchUrl(namespace));
|
||||
if (api.isNamespaced && !this.context.cluster.isGlobalWatchEnabled) {
|
||||
return this.context.contextNamespaces.map(namespace => api.getWatchUrl(namespace));
|
||||
}
|
||||
|
||||
return api.getWatchUrl();
|
||||
}).flat();
|
||||
}
|
||||
|
||||
async init({ getCluster, getNamespaces }: {
|
||||
getCluster: () => Cluster,
|
||||
getNamespaces: () => string[],
|
||||
}): Promise<void> {
|
||||
autorun(() => {
|
||||
this.cluster = getCluster();
|
||||
this.namespaces = getNamespaces();
|
||||
});
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private async init() {
|
||||
await this.contextReady;
|
||||
this.bindAutoConnect();
|
||||
}
|
||||
|
||||
@ -109,7 +104,7 @@ export class KubeWatchApi {
|
||||
}
|
||||
|
||||
isAllowedApi(api: KubeApi): boolean {
|
||||
return Boolean(this?.cluster.isAllowedResource(api.kind));
|
||||
return Boolean(this.context?.cluster.isAllowedResource(api.kind));
|
||||
}
|
||||
|
||||
subscribeApi(api: KubeApi | KubeApi[]): () => void {
|
||||
@ -130,15 +125,15 @@ export class KubeWatchApi {
|
||||
};
|
||||
}
|
||||
|
||||
preloadStores(stores: KubeObjectStore[], { loadOnce = false } = {}) {
|
||||
preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) {
|
||||
const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages
|
||||
const preloading: Promise<any>[] = [];
|
||||
|
||||
for (const store of stores) {
|
||||
preloading.push(limitRequests(async () => {
|
||||
if (store.isLoaded && loadOnce) return; // skip
|
||||
if (store.isLoaded && opts.loadOnce) return; // skip
|
||||
|
||||
return store.loadAll(this.namespaces);
|
||||
return store.loadAll({ namespaces: opts.namespaces });
|
||||
}));
|
||||
}
|
||||
|
||||
@ -154,7 +149,7 @@ export class KubeWatchApi {
|
||||
const unsubscribeList: (() => void)[] = [];
|
||||
let isUnsubscribed = false;
|
||||
|
||||
const load = () => this.preloadStores(stores, { loadOnce });
|
||||
const load = (namespaces?: string[]) => this.preloadStores(stores, { namespaces, loadOnce });
|
||||
let preloading = preload && load();
|
||||
let cancelReloading: IReactionDisposer = noop;
|
||||
|
||||
@ -175,10 +170,10 @@ export class KubeWatchApi {
|
||||
subscribe();
|
||||
}
|
||||
|
||||
// reload when context namespaces changes
|
||||
cancelReloading = reaction(() => this.namespaces, () => {
|
||||
// partial reload only selected namespaces
|
||||
cancelReloading = reaction(() => this.context.contextNamespaces, namespaces => {
|
||||
preloading?.cancelLoading();
|
||||
preloading = load();
|
||||
preloading = load(namespaces);
|
||||
}, {
|
||||
equals: comparer.shallow,
|
||||
});
|
||||
@ -291,7 +286,7 @@ export class KubeWatchApi {
|
||||
}
|
||||
|
||||
// skip updates from non-watching resources context
|
||||
if (!namespace || this.namespaces.includes(namespace)) {
|
||||
if (!namespace || this.context?.contextNamespaces.includes(namespace)) {
|
||||
this.onMessage.emit(message);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@ -30,7 +30,7 @@ export class CrdResources extends React.Component<Props> {
|
||||
const { store } = this;
|
||||
|
||||
if (store && !store.isLoading && !store.isLoaded) {
|
||||
store.loadAllFromContextNamespaces();
|
||||
store.reloadAll();
|
||||
}
|
||||
})
|
||||
]);
|
||||
@ -97,7 +97,7 @@ export class CrdResources extends React.Component<Props> {
|
||||
...extraColumns.map((column) => {
|
||||
let value = jsonPath.value(crdInstance, parseJsonPath(column.jsonPath.slice(1)));
|
||||
|
||||
if (Array.isArray(value) || typeof value === "object") {
|
||||
if (Array.isArray(value) || typeof value === "object") {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ export interface KubeEventDetailsProps {
|
||||
@observer
|
||||
export class KubeEventDetails extends React.Component<KubeEventDetailsProps> {
|
||||
async componentDidMount() {
|
||||
eventStore.loadAllFromContextNamespaces();
|
||||
eventStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -32,8 +32,8 @@ export class NamespaceDetails extends React.Component<Props> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
resourceQuotaStore.loadAllFromContextNamespaces();
|
||||
limitRangeStore.loadAllFromContextNamespaces();
|
||||
resourceQuotaStore.reloadAll();
|
||||
limitRangeStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -41,8 +41,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
}
|
||||
|
||||
private async init() {
|
||||
await this.resolveCluster();
|
||||
if (!this.cluster) return; // skip for non-cluster context window
|
||||
if (!this.context) return; // skip for non-cluster context window
|
||||
|
||||
this.setContext(this.initialNamespaces);
|
||||
this.autoLoadAllowedNamespaces();
|
||||
@ -66,7 +65,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
}
|
||||
|
||||
private autoLoadAllowedNamespaces(): IReactionDisposer {
|
||||
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll(namespaces), {
|
||||
return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), {
|
||||
fireImmediately: true,
|
||||
equals: comparer.shallow,
|
||||
});
|
||||
@ -94,8 +93,8 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
|
||||
@computed get allowedNamespaces(): string[] {
|
||||
return Array.from(new Set([
|
||||
...(this.cluster?.allowedNamespaces ?? []), // loaded names from main, updating every 30s and thus might be stale
|
||||
...this.items.map(item => item.getName()), // loaded names from hosted cluster
|
||||
...(this.context?.allNamespaces ?? []), // allowed namespaces from cluster (main), updating every 30s
|
||||
...this.items.map(item => item.getName()), // loaded namespaces from k8s api
|
||||
].flat()));
|
||||
}
|
||||
|
||||
@ -111,7 +110,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
|
||||
getSubscribeApis() {
|
||||
// if user has given static list of namespaces let's not start watches because watch adds stuff that's not wanted
|
||||
if (this.cluster?.accessibleNamespaces.length > 0) {
|
||||
if (this.context?.cluster.accessibleNamespaces.length > 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ export class NodeDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
async componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@ -7,7 +7,7 @@ import { Dialog, DialogProps } from "../dialog";
|
||||
import { Wizard, WizardStep } from "../wizard";
|
||||
import { Select, SelectOption } from "../select";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { IRoleBindingSubject, RoleBinding, ServiceAccount, Role } from "../../api/endpoints";
|
||||
import { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints";
|
||||
import { Icon } from "../icon";
|
||||
import { Input } from "../input";
|
||||
import { NamespaceSelect } from "../+namespaces/namespace-select";
|
||||
@ -19,6 +19,7 @@ import { namespaceStore } from "../+namespaces/namespace.store";
|
||||
import { serviceAccountsStore } from "../+user-management-service-accounts/service-accounts.store";
|
||||
import { roleBindingsStore } from "./role-bindings.store";
|
||||
import { showDetails } from "../kube-object";
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
|
||||
interface BindingSelectOption extends SelectOption {
|
||||
value: string; // binding name
|
||||
@ -73,14 +74,14 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
};
|
||||
|
||||
async loadData() {
|
||||
const stores = [
|
||||
const stores: KubeObjectStore[] = [
|
||||
namespaceStore,
|
||||
rolesStore,
|
||||
serviceAccountsStore,
|
||||
];
|
||||
|
||||
this.isLoading = true;
|
||||
await Promise.all(stores.map(store => store.loadAllFromContextNamespaces()));
|
||||
await Promise.all(stores.map(store => store.reloadAll()));
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
@ -136,8 +137,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
roleBinding: this.roleBinding,
|
||||
addSubjects: subjects,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const name = useRoleForBindingName ? selectedRole.getName() : bindingName;
|
||||
|
||||
roleBinding = await roleBindingsStore.create({ name, namespace }, {
|
||||
@ -265,7 +265,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
|
||||
</h5>
|
||||
);
|
||||
const disableNext = this.isLoading || !selectedRole || !selectedBindings.length;
|
||||
const nextLabel = isEditing ? "Update" : "Create";
|
||||
const nextLabel = isEditing ? "Update" : "Create";
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
||||
@ -20,7 +20,7 @@ interface Props extends KubeObjectDetailsProps<CronJob> {
|
||||
@observer
|
||||
export class CronJobDetails extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
jobStore.loadAllFromContextNamespaces();
|
||||
jobStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -30,7 +30,7 @@ export class DaemonSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@ -31,7 +31,7 @@ export class DeploymentDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@ -25,7 +25,7 @@ interface Props extends KubeObjectDetailsProps<Job> {
|
||||
@observer
|
||||
export class JobDetails extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -29,7 +29,7 @@ export class ReplicaSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
async componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@ -30,7 +30,7 @@ export class StatefulSetDetails extends React.Component<Props> {
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
podsStore.loadAllFromContextNamespaces();
|
||||
podsStore.reloadAll();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
@ -43,12 +43,13 @@ import { ClusterPageMenuRegistration, clusterPageMenuRegistry } from "../../exte
|
||||
import { TabLayout, TabLayoutRoute } from "./layout/tab-layout";
|
||||
import { StatefulSetScaleDialog } from "./+workloads-statefulsets/statefulset-scale-dialog";
|
||||
import { eventStore } from "./+events/event.store";
|
||||
import { namespaceStore } from "./+namespaces/namespace.store";
|
||||
import { nodesStore } from "./+nodes/nodes.store";
|
||||
import { podsStore } from "./+workloads-pods/pods.store";
|
||||
import { kubeWatchApi } from "../api/kube-watch-api";
|
||||
import { ReplicaSetScaleDialog } from "./+workloads-replicasets/replicaset-scale-dialog";
|
||||
import { CommandContainer } from "./command-palette/command-container";
|
||||
import { KubeObjectStore } from "../kube-object.store";
|
||||
import { clusterContext } from "./context";
|
||||
|
||||
@observer
|
||||
export class App extends React.Component {
|
||||
@ -76,10 +77,9 @@ export class App extends React.Component {
|
||||
});
|
||||
whatInput.ask(); // Start to monitor user input device
|
||||
|
||||
await kubeWatchApi.init({
|
||||
getCluster: () => getHostedCluster(),
|
||||
getNamespaces: () => namespaceStore.contextNamespaces,
|
||||
});
|
||||
// Setup hosted cluster context
|
||||
KubeObjectStore.defaultContext = clusterContext;
|
||||
kubeWatchApi.context = clusterContext;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -162,9 +162,9 @@ export class App extends React.Component {
|
||||
const tabRoutes = this.getTabLayoutRoutes(menu);
|
||||
|
||||
if (tabRoutes.length > 0) {
|
||||
const pageComponent = () => <TabLayout tabs={tabRoutes} />;
|
||||
const pageComponent = () => <TabLayout tabs={tabRoutes}/>;
|
||||
|
||||
route = <Route key={`extension-tab-layout-route-${index}`} component={pageComponent} path={tabRoutes.map((tab) => tab.routePath)} />;
|
||||
route = <Route key={`extension-tab-layout-route-${index}`} component={pageComponent} path={tabRoutes.map((tab) => tab.routePath)}/>;
|
||||
this.extensionRoutes.set(menu, route);
|
||||
} else {
|
||||
const page = clusterPageRegistry.getByPageTarget(menu.target);
|
||||
@ -228,7 +228,7 @@ export class App extends React.Component {
|
||||
<StatefulSetScaleDialog/>
|
||||
<ReplicaSetScaleDialog/>
|
||||
<CronJobTriggerDialog/>
|
||||
<CommandContainer cluster={cluster} />
|
||||
<CommandContainer cluster={cluster}/>
|
||||
</ErrorBoundary>
|
||||
</Router>
|
||||
);
|
||||
|
||||
23
src/renderer/components/context.ts
Executable file
23
src/renderer/components/context.ts
Executable file
@ -0,0 +1,23 @@
|
||||
import type { Cluster } from "../../main/cluster";
|
||||
import { getHostedCluster } from "../../common/cluster-store";
|
||||
import { namespaceStore } from "./+namespaces/namespace.store";
|
||||
|
||||
export interface ClusterContext {
|
||||
cluster?: Cluster;
|
||||
allNamespaces?: string[]; // available / allowed namespaces from cluster.ts
|
||||
contextNamespaces?: string[]; // selected by user (see: namespace-select.tsx)
|
||||
}
|
||||
|
||||
export const clusterContext: ClusterContext = {
|
||||
get cluster(): Cluster | null {
|
||||
return getHostedCluster();
|
||||
},
|
||||
|
||||
get allNamespaces(): string[] {
|
||||
return this.cluster?.allowedNamespaces ?? [];
|
||||
},
|
||||
|
||||
get contextNamespaces(): string[] {
|
||||
return namespaceStore.contextNamespaces ?? [];
|
||||
},
|
||||
};
|
||||
@ -40,7 +40,7 @@ interface Props {
|
||||
@observer
|
||||
export class Sidebar extends React.Component<Props> {
|
||||
async componentDidMount() {
|
||||
crdStore.loadAllFromContextNamespaces();
|
||||
crdStore.reloadAll();
|
||||
}
|
||||
|
||||
renderCustomResources() {
|
||||
|
||||
@ -9,7 +9,7 @@ export interface ItemObject {
|
||||
|
||||
@autobind()
|
||||
export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
||||
abstract loadAll(...args: any[]): Promise<void>;
|
||||
abstract loadAll(...args: any[]): Promise<void | T[]>;
|
||||
|
||||
protected defaultSorting = (item: T) => item.getName();
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Cluster } from "../main/cluster";
|
||||
import type { NamespaceStore } from "./components/+namespaces/namespace.store";
|
||||
import type { ClusterContext } from "./components/context";
|
||||
|
||||
import { action, computed, observable, reaction } from "mobx";
|
||||
import { action, observable, reaction, when } from "mobx";
|
||||
import { autobind } from "./utils";
|
||||
import { KubeObject } from "./api/kube-object";
|
||||
import { IKubeWatchEvent, IKubeWatchMessage, kubeWatchApi } from "./api/kube-watch-api";
|
||||
@ -17,44 +16,23 @@ export interface KubeObjectStoreLoadingParams {
|
||||
|
||||
@autobind()
|
||||
export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemStore<T> {
|
||||
@observable static defaultContext: ClusterContext; // TODO: support multiple cluster contexts
|
||||
|
||||
abstract api: KubeApi<T>;
|
||||
public readonly limit?: number;
|
||||
public readonly bufferSize: number = 50000;
|
||||
@observable.ref protected cluster: Cluster;
|
||||
|
||||
contextReady = when(() => Boolean(this.context));
|
||||
|
||||
get context(): ClusterContext {
|
||||
return KubeObjectStore.defaultContext;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.bindWatchEventsUpdater();
|
||||
}
|
||||
|
||||
// TODO: detach / remove circular dependency
|
||||
@observable.ref private namespaceStore: NamespaceStore;
|
||||
|
||||
protected async resolveNamespaceStore(): Promise<NamespaceStore> {
|
||||
const { namespaceStore } = await import("./components/+namespaces/namespace.store");
|
||||
|
||||
this.namespaceStore = namespaceStore;
|
||||
|
||||
return namespaceStore;
|
||||
}
|
||||
|
||||
protected async resolveCluster(): Promise<Cluster> {
|
||||
const { getHostedCluster, clusterStore } = await import("../common/cluster-store");
|
||||
|
||||
await clusterStore.whenLoaded;
|
||||
this.cluster = getHostedCluster();
|
||||
await this.cluster.whenReady;
|
||||
|
||||
return this.cluster;
|
||||
}
|
||||
|
||||
// TODO: figure out how to transparently replace with this.items
|
||||
@computed get contextItems(): T[] {
|
||||
const contextNamespaces = this.namespaceStore?.contextNamespaces ?? []; // not loaded
|
||||
|
||||
return this.items.filter((item: T) => !item.getNs() || contextNamespaces.includes(item.getId()));
|
||||
}
|
||||
|
||||
get query(): IKubeApiQueryParams {
|
||||
const { limit } = this;
|
||||
|
||||
@ -111,9 +89,7 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
}
|
||||
|
||||
protected async loadItems({ namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> {
|
||||
const cluster = await this.resolveCluster();
|
||||
|
||||
if (cluster.isAllowedResource(api.kind)) {
|
||||
if (this.context?.cluster.isAllowedResource(api.kind)) {
|
||||
if (api.isNamespaced) {
|
||||
return Promise
|
||||
.all(namespaces.map(namespace => api.list({ namespace })))
|
||||
@ -131,21 +107,24 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
}
|
||||
|
||||
@action
|
||||
async loadAll(namespaces?: string[], { replace = false /*partial update*/ } = {}): Promise<void> {
|
||||
async loadAll({ namespaces = [], merge = true } = {}): Promise<void | T[]> {
|
||||
await this.contextReady;
|
||||
this.isLoading = true;
|
||||
|
||||
try {
|
||||
// load all available namespaces by default
|
||||
if (!namespaces?.length) {
|
||||
const namespaceStore = await this.resolveNamespaceStore();
|
||||
|
||||
namespaces = namespaceStore.allowedNamespaces; // load all by default if list not provided
|
||||
if (!namespaces.length) {
|
||||
namespaces = this.context.allNamespaces; // load all available namespaces by default
|
||||
}
|
||||
|
||||
const items = await this.loadItems({ namespaces, api: this.api });
|
||||
|
||||
this.mergeItems(items, { replace });
|
||||
this.isLoaded = true;
|
||||
|
||||
if (merge) {
|
||||
this.mergeItems(items);
|
||||
} else {
|
||||
return items;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Loading store items failed", { error, store: this });
|
||||
this.resetOnError(error);
|
||||
@ -155,18 +134,28 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
}
|
||||
|
||||
@action
|
||||
mergeItems(partialItems: T[], { replace = false, updateStore = true, sort = true, filter = true } = {}): T[] {
|
||||
reloadAll(opts: { namespaces?: string[], merge?: boolean, force?: boolean } = {}) {
|
||||
const { force = false, ...loadingOptions } = opts;
|
||||
|
||||
if (this.isLoading || (this.isLoaded && !force)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.loadAll(loadingOptions);
|
||||
}
|
||||
|
||||
@action
|
||||
mergeItems(partialItems: T[], { replace = true, updateStore = true, sort = true, filter = true } = {}): T[] {
|
||||
let items = partialItems;
|
||||
|
||||
// update existing items
|
||||
if (!replace) {
|
||||
items = this.items.toJS();
|
||||
const partialIds = partialItems.map(item => item.getId());
|
||||
|
||||
partialItems.forEach(item => {
|
||||
const index = items.findIndex(i => i.getId() === item.getId());
|
||||
|
||||
if (index < 0) items.push(item); // add
|
||||
else items[index] = item; // update
|
||||
});
|
||||
items = [
|
||||
...this.items.filter(existingItem => !partialIds.includes(existingItem.getId())),
|
||||
...partialItems,
|
||||
];
|
||||
}
|
||||
|
||||
if (filter) items = this.filterItemsOnLoad(items);
|
||||
@ -176,10 +165,6 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
return items;
|
||||
}
|
||||
|
||||
async loadAllFromContextNamespaces(): Promise<void> {
|
||||
return this.loadAll(this.namespaceStore?.contextNamespaces);
|
||||
}
|
||||
|
||||
protected resetOnError(error: any) {
|
||||
if (error) this.reset();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user