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]

Reply via email to