mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
add more typing to sorting of tables
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
a62e1fc4e5
commit
f66d84ae4a
@ -56,4 +56,4 @@ export function resolveStatusForCronJobs(cronJob: K8sApi.CronJob): K8sApi.KubeOb
|
||||
text: `${event.message}`,
|
||||
timestamp: event.metadata.creationTimestamp
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,7 +269,6 @@ export class ExtensionDiscovery {
|
||||
// fs.remove won't throw if path is missing
|
||||
await fs.remove(path.join(extensionInstaller.extensionPackagesRoot, "package-lock.json"));
|
||||
|
||||
|
||||
try {
|
||||
// Verify write access to static/extensions, which is needed for symlinking
|
||||
await fs.access(this.inTreeFolderPath, fs.constants.W_OK);
|
||||
@ -293,19 +292,19 @@ export class ExtensionDiscovery {
|
||||
this.bundledFolderPath = this.inTreeTargetPath;
|
||||
}
|
||||
|
||||
await fs.ensureDir(this.nodeModulesPath);
|
||||
await fs.ensureDir(this.localFolderPath);
|
||||
|
||||
await Promise.all([fs.ensureDir(this.nodeModulesPath), fs.ensureDir(this.localFolderPath)]);
|
||||
|
||||
const extensions = await this.loadExtensions();
|
||||
|
||||
this.isLoaded = true;
|
||||
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the symlinked path to the extension folder,
|
||||
* e.g. "/Users/<username>/Library/Application Support/Lens/node_modules/@publisher/extension"
|
||||
* e.g. `/Users/<username>/Library/Application Support/Lens/node_modules/@publisher/extension`
|
||||
*/
|
||||
protected getInstalledPath(name: string) {
|
||||
return path.join(this.nodeModulesPath, name);
|
||||
|
||||
@ -10,12 +10,12 @@ export class ApiManager {
|
||||
private apis = observable.map<string, KubeApi>();
|
||||
private stores = observable.map<KubeApi, KubeObjectStore<any>>();
|
||||
|
||||
getApi(pathOrCallback: string | ((api: KubeApi) => boolean) = () => true) {
|
||||
getApi<T extends KubeObject = KubeObject>(pathOrCallback: string | ((api: KubeApi) => boolean) = () => true): KubeApi<T> {
|
||||
if (typeof pathOrCallback === "string") {
|
||||
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase);
|
||||
return (this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase)) as KubeApi<T>;
|
||||
}
|
||||
|
||||
return Array.from(this.apis.values()).find(pathOrCallback);
|
||||
return Array.from(this.apis.values()).find(pathOrCallback) as KubeApi<T>;
|
||||
}
|
||||
|
||||
registerApi(apiBase: string, api: KubeApi) {
|
||||
|
||||
@ -3,31 +3,29 @@ import { KubeObject } from "../kube-object";
|
||||
import { KubeJsonApiData } from "../kube-json-api";
|
||||
import { apiBase } from "../index";
|
||||
import { apiManager } from "../api-manager";
|
||||
import { CancelablePromise } from "../../utils/cancelableFetch";
|
||||
import { filter } from "lodash";
|
||||
|
||||
export const resourceApplierApi = {
|
||||
annotations: [
|
||||
"kubectl.kubernetes.io/last-applied-configuration"
|
||||
],
|
||||
|
||||
async update<D extends KubeObject>(resource: object | string): Promise<D> {
|
||||
update<D extends KubeObject>(resource: object | string): CancelablePromise<D[]> {
|
||||
if (typeof resource === "string") {
|
||||
resource = jsYaml.safeLoad(resource);
|
||||
}
|
||||
|
||||
return apiBase
|
||||
.post<KubeJsonApiData[]>("/stack", { data: resource })
|
||||
.then(data => {
|
||||
const items = data.map(obj => {
|
||||
const api = apiManager.getApi(obj.metadata.selfLink);
|
||||
.then(data => filter(
|
||||
data.map(obj => {
|
||||
const api = apiManager.getApi<D>(obj.metadata.selfLink);
|
||||
|
||||
if (api) {
|
||||
if (api?.objectConstructor) {
|
||||
return new api.objectConstructor(obj);
|
||||
} else {
|
||||
return new KubeObject(obj);
|
||||
}
|
||||
});
|
||||
|
||||
return items.length === 1 ? items[0] : items;
|
||||
});
|
||||
})
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,11 +4,8 @@ import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { KubeObject } from "../../api/kube-object";
|
||||
|
||||
@autobind()
|
||||
export class CRDResourceStore<T extends KubeObject = any> extends KubeObjectStore<T> {
|
||||
api: KubeApi;
|
||||
|
||||
constructor(api: KubeApi<T>) {
|
||||
export class CRDResourceStore<T extends KubeObject = KubeObject> extends KubeObjectStore<T> {
|
||||
constructor(public api: KubeApi<T>) {
|
||||
super();
|
||||
this.api = api;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ enum sortBy {
|
||||
age = "age",
|
||||
}
|
||||
|
||||
interface Props extends Partial<KubeObjectListLayoutProps> {
|
||||
interface Props extends Partial<KubeObjectListLayoutProps<KubeEvent>> {
|
||||
className?: IClassName;
|
||||
compact?: boolean;
|
||||
compactLimit?: number;
|
||||
@ -45,17 +45,17 @@ export class Events extends React.Component<Props> {
|
||||
store={eventStore}
|
||||
isSelectable={false}
|
||||
sortingCallbacks={{
|
||||
[sortBy.namespace]: (event: KubeEvent) => event.getNs(),
|
||||
[sortBy.type]: (event: KubeEvent) => event.involvedObject.kind,
|
||||
[sortBy.object]: (event: KubeEvent) => event.involvedObject.name,
|
||||
[sortBy.count]: (event: KubeEvent) => event.count,
|
||||
[sortBy.age]: (event: KubeEvent) => event.metadata.creationTimestamp,
|
||||
[sortBy.namespace]: event => event.getNs(),
|
||||
[sortBy.type]: event => event.involvedObject.kind,
|
||||
[sortBy.object]: event => event.involvedObject.name,
|
||||
[sortBy.count]: event => event.count,
|
||||
[sortBy.age]: event => event.metadata.creationTimestamp,
|
||||
}}
|
||||
searchFilters={[
|
||||
(event: KubeEvent) => event.getSearchFields(),
|
||||
(event: KubeEvent) => event.message,
|
||||
(event: KubeEvent) => event.getSource(),
|
||||
(event: KubeEvent) => event.involvedObject.name,
|
||||
event => event.getSearchFields(),
|
||||
event => event.message,
|
||||
event => event.getSource(),
|
||||
event => event.involvedObject.name,
|
||||
]}
|
||||
renderHeaderTitle={<Trans>Events</Trans>}
|
||||
customizeHeader={({ title, info }) => (
|
||||
@ -82,7 +82,7 @@ export class Events extends React.Component<Props> {
|
||||
{ title: <Trans>Count</Trans>, className: "count", sortBy: sortBy.count },
|
||||
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
|
||||
]}
|
||||
renderTableContents={(event: KubeEvent) => {
|
||||
renderTableContents={event => {
|
||||
const { involvedObject, type, message } = event;
|
||||
const { kind, name } = involvedObject;
|
||||
const tooltipId = `message-${event.getId()}`;
|
||||
|
||||
@ -35,16 +35,21 @@ export class ServiceAccountsDetails extends React.Component<Props> {
|
||||
return;
|
||||
}
|
||||
const namespace = serviceAccount.getNs();
|
||||
const secrets = serviceAccount.getSecrets().map(({ name }) => {
|
||||
return secretsStore.load({ name, namespace });
|
||||
});
|
||||
|
||||
this.secrets = await Promise.all(secrets);
|
||||
const imagePullSecrets = serviceAccount.getImagePullSecrets().map(async({ name }) => {
|
||||
return secretsStore.load({ name, namespace }).catch(() => this.generateDummySecretObject(name));
|
||||
});
|
||||
|
||||
this.imagePullSecrets = await Promise.all(imagePullSecrets);
|
||||
this.secrets = await Promise.all(
|
||||
serviceAccount
|
||||
.getSecrets()
|
||||
.map(({ name }) => secretsStore.load({ name, namespace }))
|
||||
);
|
||||
this.imagePullSecrets = await Promise.all(
|
||||
serviceAccount
|
||||
.getImagePullSecrets()
|
||||
.map(({ name }) => (
|
||||
secretsStore
|
||||
.load({ name, namespace })
|
||||
.catch(() => this.generateDummySecretObject(name))
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
renderSecrets() {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import "./overview.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observable, when } from "mobx";
|
||||
import { computed, observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { OverviewStatuses } from "./overview-statuses";
|
||||
import { RouteComponentProps } from "react-router";
|
||||
@ -18,60 +18,36 @@ import { Spinner } from "../spinner";
|
||||
import { Events } from "../+events";
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { filter } from "lodash";
|
||||
|
||||
interface Props extends RouteComponentProps<IWorkloadsOverviewRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class WorkloadsOverview extends React.Component<Props> {
|
||||
@observable isReady = false;
|
||||
@observable isUnmounting = false;
|
||||
@observable stores: KubeObjectStore<any>[] = [];
|
||||
unsubscribeList: (() => void)[] = [];
|
||||
|
||||
@computed get isReady() {
|
||||
return this.stores.every(store => store.isLoaded);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const stores: KubeObjectStore[] = [];
|
||||
|
||||
if (isAllowedResource("pods")) {
|
||||
stores.push(podsStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("deployments")) {
|
||||
stores.push(deploymentStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("daemonsets")) {
|
||||
stores.push(daemonSetStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("statefulsets")) {
|
||||
stores.push(statefulSetStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("replicasets")) {
|
||||
stores.push(replicaSetStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("jobs")) {
|
||||
stores.push(jobStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("cronjobs")) {
|
||||
stores.push(cronJobStore);
|
||||
}
|
||||
|
||||
if (isAllowedResource("events")) {
|
||||
stores.push(eventStore);
|
||||
}
|
||||
this.isReady = stores.every(store => store.isLoaded);
|
||||
await Promise.all(stores.map(store => store.loadAll()));
|
||||
this.isReady = true;
|
||||
const unsubscribeList = stores.map(store => store.subscribe());
|
||||
|
||||
await when(() => this.isUnmounting);
|
||||
unsubscribeList.forEach(dispose => dispose());
|
||||
this.stores = filter([
|
||||
isAllowedResource("pods") && podsStore,
|
||||
isAllowedResource("deployments") && deploymentStore,
|
||||
isAllowedResource("daemonsets") && daemonSetStore,
|
||||
isAllowedResource("statefulsets") && statefulSetStore,
|
||||
isAllowedResource("replicasets") && replicaSetStore,
|
||||
isAllowedResource("jobs") && jobStore,
|
||||
isAllowedResource("cronjobs") && cronJobStore,
|
||||
isAllowedResource("events") && eventStore,
|
||||
]);
|
||||
this.unsubscribeList = this.stores.map(store => store.subscribe());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isUnmounting = true;
|
||||
this.unsubscribeList.forEach(dispose => dispose());
|
||||
}
|
||||
|
||||
renderContents() {
|
||||
|
||||
@ -8,7 +8,7 @@ import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";
|
||||
import { KubeResource } from "../../../common/rbac";
|
||||
import { replicaSetStore } from "../+workloads-replicasets/replicasets.store";
|
||||
|
||||
export const workloadStores: Partial<Record<KubeResource, KubeObjectStore>> = {
|
||||
export const workloadStores: Partial<Record<KubeResource, KubeObjectStore<any>>> = {
|
||||
"pods": podsStore,
|
||||
"deployments": deploymentStore,
|
||||
"daemonsets": daemonSetStore,
|
||||
|
||||
@ -46,11 +46,11 @@ export class CreateResource extends React.Component<Props> {
|
||||
const errors: string[] = [];
|
||||
|
||||
await Promise.all(
|
||||
resources.map(data => {
|
||||
return resourceApplierApi.update(data)
|
||||
.then(item => createdResources.push(item.getName()))
|
||||
.catch((err: JsonApiErrorParsed) => errors.push(err.toString()));
|
||||
})
|
||||
resources.map(data => (
|
||||
resourceApplierApi.update(data)
|
||||
.then(item => createdResources.push(...item.map(item => item.getName())))
|
||||
.catch((err: JsonApiErrorParsed) => errors.push(err.toString()))
|
||||
))
|
||||
);
|
||||
|
||||
if (errors.length) {
|
||||
|
||||
@ -32,19 +32,19 @@ interface IHeaderPlaceholders {
|
||||
info: ReactNode;
|
||||
}
|
||||
|
||||
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
export interface ItemListLayoutProps<Entry extends ItemObject = ItemObject, SortingOption extends string = string> {
|
||||
className: IClassName;
|
||||
store: ItemStore<T>;
|
||||
store: ItemStore<Entry>;
|
||||
dependentStores?: ItemStore[];
|
||||
isClusterScoped?: boolean;
|
||||
hideFilters?: boolean;
|
||||
searchFilters?: SearchFilter<T>[];
|
||||
filterItems?: ItemsFilter<T>[];
|
||||
searchFilters?: SearchFilter<Entry>[];
|
||||
filterItems?: ItemsFilter<Entry>[];
|
||||
|
||||
// header (title, filtering, searching, etc.)
|
||||
showHeader?: boolean;
|
||||
headerClassName?: IClassName;
|
||||
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout<T>) => ReactNode);
|
||||
renderHeaderTitle?: ReactNode | ((parent: ItemListLayout<Entry>) => ReactNode);
|
||||
customizeHeader?: (placeholders: IHeaderPlaceholders, content: ReactNode) => Partial<IHeaderPlaceholders> | ReactNode;
|
||||
|
||||
// items list configuration
|
||||
@ -52,23 +52,23 @@ 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<T> };
|
||||
tableProps?: Partial<TableProps<T>>; // low-level table configuration
|
||||
renderTableHeader: TableCellProps[] | null;
|
||||
renderTableContents: (item: T) => (ReactNode | TableCellProps)[];
|
||||
renderItemMenu?: (item: T, store: ItemStore<T>) => ReactNode;
|
||||
customizeTableRowProps?: (item: T) => Partial<TableRowProps>;
|
||||
sortingCallbacks?: Record<SortingOption, TableSortCallback<Entry>>;
|
||||
tableProps?: Partial<TableProps<Entry>>; // low-level table configuration
|
||||
renderTableHeader: TableCellProps<SortingOption>[] | null;
|
||||
renderTableContents: (item: Entry) => (ReactNode | TableCellProps)[];
|
||||
renderItemMenu?: (item: Entry, store: ItemStore<Entry>) => ReactNode;
|
||||
customizeTableRowProps?: (item: Entry) => Partial<TableRowProps>;
|
||||
addRemoveButtons?: Partial<AddRemoveButtonsProps>;
|
||||
virtual?: boolean;
|
||||
|
||||
// item details view
|
||||
hasDetailsView?: boolean;
|
||||
detailsItem?: T;
|
||||
onDetails?: (item: T) => void;
|
||||
detailsItem?: Entry;
|
||||
onDetails?: (item: Entry) => void;
|
||||
|
||||
// other
|
||||
customizeRemoveDialog?: (selectedItems: T[]) => Partial<ConfirmDialogParams>;
|
||||
renderFooter?: (parent: ItemListLayout<T>) => React.ReactNode;
|
||||
customizeRemoveDialog?: (selectedItems: Entry[]) => Partial<ConfirmDialogParams>;
|
||||
renderFooter?: (parent: ItemListLayout<Entry>) => React.ReactNode;
|
||||
}
|
||||
|
||||
const defaultProps: Partial<ItemListLayoutProps> = {
|
||||
|
||||
@ -13,6 +13,7 @@ import { crdStore } from "../+custom-resources/crd.store";
|
||||
import { CrdResourceDetails } from "../+custom-resources";
|
||||
import { KubeObjectMenu } from "./kube-object-menu";
|
||||
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||
import { CustomResourceDefinition } from "../../api/endpoints";
|
||||
|
||||
export interface KubeObjectDetailsProps<T = KubeObject> {
|
||||
className?: string;
|
||||
@ -81,7 +82,7 @@ export class KubeObjectDetails extends React.Component {
|
||||
});
|
||||
|
||||
if (isCrdInstance && details.length === 0) {
|
||||
details.push(<CrdResourceDetails object={object} />);
|
||||
details.push(<CrdResourceDetails object={object as CustomResourceDefinition} />);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { KubeObjectMenu } from "./kube-object-menu";
|
||||
import { ItemObject } from "../../item.store";
|
||||
|
||||
export interface KubeObjectListLayoutProps<T extends ItemObject & KubeObject> extends ItemListLayoutProps<T> {
|
||||
export interface KubeObjectListLayoutProps<T extends ItemObject & KubeObject, SortOrder extends string> extends ItemListLayoutProps<T, SortOrder> {
|
||||
store: KubeObjectStore<T>;
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ function showItemDetails(item: KubeObject) {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class KubeObjectListLayout<T extends ItemObject & KubeObject> extends React.Component<KubeObjectListLayoutProps<T>> {
|
||||
export class KubeObjectListLayout<T extends ItemObject & KubeObject, SortOrder extends string> extends React.Component<KubeObjectListLayoutProps<T, SortOrder>> {
|
||||
@computed get selectedItem() {
|
||||
return this.props.store.getByPath(getSelectedDetails());
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import "./table-cell.scss";
|
||||
import type { TableSortBy, TableSortParams } from "./table";
|
||||
import type { TableSortParams } from "./table";
|
||||
|
||||
import React, { ReactNode } from "react";
|
||||
import { autobind, cssNames, displayBooleans } from "../../utils";
|
||||
@ -8,15 +8,15 @@ import { Checkbox } from "../checkbox";
|
||||
|
||||
export type TableCellElem = React.ReactElement<TableCellProps>;
|
||||
|
||||
export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
||||
export interface TableCellProps<SortingOption extends string = string> extends React.DOMAttributes<HTMLDivElement> {
|
||||
className?: string;
|
||||
title?: ReactNode;
|
||||
checkbox?: boolean; // render cell with a checkbox
|
||||
isChecked?: boolean; // mark checkbox as checked or not
|
||||
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
|
||||
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
||||
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
||||
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
|
||||
sortBy?: SortingOption; // column name, must be same as key in sortable object <Table sortable={}/>
|
||||
_sorting?: Partial<TableSortParams<SortingOption>>; // <Table> sorting state, don't use this prop outside (!)
|
||||
_sort?(sortBy: SortingOption): void; // <Table> sort function, don't use this prop outside (!)
|
||||
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
||||
}
|
||||
|
||||
|
||||
@ -14,12 +14,11 @@ import { ItemObject } from "../../item.store";
|
||||
|
||||
// todo: refactor + decouple search from location
|
||||
|
||||
export type TableSortBy = string;
|
||||
export type TableOrderBy = "asc" | "desc" | string;
|
||||
export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy };
|
||||
export type TableSortParams<SortingOption extends string> = { sortBy: SortingOption; orderBy: TableOrderBy };
|
||||
export type TableSortCallback<D> = (data: D) => string | number | (string | number)[];
|
||||
|
||||
export interface TableProps<T> extends React.DOMAttributes<HTMLDivElement> {
|
||||
export interface TableProps<T, SortingOption extends string = string> extends React.DOMAttributes<HTMLDivElement> {
|
||||
items?: ItemObject[]; // Raw items data
|
||||
className?: string;
|
||||
autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0)
|
||||
@ -32,8 +31,8 @@ export interface TableProps<T> extends React.DOMAttributes<HTMLDivElement> {
|
||||
[sortBy: string]: TableSortCallback<T>;
|
||||
};
|
||||
sortSyncWithUrl?: boolean; // sorting state is managed globally from url params
|
||||
sortByDefault?: Partial<TableSortParams>; // default sorting params
|
||||
onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url
|
||||
sortByDefault?: Partial<TableSortParams<SortingOption>>; // default sorting params
|
||||
onSort?: (params: TableSortParams<SortingOption>) => void; // callback on sort change, default: global sync with url
|
||||
noItems?: React.ReactNode; // Show no items state table list is empty
|
||||
selectedItemId?: string; // Allows to scroll list to selected item
|
||||
virtual?: boolean; // Use virtual list component to render only visible rows
|
||||
|
||||
@ -136,12 +136,8 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
return this.load({ name, namespace });
|
||||
}
|
||||
|
||||
protected async createItem(params: { name: string; namespace?: string }, data?: Partial<T>): Promise<T> {
|
||||
return this.api.create(params, data);
|
||||
}
|
||||
|
||||
async create(params: { name: string; namespace?: string }, data?: Partial<T>): Promise<T> {
|
||||
const newItem = await this.createItem(params, data);
|
||||
const newItem = await this.api.create(params, data);
|
||||
const items = this.sortItems([...this.items, newItem]);
|
||||
|
||||
this.items.replace(items);
|
||||
@ -150,7 +146,7 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
}
|
||||
|
||||
async update(item: T, data: Partial<T>): Promise<T> {
|
||||
const newItem = await item.update<T>(data);
|
||||
const [newItem] = await item.update<T>(data);
|
||||
const index = this.items.findIndex(item => item.getId() === newItem.getId());
|
||||
|
||||
this.items.splice(index, 1, newItem);
|
||||
@ -172,9 +168,11 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
protected eventsBuffer = observable<IKubeWatchEvent<KubeJsonApiData>>([], { deep: false });
|
||||
|
||||
protected bindWatchEventsUpdater(delay = 1000) {
|
||||
return reaction(() => this.eventsBuffer.toJS()[0], this.updateFromEventsBuffer, {
|
||||
delay
|
||||
});
|
||||
return reaction(
|
||||
() => this.eventsBuffer.toJS()[0],
|
||||
this.updateFromEventsBuffer,
|
||||
{ delay }
|
||||
);
|
||||
}
|
||||
|
||||
subscribe(apis = [this.api]) {
|
||||
@ -196,23 +194,22 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
|
||||
for (const {type, object} of this.eventsBuffer.clear()) {
|
||||
const { uid, selfLink } = object.metadata;
|
||||
const index = items.findIndex(item => item.getId() === uid);
|
||||
const item = items[index];
|
||||
const api = apiManager.getApi(selfLink);
|
||||
const index = items.map(item => item.getId()).indexOf(uid);
|
||||
const api = apiManager.getApi<T>(selfLink);
|
||||
|
||||
switch (type) {
|
||||
case "ADDED":
|
||||
case "MODIFIED":
|
||||
const newItem = new api.objectConstructor(object);
|
||||
|
||||
if (!item) {
|
||||
if (index < 0) {
|
||||
items.push(newItem);
|
||||
} else {
|
||||
items.splice(index, 1, newItem);
|
||||
}
|
||||
break;
|
||||
case "DELETED":
|
||||
if (item) {
|
||||
if (index >= 0) {
|
||||
items.splice(index, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -12,7 +12,7 @@ interface WrappingFunction {
|
||||
<T>(result: T): T;
|
||||
}
|
||||
|
||||
export function cancelableFetch(reqInfo: RequestInfo, reqInit: RequestInit = {}) {
|
||||
export function cancelableFetch(reqInfo: RequestInfo, reqInit: RequestInit = {}): CancelablePromise<any> {
|
||||
const abortController = new AbortController();
|
||||
const signal = abortController.signal;
|
||||
const cancel = abortController.abort.bind(abortController);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user