mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
cleanup KubeObjectListLayout
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
3300a99a78
commit
a62e1fc4e5
@ -3,18 +3,19 @@ import type { KubeObjectStore } from "../kube-object.store";
|
||||
import { action, observable } from "mobx";
|
||||
import { autobind } from "../utils";
|
||||
import { KubeApi } from "./kube-api";
|
||||
import { KubeObject } from "./kube-object";
|
||||
|
||||
@autobind()
|
||||
export class ApiManager {
|
||||
private apis = observable.map<string, KubeApi>();
|
||||
private stores = observable.map<KubeApi, KubeObjectStore>();
|
||||
private stores = observable.map<KubeApi, KubeObjectStore<any>>();
|
||||
|
||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
|
||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean) = () => true) {
|
||||
if (typeof pathOrCallback === "string") {
|
||||
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase);
|
||||
}
|
||||
|
||||
return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true));
|
||||
return Array.from(this.apis.values()).find(pathOrCallback);
|
||||
}
|
||||
|
||||
registerApi(apiBase: string, api: KubeApi) {
|
||||
@ -29,24 +30,26 @@ export class ApiManager {
|
||||
return api;
|
||||
}
|
||||
|
||||
unregisterApi(api: string | KubeApi) {
|
||||
if (typeof api === "string") this.apis.delete(api);
|
||||
else {
|
||||
const apis = Array.from(this.apis.entries());
|
||||
const entry = apis.find(entry => entry[1] === api);
|
||||
unregisterApi(api: string | KubeApi): void {
|
||||
if (typeof api === "string") {
|
||||
return void this.apis.delete(api);
|
||||
}
|
||||
|
||||
if (entry) this.unregisterApi(entry[0]);
|
||||
for (const [apiPath, kubeApi] of this.apis) {
|
||||
if (kubeApi === api) {
|
||||
return void this.apis.delete(apiPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) {
|
||||
registerStore<T extends KubeObject>(store: KubeObjectStore<T>, apis: KubeApi[] = [store.api]) {
|
||||
apis.forEach(api => {
|
||||
this.stores.set(api, store);
|
||||
});
|
||||
}
|
||||
|
||||
getStore(api: string | KubeApi): KubeObjectStore {
|
||||
getStore<T extends KubeObject>(api: string | KubeApi): KubeObjectStore<T> {
|
||||
return this.stores.get(this.resolveApi(api));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeJsonApiData } from "../kube-json-api";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
@ -29,12 +28,7 @@ export class Secret extends KubeObject {
|
||||
data: {
|
||||
[prop: string]: string;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
constructor(data: KubeJsonApiData) {
|
||||
super(data);
|
||||
this.data = this.data || {};
|
||||
}
|
||||
} = {};
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(this.data);
|
||||
|
||||
@ -79,7 +79,7 @@ export function forCluster<T extends KubeObject>(cluster: IKubeApiCluster, kubeC
|
||||
});
|
||||
}
|
||||
|
||||
export class KubeApi<T extends KubeObject = any> {
|
||||
export class KubeApi<T extends KubeObject = KubeObject> {
|
||||
static parseApi = parseKubeApi;
|
||||
|
||||
static watchAll(...apis: KubeApi[]) {
|
||||
|
||||
@ -9,6 +9,7 @@ import { KubeApi } from "./kube-api";
|
||||
import { apiManager } from "./api-manager";
|
||||
import { apiPrefix, isDevelopment } from "../../common/vars";
|
||||
import { getHostedCluster } from "../../common/cluster-store";
|
||||
import { KubeObject } from "./kube-object";
|
||||
|
||||
export interface IKubeWatchEvent<T = any> {
|
||||
type: "ADDED" | "MODIFIED" | "DELETED";
|
||||
@ -156,7 +157,7 @@ export class KubeWatchApi {
|
||||
}
|
||||
}
|
||||
|
||||
addListener(store: KubeObjectStore, callback: (evt: IKubeWatchEvent) => void) {
|
||||
addListener<T extends KubeObject>(store: KubeObjectStore<T>, callback: (evt: IKubeWatchEvent) => void) {
|
||||
const listener = (evt: IKubeWatchEvent<KubeJsonApiData>) => {
|
||||
const { selfLink, namespace, resourceVersion } = evt.object.metadata;
|
||||
const api = apiManager.getApi(selfLink);
|
||||
|
||||
@ -86,19 +86,19 @@ export class HelmReleases extends Component<Props> {
|
||||
store={releaseStore}
|
||||
dependentStores={[secretsStore]}
|
||||
sortingCallbacks={{
|
||||
[sortBy.name]: (release: HelmRelease) => release.getName(),
|
||||
[sortBy.namespace]: (release: HelmRelease) => release.getNs(),
|
||||
[sortBy.revision]: (release: HelmRelease) => release.getRevision(),
|
||||
[sortBy.chart]: (release: HelmRelease) => release.getChart(),
|
||||
[sortBy.status]: (release: HelmRelease) => release.getStatus(),
|
||||
[sortBy.updated]: (release: HelmRelease) => release.getUpdated(false, false),
|
||||
[sortBy.name]: release => release.getName(),
|
||||
[sortBy.namespace]: release => release.getNs(),
|
||||
[sortBy.revision]: release => release.getRevision(),
|
||||
[sortBy.chart]: release => release.getChart(),
|
||||
[sortBy.status]: release => release.getStatus(),
|
||||
[sortBy.updated]: release => release.getUpdated(false, false),
|
||||
}}
|
||||
searchFilters={[
|
||||
(release: HelmRelease) => release.getName(),
|
||||
(release: HelmRelease) => release.getNs(),
|
||||
(release: HelmRelease) => release.getChart(),
|
||||
(release: HelmRelease) => release.getStatus(),
|
||||
(release: HelmRelease) => release.getVersion(),
|
||||
release => release.getName(),
|
||||
release => release.getNs(),
|
||||
release => release.getChart(),
|
||||
release => release.getStatus(),
|
||||
release => release.getVersion(),
|
||||
]}
|
||||
renderHeaderTitle={<Trans>Releases</Trans>}
|
||||
renderTableHeader={[
|
||||
@ -111,31 +111,23 @@ export class HelmReleases extends Component<Props> {
|
||||
{ title: <Trans>Status</Trans>, className: "status", sortBy: sortBy.status },
|
||||
{ title: <Trans>Updated</Trans>, className: "updated", sortBy: sortBy.updated },
|
||||
]}
|
||||
renderTableContents={(release: HelmRelease) => {
|
||||
const version = release.getVersion();
|
||||
|
||||
return [
|
||||
release.getName(),
|
||||
release.getNs(),
|
||||
release.getChart(),
|
||||
release.getRevision(),
|
||||
<>
|
||||
{version}
|
||||
</>,
|
||||
release.appVersion,
|
||||
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
||||
release.getUpdated(),
|
||||
];
|
||||
}}
|
||||
renderItemMenu={(release: HelmRelease) => {
|
||||
return (
|
||||
<HelmReleaseMenu
|
||||
release={release}
|
||||
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({
|
||||
renderTableContents={release => [
|
||||
release.getName(),
|
||||
release.getNs(),
|
||||
release.getChart(),
|
||||
release.getRevision(),
|
||||
release.getVersion(),
|
||||
release.appVersion,
|
||||
{ title: release.getStatus(), className: kebabCase(release.getStatus()) },
|
||||
release.getUpdated(),
|
||||
]}
|
||||
renderItemMenu={release => (
|
||||
<HelmReleaseMenu
|
||||
release={release}
|
||||
removeConfirmationMessage={this.renderRemoveDialogMessage([release])}
|
||||
/>
|
||||
)}
|
||||
customizeRemoveDialog={selectedItems => ({
|
||||
message: this.renderRemoveDialogMessage(selectedItems)
|
||||
})}
|
||||
detailsItem={this.selectedRelease}
|
||||
|
||||
@ -4,7 +4,6 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { RouteComponentProps } from "react-router";
|
||||
import { Secret } from "../../api/endpoints";
|
||||
import { AddSecretDialog } from "./add-secret-dialog";
|
||||
import { ISecretsRouteParams } from "./secrets.route";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
@ -30,18 +29,19 @@ export class Secrets extends React.Component<Props> {
|
||||
return (
|
||||
<>
|
||||
<KubeObjectListLayout
|
||||
className="Secrets" store={secretsStore}
|
||||
className="Secrets"
|
||||
store={secretsStore}
|
||||
sortingCallbacks={{
|
||||
[sortBy.name]: (item: Secret) => item.getName(),
|
||||
[sortBy.namespace]: (item: Secret) => item.getNs(),
|
||||
[sortBy.labels]: (item: Secret) => item.getLabels(),
|
||||
[sortBy.keys]: (item: Secret) => item.getKeys(),
|
||||
[sortBy.type]: (item: Secret) => item.type,
|
||||
[sortBy.age]: (item: Secret) => item.metadata.creationTimestamp,
|
||||
[sortBy.name]: secret => secret.getName(),
|
||||
[sortBy.namespace]: secret => secret.getNs(),
|
||||
[sortBy.labels]: secret => secret.getLabels(),
|
||||
[sortBy.keys]: secret => secret.getKeys(),
|
||||
[sortBy.type]: secret => secret.type,
|
||||
[sortBy.age]: secret => secret.metadata.creationTimestamp,
|
||||
}}
|
||||
searchFilters={[
|
||||
(item: Secret) => item.getSearchFields(),
|
||||
(item: Secret) => item.getKeys(),
|
||||
secret => secret.getSearchFields(),
|
||||
secret => secret.getKeys(),
|
||||
]}
|
||||
renderHeaderTitle={<Trans>Secrets</Trans>}
|
||||
renderTableHeader={[
|
||||
@ -53,7 +53,7 @@ export class Secrets extends React.Component<Props> {
|
||||
{ title: <Trans>Type</Trans>, className: "type", sortBy: sortBy.type },
|
||||
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
|
||||
]}
|
||||
renderTableContents={(secret: Secret) => [
|
||||
renderTableContents={secret => [
|
||||
secret.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={secret} />,
|
||||
secret.getNs(),
|
||||
|
||||
@ -54,14 +54,14 @@ export class CrdResources extends React.Component<Props> {
|
||||
if (!crd) return null;
|
||||
const isNamespaced = crd.isNamespaced();
|
||||
const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details
|
||||
const sortingCallbacks: { [sortBy: string]: TableSortCallback } = {
|
||||
[sortBy.name]: (item: KubeObject) => item.getName(),
|
||||
[sortBy.namespace]: (item: KubeObject) => item.getNs(),
|
||||
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
||||
const sortingCallbacks: { [sortBy: string]: TableSortCallback<KubeObject> } = {
|
||||
[sortBy.name]: item => item.getName(),
|
||||
[sortBy.namespace]: item => item.getNs(),
|
||||
[sortBy.age]: item => item.metadata.creationTimestamp,
|
||||
};
|
||||
|
||||
extraColumns.forEach(column => {
|
||||
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, column.jsonPath.slice(1));
|
||||
sortingCallbacks[column.name] = item => jsonPath.value(item, column.jsonPath.slice(1));
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@ -23,17 +23,13 @@ export const ContainerEnvironment = observer((props: Props) => {
|
||||
useEffect(
|
||||
() =>
|
||||
autorun(() => {
|
||||
env && env.forEach(variable => {
|
||||
const { valueFrom } = variable;
|
||||
|
||||
if (valueFrom && valueFrom.configMapKeyRef) {
|
||||
env?.forEach(({ valueFrom }) => {
|
||||
if (valueFrom?.configMapKeyRef) {
|
||||
configMapsStore.load({ name: valueFrom.configMapKeyRef.name, namespace });
|
||||
}
|
||||
});
|
||||
envFrom && envFrom.forEach(item => {
|
||||
const { configMapRef } = item;
|
||||
|
||||
if (configMapRef && configMapRef.name) {
|
||||
envFrom?.forEach(({ configMapRef }) => {
|
||||
if (configMapRef?.name) {
|
||||
configMapsStore.load({ name: configMapRef.name, namespace });
|
||||
}
|
||||
});
|
||||
|
||||
@ -44,7 +44,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
// header (title, filtering, searching, etc.)
|
||||
showHeader?: boolean;
|
||||
headerClassName?: IClassName;
|
||||
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode);
|
||||
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout<T>) => ReactNode);
|
||||
customizeHeader?: (placeholders: IHeaderPlaceholders, content: ReactNode) => Partial<IHeaderPlaceholders> | ReactNode;
|
||||
|
||||
// items list configuration
|
||||
@ -52,8 +52,8 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
isSelectable?: boolean; // show checkbox in rows for selecting items
|
||||
isSearchable?: boolean; // apply search-filter & add search-input
|
||||
copyClassNameFromHeadCells?: boolean;
|
||||
sortingCallbacks?: { [sortBy: string]: TableSortCallback };
|
||||
tableProps?: Partial<TableProps>; // low-level table configuration
|
||||
sortingCallbacks?: { [sortBy: string]: TableSortCallback<T> };
|
||||
tableProps?: Partial<TableProps<T>>; // low-level table configuration
|
||||
renderTableHeader: TableCellProps[] | null;
|
||||
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
|
||||
renderItemMenu?: (item: T, store: ItemStore<T>) => ReactNode;
|
||||
@ -68,7 +68,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
|
||||
// other
|
||||
customizeRemoveDialog?: (selectedItems: T[]) => Partial<ConfirmDialogParams>;
|
||||
renderFooter?: (parent: ItemListLayout) => React.ReactNode;
|
||||
renderFooter?: (parent: ItemListLayout<T>) => React.ReactNode;
|
||||
}
|
||||
|
||||
const defaultProps: Partial<ItemListLayoutProps> = {
|
||||
@ -88,7 +88,7 @@ interface ItemListLayoutUserSettings {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
export class ItemListLayout<T extends ItemObject> extends React.Component<ItemListLayoutProps<T>> {
|
||||
static defaultProps = defaultProps as object;
|
||||
|
||||
@observable isUnmounting = false;
|
||||
@ -98,7 +98,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
showAppliedFilters: false,
|
||||
};
|
||||
|
||||
constructor(props: ItemListLayoutProps) {
|
||||
constructor(props: ItemListLayoutProps<T>) {
|
||||
super(props);
|
||||
|
||||
// keep ui user settings in local storage
|
||||
|
||||
@ -7,24 +7,24 @@ import { getSelectedDetails, showDetails } from "../../navigation";
|
||||
import { ItemListLayout, ItemListLayoutProps } from "../item-object-list/item-list-layout";
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { KubeObjectMenu } from "./kube-object-menu";
|
||||
import { ItemObject } from "../../item.store";
|
||||
|
||||
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
|
||||
store: KubeObjectStore;
|
||||
export interface KubeObjectListLayoutProps<T extends ItemObject & KubeObject> extends ItemListLayoutProps<T> {
|
||||
store: KubeObjectStore<T>;
|
||||
}
|
||||
|
||||
function showItemDetails(item: KubeObject) {
|
||||
return showDetails(item.selfLink);
|
||||
}
|
||||
|
||||
@observer
|
||||
export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutProps> {
|
||||
export class KubeObjectListLayout<T extends ItemObject & KubeObject> extends React.Component<KubeObjectListLayoutProps<T>> {
|
||||
@computed get selectedItem() {
|
||||
return this.props.store.getByPath(getSelectedDetails());
|
||||
}
|
||||
|
||||
onDetails = (item: KubeObject) => {
|
||||
if (this.props.onDetails) {
|
||||
this.props.onDetails(item);
|
||||
}
|
||||
else {
|
||||
showDetails(item.selfLink);
|
||||
}
|
||||
static defaultProps = {
|
||||
onDetails: showItemDetails
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -35,7 +35,7 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
|
||||
{...layoutProps}
|
||||
className={cssNames("KubeObjectListLayout", className)}
|
||||
detailsItem={this.selectedItem}
|
||||
onDetails={this.onDetails}
|
||||
onDetails={this.props.onDetails}
|
||||
renderItemMenu={(item) => {
|
||||
return <KubeObjectMenu object={item}/>;
|
||||
}}
|
||||
|
||||
@ -17,9 +17,9 @@ import { ItemObject } from "../../item.store";
|
||||
export type TableSortBy = string;
|
||||
export type TableOrderBy = "asc" | "desc" | string;
|
||||
export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy };
|
||||
export type TableSortCallback<D = any> = (data: D) => string | number | (string | number)[];
|
||||
export type TableSortCallback<D> = (data: D) => string | number | (string | number)[];
|
||||
|
||||
export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
||||
export interface TableProps<T> extends React.DOMAttributes<HTMLDivElement> {
|
||||
items?: ItemObject[]; // Raw items data
|
||||
className?: string;
|
||||
autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0)
|
||||
@ -29,7 +29,7 @@ export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
||||
sortable?: {
|
||||
// Define sortable callbacks for every column in <TableHead><TableCell sortBy="someCol"><TableHead>
|
||||
// @sortItem argument in the callback is an object, provided in <TableRow sortItem={someColDataItem}/>
|
||||
[sortBy: string]: TableSortCallback;
|
||||
[sortBy: string]: TableSortCallback<T>;
|
||||
};
|
||||
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
||||
sortByDefault?: Partial<TableSortParams>; // default sorting params
|
||||
@ -44,8 +44,8 @@ export interface TableProps extends React.DOMAttributes<HTMLDivElement> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Table extends React.Component<TableProps> {
|
||||
static defaultProps: TableProps = {
|
||||
export class Table<T> extends React.Component<TableProps<T>> {
|
||||
static defaultProps: TableProps<any> = {
|
||||
scrollable: true,
|
||||
autoSize: true,
|
||||
rowPadding: "8px",
|
||||
|
||||
@ -9,7 +9,7 @@ import { KubeJsonApiData } from "./api/kube-json-api";
|
||||
import { getHostedCluster } from "../common/cluster-store";
|
||||
|
||||
@autobind()
|
||||
export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemStore<T> {
|
||||
export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T> {
|
||||
abstract api: KubeApi<T>;
|
||||
public readonly limit?: number;
|
||||
public readonly bufferSize: number = 50000;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user