diff --git a/spa/src/pages/Connections/GHCloudConnections/index.tsx b/spa/src/pages/Connections/GHCloudConnections/index.tsx index 1983b3442..ceef92608 100644 --- a/spa/src/pages/Connections/GHCloudConnections/index.tsx +++ b/spa/src/pages/Connections/GHCloudConnections/index.tsx @@ -19,26 +19,30 @@ const containerStyles = xcss({ type GitHubCloudConnectionsProps = { ghCloudSubscriptions: GhCloudSubscriptions; + refetch: () => void; }; const GitHubCloudConnections = ({ ghCloudSubscriptions, + refetch, }: GitHubCloudConnectionsProps) => { const [isModalOpened, setIsModalOpened] = useState(false); const [subscriptionForModal, setSubscriptionForModal] = useState(undefined); const [selectedModal, setSelectedModal] = useState("BACKFILL"); - const openedModal = () => { + const openedModal = (refetch: () => void) => { switch (selectedModal) { case "BACKFILL": return (); case "DISCONNECT_SUBSCRIPTION": return ; // TODO: Create modals for GHE later case "DISCONNECT_SERVER": @@ -64,7 +68,7 @@ const GitHubCloudConnections = ({ { - isModalOpened && subscriptionForModal && openedModal() + isModalOpened && subscriptionForModal && openedModal(refetch) } diff --git a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.test.tsx b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.test.tsx index b28e1cadd..9dfe713a3 100644 --- a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.test.tsx +++ b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.test.tsx @@ -3,6 +3,9 @@ import { render, screen } from "@testing-library/react"; import { BrowserRouter } from "react-router-dom"; import userEvent from "@testing-library/user-event"; import DisconnectSubscriptionModal from "./DisconnectSubscriptionModal"; +import SubscriptionManager from "../../../services/subscription-manager"; + +jest.mock("../../../services/subscription-manager"); const sampleSubscription = { app_slug: "string", @@ -29,11 +32,12 @@ const sampleSubscription = { html_url: "html_url" }; const isModalOpened = jest.fn(); +const refetch = jest.fn(); -test("Disconnect subscription Modal", async () => { +test("Clicking cancel in disconnect subscription Modal", async () => { render( - + ); @@ -43,4 +47,27 @@ test("Disconnect subscription Modal", async () => { await userEvent.click(screen.getByText("Cancel")); expect(isModalOpened).toBeCalled(); + expect(refetch).not.toBeCalled(); +}); + +test("Clicking Disconnect in disconnect subscription Modal", async () => { + jest.mocked(SubscriptionManager).deleteSubscription = jest.fn().mockReturnValue(Promise.resolve(true)); + + render( + + + + ); + + expect(screen.getByText("Disconnect sample?")).toBeInTheDocument(); + const text = screen.getByTestId("disconnect-content"); + expect(text.textContent).toBe("Are you sure you want to disconnect your organization sample? This means that you will have to redo the backfill of historical data if you ever want to reconnect"); + + await userEvent.click(screen.getByText("Disconnect")); + /** + * Called twice, once when the loading is set to true, + * and later after getting the response from the API request + */ + expect(isModalOpened).toBeCalledTimes(2); + expect(refetch).toBeCalled(); }); diff --git a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx index c0c3f88e4..05c12cbc6 100644 --- a/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx +++ b/spa/src/pages/Connections/Modals/DisconnectSubscriptionModal.tsx @@ -1,18 +1,30 @@ +import { useState } from "react"; +import { AxiosError } from "axios"; import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle } from "@atlaskit/modal-dialog"; -import Button from "@atlaskit/button"; +import Button, { LoadingButton } from "@atlaskit/button"; import { SuccessfulConnection } from "../../../../../src/rest-interfaces"; +import SubscriptionManager from "../../../services/subscription-manager"; /** * NOTE: While testing in dev mode, please disable the React.StrictMode first, * otherwise this modal won't show up. */ -const DisconnectSubscriptionModal = ({ subscription, setIsModalOpened }: { +const DisconnectSubscriptionModal = ({ subscription, setIsModalOpened, refetch }: { subscription: SuccessfulConnection, - setIsModalOpened: (x: boolean) => void + setIsModalOpened: (x: boolean) => void, + refetch: () => void }) => { - const disconnect = () => { - // TODO: API call to disconnect this subscription - console.log("Disconnect", subscription.account.login); + const [isLoading, setIsLoading] = useState(false); + + const disconnect = async () => { + setIsLoading(true); + const response: boolean | AxiosError = await SubscriptionManager.deleteSubscription(subscription.subscriptionId); + if (response instanceof AxiosError) { + // TODO: Handle the error once we have the designs + console.error("Error", response); + } else { + await refetch(); + } setIsModalOpened(false); }; @@ -31,10 +43,20 @@ const DisconnectSubscriptionModal = ({ subscription, setIsModalOpened }: {

- - + { + isLoading ? + Loading button + : + } diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.test.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.test.tsx index e79e2fd62..4a6959e0f 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.test.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.test.tsx @@ -29,11 +29,12 @@ const sampleSubscription = { html_url: "html_url" }; const isModalOpened = jest.fn(); +const refetch = jest.fn(); test("Restart backfill Modal", async () => { render( - + ); diff --git a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx index f8f4e36e1..959005315 100644 --- a/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx +++ b/spa/src/pages/Connections/Modals/RestartBackfillModal.tsx @@ -10,9 +10,10 @@ import { DatePicker } from "@atlaskit/datetime-picker"; * NOTE: While testing in dev mode, please disable the React.StrictMode first, * otherwise this modal won't show up. */ -const RestartBackfillModal = ({ subscription, setIsModalOpened }: { +const RestartBackfillModal = ({ subscription, setIsModalOpened, refetch }: { subscription: SuccessfulConnection, - setIsModalOpened: (x: boolean) => void + setIsModalOpened: (x: boolean) => void, + refetch: () => void, }) => { const [restartFromDateCheck, setRestartFromDateCheck] = useState(false); const [backfillDate, setBackfillDate] = useState(""); @@ -30,9 +31,10 @@ const RestartBackfillModal = ({ subscription, setIsModalOpened }: { setTimeout(() => setIsDisabled(false), 10); }, []); - const backfill = () => { + const backfill = async () => { // TODO: API call to disconnect this subscription console.log("Backfill for", subscription.account.login, restartFromDateCheck, backfillDate); + await refetch(); setIsModalOpened(false); }; diff --git a/spa/src/pages/Connections/index.tsx b/spa/src/pages/Connections/index.tsx index 905091c9b..6b02c82bd 100644 --- a/spa/src/pages/Connections/index.tsx +++ b/spa/src/pages/Connections/index.tsx @@ -1,29 +1,28 @@ import { useEffect, useState } from "react"; -import ApiRequest from "../../api"; import SyncHeader from "../../components/SyncHeader"; import Step from "../../components/Step"; import { Wrapper } from "../../common/Wrapper"; import GitHubCloudConnections from "./GHCloudConnections"; import GitHubEnterpriseConnections from "./GHEnterpriseConnections"; import { GHSubscriptions } from "../../rest-interfaces"; -import { reportError } from "../../utils"; import SkeletonForLoading from "./SkeletonForLoading"; import { useNavigate } from "react-router-dom"; +import SubscriptionManager from "../../services/subscription-manager"; +import { AxiosError } from "axios"; const Connections = () => { const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); - const [ghSubscriptions, setSubscriptions] = useState(null); + const [subscriptions, setSubscriptions] = useState(null); const fetchGHSubscriptions = async () => { - try { - setIsLoading(true); - const { data } = await ApiRequest.subscriptions.getSubscriptions(); - setSubscriptions(data); - } catch (e) { - reportError(e, { path: "Fetching subscriptions" }); - } finally { - setIsLoading(false); + setIsLoading(true); + const response = await SubscriptionManager.getSubscriptions(); + if (response instanceof AxiosError) { + // TODO: Handle the error once we have the designs + console.error("Error", response); } + setSubscriptions(response as GHSubscriptions); + setIsLoading(false); }; useEffect(() => { fetchGHSubscriptions(); @@ -31,10 +30,10 @@ const Connections = () => { // If there are no connections then go back to the start page useEffect(() => { - if (!ghSubscriptions?.ghCloudSubscriptions && ghSubscriptions?.ghEnterpriseServers && ghSubscriptions.ghEnterpriseServers?.length === 0) { + if (!subscriptions?.ghCloudSubscriptions && subscriptions?.ghEnterpriseServers && subscriptions.ghEnterpriseServers?.length === 0) { navigate("/spa"); } - }, [ghSubscriptions, navigate]); + }, [subscriptions, navigate]); return ( @@ -42,14 +41,14 @@ const Connections = () => { { isLoading ? : <> { - ghSubscriptions?.ghCloudSubscriptions && - + subscriptions?.ghCloudSubscriptions && + } { - ghSubscriptions?.ghEnterpriseServers && ghSubscriptions.ghEnterpriseServers?.length > 0 && - + subscriptions?.ghEnterpriseServers && subscriptions.ghEnterpriseServers?.length > 0 && + } diff --git a/spa/src/services/subscription-manager/index.ts b/spa/src/services/subscription-manager/index.ts new file mode 100644 index 000000000..4c2dcd62c --- /dev/null +++ b/spa/src/services/subscription-manager/index.ts @@ -0,0 +1,47 @@ +import Api from "../../api"; +import { AxiosError } from "axios"; +import { reportError } from "../../utils"; +import { GHSubscriptions } from "../../../../src/rest-interfaces"; + +async function getSubscriptions(): Promise { + try { + const response= await Api.subscriptions.getSubscriptions(); + const status = response.status === 200; + if(!status) { + reportError( + { message: "Response status for getting subscriptions is not 204", status: response.status }, + { path: "getSubscriptions" } + ); + } + + return response.data; + } catch (e: unknown) { + reportError(new Error("Unable to delete subscription", { cause: e }), { path: "getSubscriptions" }); + return e as AxiosError; + } +} + +async function deleteSubscription(subscriptionId: number): Promise { + try { + const response= await Api.subscriptions.deleteSubscription(subscriptionId); + const ret = response.status === 204; + if(!ret) { + reportError( + { message: "Response status for deleting subscription is not 204", status: response.status }, + { path: "deleteSubscription" } + ); + } + + return ret; + } catch (e: unknown) { + reportError(new Error("Unable to delete subscription", { cause: e }), { path: "deleteSubscription" }); + return e as AxiosError; + } +} + +export default { + getSubscriptions, + deleteSubscription, +}; + +