Skip to content

Commit

Permalink
feat: add option to re-run a playbook run with the same parameters
Browse files Browse the repository at this point in the history
Fixes #1738

chore: direct to playbook run on success submission

fix: use modal to show playbook re-run
  • Loading branch information
mainawycliffe authored and moshloop committed Aug 1, 2024
1 parent c0e77fe commit bd4f7a0
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 42 deletions.
9 changes: 8 additions & 1 deletion src/api/query-hooks/playbooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ export function useGetPlaybookSpecsDetails(

export function useSubmitPlaybookRunMutation(
options: Omit<
UseMutationOptions<any, Error, SubmitPlaybookRunFormValues>,
UseMutationOptions<
{
run_id: string;
starts_at: string;
},
Error,
SubmitPlaybookRunFormValues
>,
"mutationFn"
> = {}
) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Playbooks/PlaybookSpecModalTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PlaybookSpec } from "../../api/types/playbooks";
import { Icon } from "../../ui/Icons/Icon";

type PlaybookSpecModalTitleProps = {
playbookSpec?: PlaybookSpec;
playbookSpec?: Pick<PlaybookSpec, "spec" | "name">;
defaultTitle?: string;
};

Expand Down
12 changes: 11 additions & 1 deletion src/components/Playbooks/Runs/Actions/PlaybookRunsActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useMemo, useState } from "react";
import { Link } from "react-router-dom";
import PlaybookSpecIcon from "../../Settings/PlaybookSpecIcon";
import { getResourceForRun } from "../services";
import ReRunPlaybookWithParamsButton from "../Submit/ReRunPlaybookWithParamsButton";
import { PlaybookStatusDescription } from "./../PlaybookRunsStatus";
import PlaybookRunActionFetch from "./PlaybookRunActionFetch";
import PlaybookRunsActionItem from "./PlaybookRunsActionItem";
Expand Down Expand Up @@ -43,7 +44,7 @@ export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) {

return (
<div className="flex flex-1 flex-col gap-6">
<div className="flex flex-col px-4 py-2">
<div className="flex flex-row px-4 py-2">
<div className="flex w-full flex-wrap gap-4 lg:w-auto">
<VerticalDescription
label="Playbook"
Expand Down Expand Up @@ -111,6 +112,15 @@ export default function PlaybookRunsActions({ data }: PlaybookRunActionsProps) {
/>
)}
</div>
<div className="ml-auto flex h-auto flex-col">
<ReRunPlaybookWithParamsButton
playbook={data.playbooks!}
params={data.parameters}
checkId={data.check_id}
componentId={data.component_id}
configId={data.config_id}
/>
</div>
</div>
<div className="flex h-full flex-col">
<div className="flex h-full flex-row">
Expand Down
9 changes: 6 additions & 3 deletions src/components/Playbooks/Runs/Submit/PlaybookRunParams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function parseCodeDefaultValue(parameter: PlaybookParam) {

type PlaybookRunParamsProps = {
isResourceRequired: boolean;
playbook: PlaybookSpec;
playbook: Pick<PlaybookSpec, "spec">;
};

export default function PlaybookRunParams({
Expand Down Expand Up @@ -88,13 +88,16 @@ export default function PlaybookRunParams({
useEffect(() => {
if (data?.params && data.params.length > 0) {
data.params.forEach((param) => {
if (param.default !== undefined) {
// We don't want to override form values if they are already set by user
// action, like for instance when re-running a playbook with the same
// parameters, we don't want to set the default values again
if (param.default !== undefined && !values.params?.[param.name]) {
const defaultValue = parseCodeDefaultValue(param);
setFieldValue(`params.${param.name}`, defaultValue);
}
});
}
}, [data, setFieldValue]);
}, [data, setFieldValue, values.params]);

// if no resource is selected, show a message and hide the parameters
if (!componentId && !configId && !checkId && isResourceRequired) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { PlaybookSpec } from "@flanksource-ui/api/types/playbooks";
import { Button } from "@flanksource-ui/ui/Buttons/Button";
import { useState } from "react";
import SubmitPlaybookRunForm from "./SubmitPlaybookRunForm";

type ReRunPlaybookWithParamsProps = {
params?: Record<string, unknown>;
playbook: Pick<PlaybookSpec, "id" | "spec" | "name">;
componentId?: string;
configId?: string;
checkId?: string;
};

export default function ReRunPlaybookWithParamsButton({
params,
playbook,
componentId,
configId,
checkId
}: ReRunPlaybookWithParamsProps) {
const [isOpened, setIsOpened] = useState(false);

return (
<>
<Button
onClick={() => setIsOpened(true)}
className="btn-white min-w-max space-x-2"
title="Run playbook again with the same parameters"
disabled={isOpened}
>
Re-run playbook
</Button>

<SubmitPlaybookRunForm
isOpen={isOpened}
onClose={() => setIsOpened(false)}
playbook={playbook}
componentId={componentId}
checkId={checkId}
configId={configId}
params={params}
/>
</>
);
}
57 changes: 29 additions & 28 deletions src/components/Playbooks/Runs/Submit/SubmitPlaybookRunForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { AxiosError } from "axios";
import { Form, Formik } from "formik";
import { atom, useAtom } from "jotai";
import { useMemo } from "react";
import { Link } from "react-router-dom";
import { toastError, toastSuccess } from "../../../Toast/toast";
import { useNavigate } from "react-router-dom";
import { toastError } from "../../../Toast/toast";
import PlaybookSpecModalTitle from "../../PlaybookSpecModalTitle";
import { getResourceForRun } from "../services";
import PlaybookRunParams from "./PlaybookRunParams";
Expand All @@ -33,53 +33,53 @@ export type SubmitPlaybookRunFormValues = {

type Props = {
isOpen: boolean;
onClose: () => void;
playbook: PlaybookSpec;
onClose?: () => void;
playbook: Pick<PlaybookSpec, "id" | "spec" | "name">;
checkId?: string;
componentId?: string;
configId?: string;
params?: Record<string, any>;
};

export default function SubmitPlaybookRunForm({
isOpen,
onClose,
onClose = () => {},
playbook,
checkId,
componentId,
configId
configId,
params = {}
}: Props) {
const [modalSize, setModalSize] = useAtom(
submitPlaybookRunFormModalSizesAtom
);

const navigate = useNavigate();

const initialValues: Partial<SubmitPlaybookRunFormValues> = useMemo(() => {
return {
id: playbook.id,
component_id: componentId,
check_id: checkId,
config_id: configId,
params: {}
params: {
...params
}
};
}, [checkId, componentId, configId, playbook.id]);
}, [checkId, componentId, configId, params, playbook.id]);

const { mutate: submitPlaybookRun } = useSubmitPlaybookRunMutation({
onSuccess: (run) => {
toastSuccess(
<>
<Link className="link mr-2" to={`/playbooks/runs/${run.run_id}`}>
Playbook Run
</Link>{" "}
submitted successfully
</>
);
onClose();
},
onError: (error) => {
const message =
((error as AxiosError).response?.data as any)?.error ?? error.message;
toastError(message ?? error.message);
const { mutate: submitPlaybookRun, isLoading } = useSubmitPlaybookRunMutation(
{
onSuccess: (run) => {
navigate(`/playbooks/runs/${run.run_id}`);
},
onError: (error) => {
const message =
((error as AxiosError).response?.data as any)?.error ?? error.message;
toastError(message ?? error.message);
}
}
});
);

const resource = getResourceForRun(initialValues);

Expand Down Expand Up @@ -146,11 +146,12 @@ export default function SubmitPlaybookRunForm({

<div className="flex justify-end space-x-2 rounded-b bg-slate-50 p-4 ring-1 ring-black/5">
<Button
disabled={values.id === undefined || !isValid}
text="Run"
disabled={values.id === undefined || !isValid || isLoading}
role="button"
type="submit"
/>
>
{isLoading ? "Running..." : "Run"}
</Button>
</div>
</Form>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AuthContext } from "@flanksource-ui/context";
import { UserAccessStateContextProvider } from "@flanksource-ui/context/UserAccessContext/UserAccessContext";
import { render, screen, waitFor } from "@flanksource-ui/test-utils";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { rest } from "msw";
import { setupServer } from "msw/node";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fireEvent, render, screen, waitFor } from "@flanksource-ui/test-utils";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { rest } from "msw";
import { setupServer } from "msw/node";
Expand Down Expand Up @@ -111,12 +111,10 @@ describe("SubmitPlaybookRunForm", () => {
});

it("should submit the form when the submit button is clicked", async () => {
const closeFn = jest.fn();
render(
<QueryClientProvider client={queryClient}>
<SubmitPlaybookRunForm
isOpen={true}
onClose={closeFn}
playbook={playbook}
componentId={componentId}
checkId={checkId}
Expand All @@ -138,9 +136,5 @@ describe("SubmitPlaybookRunForm", () => {
const btn = screen.getByRole("button", { name: /Run/i });

userEvent.click(btn);

await waitFor(() => {
expect(closeFn).toHaveBeenCalled();
});
});
});

0 comments on commit bd4f7a0

Please sign in to comment.