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

fixes & refactoring

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-02-04 18:20:06 +02:00
parent 4fcdf01ba2
commit 094245a240
15 changed files with 106 additions and 52 deletions

View File

@ -30,7 +30,7 @@ export class CrdResources extends React.Component<Props> {
const { store } = this; const { store } = this;
if (store && !store.isLoading && !store.isLoaded) { if (store && !store.isLoading && !store.isLoaded) {
store.loadSelectedNamespaces(); store.loadContextNamespaces();
} }
}) })
]); ]);

View File

@ -14,7 +14,7 @@ export interface KubeEventDetailsProps {
@observer @observer
export class KubeEventDetails extends React.Component<KubeEventDetailsProps> { export class KubeEventDetails extends React.Component<KubeEventDetailsProps> {
async componentDidMount() { async componentDidMount() {
eventStore.loadSelectedNamespaces(); eventStore.loadContextNamespaces();
} }
render() { render() {

View File

@ -32,8 +32,8 @@ export class NamespaceDetails extends React.Component<Props> {
} }
componentDidMount() { componentDidMount() {
resourceQuotaStore.loadSelectedNamespaces(); resourceQuotaStore.loadContextNamespaces();
limitRangeStore.loadSelectedNamespaces(); limitRangeStore.loadContextNamespaces();
} }
render() { render() {

View File

@ -103,8 +103,20 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
return []; return [];
} }
public getContextNamespaces(): string[] { getContextNamespaces(): string[] {
return Array.from(this.contextNs); const namespaces = Array.from(this.contextNs);
// show all namespaces when nothing selected
if (!namespaces.length) {
// return actual namespaces list since "allowedNamespaces" updating every 30s in cluster and thus might be stale
if (this.isLoaded) {
return this.items.map(namespace => namespace.getName());
}
return this.allowedNamespaces;
}
return namespaces;
} }
getSubscribeApis() { getSubscribeApis() {
@ -139,6 +151,11 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
this.contextNs.replace(namespaces); this.contextNs.replace(namespaces);
} }
@action
resetContext(){
this.contextNs.clear();
}
hasContext(namespaces: string | string[]) { hasContext(namespaces: string | string[]) {
return [namespaces].flat().every(namespace => this.contextNs.has(namespace)); return [namespaces].flat().every(namespace => this.contextNs.has(namespace));
} }

View File

@ -29,7 +29,7 @@ export class NodeDetails extends React.Component<Props> {
}); });
async componentDidMount() { async componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -80,7 +80,7 @@ export class AddRoleBindingDialog extends React.Component<Props> {
]; ];
this.isLoading = true; this.isLoading = true;
await Promise.all(stores.map(store => store.loadSelectedNamespaces())); await Promise.all(stores.map(store => store.loadContextNamespaces()));
this.isLoading = false; this.isLoading = false;
} }

View File

