This is an automated email from the ASF dual-hosted git repository.
jialiang pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/frontend-refactor by this push:
new 4e500e6b6a AMBARI-26181: Ambari Admin React Implementation:
De-register Remote Cluster (#3890)
4e500e6b6a is described below
commit 4e500e6b6a928ac7f186cbf68a3e79d2533971d1
Author: Sandeep Kumar <[email protected]>
AuthorDate: Fri Nov 22 11:47:56 2024 +0530
AMBARI-26181: Ambari Admin React Implementation: De-register Remote Cluster
(#3890)
---
.../RemoteClusters/DeregisterRemoteCluster.tsx | 77 +++++++++++++++
.../RemoteClusters/EditRemoteCluster.tsx | 9 +-
.../src/tests/DeregisterRemoteCluster.test.tsx | 104 +++++++++++++++++++++
3 files changed, 183 insertions(+), 7 deletions(-)
diff --git
a/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/DeregisterRemoteCluster.tsx
b/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/DeregisterRemoteCluster.tsx
new file mode 100644
index 0000000000..eaf9478d92
--- /dev/null
+++
b/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/DeregisterRemoteCluster.tsx
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { useState } from "react";
+import { useHistory } from "react-router-dom";
+import { Button } from "react-bootstrap";
+import toast from "react-hot-toast";
+import RemoteClusterApi from "../../../api/remoteCluster";
+import ConfirmationModal from "../../../components/ConfirmationModal";
+
+type PropTypes = {
+ clusterName: string;
+};
+
+export default function DeregisterRemoteCluster({ clusterName }: PropTypes) {
+ const [showConfirmationModal, setShowConfirmationModal] = useState(false);
+ const history = useHistory();
+
+ const handleDeleteCluster = (event: any) => {
+ event.preventDefault();
+ setShowConfirmationModal(true);
+ };
+
+ const handleDeleteClusterConfirmed = async () => {
+ try {
+ await RemoteClusterApi.deregisterRemoteCluster(clusterName);
+ toast.success(
+ <div className="toast-message">
+ De-registered cluster "{clusterName}" successfully!
+ </div>
+ );
+ history.push("/remoteClusters");
+ } catch (error: any) {
+ toast.error(
+ <div className="toast-message">
+ Error while deregistering cluster: {error.message}
+ </div>
+ );
+ } finally {
+ setShowConfirmationModal(false);
+ }
+ };
+
+ return (
+ <>
+ <Button
+ onClick={handleDeleteCluster}
+ variant="danger"
+ className="px-3 rounded-1"
+ size="sm"
+ >
+ DEREGISTER CLUSTER
+ </Button>
+ <ConfirmationModal
+ isOpen={showConfirmationModal}
+ onClose={() => setShowConfirmationModal(false)}
+ modalTitle="Deregister cluster"
+ modalBody={`Are you sure you want to delete cluster "${clusterName}"?
This operation cannot be undone.`}
+ successCallback={handleDeleteClusterConfirmed}
+ />
+ </>
+ );
+}
diff --git
a/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/EditRemoteCluster.tsx
b/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/EditRemoteCluster.tsx
index d93524a2c6..ebf51c9350 100644
---
a/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/EditRemoteCluster.tsx
+++
b/ambari-admin/src/main/resources/ui/ambari-admin/src/screens/ClusterManagement/RemoteClusters/EditRemoteCluster.tsx
@@ -23,6 +23,7 @@ import Spinner from "../../../components/Spinner";
import RemoteClusterApi from "../../../api/remoteCluster";
import DefaultButton from "../../../components/DefaultButton";
import AppContent from "../../../context/AppContext";
+import DeregisterRemoteCluster from "./DeregisterRemoteCluster";
export default function EditRemoteCluster() {
const { clusterName } = useParams() as any;
@@ -149,13 +150,7 @@ export default function EditRemoteCluster() {
<h4 className="mx-2">/</h4>
<h4>{cluster.name}</h4>
</div>
- <Button
- variant="danger"
- className="px-3 rounded-1"
- size="sm"
- >
- DEREGISTER CLUSTER
- </Button>
+ <DeregisterRemoteCluster clusterName={cluster.name} />
</div>
<div>
<Form onSubmit={handleUpdate} className="mt-4">
diff --git
a/ambari-admin/src/main/resources/ui/ambari-admin/src/tests/DeregisterRemoteCluster.test.tsx
b/ambari-admin/src/main/resources/ui/ambari-admin/src/tests/DeregisterRemoteCluster.test.tsx
new file mode 100644
index 0000000000..4156b5cc81
--- /dev/null
+++
b/ambari-admin/src/main/resources/ui/ambari-admin/src/tests/DeregisterRemoteCluster.test.tsx
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, it, expect, vi } from "vitest";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { createMemoryHistory } from "history";
+import { Router } from "react-router-dom";
+import "@testing-library/jest-dom/vitest";
+import RemoteClusterApi from "../api/remoteCluster";
+import DeregisterRemoteCluster from
"../screens/ClusterManagement/RemoteClusters/DeregisterRemoteCluster";
+import AppContent from "../context/AppContext";
+import toast from "react-hot-toast";
+import { get } from "lodash";
+
+const mockClusterName = "TestCluster1";
+const mockContext = {
+ cluster: { cluster_name: mockClusterName },
+ setSelectedOption: () => "RemoteCluster",
+};
+
+const renderDeregisterRemoteCluster = () => {
+ render(
+ <AppContent.Provider value={mockContext}>
+ <Router history={createMemoryHistory()}>
+ <DeregisterRemoteCluster clusterName={mockClusterName} />
+ </Router>
+ </AppContent.Provider>
+ );
+};
+
+let mockToastSuccessMessage: string;
+let mockToastErrorMessage: string;
+
+toast.success = (message) => {
+ mockToastSuccessMessage = message as string;
+ return "";
+};
+
+toast.error = (message) => {
+ mockToastErrorMessage = message as string;
+ return "";
+};
+
+describe("DeregisterRemoteCluster component", () => {
+ it("renders without crashing", () => {
+ renderDeregisterRemoteCluster();
+ });
+
+ it("should open the confirmation modal on clicking DEREGISTER CLUSTER
button", async () => {
+ renderDeregisterRemoteCluster();
+ const deregisterButton = screen.getByText("DEREGISTER CLUSTER");
+ fireEvent.click(deregisterButton);
+ const modalTitle = await screen.findByText("Deregister cluster");
+ expect(modalTitle).toBeInTheDocument();
+ });
+
+ it("should call API and show success toast on clicking Ok button in the
modal", async () => {
+ vi.spyOn(RemoteClusterApi, "deregisterRemoteCluster").mockResolvedValue({
+ status: 200,
+ });
+
+ renderDeregisterRemoteCluster();
+ const deregisterButton = screen.getByText("DEREGISTER CLUSTER");
+ fireEvent.click(deregisterButton);
+ const OkButton = await screen.findByText("OK");
+ fireEvent.click(OkButton);
+
+ await waitFor(() => {
+ expect(get(mockToastSuccessMessage, "props.children", []).join("")).toBe(
+ `De-registered cluster "${mockClusterName}" successfully!`
+ );
+ });
+ });
+
+ it("should show error toast when API call fails", async () => {
+ vi.spyOn(RemoteClusterApi,
'deregisterRemoteCluster').mockRejectedValue(new Error("API call failed"));
+
+ renderDeregisterRemoteCluster();
+ const deregisterButton = screen.getByText("DEREGISTER CLUSTER");
+ fireEvent.click(deregisterButton);
+ const OkButton = await screen.findByText("OK");
+ fireEvent.click(OkButton);
+
+ await waitFor(() => {
+ expect(get(mockToastErrorMessage, "props.children", []).join("")).toBe(
+ "Error while deregistering cluster: API call failed"
+ );
+ });
+ });
+});
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]