diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts index 33ec108a31..9d3fa94815 100644 --- a/src/common/k8s-api/endpoints/index.ts +++ b/src/common/k8s-api/endpoints/index.ts @@ -43,5 +43,4 @@ export * from "./service.api"; export * from "./service-account.api"; export * from "./stateful-set.api"; export * from "./storage-class.api"; -export * from "./legacy-globals"; export * from "./types"; diff --git a/src/common/k8s-api/endpoints/legacy-globals.ts b/src/common/k8s-api/endpoints/legacy-globals.ts deleted file mode 100644 index a0e905315b..0000000000 --- a/src/common/k8s-api/endpoints/legacy-globals.ts +++ /dev/null @@ -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 roleApiInjectable from "./role.api.injectable"; - -/** - * @deprecated use `di.inject(roleApiInjectable)` instead - */ -export const roleApi = asLegacyGlobalForExtensionApi(roleApiInjectable); diff --git a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx index 08067ed817..67d3b38fe1 100644 --- a/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx +++ b/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx @@ -6,7 +6,7 @@ import userEvent from "@testing-library/user-event"; import React from "react"; import { ClusterRole } from "../../../../../common/k8s-api/endpoints"; -import { RoleBindingDialog } from "../dialog"; +import { RoleBindingDialog } from "../dialog/view"; import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; import type { DiRender } from "../../../test-utils/renderFor"; import { renderFor } from "../../../test-utils/renderFor"; @@ -16,9 +16,12 @@ import storesAndApisCanBeCreatedInjectable from "../../../../stores-apis-can-be- 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 { OpenRoleBindingDialog } from "../dialog/open.injectable"; +import openRoleBindingDialogInjectable from "../dialog/open.injectable"; describe("RoleBindingDialog tests", () => { let render: DiRender; + let openRoleBindingDialog: OpenRoleBindingDialog; beforeEach(() => { const di = getDiForUnitTesting({ doGeneralOverrides: true }); @@ -27,6 +30,8 @@ describe("RoleBindingDialog tests", () => { di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + openRoleBindingDialog = di.inject(openRoleBindingDialogInjectable); + const createCluster = di.inject(createClusterInjectable); di.override(hostedClusterInjectable, () => createCluster({ @@ -55,11 +60,6 @@ describe("RoleBindingDialog tests", () => { ]); }); - afterEach(() => { - RoleBindingDialog.close(); - jest.resetAllMocks(); - }); - it("should render without any errors", () => { const { container } = render(); @@ -67,7 +67,7 @@ describe("RoleBindingDialog tests", () => { }); it("role select should be searchable", async () => { - RoleBindingDialog.open(); + openRoleBindingDialog(); const res = render(); userEvent.click(await res.findByText("Select role", { exact: false })); diff --git a/src/renderer/components/+user-management/+role-bindings/details.tsx b/src/renderer/components/+user-management/+role-bindings/details.tsx index eeadae080c..43d3a44f0c 100644 --- a/src/renderer/components/+user-management/+role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+role-bindings/details.tsx @@ -14,19 +14,21 @@ 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 { RoleBindingDialog } from "./dialog"; import { roleBindingStore } from "./legacy-store"; import { ObservableHashSet } from "../../../../common/utils/hash-set"; 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 { OpenRoleBindingDialog } from "./dialog/open.injectable"; +import openRoleBindingDialogInjectable from "./dialog/open.injectable"; export interface RoleBindingDetailsProps extends KubeObjectDetailsProps { } interface Dependencies { openConfirmDialog: OpenConfirmDialog; + openRoleBindingDialog: OpenRoleBindingDialog; } @observer @@ -60,7 +62,7 @@ class NonInjectedRoleBindingDetails extends React.Component RoleBindingDialog.open(roleBinding)} + onAdd={() => openRoleBindingDialog(roleBinding)} onRemove={selectedSubjects.size ? this.removeSelectedSubjects : undefined} addTooltip={`Edit bindings of ${roleRef.name}`} removeTooltip={`Remove selected bindings from ${roleRef.name}`} @@ -130,5 +132,6 @@ export const RoleBindingDetails = withInjectables ({ ...props, openConfirmDialog: di.inject(openConfirmDialogInjectable), + openRoleBindingDialog: di.inject(openRoleBindingDialogInjectable), }), }); diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts new file mode 100644 index 0000000000..1f42df1818 --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/close.injectable.ts @@ -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 roleBindingDialogStateInjectable from "./state.injectable"; + +const closeRoleBindingDialogInjectable = getInjectable({ + id: "close-role-binding-dialog", + instantiate: (di) => { + const state = di.inject(roleBindingDialogStateInjectable); + + return action(() => state.set({ isOpen: false })); + }, +}); + +export default closeRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts new file mode 100644 index 0000000000..bda4f5363b --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/open.injectable.ts @@ -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 { action } from "mobx"; +import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; +import roleBindingDialogStateInjectable from "./state.injectable"; + +export type OpenRoleBindingDialog = (roleBinding?: RoleBinding | undefined) => void; + +const openRoleBindingDialogInjectable = getInjectable({ + id: "open-role-binding-dialog", + instantiate: (di): OpenRoleBindingDialog => { + const state = di.inject(roleBindingDialogStateInjectable); + + return action((roleBinding) => state.set({ + isOpen: true, + roleBinding, + })); + }, +}); + +export default openRoleBindingDialogInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts b/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts new file mode 100644 index 0000000000..03b1093a3b --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog/state.injectable.ts @@ -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 { observable } from "mobx"; +import type { RoleBinding } from "../../../../../common/k8s-api/endpoints"; + +export type RoleBindingDialogState = { + isOpen: false; + roleBinding?: undefined; +} | { + isOpen: true; + roleBinding: RoleBinding | undefined; +}; + +const roleBindingDialogStateInjectable = getInjectable({ + id: "role-binding-dialog-state", + instantiate: () => observable.box({ isOpen: false }), +}); + +export default roleBindingDialogStateInjectable; diff --git a/src/renderer/components/+user-management/+role-bindings/dialog.scss b/src/renderer/components/+user-management/+role-bindings/dialog/view.scss similarity index 100% rename from src/renderer/components/+user-management/+role-bindings/dialog.scss rename to src/renderer/components/+user-management/+role-bindings/dialog/view.scss diff --git a/src/renderer/components/+user-management/+role-bindings/dialog.tsx b/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx similarity index 63% rename from src/renderer/components/+user-management/+role-bindings/dialog.tsx rename to src/renderer/components/+user-management/+role-bindings/dialog/view.tsx index 49ac40d7d1..ae2518eb8d 100644 --- a/src/renderer/components/+user-management/+role-bindings/dialog.tsx +++ b/src/renderer/components/+user-management/+role-bindings/dialog/view.tsx @@ -3,64 +3,65 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./dialog.scss"; +import "./view.scss"; +import type { IObservableValue } from "mobx"; import { computed, observable, makeObservable, action } from "mobx"; import { observer } from "mobx-react"; import React from "react"; - -import { roleStore } from "../+roles/legacy-store"; -import { serviceAccountStore } from "../+service-accounts/legacy-store"; -import { NamespaceSelect } from "../../+namespaces/namespace-select"; -import type { ClusterRole, Role, RoleBinding, ServiceAccount } from "../../../../common/k8s-api/endpoints"; -import { roleApi } 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 type { SelectOption } from "../../select"; -import { onMultiSelectFor, Select } from "../../select"; -import { Wizard, WizardStep } from "../../wizard"; -import { roleBindingStore } from "./legacy-store"; -import { clusterRoleStore } from "../+cluster-roles/legacy-store"; -import { Input } from "../../input"; -import { ObservableHashSet, nFircate } from "../../../utils"; -import type { Subject } from "../../../../common/k8s-api/endpoints/types/subject"; +import { NamespaceSelect } from "../../../+namespaces/namespace-select"; +import type { ClusterRole, Role, RoleApi, 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 { Notifications } from "../../../notifications"; +import type { SelectOption } from "../../../select"; +import { onMultiSelectFor, Select } from "../../../select"; +import { Wizard, WizardStep } from "../../../wizard"; +import { Input } from "../../../input"; +import { ObservableHashSet, nFircate } from "../../../../utils"; +import type { Subject } from "../../../../../common/k8s-api/endpoints/types/subject"; +import type { RoleBindingDialogState } from "./state.injectable"; +import type { RoleBindingStore } from "../store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import roleBindingStoreInjectable from "../store.injectable"; +import roleBindingDialogStateInjectable from "./state.injectable"; +import closeRoleBindingDialogInjectable from "./close.injectable"; +import type { ShowDetails } from "../../../kube-detail-params/show-details.injectable"; +import type { RoleStore } from "../../+roles/store"; +import type { ClusterRoleStore } from "../../+cluster-roles/store"; +import type { ServiceAccountStore } from "../../+service-accounts/store"; +import showDetailsInjectable from "../../../kube-detail-params/show-details.injectable"; +import clusterRoleStoreInjectable from "../../+cluster-roles/store.injectable"; +import roleStoreInjectable from "../../+roles/store.injectable"; +import serviceAccountStoreInjectable from "../../+service-accounts/store.injectable"; +import roleApiInjectable from "../../../../../common/k8s-api/endpoints/role.api.injectable"; export interface RoleBindingDialogProps extends Partial { } -interface DialogState { - isOpen: boolean; - data?: RoleBinding; +interface Dependencies { + state: IObservableValue; + roleBindingStore: RoleBindingStore; + closeRoleBindingDialog: () => void; + showDetails: ShowDetails; + roleStore: RoleStore; + clusterRoleStore: ClusterRoleStore; + serviceAccountStore: ServiceAccountStore; + roleApi: RoleApi; } @observer -export class RoleBindingDialog extends React.Component { - static state = observable.object({ - isOpen: false, - }); - - constructor(props: RoleBindingDialogProps) { +class NonInjectedRoleBindingDialog extends React.Component { + constructor(props: RoleBindingDialogProps & Dependencies) { super(props); makeObservable(this); } - static open(roleBinding?: RoleBinding) { - RoleBindingDialog.state.isOpen = true; - RoleBindingDialog.state.data = roleBinding; - } - - static close() { - RoleBindingDialog.state.isOpen = false; - RoleBindingDialog.state.data = undefined; - } - - get roleBinding() { - return RoleBindingDialog.state.data; + @computed get roleBinding() { + return this.props.state.get().roleBinding; } @computed get isEditing() { @@ -97,6 +98,10 @@ export class RoleBindingDialog extends React.Component { } @computed get roleRefOptions(): SelectOption[] { + const { + roleStore, + clusterRoleStore, + } = this.props; const roles = roleStore.items .filter(role => role.getNs() === this.bindingNamespace); const clusterRoles = clusterRoleStore.items; @@ -111,7 +116,7 @@ export class RoleBindingDialog extends React.Component { } @computed get serviceAccountOptions(): SelectOption[] { - return serviceAccountStore.items.map(serviceAccount => ({ + return this.props.serviceAccountStore.items.map(serviceAccount => ({ value: serviceAccount, label: `${serviceAccount.getName()} (${serviceAccount.getNs()})`, isSelected: this.selectedAccounts.has(serviceAccount), @@ -119,18 +124,22 @@ export class RoleBindingDialog extends React.Component { } onOpen = action(() => { + const { + roleStore, + clusterRoleStore, + serviceAccountStore, + roleApi, + } = this.props; const binding = this.roleBinding; if (!binding) { return this.reset(); } - const findByRoleRefName = (item: Role | ClusterRole) => item.getName() === binding.roleRef.name; - this.selectedRoleRef = ( binding.roleRef.kind === roleApi.kind - ? roleStore.items.find(findByRoleRefName) - : clusterRoleStore.items.find(findByRoleRefName) + ? roleStore.items.find(item => item.getName() === binding.roleRef.name) + : clusterRoleStore.items.find(item => item.getName() === binding.roleRef.name) ); this.bindingName = binding.getName(); @@ -157,6 +166,10 @@ export class RoleBindingDialog extends React.Component { }); createBindings = async () => { + const { + roleBindingStore, + showDetails, + } = this.props; const { selectedRoleRef, bindingNamespace, selectedBindings, roleBinding, bindingName } = this; if (!selectedRoleRef || !roleBinding || !bindingNamespace || !bindingName) { @@ -178,7 +191,7 @@ export class RoleBindingDialog extends React.Component { }); showDetails(newRoleBinding.selfLink); - RoleBindingDialog.close(); + this.props.closeRoleBindingDialog(); } catch (err) { Notifications.checkedError(err, `Unknown error occured while ${this.isEditing ? "editing" : "creating"} role bindings.`); } @@ -260,7 +273,7 @@ export class RoleBindingDialog extends React.Component { } render() { - const { ...dialogProps } = this.props; + const { closeRoleBindingDialog, roleBindingStore, state, ...dialogProps } = this.props; const [action, nextLabel] = this.isEditing ? ["Edit", "Update"] : ["Add", "Create"]; const disableNext = !this.selectedRoleRef || !this.selectedBindings.length || !this.bindingNamespace || !this.bindingName; @@ -268,8 +281,8 @@ export class RoleBindingDialog extends React.Component { @@ -279,7 +292,7 @@ export class RoleBindingDialog extends React.Component { {`${action} RoleBinding`} )} - done={RoleBindingDialog.close} + done={closeRoleBindingDialog} > { ); } } + +export const RoleBindingDialog = withInjectables(NonInjectedRoleBindingDialog, { + getProps: (di, props) => ({ + ...props, + roleBindingStore: di.inject(roleBindingStoreInjectable), + state: di.inject(roleBindingDialogStateInjectable), + closeRoleBindingDialog: di.inject(closeRoleBindingDialogInjectable), + showDetails: di.inject(showDetailsInjectable), + clusterRoleStore: di.inject(clusterRoleStoreInjectable), + roleStore: di.inject(roleStoreInjectable), + serviceAccountStore: di.inject(serviceAccountStoreInjectable), + roleApi: di.inject(roleApiInjectable), + }), +}); diff --git a/src/renderer/components/+user-management/+role-bindings/index.ts b/src/renderer/components/+user-management/+role-bindings/index.ts index 0dd1376b97..59d5a476cb 100644 --- a/src/renderer/components/+user-management/+role-bindings/index.ts +++ b/src/renderer/components/+user-management/+role-bindings/index.ts @@ -4,4 +4,4 @@ */ export * from "./view"; export * from "./details"; -export * from "./dialog"; +export * from "./dialog/view"; diff --git a/src/renderer/components/+user-management/+role-bindings/view.tsx b/src/renderer/components/+user-management/+role-bindings/view.tsx index 58afc14a5e..3f5e458453 100644 --- a/src/renderer/components/+user-management/+role-bindings/view.tsx +++ b/src/renderer/components/+user-management/+role-bindings/view.tsx @@ -8,7 +8,7 @@ import { observer } from "mobx-react"; import React from "react"; import { KubeObjectListLayout } from "../../kube-object-list-layout"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; -import { RoleBindingDialog } from "./dialog"; +import { RoleBindingDialog } from "./dialog/view"; import { SiblingsInTabLayout } from "../../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../../kube-object/age"; import type { RoleStore } from "../+roles/store"; @@ -23,6 +23,8 @@ import filterByNamespaceInjectable from "../../+namespaces/namespace-select-filt import roleBindingStoreInjectable from "./store.injectable"; import roleStoreInjectable from "../+roles/store.injectable"; import serviceAccountStoreInjectable from "../+service-accounts/store.injectable"; +import type { OpenRoleBindingDialog } from "./dialog/open.injectable"; +import openRoleBindingDialogInjectable from "./dialog/open.injectable"; enum columnId { name = "name", @@ -37,6 +39,7 @@ interface Dependencies { clusterRoleStore: ClusterRoleStore; serviceAccountStore: ServiceAccountStore; filterByNamespace: FilterByNamespace; + openRoleBindingDialog: OpenRoleBindingDialog; } @observer @@ -48,6 +51,7 @@ class NonInjectedRoleBindings extends React.Component { roleStore, serviceAccountStore, filterByNamespace, + openRoleBindingDialog, } = this.props; return ( @@ -90,7 +94,7 @@ class NonInjectedRoleBindings extends React.Component { , ]} addRemoveButtons={{ - onAdd: () => RoleBindingDialog.open(), + onAdd: () => openRoleBindingDialog(), addTooltip: "Create new RoleBinding", }} /> @@ -108,5 +112,6 @@ export const RoleBindings = withInjectables(NonInjectedRoleBinding roleBindingStore: di.inject(roleBindingStoreInjectable), roleStore: di.inject(roleStoreInjectable), serviceAccountStore: di.inject(serviceAccountStoreInjectable), + openRoleBindingDialog: di.inject(openRoleBindingDialogInjectable), }), });