Remove a lot of usages of legacy globals (#6825)

* Remove all usages of legacy global logger

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global storageClassApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global serviceApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global secretApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global resourceQuotaApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global persistentVolumeClaimApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global nodeApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global networkPolicyApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix formatting

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global configMapApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global jobApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global cronJobApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global podApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global roleApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove exports of NonInjected components
- To improve auto-complete

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global catalogEntityRegistry

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add lint against using legacy global works

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint in KubeObject file

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global catalogCategoryRegistry

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global navigation

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global navigate

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove unneeded index file

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global customResourceDefinitionStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global eventStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global podSecurityPolicyStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global storageClassStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global persistentVolumeStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global clusterRoleBindingStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global clusterRoleStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global roleBindingStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move files around

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global roleStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global serviceAccountStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove usages of legacy global podStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove final uses of asLegacyGlobalForExtensionApi in application code

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint rule and start fixing lint errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make listHelmReleases injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make installHelmChart injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make deleteHelmRelease injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make getHelmReleaseValues injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make getHelmReleaseHistory injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make rollbackHelmRelease injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove unnecessary overrides from getDiForUnitTesting

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add global override for logger

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add global override for history

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Cleanup typing around getting injectables within jest

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move some more overrides to global if needed

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add global override for forcusWindow

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint in KubeApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint in catalog/index.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usages of legacy global getDetailsUrl

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usages of legacy global showDetails

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add functions back to extension API

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove all uses of legacy global Notification functions

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove no longer existing export

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove unnecessary overrides

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix injecting side effects by using more injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix cluster role binding dialog

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix cluster role dialog

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix create service account dialog

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix history override

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make KubeApi consume its dependencies while not breaking the extension API

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix CephFs tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix user store tests by using override of config

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in hotbar store tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add direct devDep of memfs

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in cluster store tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in router tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in kube auth proxy tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in log resource selector tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in hotbar remove command tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in the select component tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove mock-fs as a dependency

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove no longer necessary mocks

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix namespace select filter tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove use of mockFs in the extension loader tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fixing edit-namespace-from-new-tab test

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix app paths tests by making the tests run in "production" mode

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last vestiges of isTestEnv and the is* globals

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix delete-cluster-dialog tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix install-helm-chart-from-previously-opened-tab tests
- Split out storage initialization to a runnable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove unnecessary override of non side effect injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix disable-kube-object-detail-items-when-cluster-is-not-relevant tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix reactively-hide-kube-object-detail-item tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix lint

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix sidebar-and-tab-navigation-for-core tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove need to override lensLocalStoragePath in tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Replace more overrides with global ones

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix typings

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove no longer needed technical test

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Update snapshots

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2023-01-09 16:15:37 -05:00
1 parent 900a8bbda3
commit 2b33d5eed6
530 files changed
+11320 -5399

No files matched your search

@@ -3,6 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import defaultWebsocketApiParamsInjectable from "../default-websocket-api-params.injectable";
import type { WebSocketEvents } from "../websocket-api";
import { WebSocketApi } from "../websocket-api";
@@ -16,7 +18,11 @@ describe("WebsocketApi tests", () => {
let api: TestWebSocketApi;
beforeEach(() => {
api = new TestWebSocketApi({});
const di = getDiForUnitTesting({ doGeneralOverrides: true });
api = new TestWebSocketApi({
defaultParams: di.inject(defaultWebsocketApiParamsInjectable),
}, {});
});
describe("before connection", () => {
@@ -1,11 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable";
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
export type { CategoryFilter } from "../../common/catalog";
export const catalogCategoryRegistry = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable);
@@ -4,8 +4,10 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import loggerInjectable from "../../common/logger.injectable";
import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-id.injectable";
import type { TerminalApiQuery } from "./terminal-api";
import defaultWebsocketApiParamsInjectable from "./default-websocket-api-params.injectable";
import type { TerminalApiDependencies, TerminalApiQuery } from "./terminal-api";
import { TerminalApi } from "./terminal-api";
export type CreateTerminalApi = (query: TerminalApiQuery) => TerminalApi;
@@ -14,12 +16,17 @@ const createTerminalApiInjectable = getInjectable({
id: "create-terminal-api",
instantiate: (di): CreateTerminalApi => {
const hostedClusterId = di.inject(hostedClusterIdInjectable);
const deps: Omit<TerminalApiDependencies, "hostedClusterId"> = {
logger: di.inject(loggerInjectable),
defaultParams: di.inject(defaultWebsocketApiParamsInjectable),
};
return (query) => {
assert(hostedClusterId, "Can only create terminal APIs within a cluster frame");
return new TerminalApi({
hostedClusterId,
...deps,
}, query);
};
},
@@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { TerminalMessage } from "../../common/terminal/channels";
import { TerminalChannels } from "../../common/terminal/channels";
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
export type DefaultWebsocketApiParams = ReturnType<typeof defaultWebsocketApiParamsInjectable.instantiate>;
const defaultWebsocketApiParamsInjectable = getInjectable({
id: "default-websocket-api-params",
instantiate: (di) => ({
logging: di.inject(isDevelopmentInjectable),
reconnectDelay: 10,
flushOnOpen: true,
pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage),
}),
});
export default defaultWebsocketApiParamsInjectable;
+7 -6
View File
@@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { WebSocketEvents } from "./websocket-api";
import type { WebSocketApiDependencies, WebSocketEvents } from "./websocket-api";
import { WebSocketApi } from "./websocket-api";
import isEqual from "lodash/isEqual";
import url from "url";
import { makeObservable, observable } from "mobx";
import { ipcRenderer } from "electron";
import logger from "../../common/logger";
import type { Logger } from "../../common/logger";
import { once } from "lodash";
import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels";
@@ -36,8 +36,9 @@ export interface TerminalEvents extends WebSocketEvents {
connected: () => void;
}
export interface TerminalApiDependencies {
export interface TerminalApiDependencies extends WebSocketApiDependencies {
readonly hostedClusterId: string;
readonly logger: Logger;
}
export class TerminalApi extends WebSocketApi<TerminalEvents> {
@@ -46,7 +47,7 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
@observable public isReady = false;
constructor(protected readonly dependencies: TerminalApiDependencies, protected readonly query: TerminalApiQuery) {
super({
super(dependencies, {
flushOnOpen: false,
pingInterval: 30,
});
@@ -145,11 +146,11 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
this.emit("connected");
break;
default:
logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message);
this.dependencies.logger.warn(`[TERMINAL-API]: unknown or unhandleable message type`, message);
break;
}
} catch (error) {
logger.error(`[TERMINAL-API]: failed to handle message`, error);
this.dependencies.logger.error(`[TERMINAL-API]: failed to handle message`, error);
}
}
+8 -12
View File
@@ -7,9 +7,8 @@ import { observable, makeObservable } from "mobx";
import EventEmitter from "events";
import type TypedEventEmitter from "typed-emitter";
import type { Arguments } from "typed-emitter";
import { isDevelopment } from "../../common/vars";
import type { Defaulted } from "../utils";
import { TerminalChannels, type TerminalMessage } from "../../common/terminal/channels";
import type { DefaultWebsocketApiParams } from "./default-websocket-api-params.injectable";
interface WebsocketApiParams {
/**
@@ -64,27 +63,24 @@ export interface WebSocketEvents {
close: () => void;
}
export interface WebSocketApiDependencies {
readonly defaultParams: DefaultWebsocketApiParams;
}
export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter as { new<T>(): TypedEventEmitter<T> })<Events> {
protected socket: WebSocket | null = null;
protected pendingCommands: string[] = [];
protected reconnectTimer?: number;
protected pingTimer?: number;
protected params: Defaulted<WebsocketApiParams, keyof typeof WebSocketApi["defaultParams"]>;
protected params: Defaulted<WebsocketApiParams, keyof DefaultWebsocketApiParams>;
@observable readyState = WebSocketApiState.PENDING;
private static readonly defaultParams = {
logging: isDevelopment,
reconnectDelay: 10,
flushOnOpen: true,
pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage),
};
constructor(params: WebsocketApiParams) {
constructor(protected readonly dependencies: WebSocketApiDependencies, params: WebsocketApiParams) {
super();
makeObservable(this);
this.params = {
...WebSocketApi.defaultParams,
...this.dependencies.defaultParams,
...params,
};
const { pingInterval } = this.params;
@@ -7,11 +7,14 @@ import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manag
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resource.store";
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
import type { KubeApiDependencies } from "../../../common/k8s-api/kube-api";
import { KubeApi } from "../../../common/k8s-api/kube-api";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable";
const setupAutoRegistrationInjectable = getInjectable({
id: "setup-auto-registration",
@@ -21,8 +24,13 @@ const setupAutoRegistrationInjectable = getInjectable({
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
const beforeApiManagerInitializationApis: KubeApi[] = [];
const deps: KubeObjectStoreDependencies = {
const kubeApiDependencies: KubeApiDependencies = {
logger: di.inject(loggerInjectable),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
};
const kubeObjectStoreDependencies: KubeObjectStoreDependencies = {
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
};
let initialized = false;
@@ -40,7 +48,7 @@ const setupAutoRegistrationInjectable = getInjectable({
return rawApi;
}
const api = new KubeApi({ objectConstructor });
const api = new KubeApi(kubeApiDependencies, { objectConstructor });
apiManager.registerApi(api);
@@ -48,7 +56,7 @@ const setupAutoRegistrationInjectable = getInjectable({
})();
if (!apiManager.getStore(api)) {
apiManager.registerStore(new CustomResourceStore(deps, api));
apiManager.registerStore(new CustomResourceStore(kubeObjectStoreDependencies, api));
}
};
const autoInitKubeApi = (api: KubeApi) => {
@@ -42,7 +42,7 @@ const setupKubernetesClusterContextMenuOpenInjectable = getInjectable({
if (result.error) {
logger.error("[KUBERNETES-CLUSTER]: failed to parse kubeconfig file", result.error);
} else {
openDeleteClusterDialog({ cluster, config: result.config });
openDeleteClusterDialog(result.config, cluster);
}
},
});
+1 -1
View File
@@ -13,7 +13,7 @@ import type { DiContainer } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable";
import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable";
import initRootFrameInjectable from "./frames/root-frame/init-root-frame.injectable";
import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable";
import { Router } from "react-router";
import historyInjectable from "./navigation/history.injectable";
@@ -16,17 +16,19 @@ import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers"
import { docsUrl } from "../../../common/vars";
import { isDefined, iter } from "../../utils";
import { Button } from "../button";
import { Notifications } from "../notifications";
import type { ShowNotification } from "../notifications";
import { SettingLayout } from "../layout/setting-layout";
import { MonacoEditor } from "../monaco-editor";
import { withInjectables } from "@ogre-tools/injectable-react";
import getCustomKubeConfigDirectoryInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import type { EmitAppEvent } from "../../../common/app-event-bus/emit-event.injectable";
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable";
import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable";
import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
interface Option {
config: KubeConfig;
@@ -38,6 +40,8 @@ interface Dependencies {
navigateToCatalog: NavigateToCatalog;
getDirnameOfPath: GetDirnameOfPath;
emitAppEvent: EmitAppEvent;
showSuccessNotification: ShowNotification;
showErrorNotification: ShowNotification;
}
function getContexts(config: KubeConfig): Map<string, Option> {
@@ -97,11 +101,11 @@ class NonInjectedAddCluster extends React.Component<Dependencies> {
await fse.ensureDir(this.props.getDirnameOfPath(absPath));
await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 });
Notifications.ok(`Successfully added ${this.kubeContexts.size} new cluster(s)`);
this.props.showSuccessNotification(`Successfully added ${this.kubeContexts.size} new cluster(s)`);
return this.props.navigateToCatalog();
} catch (error) {
Notifications.error(`Failed to add clusters: ${error}`);
this.props.showErrorNotification(`Failed to add clusters: ${error}`);
}
});
@@ -159,9 +163,11 @@ class NonInjectedAddCluster extends React.Component<Dependencies> {
export const AddCluster = withInjectables<Dependencies>(NonInjectedAddCluster, {
getProps: (di) => ({
getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigDirectoryInjectable),
getCustomKubeConfigDirectory: di.inject(getCustomKubeConfigFilePathInjectable),
navigateToCatalog: di.inject(navigateToCatalogInjectable),
getDirnameOfPath: di.inject(getDirnameOfPathInjectable),
emitAppEvent: di.inject(emitAppEventInjectable),
showSuccessNotification: di.inject(showSuccessNotificationInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable),
}),
});
@@ -10,10 +10,13 @@ import { Icon } from "../icon";
import { observer } from "mobx-react";
import { observable, makeObservable, action } from "mobx";
import { autoBind } from "../../../common/utils";
import type { CatalogCategory, CatalogEntityAddMenuContext, CatalogEntityAddMenu } from "../../api/catalog-entity";
import type { CatalogCategory, CatalogEntityAddMenu } from "../../api/catalog-entity";
import { EventEmitter } from "events";
import { navigate } from "../../navigation";
import { catalogCategoryRegistry } from "../../api/catalog-category-registry";
import type { CatalogCategoryRegistry } from "../../../common/catalog";
import { withInjectables } from "@ogre-tools/injectable-react";
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
import type { Navigate } from "../../navigation/navigate.injectable";
import navigateInjectable from "../../navigation/navigate.injectable";
export interface CatalogAddButtonProps {
category: CatalogCategory;
@@ -21,12 +24,17 @@ export interface CatalogAddButtonProps {
type CategoryId = string;
interface Dependencies {
catalogCategoryRegistry: CatalogCategoryRegistry;
navigate: Navigate;
}
@observer
export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
class NonInjectedCatalogAddButton extends React.Component<CatalogAddButtonProps & Dependencies> {
@observable protected isOpen = false;
@observable menuItems = new Map<CategoryId, CatalogEntityAddMenu[]>();
constructor(props: CatalogAddButtonProps) {
constructor(props: CatalogAddButtonProps & Dependencies) {
super(props);
makeObservable(this);
autoBind(this);
@@ -43,7 +51,7 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
}
get categories() {
return catalogCategoryRegistry.filteredItems;
return this.props.catalogCategoryRegistry.filteredItems;
}
@action
@@ -61,12 +69,11 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
updateCategoryItems = action((category: CatalogCategory) => {
if (category instanceof EventEmitter) {
const menuItems: CatalogEntityAddMenu[] = [];
const context: CatalogEntityAddMenuContext = {
navigate: (url: string) => navigate(url),
menuItems,
};
category.emit("catalogAddMenu", context);
category.emit("catalogAddMenu", {
navigate: this.props.navigate,
menuItems,
});
this.menuItems.set(category.getId(), menuItems);
}
});
@@ -133,3 +140,11 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
);
}
}
export const CatalogAddButton = withInjectables<Dependencies, CatalogAddButtonProps>(NonInjectedCatalogAddButton, {
getProps: (di, props) => ({
...props,
catalogCategoryRegistry: di.inject(catalogCategoryRegistryInjectable),
navigate: di.inject(navigateInjectable),
}),
});
@@ -23,7 +23,7 @@ import type { EventStore } from "../+events/store";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import eventStoreInjectable from "../+events/store.injectable";
import nodeStoreInjectable from "../+nodes/store.injectable";
import type { PageParam } from "../../navigation";
import type { PageParam } from "../../navigation/page-param";
import type { ToggleKubeDetailsPane } from "../kube-detail-params/toggle-details.injectable";
import kubeSelectedUrlParamInjectable from "../kube-detail-params/kube-selected-url.injectable";
import toggleKubeDetailsPaneInjectable from "../kube-detail-params/toggle-details.injectable";
@@ -14,6 +14,7 @@ import assert from "assert";
import nodeStoreInjectable from "../../+nodes/store.injectable";
import requestClusterMetricsByNodeNamesInjectable from "../../../../common/k8s-api/endpoints/metrics.api/request-cluster-metrics-by-node-names.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
const clusterOverviewStoreInjectable = getInjectable({
id: "cluster-overview-store",
@@ -34,6 +35,7 @@ const clusterOverviewStoreInjectable = getInjectable({
nodeStore: di.inject(nodeStoreInjectable),
requestClusterMetricsByNodeNames: di.inject(requestClusterMetricsByNodeNamesInjectable),
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, clusterApi);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -16,17 +16,19 @@ import type { HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricTa
import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api";
import { Table, TableCell, TableHead, TableRow } from "../table";
import type { ApiManager } from "../../../common/k8s-api/api-manager";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import loggerInjectable from "../../../common/logger.injectable";
export interface HpaDetailsProps extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
}
interface Dependencies {
apiManager: ApiManager;
logger: Logger;
getDetailsUrl: GetDetailsUrl;
}
@@ -104,7 +106,7 @@ class NonInjectedHpaDetails extends React.Component<HpaDetailsProps & Dependenci
}
render() {
const { object: hpa, apiManager, getDetailsUrl } = this.props;
const { object: hpa, apiManager, getDetailsUrl, logger } = this.props;
if (!hpa) {
return null;
@@ -172,5 +174,6 @@ export const HpaDetails = withInjectables<Dependencies, HpaDetailsProps>(NonInje
...props,
apiManager: di.inject(apiManagerInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import horizontalPodAutoscalerApiInjectable from "../../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { HorizontalPodAutoscalerStore } from "./store";
@@ -19,6 +20,7 @@ const horizontalPodAutoscalerStoreInjectable = getInjectable({
return new HorizontalPodAutoscalerStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import leaseApiInjectable from "../../../common/k8s-api/endpoints/lease.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { LeaseStore } from "./store";
@@ -19,6 +20,7 @@ const leaseStoreInjectable = getInjectable({
return new LeaseStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -12,7 +12,9 @@ import type { LimitRangeItem } from "../../../common/k8s-api/endpoints/limit-ran
import { LimitPart, LimitRange, Resource } from "../../../common/k8s-api/endpoints/limit-range.api";
import { DrawerItem } from "../drawer/drawer-item";
import { Badge } from "../badge";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface LimitRangeDetailsProps extends KubeObjectDetailsProps<LimitRange> {
}
@@ -51,10 +53,14 @@ function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) {
));
}
interface Dependencies {
logger: Logger;
}
@observer
export class LimitRangeDetails extends React.Component<LimitRangeDetailsProps> {
class NonInjectedLimitRangeDetails extends React.Component<LimitRangeDetailsProps & Dependencies> {
render() {
const { object: limitRange } = this.props;
const { object: limitRange, logger } = this.props;
if (!limitRange) {
return null;
@@ -97,3 +103,10 @@ export class LimitRangeDetails extends React.Component<LimitRangeDetailsProps> {
);
}
}
export const LimitRangeDetails = withInjectables<Dependencies, LimitRangeDetailsProps>(NonInjectedLimitRangeDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import limitRangeApiInjectable from "../../../common/k8s-api/endpoints/limit-range.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { LimitRangeStore } from "./store";
@@ -19,6 +20,7 @@ const limitRangeStoreInjectable = getInjectable({
return new LimitRangeStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import configMapApiInjectable from "../../../common/k8s-api/endpoints/config-map.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ConfigMapStore } from "./store";
@@ -19,6 +20,7 @@ const configMapStoreInjectable = getInjectable({
return new ConfigMapStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -11,13 +11,19 @@ import { DrawerItem } from "../drawer";
import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import { PodDisruptionBudget } from "../../../common/k8s-api/endpoints";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface PodDisruptionBudgetDetailsProps extends KubeObjectDetailsProps<PodDisruptionBudget> {
}
interface Dependencies {
logger: Logger;
}
@observer
export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBudgetDetailsProps> {
class NonInjectedPodDisruptionBudgetDetails extends React.Component<PodDisruptionBudgetDetailsProps & Dependencies> {
render() {
const { object: pdb } = this.props;
@@ -27,7 +33,7 @@ export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBud
}
if (!(pdb instanceof PodDisruptionBudget)) {
logger.error("[PodDisruptionBudgetDetails]: passed object that is not an instanceof PodDisruptionBudget", pdb);
this.props.logger.error("[PodDisruptionBudgetDetails]: passed object that is not an instanceof PodDisruptionBudget", pdb);
return null;
}
@@ -64,3 +70,10 @@ export class PodDisruptionBudgetDetails extends React.Component<PodDisruptionBud
);
}
}
export const PodDisruptionBudgetDetails = withInjectables<Dependencies, PodDisruptionBudgetDetailsProps>(NonInjectedPodDisruptionBudgetDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import podDisruptionBudgetApiInjectable from "../../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PodDisruptionBudgetStore } from "./store";
@@ -19,6 +20,7 @@ const podDisruptionBudgetStoreInjectable = getInjectable({
return new PodDisruptionBudgetStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PriorityClassStore } from "./store";
@@ -19,6 +20,7 @@ const priorityClassStoreInjectable = getInjectable({
return new PriorityClassStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import addQuotaDialogOpenStateInjectable from "./open-state.injectable";
const closeAddQuotaDialogInjectable = getInjectable({
id: "close-add-quota-dialog",
instantiate: (di) => {
const state = di.inject(addQuotaDialogOpenStateInjectable);
return action(() => state.set(false));
},
});
export default closeAddQuotaDialogInjectable;
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import addQuotaDialogOpenStateInjectable from "./open-state.injectable";
const isAddQuotaDialogOpenInjectable = getInjectable({
id: "is-add-quota-dialog-open",
instantiate: (di) => {
const state = di.inject(addQuotaDialogOpenStateInjectable);
return computed(() => state.get());
},
});
export default isAddQuotaDialogOpenInjectable;
@@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
const addQuotaDialogOpenStateInjectable = getInjectable({
id: "add-quota-dialog-open-state",
instantiate: () => observable.box(false),
});
export default addQuotaDialogOpenStateInjectable;
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import addQuotaDialogOpenStateInjectable from "./open-state.injectable";
const openAddQuotaDialogInjectable = getInjectable({
id: "open-add-quota-dialog",
instantiate: (di) => {
const state = di.inject(addQuotaDialogOpenStateInjectable);
return action(() => state.set(true));
},
});
export default openAddQuotaDialogInjectable;
@@ -3,54 +3,63 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./add-quota-dialog.scss";
import "./view.scss";
import React from "react";
import type { IComputedValue } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import type { DialogProps } from "../dialog";
import { Dialog } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Input } from "../input";
import { systemName } from "../input/input_validators";
import type { IResourceQuotaValues } from "../../../common/k8s-api/endpoints";
import { resourceQuotaApi } from "../../../common/k8s-api/endpoints";
import { Select } from "../select";
import { Icon } from "../icon";
import { Button } from "../button";
import { Notifications } from "../notifications";
import { NamespaceSelect } from "../+namespaces/namespace-select";
import { SubTitle } from "../layout/sub-title";
import type { DialogProps } from "../../dialog";
import { Dialog } from "../../dialog";
import { Wizard, WizardStep } from "../../wizard";
import { Input } from "../../input";
import { systemName } from "../../input/input_validators";
import type { IResourceQuotaValues, ResourceQuotaApi } from "../../../../common/k8s-api/endpoints";
import { Select } from "../../select";
import { Icon } from "../../icon";
import { Button } from "../../button";
import { NamespaceSelect } from "../../+namespaces/namespace-select";
import { SubTitle } from "../../layout/sub-title";
import { withInjectables } from "@ogre-tools/injectable-react";
import closeAddQuotaDialogInjectable from "./close.injectable";
import isAddQuotaDialogOpenInjectable from "./is-open.injectable";
import resourceQuotaApiInjectable from "../../../../common/k8s-api/endpoints/resource-quota.api.injectable";
import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable";
import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable";
export interface AddQuotaDialogProps extends DialogProps {
}
const dialogState = observable.object({
isOpen: false,
});
interface Dependencies {
resourceQuotaApi: ResourceQuotaApi;
isAddQuotaDialogOpen: IComputedValue<boolean>;
closeAddQuotaDialog: () => void;
showCheckedErrorNotification: ShowCheckedErrorNotification;
}
const defaultQuotas = JSON.stringify({
"limits.cpu": "",
"limits.memory": "",
"requests.cpu": "",
"requests.memory": "",
"requests.storage": "",
"persistentvolumeclaims": "",
"count/pods": "",
"count/persistentvolumeclaims": "",
"count/services": "",
"count/secrets": "",
"count/configmaps": "",
"count/replicationcontrollers": "",
"count/deployments.apps": "",
"count/replicasets.apps": "",
"count/statefulsets.apps": "",
"count/jobs.batch": "",
"count/cronjobs.batch": "",
"count/deployments.extensions": "",
} as IResourceQuotaValues);
@observer
export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
static defaultQuotas: IResourceQuotaValues = {
"limits.cpu": "",
"limits.memory": "",
"requests.cpu": "",
"requests.memory": "",
"requests.storage": "",
"persistentvolumeclaims": "",
"count/pods": "",
"count/persistentvolumeclaims": "",
"count/services": "",
"count/secrets": "",
"count/configmaps": "",
"count/replicationcontrollers": "",
"count/deployments.apps": "",
"count/replicasets.apps": "",
"count/statefulsets.apps": "",
"count/jobs.batch": "",
"count/cronjobs.batch": "",
"count/deployments.extensions": "",
};
class NonInjectedAddQuotaDialog extends React.Component<AddQuotaDialogProps & Dependencies> {
public defaultNamespace = "default";
@@ -58,21 +67,13 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
@observable quotaSelectValue: string | null = null;
@observable quotaInputValue = "";
@observable namespace: string | null = this.defaultNamespace;
@observable quotas = AddQuotaDialog.defaultQuotas;
@observable quotas: IResourceQuotaValues = JSON.parse(defaultQuotas);
constructor(props: AddQuotaDialogProps) {
constructor(props: AddQuotaDialogProps & Dependencies) {
super(props);
makeObservable(this);
}
static open() {
dialogState.isOpen = true;
}
static close() {
dialogState.isOpen = false;
}
@computed get quotaEntries() {
return Object.entries(this.quotas)
.filter(([, value]) => !!value?.trim());
@@ -101,7 +102,7 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
};
close = () => {
AddQuotaDialog.close();
this.props.closeAddQuotaDialog();
};
reset = () => {
@@ -109,7 +110,7 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
this.quotaSelectValue = "";
this.quotaInputValue = "";
this.namespace = this.defaultNamespace;
this.quotas = AddQuotaDialog.defaultQuotas;
this.quotas = JSON.parse(defaultQuotas);
};
addQuota = async () => {
@@ -122,14 +123,14 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
try {
const quotas = Object.fromEntries(this.quotaEntries);
await resourceQuotaApi.create({ namespace, name: quotaName }, {
await this.props.resourceQuotaApi.create({ namespace, name: quotaName }, {
spec: {
hard: quotas,
},
});
this.close();
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while creating ResourceQuota");
this.props.showCheckedErrorNotification(err, "Unknown error occured while creating ResourceQuota");
}
};
@@ -143,14 +144,14 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
};
render() {
const { ...dialogProps } = this.props;
const { closeAddQuotaDialog, isAddQuotaDialogOpen, resourceQuotaApi, ...dialogProps } = this.props;
const header = <h5>Create ResourceQuota</h5>;
return (
<Dialog
{...dialogProps}
className="AddQuotaDialog"
isOpen={dialogState.isOpen}
isOpen={isAddQuotaDialogOpen.get()}
close={this.close}
>
<Wizard header={header} done={this.close}>
@@ -244,3 +245,13 @@ export class AddQuotaDialog extends React.Component<AddQuotaDialogProps> {
);
}
}
export const AddQuotaDialog = withInjectables<Dependencies, AddQuotaDialogProps>(NonInjectedAddQuotaDialog, {
getProps: (di, props) => ({
...props,
closeAddQuotaDialog: di.inject(closeAddQuotaDialogInjectable),
isAddQuotaDialogOpen: di.inject(isAddQuotaDialogOpenInjectable),
resourceQuotaApi: di.inject(resourceQuotaApiInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
}),
});
@@ -13,7 +13,9 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { ResourceQuota } from "../../../common/k8s-api/endpoints/resource-quota.api";
import { LineProgress } from "../line-progress";
import { Table, TableCell, TableHead, TableRow } from "../table";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface ResourceQuotaDetailsProps extends KubeObjectDetailsProps<ResourceQuota> {
}
@@ -75,8 +77,12 @@ function renderQuotas(quota: ResourceQuota): JSX.Element[] {
});
}
interface Dependencies {
logger: Logger;
}
@observer
export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsProps> {
class NonInjectedResourceQuotaDetails extends React.Component<ResourceQuotaDetailsProps & Dependencies> {
render() {
const { object: quota } = this.props;
@@ -85,7 +91,7 @@ export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsPr
}
if (!(quota instanceof ResourceQuota)) {
logger.error("[ResourceQuotaDetails]: passed object that is not an instanceof ResourceQuota", quota);
this.props.logger.error("[ResourceQuotaDetails]: passed object that is not an instanceof ResourceQuota", quota);
return null;
}
@@ -125,3 +131,10 @@ export class ResourceQuotaDetails extends React.Component<ResourceQuotaDetailsPr
);
}
}
export const ResourceQuotaDetails = withInjectables<Dependencies, ResourceQuotaDetailsProps>(NonInjectedResourceQuotaDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -8,7 +8,7 @@ import "./resource-quotas.scss";
import React from "react";
import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { AddQuotaDialog } from "./add-quota-dialog";
import { AddQuotaDialog } from "./add-dialog/view";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
@@ -18,6 +18,7 @@ import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-m
import { withInjectables } from "@ogre-tools/injectable-react";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import resourceQuotaStoreInjectable from "./store.injectable";
import openAddQuotaDialogInjectable from "./add-dialog/open.injectable";
enum columnId {
name = "name",
@@ -28,6 +29,7 @@ enum columnId {
interface Dependencies {
resourceQuotaStore: ResourceQuotaStore;
filterByNamespace: FilterByNamespace;
openAddQuotaDialog: () => void;
}
@observer
@@ -69,7 +71,7 @@ class NonInjectedResourceQuotas extends React.Component<Dependencies> {
<KubeObjectAge key="age" object={resourceQuota} />,
]}
addRemoveButtons={{
onAdd: () => AddQuotaDialog.open(),
onAdd: this.props.openAddQuotaDialog,
addTooltip: "Create new ResourceQuota",
}}
/>
@@ -84,5 +86,6 @@ export const ResourceQuotas = withInjectables<Dependencies>(NonInjectedResourceQ
...props,
filterByNamespace: di.inject(filterByNamespaceInjectable),
resourceQuotaStore: di.inject(resourceQuotaStoreInjectable),
openAddQuotaDialog: di.inject(openAddQuotaDialogInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import resourceQuotaApiInjectable from "../../../common/k8s-api/endpoints/resource-quota.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ResourceQuotaStore } from "./store";
@@ -19,6 +20,7 @@ const resourceQuotaStoreInjectable = getInjectable({
return new ResourceQuotaStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { RuntimeClassStore } from "./store";
@@ -19,6 +20,7 @@ const runtimeClassStoreInjectable = getInjectable({
return new RuntimeClassStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import addSecretDialogOpenStateInjectable from "./state.injectable";
const closeAddSecretDialogInjectable = getInjectable({
id: "close-add-secret-dialog",
instantiate: (di) => {
const state = di.inject(addSecretDialogOpenStateInjectable);
return action(() => state.set(false));
},
});
export default closeAddSecretDialogInjectable;
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import addSecretDialogOpenStateInjectable from "./state.injectable";
const isAddSecretDialogOpenInjectable = getInjectable({
id: "is-add-secret-dialog-open",
instantiate: (di) => {
const state = di.inject(addSecretDialogOpenStateInjectable);
return computed(() => state.get());
},
});
export default isAddSecretDialogOpenInjectable;
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import addSecretDialogOpenStateInjectable from "./state.injectable";
const openAddSecretDialogInjectable = getInjectable({
id: "open-add-secret-dialog",
instantiate: (di) => {
const state = di.inject(addSecretDialogOpenStateInjectable);
return action(() => state.set(true));
},
});
export default openAddSecretDialogInjectable;
@@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
const addSecretDialogOpenStateInjectable = getInjectable({
id: "add-secret-dialog-state",
instantiate: () => observable.box(false),
});
export default addSecretDialogOpenStateInjectable;
@@ -3,26 +3,34 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./add-secret-dialog.scss";
import "./view.scss";
import React from "react";
import type { IComputedValue } from "mobx";
import { observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import type { DialogProps } from "../dialog";
import { Dialog } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { Input } from "../input";
import { systemName } from "../input/input_validators";
import { reverseSecretTypeMap, secretApi, SecretType } from "../../../common/k8s-api/endpoints";
import { SubTitle } from "../layout/sub-title";
import { NamespaceSelect } from "../+namespaces/namespace-select";
import { Select } from "../select";
import { Icon } from "../icon";
import { base64, iter } from "../../utils";
import { Notifications } from "../notifications";
import type { DialogProps } from "../../dialog";
import { Dialog } from "../../dialog";
import { Wizard, WizardStep } from "../../wizard";
import { Input } from "../../input";
import { systemName } from "../../input/input_validators";
import type { SecretApi } from "../../../../common/k8s-api/endpoints";
import { reverseSecretTypeMap, SecretType } from "../../../../common/k8s-api/endpoints";
import { SubTitle } from "../../layout/sub-title";
import { NamespaceSelect } from "../../+namespaces/namespace-select";
import { Select } from "../../select";
import { Icon } from "../../icon";
import { base64, iter } from "../../../utils";
import upperFirst from "lodash/upperFirst";
import { showDetails } from "../kube-detail-params";
import { fromEntries } from "../../../common/utils/objects";
import { fromEntries } from "../../../../common/utils/objects";
import type { ShowDetails } from "../../kube-detail-params/show-details.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import closeAddSecretDialogInjectable from "./close.injectable";
import secretApiInjectable from "../../../../common/k8s-api/endpoints/secret.api.injectable";
import showDetailsInjectable from "../../kube-detail-params/show-details.injectable";
import isAddSecretDialogOpenInjectable from "./is-open.injectable";
import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable";
import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable";
export interface AddSecretDialogProps extends Partial<DialogProps> {
}
@@ -42,25 +50,21 @@ interface SecretTemplate {
type ISecretField = keyof SecretTemplate;
const dialogState = observable.object({
isOpen: false,
});
interface Dependencies {
secretApi: SecretApi;
isAddSecretDialogOpen: IComputedValue<boolean>;
closeAddSecretDialog: () => void;
showDetails: ShowDetails;
showCheckedErrorNotification: ShowCheckedErrorNotification;
}
@observer
export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
constructor(props: AddSecretDialogProps) {
class NonInjectedAddSecretDialog extends React.Component<AddSecretDialogProps & Dependencies> {
constructor(props: AddSecretDialogProps & Dependencies) {
super(props);
makeObservable(this);
}
static open() {
dialogState.isOpen = true;
}
static close() {
dialogState.isOpen = false;
}
private secretTemplate: Partial<Record<SecretType, SecretTemplate>> = {
[SecretType.Opaque]: {},
[SecretType.ServiceAccountToken]: {
@@ -82,7 +86,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
};
close = () => {
AddSecretDialog.close();
this.props.closeAddSecretDialog();
};
private getDataFromFields = (fields: SecretTemplateField[] = [], processValue: (val: string) => string = (val => val)) => {
@@ -100,7 +104,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
const { data = [], labels = [], annotations = [] } = this.secret[type] ?? {};
try {
const newSecret = await secretApi.create({ namespace, name }, {
const newSecret = await this.props.secretApi.create({ namespace, name }, {
type,
data: this.getDataFromFields(data, val => val ? base64.encode(val) : ""),
metadata: {
@@ -111,10 +115,10 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
},
});
showDetails(newSecret?.selfLink);
this.props.showDetails(newSecret?.selfLink);
this.close();
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while creating a Secret");
this.props.showCheckedErrorNotification(err, "Unknown error occured while creating a Secret");
}
};
@@ -183,7 +187,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
}
render() {
const { ...dialogProps } = this.props;
const { closeAddSecretDialog, isAddSecretDialogOpen, secretApi, showDetails, ...dialogProps } = this.props;
const { namespace, name, type } = this;
const header = <h5>Create Secret</h5>;
@@ -191,7 +195,7 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
<Dialog
{...dialogProps}
className="AddSecretDialog"
isOpen={dialogState.isOpen}
isOpen={isAddSecretDialogOpen.get()}
onOpen={this.reset}
close={this.close}
>
@@ -244,3 +248,14 @@ export class AddSecretDialog extends React.Component<AddSecretDialogProps> {
);
}
}
export const AddSecretDialog = withInjectables<Dependencies, AddSecretDialogProps>(NonInjectedAddSecretDialog, {
getProps: (di, props) => ({
...props,
closeAddSecretDialog: di.inject(closeAddSecretDialogInjectable),
secretApi: di.inject(secretApiInjectable),
showDetails: di.inject(showDetailsInjectable),
isAddSecretDialogOpen: di.inject(isAddSecretDialogOpenInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
}),
});
@@ -7,7 +7,7 @@ import "./secrets.scss";
import React from "react";
import { observer } from "mobx-react";
import { AddSecretDialog } from "./add-secret-dialog";
import { AddSecretDialog } from "./add-dialog/view";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { Badge } from "../badge";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
@@ -19,6 +19,7 @@ import type { SecretStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import secretStoreInjectable from "./store.injectable";
import openAddSecretDialogInjectable from "./add-dialog/open.injectable";
enum columnId {
name = "name",
@@ -32,6 +33,7 @@ enum columnId {
interface Dependencies {
filterByNamespace: FilterByNamespace;
secretStore: SecretStore;
openAddSecretDialog: () => void;
}
@observer
@@ -89,7 +91,7 @@ class NonInjectedSecrets extends React.Component<Dependencies> {
<KubeObjectAge key="age" object={secret} />,
]}
addRemoveButtons={{
onAdd: () => AddSecretDialog.open(),
onAdd: this.props.openAddSecretDialog,
addTooltip: "Create new Secret",
}}
/>
@@ -104,5 +106,6 @@ export const Secrets = withInjectables<Dependencies>(NonInjectedSecrets, {
...props,
filterByNamespace: di.inject(filterByNamespaceInjectable),
secretStore: di.inject(secretStoreInjectable),
openAddSecretDialog: di.inject(openAddSecretDialogInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import secretApiInjectable from "../../../common/k8s-api/endpoints/secret.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { SecretStore } from "./store";
@@ -19,6 +20,7 @@ const secretStoreInjectable = getInjectable({
return new SecretStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -15,13 +15,19 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { Input } from "../input";
import { MonacoEditor } from "../monaco-editor";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface CRDDetailsProps extends KubeObjectDetailsProps<CustomResourceDefinition> {
}
interface Dependencies {
logger: Logger;
}
@observer
export class CRDDetails extends React.Component<CRDDetailsProps> {
class NonInjectedCRDDetails extends React.Component<CRDDetailsProps & Dependencies> {
render() {
const { object: crd } = this.props;
@@ -30,7 +36,7 @@ export class CRDDetails extends React.Component<CRDDetailsProps> {
}
if (!(crd instanceof CustomResourceDefinition)) {
logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd);
this.props.logger.error("[CRDDetails]: passed object that is not an instanceof CustomResourceDefinition", crd);
return null;
}
@@ -152,3 +158,10 @@ export class CRDDetails extends React.Component<CRDDetailsProps> {
);
}
}
export const CRDDetails = withInjectables<Dependencies, CRDDetailsProps>(NonInjectedCRDDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -11,12 +11,11 @@ import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { iter, stopPropagation } from "../../utils";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { customResourceDefinitionStore } from "./legacy-store";
import { Select } from "../select";
import { Icon } from "../icon";
import { KubeObjectAge } from "../kube-object/age";
import { TabLayout } from "../layout/tab-layout-2";
import type { PageParam } from "../../navigation";
import type { PageParam } from "../../navigation/page-param";
import type { CustomResourceDefinitionStore } from "./definition.store";
import { withInjectables } from "@ogre-tools/injectable-react";
import crdGroupsUrlParamInjectable from "./crd-groups-url-param.injectable";
@@ -87,7 +86,7 @@ class NonInjectedCustomResourceDefinitions extends React.Component<Dependencies>
isConfigurable
tableId="crd"
className="CrdList"
store={customResourceDefinitionStore}
store={this.props.customResourceDefinitionStore}
// Don't subscribe the `customResourceDefinitionStore` because <Sidebar> already has and is always mounted
subscribeStores={false}
items={this.items}
@@ -17,7 +17,9 @@ import { CustomResourceDefinition } from "../../../common/k8s-api/endpoints/cust
import { safeJSONPathValue } from "../../utils/jsonPath";
import type { KubeObjectMetadata, KubeObjectStatus } from "../../../common/k8s-api/kube-object";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface CustomResourceDetailsProps extends KubeObjectDetailsProps<KubeObject> {
crd?: CustomResourceDefinition;
@@ -59,8 +61,12 @@ function convertSpecValue(value: unknown): React.ReactNode {
return null;
}
interface Dependencies {
logger: Logger;
}
@observer
export class CustomResourceDetails extends React.Component<CustomResourceDetailsProps> {
class NonInjectedCustomResourceDetails extends React.Component<CustomResourceDetailsProps & Dependencies> {
renderAdditionalColumns(resource: KubeObject, columns: AdditionalPrinterColumnsV1[]) {
return columns.map(({ name, jsonPath }) => (
<DrawerItem key={name} name={name}>
@@ -106,7 +112,7 @@ export class CustomResourceDetails extends React.Component<CustomResourceDetails
}
render() {
const { props: { object, crd }} = this;
const { props: { object, crd, logger }} = this;
if (!object || !crd) {
return null;
@@ -134,3 +140,10 @@ export class CustomResourceDetails extends React.Component<CustomResourceDetails
);
}
}
export const CustomResourceDetails = withInjectables<Dependencies, CustomResourceDetailsProps>(NonInjectedCustomResourceDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -7,6 +7,7 @@ import assert from "assert";
import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { CustomResourceDefinitionStore } from "./definition.store";
@@ -21,6 +22,7 @@ const customResourceDefinitionStoreInjectable = getInjectable({
return new CustomResourceDefinitionStore({
autoRegistration: di.inject(autoRegistrationEmitterInjectable),
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import customResourceDefinitionStoreInjectable from "./definition.store.injectable";
/**
* @deprecated use `di.inject(customResourceDefinitionStoreInjectable)` instead
*/
export const customResourceDefinitionStore = asLegacyGlobalForExtensionApi(customResourceDefinitionStoreInjectable);
@@ -14,13 +14,14 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
import { Table, TableCell, TableHead, TableRow } from "../table";
import type { ApiManager } from "../../../common/k8s-api/api-manager";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { DurationAbsoluteTimestamp } from "./duration-absolute";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import { cssNames } from "../../utils";
import loggerInjectable from "../../../common/logger.injectable";
export interface EventDetailsProps extends KubeObjectDetailsProps<KubeEvent> {
}
@@ -28,6 +29,7 @@ export interface EventDetailsProps extends KubeObjectDetailsProps<KubeEvent> {
interface Dependencies {
getDetailsUrl: GetDetailsUrl;
apiManager: ApiManager;
logger: Logger;
}
const NonInjectedEventDetails = observer(({
@@ -35,6 +37,7 @@ const NonInjectedEventDetails = observer(({
getDetailsUrl,
object: event,
className,
logger,
}: Dependencies & EventDetailsProps) => {
if (!event) {
return null;
@@ -101,5 +104,6 @@ export const EventDetails = withInjectables<Dependencies, EventDetailsProps>(Non
...props,
apiManager: di.inject(apiManagerInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
logger: di.inject(loggerInjectable),
}),
});
@@ -12,12 +12,13 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { cssNames } from "../../utils";
import { LocaleDate } from "../locale-date";
import type { EventStore } from "./store";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import eventStoreInjectable from "./store.injectable";
import loggerInjectable from "../../../common/logger.injectable";
export interface KubeEventDetailsProps {
object: KubeObject;
@@ -26,6 +27,7 @@ export interface KubeEventDetailsProps {
interface Dependencies {
subscribeStores: SubscribeStores;
eventStore: EventStore;
logger: Logger;
}
@observer
@@ -46,7 +48,7 @@ class NonInjectedKubeEventDetails extends React.Component<KubeEventDetailsProps
}
if (!(object instanceof KubeObject)) {
logger.error("[KubeEventDetails]: passed object that is not an instanceof KubeObject", object);
this.props.logger.error("[KubeEventDetails]: passed object that is not an instanceof KubeObject", object);
return null;
}
@@ -93,6 +95,7 @@ export const KubeEventDetails = withInjectables<Dependencies, KubeEventDetailsPr
...props,
subscribeStores: di.inject(subscribeStoresInjectable),
eventStore: di.inject(eventStoreInjectable),
logger: di.inject(loggerInjectable),
}),
});
@@ -8,10 +8,12 @@ import "./kube-event-icon.scss";
import React from "react";
import { Icon } from "../icon";
import type { KubeObject } from "../../../common/k8s-api/kube-object";
import { eventStore } from "./legacy-store";
import { cssNames } from "../../utils";
import type { KubeEvent } from "../../../common/k8s-api/endpoints/events.api";
import { KubeObjectAge } from "../kube-object/age";
import type { EventStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import eventStoreInjectable from "./store.injectable";
export interface KubeEventIconProps {
object: KubeObject;
@@ -19,22 +21,23 @@ export interface KubeEventIconProps {
filterEvents?: (events: KubeEvent[]) => KubeEvent[];
}
interface Dependencies {
eventStore: EventStore;
}
const defaultProps: Partial<KubeEventIconProps> = {
showWarningsOnly: true,
};
export class KubeEventIcon extends React.Component<KubeEventIconProps> {
static defaultProps = defaultProps as object;
class NonInjectedKubeEventIcon extends React.Component<KubeEventIconProps & Dependencies> {
get showWarningsOnly() {
return this.props.showWarningsOnly ?? true;
}
render() {
const { object, showWarningsOnly, filterEvents } = this.props;
const { object, filterEvents, eventStore } = this.props;
const events = eventStore.getEventsByObject(object);
let warnings = events.filter(evt => evt.isWarning());
if (filterEvents) warnings = filterEvents(warnings);
if (!events.length || (showWarningsOnly && !warnings.length)) {
if (!events.length || (this.showWarningsOnly && !warnings.length)) {
return null;
}
const event = [...warnings, ...events][0]; // get latest event
@@ -58,3 +61,10 @@ export class KubeEventIcon extends React.Component<KubeEventIconProps> {
);
}
}
export const KubeEventIcon = withInjectables<Dependencies, KubeEventIconProps>(NonInjectedKubeEventIcon, {
getProps: (di, props) => ({
...props,
eventStore: di.inject(eventStoreInjectable),
}),
});
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import eventStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(eventStoreInjectable)` instead
*/
export const eventStore = asLegacyGlobalForExtensionApi(eventStoreInjectable);
@@ -7,6 +7,7 @@ import assert from "assert";
import getPodByIdInjectable from "../+workloads-pods/get-pod-by-id.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import kubeEventApiInjectable from "../../../common/k8s-api/endpoints/events.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { EventStore } from "./store";
@@ -21,6 +22,7 @@ const eventStoreInjectable = getInjectable({
return new EventStore({
getPodById: di.inject(getPodByIdInjectable),
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable";
import uninstallExtensionInjectable from "../uninstall-extension/uninstall-extension.injectable";
import uninstallExtensionInjectable from "../uninstall-extension.injectable";
import type { UnpackExtension } from "./unpack-extension.injectable";
import unpackExtensionInjectable from "./unpack-extension.injectable";
import type { GetExtensionDestFolder } from "./get-extension-dest-folder.injectable";
@@ -14,7 +14,7 @@ import createTempFilesAndValidateInjectable from "./create-temp-files-and-valida
import extensionInstallationStateStoreInjectable from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import type { Disposer } from "../../../../common/utils";
import { disposer } from "../../../../common/utils";
import { Notifications } from "../../notifications";
import type { ShowNotification } from "../../notifications";
import { Button } from "../../button";
import type { ExtensionLoader } from "../../../../extensions/extension-loader";
import type { LensExtensionId } from "../../../../extensions/lens-extension";
@@ -23,6 +23,8 @@ import { remove as removeDir } from "fs-extra";
import { shell } from "electron";
import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
import { ExtensionInstallationState } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable";
import showInfoNotificationInjectable from "../../notifications/show-info-notification.injectable";
export interface InstallRequest {
fileName: string;
@@ -36,6 +38,8 @@ interface Dependencies {
createTempFilesAndValidate: CreateTempFilesAndValidate;
getExtensionDestFolder: GetExtensionDestFolder;
installStateStore: ExtensionInstallationStateStore;
showErrorNotification: ShowNotification;
showInfoNotification: ShowNotification;
}
export type AttemptInstall = (request: InstallRequest, cleanup?: Disposer) => Promise<void>;
@@ -47,6 +51,8 @@ const attemptInstall = ({
createTempFilesAndValidate,
getExtensionDestFolder,
installStateStore,
showErrorNotification,
showInfoNotification,
}: Dependencies): AttemptInstall =>
async (request, cleanup) => {
const dispose = disposer(
@@ -66,7 +72,7 @@ const attemptInstall = ({
if (curState !== ExtensionInstallationState.IDLE) {
dispose();
return void Notifications.error(
return void showErrorNotification(
<div className="flex column gaps">
<b>Extension Install Collision:</b>
<p>
@@ -86,7 +92,7 @@ const attemptInstall = ({
const { version: oldVersion } = installedExtension.manifest;
// confirm to uninstall old version before installing new version
const removeNotification = Notifications.info(
const removeNotification = showInfoNotification(
<div className="InstallingExtensionNotification flex gaps align-center">
<div className="flex column gaps">
<p>
@@ -142,6 +148,8 @@ const attemptInstallInjectable = getInjectable({
createTempFilesAndValidate: di.inject(createTempFilesAndValidateInjectable),
getExtensionDestFolder: di.inject(getExtensionDestFolderInjectable),
installStateStore: di.inject(extensionInstallationStateStoreInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable),
showInfoNotification: di.inject(showInfoNotificationInjectable),
}),
});
@@ -9,7 +9,7 @@ import type { LensExtensionId } from "../../../extensions/lens-extension";
import { extensionDisplayName } from "../../../extensions/lens-extension";
import type { Confirm } from "../confirm-dialog/confirm.injectable";
import confirmInjectable from "../confirm-dialog/confirm.injectable";
import uninstallExtensionInjectable from "./uninstall-extension/uninstall-extension.injectable";
import uninstallExtensionInjectable from "./uninstall-extension.injectable";
interface Dependencies {
uninstallExtension: (id: LensExtensionId) => Promise<boolean>;
@@ -0,0 +1,84 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import extensionDiscoveryInjectable from "../../../extensions/extension-discovery/extension-discovery.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import type { LensExtensionId } from "../../../extensions/lens-extension";
import { extensionDisplayName } from "../../../extensions/lens-extension";
import React from "react";
import { when } from "mobx";
import { getMessageFromError } from "./get-message-from-error/get-message-from-error";
import showSuccessNotificationInjectable from "../notifications/show-success-notification.injectable";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
const uninstallExtensionInjectable = getInjectable({
id: "uninstall-extension",
instantiate: (di) => {
const extensionLoader = di.inject(extensionLoaderInjectable);
const extensionDiscovery = di.inject(extensionDiscoveryInjectable);
const extensionInstallationStateStore = di.inject(extensionInstallationStateStoreInjectable);
const logger = di.inject(loggerInjectable);
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
const showErrorNotification = di.inject(showErrorNotificationInjectable);
return async (extensionId: LensExtensionId): Promise<boolean> => {
const ext = extensionLoader.getExtension(extensionId);
if (!ext) {
logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`);
return true;
}
const { manifest } = ext;
const displayName = extensionDisplayName(manifest.name, manifest.version);
try {
logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`);
extensionInstallationStateStore.setUninstalling(extensionId);
await extensionDiscovery.uninstallExtension(extensionId);
// wait for the ExtensionLoader to actually uninstall the extension
await when(() => !extensionLoader.userExtensions.has(extensionId));
showSuccessNotification(
<p>
{"Extension "}
<b>{displayName}</b>
{" successfully uninstalled!"}
</p>,
);
return true;
} catch (error) {
const message = getMessageFromError(error);
logger.info(
`[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`,
{ error },
);
showErrorNotification(
<p>
{"Uninstalling extension "}
<b>{displayName}</b>
{" has failed: "}
<em>{message}</em>
</p>,
);
return false;
} finally {
// Remove uninstall state on uninstall failure
extensionInstallationStateStore.clearUninstalling(extensionId);
}
};
},
});
export default uninstallExtensionInjectable;
@@ -1,24 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable";
import { uninstallExtension } from "./uninstall-extension";
import extensionInstallationStateStoreInjectable
from "../../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import extensionDiscoveryInjectable
from "../../../../extensions/extension-discovery/extension-discovery.injectable";
const uninstallExtensionInjectable = getInjectable({
id: "uninstall-extension",
instantiate: (di) =>
uninstallExtension({
extensionLoader: di.inject(extensionLoaderInjectable),
extensionDiscovery: di.inject(extensionDiscoveryInjectable),
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
}),
});
export default uninstallExtensionInjectable;
@@ -1,79 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExtensionLoader } from "../../../../extensions/extension-loader";
import type { LensExtensionId } from "../../../../extensions/lens-extension";
import { extensionDisplayName } from "../../../../extensions/lens-extension";
import logger from "../../../../main/logger";
import type { ExtensionDiscovery } from "../../../../extensions/extension-discovery/extension-discovery";
import { Notifications } from "../../notifications";
import React from "react";
import { when } from "mobx";
import { getMessageFromError } from "../get-message-from-error/get-message-from-error";
import type { ExtensionInstallationStateStore } from "../../../../extensions/extension-installation-state-store/extension-installation-state-store";
interface Dependencies {
extensionLoader: ExtensionLoader;
extensionDiscovery: ExtensionDiscovery;
extensionInstallationStateStore: ExtensionInstallationStateStore;
}
export const uninstallExtension = ({
extensionLoader,
extensionDiscovery,
extensionInstallationStateStore,
}: Dependencies) => (
async (extensionId: LensExtensionId): Promise<boolean> => {
const ext = extensionLoader.getExtension(extensionId);
if (!ext) {
logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`);
return true;
}
const { manifest } = ext;
const displayName = extensionDisplayName(manifest.name, manifest.version);
try {
logger.debug(`[EXTENSIONS]: trying to uninstall ${extensionId}`);
extensionInstallationStateStore.setUninstalling(extensionId);
await extensionDiscovery.uninstallExtension(extensionId);
// wait for the ExtensionLoader to actually uninstall the extension
await when(() => !extensionLoader.userExtensions.has(extensionId));
Notifications.ok(
<p>
{"Extension "}
<b>{displayName}</b>
{" successfully uninstalled!"}
</p>,
);
return true;
} catch (error) {
const message = getMessageFromError(error);
logger.info(
`[EXTENSION-UNINSTALL]: uninstalling ${displayName} has failed: ${error}`,
{ error },
);
Notifications.error(
<p>
{"Uninstalling extension "}
<b>{displayName}</b>
{" has failed: "}
<em>{message}</em>
</p>,
);
return false;
} finally {
// Remove uninstall state on uninstall failure
extensionInstallationStateStore.clearUninstalling(extensionId);
}
}
);
@@ -14,7 +14,6 @@ import { Dialog } from "../../dialog";
import { Wizard, WizardStep } from "../../wizard";
import type { HelmRelease } from "../../../../common/k8s-api/endpoints/helm-releases.api";
import { Select } from "../../select";
import { Notifications } from "../../notifications";
import orderBy from "lodash/orderBy";
import { withInjectables } from "@ogre-tools/injectable-react";
import releaseRollbackDialogStateInjectable from "./state.injectable";
@@ -22,6 +21,8 @@ import type { RollbackRelease } from "../rollback-release/rollback-release.injec
import rollbackReleaseInjectable from "../rollback-release/rollback-release.injectable";
import type { HelmReleaseRevision, RequestHelmReleaseHistory } from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable";
import requestHelmReleaseHistoryInjectable from "../../../../common/k8s-api/endpoints/helm-releases.api/request-history.injectable";
import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable";
import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable";
export interface ReleaseRollbackDialogProps extends DialogProps {
}
@@ -30,6 +31,7 @@ interface Dependencies {
rollbackRelease: RollbackRelease;
state: IObservableValue<HelmRelease | undefined>;
requestHelmReleaseHistory: RequestHelmReleaseHistory;
showCheckedErrorNotification: ShowCheckedErrorNotification;
}
@observer
@@ -71,7 +73,7 @@ class NonInjectedReleaseRollbackDialog extends React.Component<ReleaseRollbackDi
await this.props.rollbackRelease(release.getName(), release.getNs(), revision.revision);
this.close();
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while rolling back release");
this.props.showCheckedErrorNotification(err, "Unknown error occured while rolling back release");
}
};
@@ -143,5 +145,6 @@ export const ReleaseRollbackDialog = withInjectables<Dependencies, ReleaseRollba
rollbackRelease: di.inject(rollbackReleaseInjectable),
state: di.inject(releaseRollbackDialogStateInjectable),
requestHelmReleaseHistory: di.inject(requestHelmReleaseHistoryInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
}),
});
@@ -15,13 +15,14 @@ import { Wizard, WizardStep } from "../../wizard";
import type { Namespace } from "../../../../common/k8s-api/endpoints";
import { Input } from "../../input";
import { systemName } from "../../input/input_validators";
import { Notifications } from "../../notifications";
import { withInjectables } from "@ogre-tools/injectable-react";
import namespaceStoreInjectable from "../store.injectable";
import addNamespaceDialogStateInjectable
from "./state.injectable";
import type { NamespaceStore } from "../store";
import { autoBind } from "../../../utils";
import type { ShowCheckedErrorNotification } from "../../notifications/show-checked-error.injectable";
import showCheckedErrorNotificationInjectable from "../../notifications/show-checked-error.injectable";
export interface AddNamespaceDialogProps extends DialogProps {
onSuccess?(ns: Namespace): void;
@@ -31,6 +32,7 @@ export interface AddNamespaceDialogProps extends DialogProps {
interface Dependencies {
namespaceStore: NamespaceStore;
state: IObservableValue<boolean>;
showCheckedErrorNotification: ShowCheckedErrorNotification;
}
@observer
@@ -63,7 +65,7 @@ class NonInjectedAddNamespaceDialog extends React.Component<AddNamespaceDialogPr
onSuccess?.(created);
this.close();
} catch (err) {
Notifications.checkedError(err, "Unknown error occured while creating the namespace");
this.props.showCheckedErrorNotification(err, "Unknown error occured while creating the namespace");
onError?.(err);
}
}
@@ -112,5 +114,6 @@ export const AddNamespaceDialog = withInjectables<Dependencies, AddNamespaceDial
...props,
namespaceStore: di.inject(namespaceStoreInjectable),
state: di.inject(addNamespaceDialogStateInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
}),
});
@@ -55,6 +55,9 @@ describe("<NamespaceSelectFilter />", () => {
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
fetchMock = asyncFn();
di.override(fetchInjectable, () => fetchMock);
const createCluster = di.inject(createClusterInjectable);
di.override(hostedClusterInjectable, () => createCluster({
@@ -71,9 +74,6 @@ describe("<NamespaceSelectFilter />", () => {
cleanup = disposer(subscribeStores([namespaceStore]));
fetchMock = asyncFn();
di.override(fetchInjectable, () => fetchMock);
const render = renderFor(di);
result = render((
@@ -29,7 +29,7 @@ interface Dependencies {
openAddNamespaceDialog: () => void;
}
export const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies) => (
const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }: Dependencies) => (
<TabLayout>
<KubeObjectListLayout
isConfigurable
@@ -11,6 +11,7 @@ import assert from "assert";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import clusterConfiguredAccessibleNamespacesInjectable from "../../cluster/accessible-namespaces.injectable";
import loggerInjectable from "../../../common/logger.injectable";
const namespaceStoreInjectable = getInjectable({
id: "namespace-store",
@@ -25,6 +26,7 @@ const namespaceStoreInjectable = getInjectable({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
storage: createStorage<string[] | undefined>("selected_namespaces", undefined),
clusterConfiguredAccessibleNamespaces: di.inject(clusterConfiguredAccessibleNamespacesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
+1 -6
View File
@@ -23,12 +23,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
makeObservable(this);
autoBind(this);
this.init();
}
private async init() {
await this.dependencies.storage.whenReady;
// initialize allowed namespaces
const { allowedNamespaces } = this;
const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load
@@ -11,13 +11,19 @@ import { DrawerTitle } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Endpoints } from "../../../common/k8s-api/endpoints";
import { EndpointSubsetList } from "./endpoint-subset-list";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface EndpointsDetailsProps extends KubeObjectDetailsProps<Endpoints> {
}
interface Dependencies {
logger: Logger;
}
@observer
export class EndpointsDetails extends React.Component<EndpointsDetailsProps> {
class NonInjectedEndpointsDetails extends React.Component<EndpointsDetailsProps & Dependencies> {
render() {
const { object: endpoint } = this.props;
@@ -26,7 +32,7 @@ export class EndpointsDetails extends React.Component<EndpointsDetailsProps> {
}
if (!(endpoint instanceof Endpoints)) {
logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint);
this.props.logger.error("[EndpointDetails]: passed object that is not an instanceof Endpoint", endpoint);
return null;
}
@@ -47,3 +53,10 @@ export class EndpointsDetails extends React.Component<EndpointsDetailsProps> {
);
}
}
export const EndpointsDetails = withInjectables<Dependencies, EndpointsDetailsProps>(NonInjectedEndpointsDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import endpointsApiInjectable from "../../../common/k8s-api/endpoints/endpoint.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { EndpointsStore } from "./store";
@@ -19,6 +20,7 @@ const endpointsStoreInjectable = getInjectable({
return new EndpointsStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -4,15 +4,13 @@
*/
import assert from "assert";
import { getInjectable } from "@ogre-tools/injectable";
import {
kubeObjectStoreInjectionToken,
} from "../../../common/k8s-api/api-manager/manager.injectable";
import ingressClassApiInjectable
from "../../../common/k8s-api/endpoints/ingress-class.api.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import ingressClassApiInjectable from "../../../common/k8s-api/endpoints/ingress-class.api.injectable";
import { IngressClassStore } from "./ingress-class-store";
import clusterFrameContextForNamespacedResourcesInjectable
from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
const ingressClassStoreInjectable = getInjectable({
id: "ingress-class-store",
@@ -23,7 +21,8 @@ const ingressClassStoreInjectable = getInjectable({
const api = di.inject(ingressClassApiInjectable);
return new IngressClassStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { IngressStore } from "./ingress-store";
@@ -19,6 +20,7 @@ const ingressStoreInjectable = getInjectable({
return new IngressStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -13,15 +13,21 @@ import { Badge } from "../badge";
import { SubTitle } from "../layout/sub-title";
import { observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import type { LabelMatchExpression, LabelSelector } from "../../../common/k8s-api/kube-object";
import { isEmpty } from "lodash";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface NetworkPolicyDetailsProps extends KubeObjectDetailsProps<NetworkPolicy> {
}
interface Dependencies {
logger: Logger;
}
@observer
export class NetworkPolicyDetails extends React.Component<NetworkPolicyDetailsProps> {
class NonInjectedNetworkPolicyDetails extends React.Component<NetworkPolicyDetailsProps & Dependencies> {
renderIPolicyIpBlock(ipBlock: IPolicyIpBlock | undefined) {
if (!ipBlock) {
return null;
@@ -159,7 +165,7 @@ export class NetworkPolicyDetails extends React.Component<NetworkPolicyDetailsPr
}
if (!(policy instanceof NetworkPolicy)) {
logger.error("[NetworkPolicyDetails]: passed object that is not an instanceof NetworkPolicy", policy);
this.props.logger.error("[NetworkPolicyDetails]: passed object that is not an instanceof NetworkPolicy", policy);
return null;
}
@@ -204,3 +210,10 @@ export class NetworkPolicyDetails extends React.Component<NetworkPolicyDetailsPr
);
}
}
export const NetworkPolicyDetails = withInjectables<Dependencies, NetworkPolicyDetailsProps>(NonInjectedNetworkPolicyDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import networkPolicyApiInjectable from "../../../common/k8s-api/endpoints/network-policy.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { NetworkPolicyStore } from "./store";
@@ -19,6 +20,7 @@ const networkPolicyStoreInjectable = getInjectable({
return new NetworkPolicyStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -11,19 +11,33 @@ import type { PortForwardItem } from "../../port-forward";
import { portForwardAddress } from "../../port-forward";
import { Drawer, DrawerItem } from "../drawer";
import { cssNames } from "../../utils";
import { podApi, serviceApi } from "../../../common/k8s-api/endpoints";
import { getDetailsUrl } from "../kube-detail-params";
import type { PodApi, ServiceApi } from "../../../common/k8s-api/endpoints";
import { PortForwardMenu } from "./port-forward-menu";
import { withInjectables } from "@ogre-tools/injectable-react";
import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable";
export interface PortForwardDetailsProps {
portForward: PortForwardItem;
hideDetails(): void;
}
export class PortForwardDetails extends React.Component<PortForwardDetailsProps> {
interface Dependencies {
serviceApi: ServiceApi;
getDetailsUrl: GetDetailsUrl;
podApi: PodApi;
}
class NonInjectedPortForwardDetails extends React.Component<PortForwardDetailsProps & Dependencies> {
renderResourceName() {
const { portForward } = this.props;
const {
portForward,
serviceApi,
podApi,
getDetailsUrl,
} = this.props;
const name = portForward.getName();
const api = {
"service": serviceApi,
@@ -37,7 +51,7 @@ export class PortForwardDetails extends React.Component<PortForwardDetailsProps>
}
return (
<Link to={getDetailsUrl(api.getUrl({ name, namespace: portForward.getNs() }))}>
<Link to={getDetailsUrl(api.formatUrlForNotListing({ name, namespace: portForward.getNs() }))}>
{name}
</Link>
);
@@ -99,3 +113,12 @@ export class PortForwardDetails extends React.Component<PortForwardDetailsProps>
);
}
}
export const PortForwardDetails = withInjectables<Dependencies, PortForwardDetailsProps>(NonInjectedPortForwardDetails, {
getProps: (di, props) => ({
...props,
serviceApi: di.inject(serviceApiInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
podApi: di.inject(podApiInjectable),
}),
});
@@ -10,12 +10,13 @@ import type { MenuActionsProps } from "../menu/menu-actions";
import { MenuActions } from "../menu/menu-actions";
import { MenuItem } from "../menu";
import { Icon } from "../icon";
import { Notifications } from "../notifications";
import type { ShowNotification } from "../notifications";
import { withInjectables } from "@ogre-tools/injectable-react";
import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable";
import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable";
import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable";
import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
export interface PortForwardMenuProps extends MenuActionsProps {
portForward: PortForwardItem;
@@ -26,6 +27,7 @@ interface Dependencies {
portForwardStore: PortForwardStore;
openPortForwardDialog: (item: PortForwardItem) => void;
openPortForward: OpenPortForward;
showErrorNotification: ShowNotification;
}
class NonInjectedPortForwardMenu<Props extends PortForwardMenuProps & Dependencies> extends React.Component<Props> {
@@ -35,12 +37,12 @@ class NonInjectedPortForwardMenu<Props extends PortForwardMenuProps & Dependenci
}
remove() {
const { portForward } = this.props;
const { portForward, showErrorNotification } = this.props;
try {
this.portForwardStore.remove(portForward);
} catch (error) {
Notifications.error(`Error occurred stopping the port-forward from port ${portForward.forwardPort}. The port-forward may still be active.`);
showErrorNotification(`Error occurred stopping the port-forward from port ${portForward.forwardPort}. The port-forward may still be active.`);
}
}
@@ -49,14 +51,14 @@ class NonInjectedPortForwardMenu<Props extends PortForwardMenuProps & Dependenci
}
private startPortForwarding = async () => {
const { portForward } = this.props;
const { portForward, showErrorNotification } = this.props;
const pf = await this.portForwardStore.start(portForward);
if (pf.status === "Disabled") {
const { name, kind, forwardPort } = portForward;
Notifications.error(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`);
showErrorNotification(`Error occurred starting port-forward, the local port ${forwardPort} may not be available or the ${kind} ${name} may not be reachable`);
}
};
@@ -134,15 +136,12 @@ class NonInjectedPortForwardMenu<Props extends PortForwardMenuProps & Dependenci
}
}
export const PortForwardMenu = withInjectables<Dependencies, PortForwardMenuProps>(
NonInjectedPortForwardMenu,
{
getProps: (di, props) => ({
portForwardStore: di.inject(portForwardStoreInjectable),
openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open,
openPortForward: di.inject(openPortForwardInjectable),
...props,
}),
},
);
export const PortForwardMenu = withInjectables<Dependencies, PortForwardMenuProps>(NonInjectedPortForwardMenu, {
getProps: (di, props) => ({
...props,
portForwardStore: di.inject(portForwardStoreInjectable),
openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open,
openPortForward: di.inject(openPortForwardInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable),
}),
});
@@ -15,12 +15,13 @@ import { ServicePortComponent } from "./service-port-component";
import type { EndpointsStore } from "../+network-endpoints/store";
import { ServiceDetailsEndpoint } from "./service-details-endpoint";
import type { PortForwardStore } from "../../port-forward";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import endpointsStoreInjectable from "../+network-endpoints/store.injectable";
import loggerInjectable from "../../../common/logger.injectable";
export interface ServiceDetailsProps extends KubeObjectDetailsProps<Service> {
}
@@ -29,6 +30,7 @@ interface Dependencies {
subscribeStores: SubscribeStores;
portForwardStore: PortForwardStore;
endpointsStore: EndpointsStore;
logger: Logger;
}
@observer
@@ -59,7 +61,7 @@ class NonInjectedServiceDetails extends React.Component<ServiceDetailsProps & De
}
if (!(service instanceof Service)) {
logger.error("[ServiceDetails]: passed object that is not an instanceof Service", service);
this.props.logger.error("[ServiceDetails]: passed object that is not an instanceof Service", service);
return null;
}
@@ -155,5 +157,6 @@ export const ServiceDetails = withInjectables<Dependencies, ServiceDetailsProps>
subscribeStores: di.inject(subscribeStoresInjectable),
portForwardStore: di.inject(portForwardStoreInjectable),
endpointsStore: di.inject(endpointsStoreInjectable),
logger: di.inject(loggerInjectable),
}),
});
@@ -10,7 +10,7 @@ import { disposeOnUnmount, observer } from "mobx-react";
import type { Service, ServicePort } from "../../../common/k8s-api/endpoints";
import { action, makeObservable, observable, reaction } from "mobx";
import { cssNames } from "../../utils";
import { Notifications } from "../notifications";
import type { ShowNotification } from "../notifications";
import { Button } from "../button";
import type { ForwardedPort, PortForwardStore } from "../../port-forward";
import { predictProtocol } from "../../port-forward";
@@ -18,11 +18,13 @@ import { Spinner } from "../spinner";
import { withInjectables } from "@ogre-tools/injectable-react";
import portForwardStoreInjectable from "../../port-forward/port-forward-store/port-forward-store.injectable";
import portForwardDialogModelInjectable from "../../port-forward/port-forward-dialog-model/port-forward-dialog-model.injectable";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import aboutPortForwardingInjectable from "../../port-forward/about-port-forwarding.injectable";
import notifyErrorPortForwardingInjectable from "../../port-forward/notify-error-port-forwarding.injectable";
import type { OpenPortForward } from "../../port-forward/open-port-forward.injectable";
import openPortForwardInjectable from "../../port-forward/open-port-forward.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
export interface ServicePortComponentProps {
service: Service;
@@ -31,10 +33,12 @@ export interface ServicePortComponentProps {
interface Dependencies {
portForwardStore: PortForwardStore;
logger: Logger;
openPortForwardDialog: (item: ForwardedPort, options: { openInBrowser: boolean; onClose: () => void }) => void;
aboutPortForwarding: () => void;
notifyErrorPortForwarding: (message: string) => void;
openPortForward: OpenPortForward;
showErrorNotification: ShowNotification;
}
@observer
@@ -127,7 +131,7 @@ class NonInjectedServicePortComponent extends React.Component<ServicePortCompone
this.props.notifyErrorPortForwarding(`Error occurred starting port-forward, the local port may not be available or the ${portForward.kind} ${portForward.name} may not be reachable`);
}
} catch (error) {
logger.error("[SERVICE-PORT-COMPONENT]:", error, portForward);
this.props.logger.error("[SERVICE-PORT-COMPONENT]:", error, portForward);
} finally {
this.checkExistingPortForwarding();
this.waiting = false;
@@ -136,7 +140,7 @@ class NonInjectedServicePortComponent extends React.Component<ServicePortCompone
@action
async stopPortForward() {
const { service, port } = this.props;
const { service, port, showErrorNotification } = this.props;
const portForward: ForwardedPort = {
kind: "service",
name: service.getName(),
@@ -150,7 +154,7 @@ class NonInjectedServicePortComponent extends React.Component<ServicePortCompone
try {
await this.portForwardStore.remove(portForward);
} catch (error) {
Notifications.error(`Error occurred stopping the port-forward from port ${portForward.forwardPort}.`);
showErrorNotification(`Error occurred stopping the port-forward from port ${portForward.forwardPort}.`);
} finally {
this.checkExistingPortForwarding();
this.forwardPort = 0;
@@ -196,18 +200,16 @@ class NonInjectedServicePortComponent extends React.Component<ServicePortCompone
}
}
export const ServicePortComponent = withInjectables<Dependencies, ServicePortComponentProps>(
NonInjectedServicePortComponent,
{
getProps: (di, props) => ({
portForwardStore: di.inject(portForwardStoreInjectable),
openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open,
aboutPortForwarding: di.inject(aboutPortForwardingInjectable),
notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable),
openPortForward: di.inject(openPortForwardInjectable),
...props,
}),
},
);
export const ServicePortComponent = withInjectables<Dependencies, ServicePortComponentProps>(NonInjectedServicePortComponent, {
getProps: (di, props) => ({
...props,
portForwardStore: di.inject(portForwardStoreInjectable),
openPortForwardDialog: di.inject(portForwardDialogModelInjectable).open,
aboutPortForwarding: di.inject(aboutPortForwardingInjectable),
notifyErrorPortForwarding: di.inject(notifyErrorPortForwardingInjectable),
openPortForward: di.inject(openPortForwardInjectable),
logger: di.inject(loggerInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import serviceApiInjectable from "../../../common/k8s-api/endpoints/service.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { ServiceStore } from "./store";
@@ -19,6 +20,7 @@ const serviceStoreInjectable = getInjectable({
return new ServiceStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import nodeApiInjectable from "../../../common/k8s-api/endpoints/node.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { NodeStore } from "./store";
@@ -19,6 +20,7 @@ const nodeStoreInjectable = getInjectable({
return new NodeStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import podSecurityPolicyStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(podSecurityPolicyStoreInjectable)` instead
*/
export const podSecurityPolicyStore = asLegacyGlobalForExtensionApi(podSecurityPolicyStoreInjectable);
@@ -8,10 +8,12 @@ import "./pod-security-policies.scss";
import React from "react";
import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { podSecurityPolicyStore } from "./legacy-store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import type { PodSecurityPolicyStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import podSecurityPolicyStoreInjectable from "./store.injectable";
enum columnId {
name = "name",
@@ -20,8 +22,12 @@ enum columnId {
age = "age",
}
interface Dependencies {
podSecurityPolicyStore: PodSecurityPolicyStore;
}
@observer
export class PodSecurityPolicies extends React.Component {
class NonInjectedPodSecurityPolicies extends React.Component<Dependencies> {
render() {
return (
<SiblingsInTabLayout>
@@ -29,7 +35,7 @@ export class PodSecurityPolicies extends React.Component {
isConfigurable
tableId="access_pod_security_policies"
className="PodSecurityPolicies"
store={podSecurityPolicyStore}
store={this.props.podSecurityPolicyStore}
sortingCallbacks={{
[columnId.name]: podSecurityPolicy => podSecurityPolicy.getName(),
[columnId.volumes]: podSecurityPolicy => podSecurityPolicy.getVolumes(),
@@ -61,3 +67,10 @@ export class PodSecurityPolicies extends React.Component {
);
}
}
export const PodSecurityPolicies = withInjectables<Dependencies>(NonInjectedPodSecurityPolicies, {
getProps: (di, props) => ({
...props,
podSecurityPolicyStore: di.inject(podSecurityPolicyStoreInjectable),
}),
});
@@ -12,7 +12,9 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
import { PodSecurityPolicy } from "../../../common/k8s-api/endpoints";
import { Badge } from "../badge";
import { Table, TableCell, TableHead, TableRow } from "../table";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
export interface PodSecurityPolicyDetailsProps extends KubeObjectDetailsProps<PodSecurityPolicy> {
}
@@ -25,8 +27,12 @@ interface RuleGroup {
}[];
}
interface Dependencies {
logger: Logger;
}
@observer
export class PodSecurityPolicyDetails extends React.Component<PodSecurityPolicyDetailsProps> {
class NonInjectedPodSecurityPolicyDetails extends React.Component<PodSecurityPolicyDetailsProps & Dependencies> {
renderRuleGroup(title: React.ReactNode, group: RuleGroup | undefined) {
if (!group) return null;
const { rule, ranges } = group;
@@ -59,7 +65,7 @@ export class PodSecurityPolicyDetails extends React.Component<PodSecurityPolicyD
}
if (!(psp instanceof PodSecurityPolicy)) {
logger.error("[PodSecurityPolicyDetails]: passed object that is not an instanceof PodSecurityPolicy", psp);
this.props.logger.error("[PodSecurityPolicyDetails]: passed object that is not an instanceof PodSecurityPolicy", psp);
return null;
}
@@ -225,3 +231,10 @@ export class PodSecurityPolicyDetails extends React.Component<PodSecurityPolicyD
);
}
}
export const PodSecurityPolicyDetails = withInjectables<Dependencies, PodSecurityPolicyDetailsProps>(NonInjectedPodSecurityPolicyDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
}),
});
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import podSecurityPolicyApiInjectable from "../../../common/k8s-api/endpoints/pod-security-policy.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PodSecurityPolicyStore } from "./store";
@@ -19,6 +20,7 @@ const podSecurityPolicyStoreInjectable = getInjectable({
return new PodSecurityPolicyStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import storageClassStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(storageClassStoreInjectable)` instead
*/
export const storageClassStore = asLegacyGlobalForExtensionApi(storageClassStoreInjectable);
@@ -15,12 +15,13 @@ import { StorageClass } from "../../../common/k8s-api/endpoints";
import type { StorageClassStore } from "./store";
import { VolumeDetailsList } from "../+storage-volumes/volume-details-list";
import type { PersistentVolumeStore } from "../+storage-volumes/store";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { SubscribeStores } from "../../kube-watch-api/kube-watch-api";
import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable";
import storageClassStoreInjectable from "./store.injectable";
import persistentVolumeStoreInjectable from "../+storage-volumes/store.injectable";
import loggerInjectable from "../../../common/logger.injectable";
export interface StorageClassDetailsProps extends KubeObjectDetailsProps<StorageClass> {
}
@@ -29,6 +30,7 @@ interface Dependencies {
subscribeStores: SubscribeStores;
storageClassStore: StorageClassStore;
persistentVolumeStore: PersistentVolumeStore;
logger: Logger;
}
@observer
@@ -49,7 +51,7 @@ class NonInjectedStorageClassDetails extends React.Component<StorageClassDetails
}
if (!(storageClass instanceof StorageClass)) {
logger.error("[StorageClassDetails]: passed object that is not an instanceof StorageClass", storageClass);
this.props.logger.error("[StorageClassDetails]: passed object that is not an instanceof StorageClass", storageClass);
return null;
}
@@ -100,6 +102,7 @@ export const StorageClassDetails = withInjectables<Dependencies, StorageClassDet
subscribeStores: di.inject(subscribeStoresInjectable),
storageClassStore: di.inject(storageClassStoreInjectable),
persistentVolumeStore: di.inject(persistentVolumeStoreInjectable),
logger: di.inject(loggerInjectable),
}),
});
@@ -8,10 +8,12 @@ import "./storage-classes.scss";
import React from "react";
import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { storageClassStore } from "./legacy-store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import type { StorageClassStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import storageClassStoreInjectable from "./store.injectable";
enum columnId {
name = "name",
@@ -21,8 +23,12 @@ enum columnId {
reclaimPolicy = "reclaim",
}
interface Dependencies {
storageClassStore: StorageClassStore;
}
@observer
export class StorageClasses extends React.Component {
class NonInjectedStorageClasses extends React.Component<Dependencies> {
render() {
return (
<SiblingsInTabLayout>
@@ -30,7 +36,7 @@ export class StorageClasses extends React.Component {
isConfigurable
tableId="storage_classes"
className="StorageClasses"
store={storageClassStore}
store={this.props.storageClassStore}
sortingCallbacks={{
[columnId.name]: storageClass => storageClass.getName(),
[columnId.age]: storageClass => -storageClass.getCreationTimestamp(),
@@ -63,3 +69,10 @@ export class StorageClasses extends React.Component {
);
}
}
export const StorageClasses = withInjectables<Dependencies>(NonInjectedStorageClasses, {
getProps: (di, props) => ({
...props,
storageClassStore: di.inject(storageClassStoreInjectable),
}),
});
@@ -7,6 +7,7 @@ import assert from "assert";
import getPersistentVolumesByStorageClassInjectable from "../+storage-volumes/get-persisten-volumes-by-storage-class.injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { StorageClassStore } from "./store";
@@ -21,6 +22,7 @@ const storageClassStoreInjectable = getInjectable({
return new StorageClassStore({
getPersistentVolumesByStorageClass: di.inject(getPersistentVolumesByStorageClassInjectable),
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PersistentVolumeClaimStore } from "./store";
@@ -19,6 +20,7 @@ const persistentVolumeClaimStoreInjectable = getInjectable({
return new PersistentVolumeClaimStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import persistentVolumeStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(persistentVolumeStoreInjectable)` instead
*/
export const persistentVolumeStore = asLegacyGlobalForExtensionApi(persistentVolumeStoreInjectable);
@@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable";
import persistentVolumeApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume.api.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import { PersistentVolumeStore } from "./store";
@@ -19,6 +20,7 @@ const persistentVolumeStoreInjectable = getInjectable({
return new PersistentVolumeStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -10,14 +10,17 @@ import { observer } from "mobx-react";
import type { PersistentVolume } from "../../../common/k8s-api/endpoints/persistent-volume.api";
import { TableRow } from "../table/table-row";
import { cssNames, prevDefault } from "../../utils";
import { showDetails } from "../kube-detail-params";
import { TableCell } from "../table/table-cell";
import { Spinner } from "../spinner/spinner";
import { DrawerTitle } from "../drawer/drawer-title";
import { Table } from "../table/table";
import { TableHead } from "../table/table-head";
import { persistentVolumeStore } from "./legacy-store";
import kebabCase from "lodash/kebabCase";
import type { PersistentVolumeStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import persistentVolumeStoreInjectable from "./store.injectable";
import type { ShowDetails } from "../kube-detail-params/show-details.injectable";
import showDetailsInjectable from "../kube-detail-params/show-details.injectable";
export interface VolumeDetailsListProps {
persistentVolumes: PersistentVolume[];
@@ -29,8 +32,13 @@ enum sortBy {
capacity = "capacity",
}
interface Dependencies {
persistentVolumeStore: PersistentVolumeStore;
showDetails: ShowDetails;
}
@observer
export class VolumeDetailsList extends React.Component<VolumeDetailsListProps> {
class NonInjectedVolumeDetailsList extends React.Component<VolumeDetailsListProps & Dependencies> {
private sortingCallbacks = {
[sortBy.name]: (volume: PersistentVolume) => volume.getName(),
[sortBy.capacity]: (volume: PersistentVolume) => volume.getCapacity(),
@@ -38,7 +46,7 @@ export class VolumeDetailsList extends React.Component<VolumeDetailsListProps> {
};
getTableRow = (uid: string) => {
const { persistentVolumes } = this.props;
const { persistentVolumes, showDetails } = this.props;
const volume = persistentVolumes.find(volume => volume.getId() === uid);
if (!volume) {
@@ -60,7 +68,7 @@ export class VolumeDetailsList extends React.Component<VolumeDetailsListProps> {
};
render() {
const { persistentVolumes } = this.props;
const { persistentVolumes, persistentVolumeStore } = this.props;
const virtual = persistentVolumes.length > 100;
if (!persistentVolumes.length) {
@@ -94,3 +102,11 @@ export class VolumeDetailsList extends React.Component<VolumeDetailsListProps> {
);
}
}
export const VolumeDetailsList = withInjectables<Dependencies, VolumeDetailsListProps>(NonInjectedVolumeDetailsList, {
getProps: (di, props) => ({
...props,
persistentVolumeStore: di.inject(persistentVolumeStoreInjectable),
showDetails: di.inject(showDetailsInjectable),
}),
});
@@ -11,19 +11,38 @@ import { Link } from "react-router-dom";
import { observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { PersistentVolume, persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints";
import type { PersistentVolumeClaimApi, StorageClassApi } from "../../../common/k8s-api/endpoints";
import { PersistentVolume } from "../../../common/k8s-api/endpoints";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import { getDetailsUrl } from "../kube-detail-params";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { stopPropagation } from "../../../renderer/utils";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable";
import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable";
export interface PersistentVolumeDetailsProps extends KubeObjectDetailsProps<PersistentVolume> {
}
interface Dependencies {
logger: Logger;
getDetailsUrl: GetDetailsUrl;
storageClassApi: StorageClassApi;
persistentVolumeClaimApi: PersistentVolumeClaimApi;
}
@observer
export class PersistentVolumeDetails extends React.Component<PersistentVolumeDetailsProps> {
class NonInjectedPersistentVolumeDetails extends React.Component<PersistentVolumeDetailsProps & Dependencies> {
render() {
const { object: volume } = this.props;
const {
object: volume,
storageClassApi,
getDetailsUrl,
logger,
persistentVolumeClaimApi,
} = this.props;
if (!volume) {
return null;
@@ -37,7 +56,7 @@ export class PersistentVolumeDetails extends React.Component<PersistentVolumeDet
const { accessModes, capacity, persistentVolumeReclaimPolicy, storageClassName, claimRef, flexVolume, mountOptions, nfs } = volume.spec;
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.formatUrlForNotListing({
name: storageClassName,
}));
@@ -108,7 +127,7 @@ export class PersistentVolumeDetails extends React.Component<PersistentVolumeDet
{claimRef.kind}
</DrawerItem>
<DrawerItem name="Name">
<Link to={getDetailsUrl(persistentVolumeClaimApi.getUrl(claimRef))}>
<Link to={getDetailsUrl(persistentVolumeClaimApi.formatUrlForNotListing(claimRef))}>
{claimRef.name}
</Link>
</DrawerItem>
@@ -121,3 +140,13 @@ export class PersistentVolumeDetails extends React.Component<PersistentVolumeDet
);
}
}
export const PersistentVolumeDetails = withInjectables<Dependencies, PersistentVolumeDetailsProps>(NonInjectedPersistentVolumeDetails, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable),
storageClassApi: di.inject(storageClassApiInjectable),
}),
});
@@ -9,13 +9,18 @@ import React from "react";
import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { getDetailsUrl } from "../kube-detail-params";
import { stopPropagation } from "../../utils";
import { persistentVolumeStore } from "./legacy-store";
import { persistentVolumeClaimApi, storageClassApi } from "../../../common/k8s-api/endpoints";
import type { PersistentVolumeClaimApi, StorageClassApi } from "../../../common/k8s-api/endpoints";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import type { PersistentVolumeStore } from "./store";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import persistentVolumeClaimApiInjectable from "../../../common/k8s-api/endpoints/persistent-volume-claim.api.injectable";
import persistentVolumeStoreInjectable from "./store.injectable";
import storageClassApiInjectable from "../../../common/k8s-api/endpoints/storage-class.api.injectable";
enum columnId {
name = "name",
@@ -26,9 +31,23 @@ enum columnId {
age = "age",
}
interface Dependencies {
storageClassApi: StorageClassApi;
persistentVolumeStore: PersistentVolumeStore;
persistentVolumeClaimApi: PersistentVolumeClaimApi;
getDetailsUrl: GetDetailsUrl;
}
@observer
export class PersistentVolumes extends React.Component {
class NonInjectedPersistentVolumes extends React.Component<Dependencies> {
render() {
const {
getDetailsUrl,
persistentVolumeStore,
storageClassApi,
persistentVolumeClaimApi,
} = this.props;
return (
<SiblingsInTabLayout>
<KubeObjectListLayout
@@ -59,7 +78,7 @@ export class PersistentVolumes extends React.Component {
]}
renderTableContents={volume => {
const { claimRef, storageClassName } = volume.spec;
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({
const storageClassDetailsUrl = getDetailsUrl(storageClassApi.formatUrlForNotListing({
name: storageClassName,
}));
@@ -75,7 +94,7 @@ export class PersistentVolumes extends React.Component {
</Link>,
volume.getCapacity(),
claimRef && (
<Link to={getDetailsUrl(persistentVolumeClaimApi.getUrl(claimRef))} onClick={stopPropagation}>
<Link to={getDetailsUrl(persistentVolumeClaimApi.formatUrlForNotListing(claimRef))} onClick={stopPropagation}>
{claimRef.name}
</Link>
),
@@ -88,3 +107,13 @@ export class PersistentVolumes extends React.Component {
);
}
}
export const PersistentVolumes = withInjectables<Dependencies>(NonInjectedPersistentVolumes, {
getProps: (di, props) => ({
...props,
getDetailsUrl: di.inject(getDetailsUrlInjectable),
persistentVolumeClaimApi: di.inject(persistentVolumeClaimApiInjectable),
persistentVolumeStore: di.inject(persistentVolumeStoreInjectable),
storageClassApi: di.inject(storageClassApiInjectable),
}),
});
@@ -4,7 +4,7 @@
*/
import React from "react";
import { ClusterRoleBindingDialog } from "../dialog";
import { ClusterRoleBindingDialog } from "../dialog/view";
import { ClusterRole } from "../../../../../common/k8s-api/endpoints";
import userEvent from "@testing-library/user-event";
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
@@ -16,9 +16,15 @@ import directoryForUserDataInjectable from "../../../../../common/app-paths/dire
import directoryForKubeConfigsInjectable from "../../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import hostedClusterInjectable from "../../../../cluster-frame-context/hosted-cluster.injectable";
import createClusterInjectable from "../../../../cluster/create-cluster.injectable";
import type { CloseClusterRoleBindingDialog } from "../dialog/close.injectable";
import closeClusterRoleBindingDialogInjectable from "../dialog/close.injectable";
import type { OpenClusterRoleBindingDialog } from "../dialog/open.injectable";
import openClusterRoleBindingDialogInjectable from "../dialog/open.injectable";
describe("ClusterRoleBindingDialog tests", () => {
let render: DiRender;
let closeClusterRoleBindingDialog: CloseClusterRoleBindingDialog;
let openClusterRoleBindingDialog: OpenClusterRoleBindingDialog;
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
@@ -27,6 +33,9 @@ describe("ClusterRoleBindingDialog tests", () => {
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
closeClusterRoleBindingDialog = di.inject(closeClusterRoleBindingDialogInjectable);
openClusterRoleBindingDialog = di.inject(openClusterRoleBindingDialogInjectable);
const createCluster = di.inject(createClusterInjectable);
di.override(hostedClusterInjectable, () => createCluster({
@@ -56,7 +65,7 @@ describe("ClusterRoleBindingDialog tests", () => {
});
afterEach(() => {
ClusterRoleBindingDialog.close();
closeClusterRoleBindingDialog();
jest.resetAllMocks();
});
@@ -67,7 +76,7 @@ describe("ClusterRoleBindingDialog tests", () => {
});
it("clusterrole select should be searchable", async () => {
ClusterRoleBindingDialog.open();
openClusterRoleBindingDialog();
const res = render(<ClusterRoleBindingDialog />);
userEvent.keyboard("a");
@@ -15,18 +15,22 @@ import { AddRemoveButtons } from "../../add-remove-buttons";
import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { Table, TableCell, TableHead, TableRow } from "../../table";
import { ClusterRoleBindingDialog } from "./dialog";
import { clusterRoleBindingStore } from "./legacy-store";
import { hashSubject } from "../hashers";
import type { OpenConfirmDialog } from "../../confirm-dialog/open.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import openConfirmDialogInjectable from "../../confirm-dialog/open.injectable";
import type { ClusterRoleBindingStore } from "./store";
import type { OpenClusterRoleBindingDialog } from "./dialog/open.injectable";
import openClusterRoleBindingDialogInjectable from "./dialog/open.injectable";
import clusterRoleBindingStoreInjectable from "./store.injectable";
export interface ClusterRoleBindingDetailsProps extends KubeObjectDetailsProps<ClusterRoleBinding> {
}
interface Dependencies {
openConfirmDialog: OpenConfirmDialog;
openClusterRoleBindingDialog: OpenClusterRoleBindingDialog;
clusterRoleBindingStore: ClusterRoleBindingStore;
}
@observer
@@ -47,7 +51,7 @@ class NonInjectedClusterRoleBindingDetails extends React.Component<ClusterRoleBi
}
removeSelectedSubjects() {
const { object: clusterRoleBinding, openConfirmDialog } = this.props;
const { object: clusterRoleBinding, openConfirmDialog, clusterRoleBindingStore } = this.props;
const { selectedSubjects } = this;
openConfirmDialog({
@@ -65,7 +69,7 @@ class NonInjectedClusterRoleBindingDetails extends React.Component<ClusterRoleBi
render() {
const { selectedSubjects } = this;
const { object: clusterRoleBinding } = this.props;
const { object: clusterRoleBinding, openClusterRoleBindingDialog } = this.props;
if (!clusterRoleBinding) {
return null;
@@ -121,7 +125,7 @@ class NonInjectedClusterRoleBindingDetails extends React.Component<ClusterRoleBi
)}
<AddRemoveButtons
onAdd={() => ClusterRoleBindingDialog.open(clusterRoleBinding)}
onAdd={() => openClusterRoleBindingDialog(clusterRoleBinding)}
onRemove={selectedSubjects.size ? this.removeSelectedSubjects : undefined}
addTooltip={`Add bindings to ${roleRef.name}`}
removeTooltip={`Remove selected bindings from ${roleRef.name}`}
@@ -135,5 +139,7 @@ export const ClusterRoleBindingDetails = withInjectables<Dependencies, ClusterRo
getProps: (di, props) => ({
...props,
openConfirmDialog: di.inject(openConfirmDialogInjectable),
openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable),
clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable),
}),
});
@@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable";
import clusterRoleBindingDialogStateInjectable from "./state.injectable";
export type CloseClusterRoleBindingDialog = () => void;
const closeClusterRoleBindingDialogInjectable = getInjectable({
id: "close-cluster-role-binding-dialog",
instantiate: (di): CloseClusterRoleBindingDialog => {
const state = di.inject(clusterRoleBindingDialogStateInjectable);
const editNameState = di.inject(editClusterRoleBindingNameStateInjectable);
return action(() => {
state.set({
isOpen: false,
});
editNameState.set("");
});
},
});
export default closeClusterRoleBindingDialogInjectable;
@@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
const editClusterRoleBindingNameStateInjectable = getInjectable({
id: "edit-cluster-role-binding-name-state",
instantiate: () => observable.box(""),
});
export default editClusterRoleBindingNameStateInjectable;
@@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { action } from "mobx";
import type { ClusterRoleBinding } from "../../../../../common/k8s-api/endpoints";
import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable";
import clusterRoleBindingDialogStateInjectable from "./state.injectable";
export type OpenClusterRoleBindingDialog = (clusterRoleBinding?: ClusterRoleBinding) => void;
const openClusterRoleBindingDialogInjectable = getInjectable({
id: "open-cluster-role-binding-dialog",
instantiate: (di): OpenClusterRoleBindingDialog => {
const state = di.inject(clusterRoleBindingDialogStateInjectable);
const editNameState = di.inject(editClusterRoleBindingNameStateInjectable);
return action((clusterRoleBinding) => {
state.set({
isOpen: true,
clusterRoleBinding,
});
editNameState.set(clusterRoleBinding?.getName() ?? "");
});
},
});
export default openClusterRoleBindingDialogInjectable;
@@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
import type { ClusterRoleBinding } from "../../../../../common/k8s-api/endpoints";
export type ClusterRoleBindingDialogState = {
isOpen: false;
clusterRoleBinding?: undefined;
} | {
isOpen: true;
clusterRoleBinding: ClusterRoleBinding | undefined;
};
const clusterRoleBindingDialogStateInjectable = getInjectable({
id: "cluster-role-binding-dialog-state",
instantiate: () => observable.box<ClusterRoleBindingDialogState>({
isOpen: false,
}),
});
export default clusterRoleBindingDialogStateInjectable;
@@ -3,75 +3,75 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./dialog.scss";
import "./view.scss";
import { action, computed, makeObservable, observable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import type { IObservableValue } from "mobx";
import { action, computed, makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import { serviceAccountStore } from "../+service-accounts/legacy-store";
import type { ClusterRole, ClusterRoleBinding, ServiceAccount } from "../../../../common/k8s-api/endpoints";
import type { DialogProps } from "../../dialog";
import { Dialog } from "../../dialog";
import { EditableList } from "../../editable-list";
import { Icon } from "../../icon";
import { showDetails } from "../../kube-detail-params";
import { SubTitle } from "../../layout/sub-title";
import { Notifications } from "../../notifications";
import { onMultiSelectFor, Select } from "../../select";
import { Wizard, WizardStep } from "../../wizard";
import { clusterRoleBindingStore } from "./legacy-store";
import { clusterRoleStore } from "../+cluster-roles/legacy-store";
import { ObservableHashSet, nFircate } from "../../../utils";
import { Input } from "../../input";
import { TooltipPosition } from "../../tooltip";
import type { Subject } from "../../../../common/k8s-api/endpoints/types/subject";
import type { ClusterRole, ServiceAccount } from "../../../../../common/k8s-api/endpoints";
import type { DialogProps } from "../../../dialog";
import { Dialog } from "../../../dialog";
import { EditableList } from "../../../editable-list";
import { Icon } from "../../../icon";
import { SubTitle } from "../../../layout/sub-title";
import { onMultiSelectFor, Select } from "../../../select";
import { Wizard, WizardStep } from "../../../wizard";
import { ObservableHashSet, nFircate } from "../../../../utils";
import { Input } from "../../../input";
import { TooltipPosition } from "../../../tooltip";
import type { Subject } from "../../../../../common/k8s-api/endpoints/types/subject";
import type { ClusterRoleBindingDialogState } from "./state.injectable";
import type { ClusterRoleStore } from "../../+cluster-roles/store";
import type { ServiceAccountStore } from "../../+service-accounts/store";
import { withInjectables } from "@ogre-tools/injectable-react";
import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable";
import editClusterRoleBindingNameStateInjectable from "./edit-name-state.injectable";
import serviceAccountStoreInjectable from "../../+service-accounts/store.injectable";
import clusterRoleBindingDialogStateInjectable from "./state.injectable";
import type { CloseClusterRoleBindingDialog } from "./close.injectable";
import type { OpenClusterRoleBindingDialog } from "./open.injectable";
import openClusterRoleBindingDialogInjectable from "./open.injectable";
import closeClusterRoleBindingDialogInjectable from "./close.injectable";
import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable";
import type { ClusterRoleBindingStore } from "../store";
import clusterRoleBindingStoreInjectable from "../store.injectable";
import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable";
import type { ShowCheckedErrorNotification } from "../../../notifications/show-checked-error.injectable";
import showCheckedErrorNotificationInjectable from "../../../notifications/show-checked-error.injectable";
export interface ClusterRoleBindingDialogProps extends Partial<DialogProps> {
}
interface DialogState {
isOpen: boolean;
data?: ClusterRoleBinding;
interface Dependencies {
state: IObservableValue<ClusterRoleBindingDialogState>;
editBindingNameState: IObservableValue<string>;
clusterRoleStore: ClusterRoleStore;
serviceAccountStore: ServiceAccountStore;
clusterRoleBindingStore: ClusterRoleBindingStore;
closeClusterRoleBindingDialog: CloseClusterRoleBindingDialog;
openClusterRoleBindingDialog: OpenClusterRoleBindingDialog;
showDetails: ShowDetails;
showCheckedErrorNotification: ShowCheckedErrorNotification;
}
const dialogState = observable.object<DialogState>({
isOpen: false,
});
@observer
export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBindingDialogProps> {
constructor(props: ClusterRoleBindingDialogProps) {
class NonInjectedClusterRoleBindingDialog extends React.Component<ClusterRoleBindingDialogProps & Dependencies> {
constructor(props: ClusterRoleBindingDialogProps & Dependencies) {
super(props);
makeObservable(this);
}
componentDidMount() {
disposeOnUnmount(this, [
reaction(() => this.isEditing, () => {
this.bindingName = dialogState.data?.getName() ?? "";
}),
]);
}
static open(roleBinding?: ClusterRoleBinding) {
dialogState.isOpen = true;
dialogState.data = roleBinding;
}
static close() {
dialogState.isOpen = false;
}
@computed get clusterRoleOptions() {
return clusterRoleStore.items.map(clusterRole => ({
return this.props.clusterRoleStore.items.map(clusterRole => ({
value: clusterRole,
label: clusterRole.getName(),
}));
}
@computed get serviceAccountOptions() {
return serviceAccountStore.items.map(serviceAccount => ({
return this.props.serviceAccountStore.items.map(serviceAccount => ({
value: serviceAccount,
label: `${serviceAccount.getName()} (${serviceAccount.getNs()})`,
isSelected: this.selectedAccounts.has(serviceAccount),
@@ -79,7 +79,7 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
}
get clusterRoleBinding() {
return dialogState.data;
return this.props.state.get().clusterRoleBinding;
}
get isEditing() {
@@ -87,7 +87,6 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
}
@observable selectedRoleRef: ClusterRole | undefined = undefined;
@observable bindingName = "";
selectedAccounts = new ObservableHashSet<ServiceAccount>([], sa => sa.metadata.uid);
selectedUsers = observable.set<string>([]);
selectedGroups = observable.set<string>([]);
@@ -121,16 +120,15 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
return this.reset();
}
this.selectedRoleRef = clusterRoleStore
this.selectedRoleRef = this.props.clusterRoleStore
.items
.find(item => item.getName() === binding.roleRef.name);
this.bindingName = binding.getName();
const [saSubjects, uSubjects, gSubjects] = nFircate(binding.getSubjects(), "kind", ["ServiceAccount", "User", "Group"]);
const accountNames = new Set(saSubjects.map(acc => acc.name));
this.selectedAccounts.replace(
serviceAccountStore.items
this.props.serviceAccountStore.items
.filter(sa => accountNames.has(sa.getName())),
);
this.selectedUsers.replace(uSubjects.map(user => user.name));
@@ -139,14 +137,19 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
reset = action(() => {
this.selectedRoleRef = undefined;
this.bindingName = "";
this.selectedAccounts.clear();
this.selectedUsers.clear();
this.selectedGroups.clear();
});
createBindings = async () => {
const { selectedRoleRef, selectedBindings, bindingName, clusterRoleBinding } = this;
const {
closeClusterRoleBindingDialog,
clusterRoleBindingStore,
editBindingNameState,
showDetails,
} = this.props;
const { selectedRoleRef, selectedBindings, clusterRoleBinding } = this;
if (!clusterRoleBinding || !selectedRoleRef) {
return;
@@ -155,7 +158,7 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
try {
const { selfLink } = this.isEditing
? await clusterRoleBindingStore.updateSubjects(clusterRoleBinding, selectedBindings)
: await clusterRoleBindingStore.create({ name: bindingName }, {
: await clusterRoleBindingStore.create({ name: editBindingNameState.get() }, {
subjects: selectedBindings,
roleRef: {
name: selectedRoleRef.getName(),
@@ -164,9 +167,9 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
});
showDetails(selfLink);
ClusterRoleBindingDialog.close();
closeClusterRoleBindingDialog();
} catch (err) {
Notifications.checkedError(err, `Unknown error occured while ${this.isEditing ? "editing the" : "creating a"} ClusterRoleBinding`);
this.props.showCheckedErrorNotification(err, `Unknown error occured while ${this.isEditing ? "editing the" : "creating a"} ClusterRoleBinding`);
}
};
@@ -198,9 +201,10 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
)}
onChange={option => {
this.selectedRoleRef = option?.value;
const bindingName = this.props.editBindingNameState.get();
if (!this.selectedRoleRef || this.bindingName === this.selectedRoleRef.getName()) {
this.bindingName = option?.value?.getName() ?? "";
if (!this.selectedRoleRef || bindingName === this.selectedRoleRef.getName()) {
this.props.editBindingNameState.set(option?.value?.getName() ?? "");
}
}}
/>
@@ -209,8 +213,8 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
<Input
placeholder="Name of ClusterRoleBinding ..."
disabled={this.isEditing}
value={this.bindingName}
onChange={val => this.bindingName = val}
value={this.props.editBindingNameState.get()}
onChange={val => this.props.editBindingNameState.set(val)}
/>
<SubTitle title="Binding targets" />
@@ -252,16 +256,24 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
}
render() {
const { ...dialogProps } = this.props;
const {
closeClusterRoleBindingDialog,
openClusterRoleBindingDialog,
clusterRoleStore,
editBindingNameState,
serviceAccountStore,
state,
...dialogProps
} = this.props;
const [action, nextLabel] = this.isEditing ? ["Edit", "Update"] : ["Add", "Create"];
const disableNext = !this.selectedRoleRef || !this.selectedBindings.length || !this.bindingName;
const disableNext = !this.selectedRoleRef || !this.selectedBindings.length || !editBindingNameState.get();
return (
<Dialog
{...dialogProps}
className="AddClusterRoleBindingDialog"
isOpen={dialogState.isOpen}
close={ClusterRoleBindingDialog.close}
isOpen={state.get().isOpen}
close={closeClusterRoleBindingDialog}
onClose={this.reset}
onOpen={this.onOpen}
>
@@ -271,7 +283,7 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
{`${action} ClusterRoleBinding`}
</h5>
)}
done={ClusterRoleBindingDialog.close}
done={closeClusterRoleBindingDialog}
>
<WizardStep
nextLabel={nextLabel}
@@ -285,3 +297,18 @@ export class ClusterRoleBindingDialog extends React.Component<ClusterRoleBinding
);
}
}
export const ClusterRoleBindingDialog = withInjectables<Dependencies, ClusterRoleBindingDialogProps>(NonInjectedClusterRoleBindingDialog, {
getProps: (di, props) => ({
...props,
clusterRoleStore: di.inject(clusterRoleStoreInjectable),
editBindingNameState: di.inject(editClusterRoleBindingNameStateInjectable),
serviceAccountStore: di.inject(serviceAccountStoreInjectable),
state: di.inject(clusterRoleBindingDialogStateInjectable),
clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable),
openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable),
closeClusterRoleBindingDialog: di.inject(closeClusterRoleBindingDialogInjectable),
showDetails: di.inject(showDetailsInjectable),
showCheckedErrorNotification: di.inject(showCheckedErrorNotificationInjectable),
}),
});
@@ -4,4 +4,4 @@
*/
export * from "./view";
export * from "./details";
export * from "./dialog";
export * from "./dialog/view";
@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import clusterRoleBindingStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(clusterRoleBindingStoreInjectable)` instead
*/
export const clusterRoleBindingStore = asLegacyGlobalForExtensionApi(clusterRoleBindingStoreInjectable);
@@ -9,6 +9,7 @@ import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoint
import { kubeObjectStoreInjectionToken } from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ClusterRoleBindingStore } from "./store";
import clusterFrameContextForClusterScopedResourcesInjectable from "../../../cluster-frame-context/for-cluster-scoped-resources.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
const clusterRoleBindingStoreInjectable = getInjectable({
id: "cluster-role-binding-store",
@@ -19,6 +20,7 @@ const clusterRoleBindingStoreInjectable = getInjectable({
return new ClusterRoleBindingStore({
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
@@ -9,12 +9,18 @@ import { observer } from "mobx-react";
import React from "react";
import { KubeObjectListLayout } from "../../kube-object-list-layout";
import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
import { ClusterRoleBindingDialog } from "./dialog";
import { clusterRoleBindingStore } from "./legacy-store";
import { clusterRoleStore } from "../+cluster-roles/legacy-store";
import { serviceAccountStore } from "../+service-accounts/legacy-store";
import { ClusterRoleBindingDialog } from "./dialog/view";
import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../../kube-object/age";
import type { ClusterRoleBindingStore } from "./store";
import { withInjectables } from "@ogre-tools/injectable-react";
import clusterRoleBindingStoreInjectable from "./store.injectable";
import type { ClusterRoleStore } from "../+cluster-roles/store";
import type { ServiceAccountStore } from "../+service-accounts/store";
import clusterRoleStoreInjectable from "../+cluster-roles/store.injectable";
import serviceAccountStoreInjectable from "../+service-accounts/store.injectable";
import type { OpenClusterRoleBindingDialog } from "./dialog/open.injectable";
import openClusterRoleBindingDialogInjectable from "./dialog/open.injectable";
enum columnId {
name = "name",
@@ -23,9 +29,23 @@ enum columnId {
age = "age",
}
interface Dependencies {
clusterRoleBindingStore: ClusterRoleBindingStore;
clusterRoleStore: ClusterRoleStore;
serviceAccountStore: ServiceAccountStore;
openClusterRoleBindingDialog: OpenClusterRoleBindingDialog;
}
@observer
export class ClusterRoleBindings extends React.Component {
class NonInjectedClusterRoleBindings extends React.Component<Dependencies> {
render() {
const {
clusterRoleBindingStore,
clusterRoleStore,
serviceAccountStore,
openClusterRoleBindingDialog,
} = this.props;
return (
<SiblingsInTabLayout>
<KubeObjectListLayout
@@ -57,7 +77,7 @@ export class ClusterRoleBindings extends React.Component {
<KubeObjectAge key="age" object={binding} />,
]}
addRemoveButtons={{
onAdd: () => ClusterRoleBindingDialog.open(),
onAdd: () => openClusterRoleBindingDialog(),
addTooltip: "Create new ClusterRoleBinding",
}}
/>
@@ -66,3 +86,13 @@ export class ClusterRoleBindings extends React.Component {
);
}
}
export const ClusterRoleBindings = withInjectables<Dependencies>(NonInjectedClusterRoleBindings, {
getProps: (di, props) => ({
...props,
clusterRoleBindingStore: di.inject(clusterRoleBindingStoreInjectable),
clusterRoleStore: di.inject(clusterRoleStoreInjectable),
serviceAccountStore: di.inject(serviceAccountStoreInjectable),
openClusterRoleBindingDialog: di.inject(openClusterRoleBindingDialogInjectable),
}),
});
@@ -1,91 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import "./add-dialog.scss";
import { makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import type { DialogProps } from "../../dialog";
import { Dialog } from "../../dialog";
import { Input } from "../../input";
import { showDetails } from "../../kube-detail-params";
import { SubTitle } from "../../layout/sub-title";
import { Notifications } from "../../notifications";
import { Wizard, WizardStep } from "../../wizard";
import { clusterRoleStore } from "./legacy-store";
export interface AddClusterRoleDialogProps extends Partial<DialogProps> {
}
@observer
export class AddClusterRoleDialog extends React.Component<AddClusterRoleDialogProps> {
static isOpen = observable.box(false);
@observable clusterRoleName = "";
constructor(props: AddClusterRoleDialogProps) {
super(props);
makeObservable(this);
}
static open() {
AddClusterRoleDialog.isOpen.set(true);
}
static close() {
AddClusterRoleDialog.isOpen.set(false);
}
reset = () => {
this.clusterRoleName = "";
};
createRole = async () => {
try {
const role = await clusterRoleStore.create({ name: this.clusterRoleName });
showDetails(role.selfLink);
this.reset();
AddClusterRoleDialog.close();
} catch (error) {
Notifications.checkedError(error, "Unknown error occured while creating the role");
}
};
render() {
const { ...dialogProps } = this.props;
return (
<Dialog
{...dialogProps}
className="AddClusterRoleDialog"
isOpen={AddClusterRoleDialog.isOpen.get()}
close={AddClusterRoleDialog.close}
>
<Wizard
header={<h5>Create ClusterRole</h5>}
done={AddClusterRoleDialog.close}
>
<WizardStep
contentClass="flex gaps column"
nextLabel="Create"
next={this.createRole}
>
<SubTitle title="ClusterRole Name" />
<Input
required
autoFocus
placeholder="Name"
iconLeft="supervisor_account"
value={this.clusterRoleName}
onChange={v => this.clusterRoleName = v}
/>
</WizardStep>
</Wizard>
</Dialog>
);
}
}
Loaded 100 of 530 files, more files were not shown because too many files have changed in this diff. Show more