@ -20,7 +20,7 @@ interface Props extends KubeObjectDetailsProps<CronJob> {
@observer @observer
export class CronJobDetails extends React.Component<Props> { export class CronJobDetails extends React.Component<Props> {
async componentDidMount() { async componentDidMount() {
jobStore.loadSelectedNamespaces(); jobStore.loadContextNamespaces();
} }
render() { render() {

View File

@ -30,7 +30,7 @@ export class DaemonSetDetails extends React.Component<Props> {
}); });
componentDidMount() { componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -31,7 +31,7 @@ export class DeploymentDetails extends React.Component<Props> {
}); });
componentDidMount() { componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -25,7 +25,7 @@ interface Props extends KubeObjectDetailsProps<Job> {
@observer @observer
export class JobDetails extends React.Component<Props> { export class JobDetails extends React.Component<Props> {
async componentDidMount() { async componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
render() { render() {

View File

@ -29,7 +29,7 @@ export class ReplicaSetDetails extends React.Component<Props> {
}); });
async componentDidMount() { async componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -30,7 +30,7 @@ export class StatefulSetDetails extends React.Component<Props> {
}); });
componentDidMount() { componentDidMount() {
podsStore.loadSelectedNamespaces(); podsStore.loadContextNamespaces();
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -40,7 +40,7 @@ interface Props {
@observer @observer
export class Sidebar extends React.Component<Props> { export class Sidebar extends React.Component<Props> {
async componentDidMount() { async componentDidMount() {
crdStore.loadSelectedNamespaces(); crdStore.loadContextNamespaces();
} }
renderCustomResources() { renderCustomResources() {

View File

@ -22,13 +22,17 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
return this.items.filter(item => this.selectedItemsIds.get(item.getId())); return this.items.filter(item => this.selectedItemsIds.get(item.getId()));
} }
public getItems(): T[] {
return this.items.toJS();
}
getByName(name: string, ...args: any[]): T; getByName(name: string, ...args: any[]): T;
getByName(name: string): T { getByName(name: string): T {
return this.items.find(item => item.getName() === name); return this.items.find(item => item.getName() === name);
} }
getIndex(item: T): number { getIndexById(id: string): number {
return this.items.findIndex(i => i === item); return this.items.findIndex(item => item.getId() === id);
} }
@action @action

View File

@ -1,5 +1,7 @@
import type { Cluster } from "../main/cluster"; import type { Cluster } from "../main/cluster";
import { action, observable, reaction } from "mobx"; import type { NamespaceStore } from "./components/+namespaces/namespace.store";
import { action, computed, observable, reaction } 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,6 +26,36 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
this.bindWatchEventsUpdater(); this.bindWatchEventsUpdater();
} }
// TODO: detach / remove circular dependency
@observable.ref private namespaceStore: NamespaceStore;
private async resolveNamespaceStore(): Promise<NamespaceStore> {
const { namespaceStore } = await import("./components/+namespaces/namespace.store");
await namespaceStore.whenReady;
this.namespaceStore = namespaceStore;
return namespaceStore;
}
// TODO: detach / inject dependency as in kube-watch-api
@observable.ref private cluster: Cluster;
private async resolveCluster(): Promise<Cluster> {
const { getHostedCluster } = await import("../common/cluster-store");
this.cluster = getHostedCluster();
return this.cluster;
}
// TODO: figure out how to transparently replace with this.items
@computed get contextItems(): T[] {
const contextNamespaces = this.namespaceStore?.getContextNamespaces() ?? []; // not loaded
return this.items.filter((item: T) => !item.getNs() || contextNamespaces.includes(item.getId()));
}
get query(): IKubeApiQueryParams { get query(): IKubeApiQueryParams {
const { limit } = this; const { limit } = this;
@ -79,12 +111,6 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
} }
protected async resolveCluster(): Promise<Cluster> {
const { getHostedCluster } = await import("../common/cluster-store");
return getHostedCluster();
}
protected async loadItems({ namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> { protected async loadItems({ namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> {
const cluster = await this.resolveCluster(); const cluster = await this.resolveCluster();
@ -106,23 +132,19 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
@action @action
async loadAll(namespaces: string[] = []): Promise<void> { async loadAll(namespaces: string[] = [], { replace = true } = {}): Promise<void> {
this.isLoading = true; this.isLoading = true;
try { try {
// load all available namespaces by default
if (!namespaces.length) { if (!namespaces.length) {
const { namespaceStore } = await import("./components/+namespaces/namespace.store"); const namespaceStore = await this.resolveNamespaceStore();
// load all available namespaces by default namespaces = namespaceStore.getContextNamespaces();
namespaces.push(...namespaceStore.allowedNamespaces);
} }
let items = await this.loadItems({ namespaces, api: this.api }); let items = await this.loadItems({ namespaces, api: this.api });
this.mergeItems(items, { replace });
items = this.filterItemsOnLoad(items);
items = this.sortItems(items);
this.items.replace(items);
this.isLoaded = true; this.isLoaded = true;
} catch (error) { } catch (error) {
console.error("Loading store items failed", { error, store: this }); console.error("Loading store items failed", { error, store: this });
@ -132,10 +154,26 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
} }
} }
async loadSelectedNamespaces(): Promise<void> { @action
const { namespaceStore } = await import("./components/+namespaces/namespace.store"); private mergeItems(partialItems: T[], { replace = false, sort = true, filter = true } = {}): T[] {
let items = this.items.toJS();
return this.loadAll(namespaceStore.getContextNamespaces()); partialItems.forEach(item => {
const index = items.findIndex(i => i.getId() === item.getId());
if (index < 0) items.push(item); // add
else items[index] = item; // update
});
if (filter) items = this.filterItemsOnLoad(items)
if (sort) items = this.sortItems(items);
if (replace) this.items.replace(items);
return items;
}
async loadContextNamespaces(): Promise<void> {
return this.loadAll(this.namespaceStore?.getContextNamespaces());
} }
protected resetOnError(error: any) { protected resetOnError(error: any) {
@ -224,14 +262,11 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
@action @action
protected updateFromEventsBuffer() { protected updateFromEventsBuffer() {
const eventsBuffer = this.eventsBuffer.clear(); const items = this.items.toJS();
if (!eventsBuffer.length) { for (const { type, object } of this.eventsBuffer.clear()) {
return; const index = items.findIndex(item => item.getId() === object.metadata?.uid);
} const item = items[index];
for (const { type, object } of eventsBuffer) {
const item = this.getById(object.metadata?.uid);
const api = apiManager.getApiByKind(object.kind, object.apiVersion); const api = apiManager.getApiByKind(object.kind, object.apiVersion);
switch (type) { switch (type) {
@ -240,22 +275,20 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
const newItem = new api.objectConstructor(object); const newItem = new api.objectConstructor(object);
if (!item) { if (!item) {
this.items.push(newItem); items.push(newItem);
} else { } else {
const index = this.getIndex(item); items[index] = newItem;
this.items.splice(index, 1, newItem);
} }
break; break;
case "DELETED": case "DELETED":
this.items.remove(item); if (item) {
items.splice(index, 1);
}
break; break;
} }
} }
// sort and limit items to store's maximum // update items
const newItems = this.sortItems(this.items.slice(-this.bufferSize)); this.items.replace(this.sortItems(items.slice(-this.bufferSize)));
this.items.replace(newItems);
} }
} }