mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix pod logs not always showing on reconnect to cluster
- Pod logs are now independent of the namespace select filter - The logs list now shows a spinner while loading Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
0b400d2b34
commit
ae4869b3fc
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { bind } from "../../../utils";
|
||||||
|
import type { TabId } from "../dock/store";
|
||||||
|
import type { LogStore } from "./store";
|
||||||
|
import logStoreInjectable from "./store.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
logStore: LogStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
function areLogsPresent({ logStore }: Dependencies, tabId: TabId) {
|
||||||
|
return logStore.areLogsPresent(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const areLogsPresentInjectable = getInjectable({
|
||||||
|
instantiate: (di) => bind(areLogsPresent, null, {
|
||||||
|
logStore: di.inject(logStoreInjectable),
|
||||||
|
}),
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default areLogsPresentInjectable;
|
||||||
@ -19,6 +19,7 @@ import { array, boundMethod, cssNames } from "../../../utils";
|
|||||||
import { VirtualList } from "../../virtual-list";
|
import { VirtualList } from "../../virtual-list";
|
||||||
import { ToBottom } from "./to-bottom";
|
import { ToBottom } from "./to-bottom";
|
||||||
import type { LogTabViewModel } from "../logs/logs-view-model";
|
import type { LogTabViewModel } from "../logs/logs-view-model";
|
||||||
|
import { Spinner } from "../../spinner";
|
||||||
|
|
||||||
export interface LogListProps {
|
export interface LogListProps {
|
||||||
model: LogTabViewModel;
|
model: LogTabViewModel;
|
||||||
@ -212,10 +213,18 @@ export class LogList extends React.Component<LogListProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.props.model.isLoading.get()) {
|
||||||
|
return (
|
||||||
|
<div className="LogList flex box grow align-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.logs.length) {
|
if (!this.logs.length) {
|
||||||
return (
|
return (
|
||||||
<div className="LogList flex box grow align-center justify-center">
|
<div className="LogList flex box grow align-center justify-center">
|
||||||
There are no logs available for container
|
There are no logs available for container {this.props.model.logTabData.get()?.selectedContainer}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
import { bind } from "../../../utils";
|
import { bind } from "../../../utils";
|
||||||
import type { LogStore } from "./store";
|
import type { LogStore } from "./store";
|
||||||
import logStoreInjectable from "./store.injectable";
|
import logStoreInjectable from "./store.injectable";
|
||||||
@ -13,8 +14,8 @@ interface Dependencies {
|
|||||||
logStore: LogStore;
|
logStore: LogStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLogs({ logStore }: Dependencies, tabId: string, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
function loadLogs({ logStore }: Dependencies, tabId: string, pod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
||||||
return logStore.load(tabId, logTabData);
|
return logStore.load(tabId, pod, logTabData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadLogsInjectable = getInjectable({
|
const loadLogsInjectable = getInjectable({
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import stopLoadingLogsInjectable from "./stop-loading-logs.injectable";
|
|||||||
import { podsStore } from "../../+workloads-pods/pods.store";
|
import { podsStore } from "../../+workloads-pods/pods.store";
|
||||||
import createSearchStoreInjectable from "../../../search-store/create-search-store.injectable";
|
import createSearchStoreInjectable from "../../../search-store/create-search-store.injectable";
|
||||||
import renameTabInjectable from "../dock/rename-tab.injectable";
|
import renameTabInjectable from "../dock/rename-tab.injectable";
|
||||||
|
import areLogsPresentInjectable from "./are-logs-present.injectable";
|
||||||
|
|
||||||
export interface InstantiateArgs {
|
export interface InstantiateArgs {
|
||||||
tabId: TabId;
|
tabId: TabId;
|
||||||
@ -32,6 +33,7 @@ const logsViewModelInjectable = getInjectable({
|
|||||||
loadLogs: di.inject(loadLogsInjectable),
|
loadLogs: di.inject(loadLogsInjectable),
|
||||||
renameTab: di.inject(renameTabInjectable),
|
renameTab: di.inject(renameTabInjectable),
|
||||||
stopLoadingLogs: di.inject(stopLoadingLogsInjectable),
|
stopLoadingLogs: di.inject(stopLoadingLogsInjectable),
|
||||||
|
areLogsPresent: di.inject(areLogsPresentInjectable),
|
||||||
getPodById: id => podsStore.getById(id),
|
getPodById: id => podsStore.getById(id),
|
||||||
getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id),
|
getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id),
|
||||||
createSearchStore: di.inject(createSearchStoreInjectable),
|
createSearchStore: di.inject(createSearchStoreInjectable),
|
||||||
|
|||||||
@ -14,18 +14,20 @@ export interface LogTabViewModelDependencies {
|
|||||||
getTimestampSplitLogs: (tabId: TabId) => [string, string][];
|
getTimestampSplitLogs: (tabId: TabId) => [string, string][];
|
||||||
getLogTabData: (tabId: TabId) => LogTabData;
|
getLogTabData: (tabId: TabId) => LogTabData;
|
||||||
setLogTabData: (tabId: TabId, data: LogTabData) => void;
|
setLogTabData: (tabId: TabId, data: LogTabData) => void;
|
||||||
loadLogs: (tabId: TabId, logTabData: IComputedValue<LogTabData>) => Promise<void>;
|
loadLogs: (tabId: TabId, pod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>) => Promise<void>;
|
||||||
reloadLogs: (tabId: TabId, logTabData: IComputedValue<LogTabData>) => Promise<void>;
|
reloadLogs: (tabId: TabId, pod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>) => Promise<void>;
|
||||||
renameTab: (tabId: TabId, title: string) => void;
|
renameTab: (tabId: TabId, title: string) => void;
|
||||||
stopLoadingLogs: (tabId: TabId) => void;
|
stopLoadingLogs: (tabId: TabId) => void;
|
||||||
getPodById: (id: string) => Pod | undefined;
|
getPodById: (id: string) => Pod | undefined;
|
||||||
getPodsByOwnerId: (id: string) => Pod[];
|
getPodsByOwnerId: (id: string) => Pod[];
|
||||||
|
areLogsPresent: (tabId: TabId) => boolean;
|
||||||
createSearchStore: () => SearchStore;
|
createSearchStore: () => SearchStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogTabViewModel {
|
export class LogTabViewModel {
|
||||||
constructor(protected readonly tabId: TabId, private readonly dependencies: LogTabViewModelDependencies) {}
|
constructor(protected readonly tabId: TabId, private readonly dependencies: LogTabViewModelDependencies) {}
|
||||||
|
|
||||||
|
readonly isLoading = computed(() => this.dependencies.areLogsPresent(this.tabId));
|
||||||
readonly logs = computed(() => this.dependencies.getLogs(this.tabId));
|
readonly logs = computed(() => this.dependencies.getLogs(this.tabId));
|
||||||
readonly logsWithoutTimestamps = computed(() => this.dependencies.getLogsWithoutTimestamps(this.tabId));
|
readonly logsWithoutTimestamps = computed(() => this.dependencies.getLogsWithoutTimestamps(this.tabId));
|
||||||
readonly timestampSplitLogs = computed(() => this.dependencies.getTimestampSplitLogs(this.tabId));
|
readonly timestampSplitLogs = computed(() => this.dependencies.getTimestampSplitLogs(this.tabId));
|
||||||
@ -58,8 +60,8 @@ export class LogTabViewModel {
|
|||||||
this.dependencies.setLogTabData(this.tabId, { ...this.logTabData.get(), ...partialData });
|
this.dependencies.setLogTabData(this.tabId, { ...this.logTabData.get(), ...partialData });
|
||||||
};
|
};
|
||||||
|
|
||||||
loadLogs = () => this.dependencies.loadLogs(this.tabId, this.logTabData);
|
loadLogs = () => this.dependencies.loadLogs(this.tabId, this.pod, this.logTabData);
|
||||||
reloadLogs = () => this.dependencies.reloadLogs(this.tabId, this.logTabData);
|
reloadLogs = () => this.dependencies.reloadLogs(this.tabId, this.pod, this.logTabData);
|
||||||
renameTab = (title: string) => this.dependencies.renameTab(this.tabId, title);
|
renameTab = (title: string) => this.dependencies.renameTab(this.tabId, title);
|
||||||
stopLoadingLogs = () => this.dependencies.stopLoadingLogs(this.tabId);
|
stopLoadingLogs = () => this.dependencies.stopLoadingLogs(this.tabId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
import { bind } from "../../../utils";
|
import { bind } from "../../../utils";
|
||||||
import type { LogStore } from "./store";
|
import type { LogStore } from "./store";
|
||||||
import logStoreInjectable from "./store.injectable";
|
import logStoreInjectable from "./store.injectable";
|
||||||
@ -13,8 +14,8 @@ interface Dependencies {
|
|||||||
logStore: LogStore;
|
logStore: LogStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadLogs({ logStore }: Dependencies, tabId: string, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
function reloadLogs({ logStore }: Dependencies, tabId: string, pod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
||||||
return logStore.reload(tabId, logTabData);
|
return logStore.reload(tabId, pod, logTabData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadLogsInjectable = getInjectable({
|
const reloadLogsInjectable = getInjectable({
|
||||||
|
|||||||
@ -5,12 +5,10 @@
|
|||||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
import { LogStore } from "./store";
|
import { LogStore } from "./store";
|
||||||
import callForLogsInjectable from "./call-for-logs.injectable";
|
import callForLogsInjectable from "./call-for-logs.injectable";
|
||||||
import { podsStore } from "../../+workloads-pods/pods.store";
|
|
||||||
|
|
||||||
const logStoreInjectable = getInjectable({
|
const logStoreInjectable = getInjectable({
|
||||||
instantiate: (di) => new LogStore({
|
instantiate: (di) => new LogStore({
|
||||||
callForLogs: di.inject(callForLogsInjectable),
|
callForLogs: di.inject(callForLogsInjectable),
|
||||||
getPodById: id => podsStore.getById(id),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
lifecycle: lifecycleEnum.singleton,
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { observable, IComputedValue } from "mobx";
|
import { observable, IComputedValue, when } from "mobx";
|
||||||
import type { IPodLogsQuery, Pod } from "../../../../common/k8s-api/endpoints";
|
import type { IPodLogsQuery, Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
import { getOrInsertWith, interval, IntervalFn } from "../../../utils";
|
import { getOrInsertWith, interval, IntervalFn } from "../../../utils";
|
||||||
import type { TabId } from "../dock/store";
|
import type { TabId } from "../dock/store";
|
||||||
@ -14,7 +14,6 @@ type PodLogLine = string;
|
|||||||
const logLinesToLoad = 500;
|
const logLinesToLoad = 500;
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
getPodById: (id: string) => Pod | undefined;
|
|
||||||
callForLogs: ({ namespace, name }: { namespace: string, name: string }, query: IPodLogsQuery) => Promise<string>
|
callForLogs: ({ namespace, name }: { namespace: string, name: string }, query: IPodLogsQuery) => Promise<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,24 +43,24 @@ export class LogStore {
|
|||||||
* Also, it handles loading errors, rewriting whole logs with error
|
* Also, it handles loading errors, rewriting whole logs with error
|
||||||
* messages
|
* messages
|
||||||
*/
|
*/
|
||||||
public async load(tabId: TabId, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
public async load(tabId: TabId, computedPod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const logs = await this.loadLogs(logTabData, {
|
const logs = await this.loadLogs(computedPod, logTabData, {
|
||||||
tailLines: this.getLogLines(tabId) + logLinesToLoad,
|
tailLines: this.getLogLines(tabId) + logLinesToLoad,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getRefresher(tabId, logTabData).start();
|
this.getRefresher(tabId, computedPod, logTabData).start();
|
||||||
this.podLogs.set(tabId, logs);
|
this.podLogs.set(tabId, logs);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handlerError(tabId, error);
|
this.handlerError(tabId, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRefresher(tabId: TabId, logTabData: IComputedValue<LogTabData>): IntervalFn {
|
private getRefresher(tabId: TabId, computedPod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): IntervalFn {
|
||||||
return getOrInsertWith(this.refreshers, tabId, () => (
|
return getOrInsertWith(this.refreshers, tabId, () => (
|
||||||
interval(10, () => {
|
interval(10, () => {
|
||||||
if (this.podLogs.has(tabId)) {
|
if (this.podLogs.has(tabId)) {
|
||||||
this.loadMore(tabId, logTabData);
|
this.loadMore(tabId, computedPod, logTabData);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
@ -81,14 +80,14 @@ export class LogStore {
|
|||||||
* starting from last line received.
|
* starting from last line received.
|
||||||
* @param tabId
|
* @param tabId
|
||||||
*/
|
*/
|
||||||
public async loadMore(tabId: TabId, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
public async loadMore(tabId: TabId, computedPod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
||||||
if (!this.podLogs.get(tabId).length) {
|
if (!this.podLogs.get(tabId).length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const oldLogs = this.podLogs.get(tabId);
|
const oldLogs = this.podLogs.get(tabId);
|
||||||
const logs = await this.loadLogs(logTabData, {
|
const logs = await this.loadLogs(computedPod, logTabData, {
|
||||||
sinceTime: this.getLastSinceTime(tabId),
|
sinceTime: this.getLastSinceTime(tabId),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,9 +105,11 @@ export class LogStore {
|
|||||||
* @param params request parameters described in IPodLogsQuery interface
|
* @param params request parameters described in IPodLogsQuery interface
|
||||||
* @returns A fetch request promise
|
* @returns A fetch request promise
|
||||||
*/
|
*/
|
||||||
private async loadLogs(logTabData: IComputedValue<LogTabData>, params: Partial<IPodLogsQuery>): Promise<string[]> {
|
private async loadLogs(computedPod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>, params: Partial<IPodLogsQuery>): Promise<string[]> {
|
||||||
const { selectedContainer, showPrevious, selectedPodId } = logTabData.get();
|
await when(() => Boolean(computedPod.get() && logTabData.get()), { timeout: 5_000 });
|
||||||
const pod = this.dependencies.getPodById(selectedPodId);
|
|
||||||
|
const { selectedContainer, showPrevious } = logTabData.get();
|
||||||
|
const pod = computedPod.get();
|
||||||
const namespace = pod.getNs();
|
const namespace = pod.getNs();
|
||||||
const name = pod.getName();
|
const name = pod.getName();
|
||||||
|
|
||||||
@ -135,6 +136,10 @@ export class LogStore {
|
|||||||
return this.getLogs(tabId).length;
|
return this.getLogs(tabId).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
areLogsPresent(tabId: TabId): boolean {
|
||||||
|
return !this.podLogs.has(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
getLogs(tabId: TabId): string[]{
|
getLogs(tabId: TabId): string[]{
|
||||||
return this.podLogs.get(tabId) ?? [];
|
return this.podLogs.get(tabId) ?? [];
|
||||||
}
|
}
|
||||||
@ -201,9 +206,9 @@ export class LogStore {
|
|||||||
this.podLogs.delete(tabId);
|
this.podLogs.delete(tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(tabId: TabId, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
reload(tabId: TabId, computedPod: IComputedValue<Pod | undefined>, logTabData: IComputedValue<LogTabData>): Promise<void> {
|
||||||
this.clearLogs(tabId);
|
this.clearLogs(tabId);
|
||||||
|
|
||||||
return this.load(tabId, logTabData);
|
return this.load(tabId, computedPod, logTabData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,6 @@ class WrappedAbortController extends AbortController {
|
|||||||
interface SubscribeStoreParams {
|
interface SubscribeStoreParams {
|
||||||
store: KubeObjectStore<KubeObject>;
|
store: KubeObjectStore<KubeObject>;
|
||||||
parent: AbortController;
|
parent: AbortController;
|
||||||
watchChanges: boolean;
|
|
||||||
namespaces: string[];
|
namespaces: string[];
|
||||||
onLoadFailure?: (err: any) => void;
|
onLoadFailure?: (err: any) => void;
|
||||||
}
|
}
|
||||||
@ -75,7 +74,7 @@ export interface KubeWatchSubscribeStoreOptions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that is called when listing fails. If set then blocks errors
|
* A function that is called when listing fails. If set then blocks errors
|
||||||
* being rejected with
|
* from rejecting promises
|
||||||
*/
|
*/
|
||||||
onLoadFailure?: (err: any) => void;
|
onLoadFailure?: (err: any) => void;
|
||||||
}
|
}
|
||||||
@ -89,12 +88,16 @@ export class KubeWatchApi {
|
|||||||
|
|
||||||
constructor(private dependencies: Dependencies) {}
|
constructor(private dependencies: Dependencies) {}
|
||||||
|
|
||||||
private subscribeStore({ store, parent, watchChanges, namespaces, onLoadFailure }: SubscribeStoreParams): Disposer {
|
private subscribeStore({ store, parent, namespaces, onLoadFailure }: SubscribeStoreParams): Disposer {
|
||||||
if (this.#watch.inc(store) > 1) {
|
const isNamespaceFilterWatch = !namespaces;
|
||||||
|
|
||||||
|
if (isNamespaceFilterWatch && this.#watch.inc(store) > 1) {
|
||||||
// don't load or subscribe to a store more than once
|
// don't load or subscribe to a store more than once
|
||||||
return () => this.#watch.dec(store);
|
return () => this.#watch.dec(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespaces ??= this.dependencies.clusterFrameContext?.contextNamespaces ?? [];
|
||||||
|
|
||||||
let childController = new WrappedAbortController(parent);
|
let childController = new WrappedAbortController(parent);
|
||||||
const unsubscribe = disposer();
|
const unsubscribe = disposer();
|
||||||
|
|
||||||
@ -117,7 +120,7 @@ export class KubeWatchApi {
|
|||||||
*/
|
*/
|
||||||
loadThenSubscribe(namespaces).catch(noop);
|
loadThenSubscribe(namespaces).catch(noop);
|
||||||
|
|
||||||
const cancelReloading = watchChanges
|
const cancelReloading = isNamespaceFilterWatch && store.api.isNamespaced
|
||||||
? reaction(
|
? reaction(
|
||||||
// Note: must slice because reaction won't fire if it isn't there
|
// Note: must slice because reaction won't fire if it isn't there
|
||||||
() => [this.dependencies.clusterFrameContext.contextNamespaces.slice(), this.dependencies.clusterFrameContext.hasSelectedAll] as const,
|
() => [this.dependencies.clusterFrameContext.contextNamespaces.slice(), this.dependencies.clusterFrameContext.hasSelectedAll] as const,
|
||||||
@ -141,7 +144,7 @@ export class KubeWatchApi {
|
|||||||
: noop; // don't watch namespaces if namespaces were provided
|
: noop; // don't watch namespaces if namespaces were provided
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (this.#watch.dec(store) === 0) {
|
if (isNamespaceFilterWatch && this.#watch.dec(store) === 0) {
|
||||||
// only stop the subcribe if this is the last one
|
// only stop the subcribe if this is the last one
|
||||||
cancelReloading();
|
cancelReloading();
|
||||||
childController.abort();
|
childController.abort();
|
||||||
@ -156,8 +159,7 @@ export class KubeWatchApi {
|
|||||||
...stores.map(store => this.subscribeStore({
|
...stores.map(store => this.subscribeStore({
|
||||||
store,
|
store,
|
||||||
parent,
|
parent,
|
||||||
watchChanges: !namespaces && store.api.isNamespaced,
|
namespaces,
|
||||||
namespaces: namespaces ?? this.dependencies.clusterFrameContext?.contextNamespaces ?? [],
|
|
||||||
onLoadFailure,
|
onLoadFailure,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user