Skip to content

bugfix: add steps to delete flows caused by new constraints #3045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 30, 2024
17 changes: 14 additions & 3 deletions src/backend/base/langflow/api/v1/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import zipfile

from fastapi.responses import StreamingResponse
from langflow.services.database.models.transactions.crud import get_transactions_by_flow_id
from langflow.services.database.models.vertex_builds.crud import get_vertex_builds_by_flow_id
import orjson
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from fastapi.encoders import jsonable_encoder
Expand Down Expand Up @@ -334,11 +336,20 @@ async def delete_multiple_flows(

"""
try:
deleted_flows = db.exec(select(Flow).where(col(Flow.id).in_(flow_ids)).where(Flow.user_id == user.id)).all()
for flow in deleted_flows:
flows_to_delete = db.exec(select(Flow).where(col(Flow.id).in_(flow_ids)).where(Flow.user_id == user.id)).all()
for flow in flows_to_delete:
transactions_to_delete = get_transactions_by_flow_id(db, flow.id)
for transaction in transactions_to_delete:
db.delete(transaction)

builds_to_delete = get_vertex_builds_by_flow_id(db, flow.id)
for build in builds_to_delete:
db.delete(build)

db.delete(flow)

db.commit()
return {"deleted": len(deleted_flows)}
return {"deleted": len(flows_to_delete)}
except Exception as exc:
logger.exception(exc)
raise HTTPException(status_code=500, detail=str(exc)) from exc
Expand Down
32 changes: 32 additions & 0 deletions src/frontend/src/controllers/API/queries/flows/use-delete-flows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useMutationFunctionType } from "@/types/api";
import { UseMutationResult } from "@tanstack/react-query";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";

interface IDeleteFlows {
flow_ids: string[];
}

export const useDeleteFlows: useMutationFunctionType<
undefined,
IDeleteFlows
> = (options?) => {
const { mutate } = UseRequestProcessor();

const deleteFlowsFn = async (payload: IDeleteFlows): Promise<any> => {
const response = await api.delete<any>(`${getURL("FLOWS")}/`, {
data: payload.flow_ids,
});

return response.data;
};

const mutation: UseMutationResult<IDeleteFlows, any, IDeleteFlows> = mutate(
["useLoginUser"],
deleteFlowsFn,
options,
);

return mutation;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { usePostDownloadMultipleFlows } from "@/controllers/API/queries/flows";
import { useDeleteFlows } from "@/controllers/API/queries/flows/use-delete-flows";
import { useEffect, useMemo, useState } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useLocation } from "react-router-dom";
Expand Down Expand Up @@ -188,19 +189,36 @@ export default function ComponentsComponent({
handleExport,
);

const { handleDeleteMultiple } = useDeleteMultipleFlows(
selectedFlowsComponentsCards,
removeFlow,
resetFilter,
getFoldersApi,
folderId,
myCollectionId!,
getFolderById,
setSuccessData,
setErrorData,
setAllFlows,
setSelectedFolder,
);
const { mutate: mutateDeleteMultipleFlows } = useDeleteFlows();

const handleDeleteMultiple = () => {
mutateDeleteMultipleFlows(
{
flow_ids: selectedFlowsComponentsCards,
},
{
onSuccess: () => {
setAllFlows([]);
setSelectedFolder(null);

resetFilter();
getFoldersApi(true);
if (!folderId || folderId === myCollectionId) {
getFolderById(folderId ? folderId : myCollectionId);
}
setSuccessData({
title: "Selected items deleted successfully",
});
},
onError: () => {
setErrorData({
title: "Error deleting items",
list: ["Please try again"],
});
},
},
);
};

useSelectedFlows(entireFormValues, setSelectedFlowsComponentsCards);

Expand Down
35 changes: 35 additions & 0 deletions src/frontend/tests/end-to-end/actionsMainPage-shard-1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,41 @@ test("select and delete all", async ({ page }) => {
await page.getByText("Selected items deleted successfully").isVisible();
});

test("select and delete a flow", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);

let modalCount = 0;
try {
const modalTitleElement = await page?.getByTestId("modal-title");
if (modalTitleElement) {
modalCount = await modalTitleElement.count();
}
} catch (error) {
modalCount = 0;
}

while (modalCount === 0) {
await page.getByText("New Project", { exact: true }).click();
await page.waitForTimeout(5000);
modalCount = await page.getByTestId("modal-title")?.count();
}
await page.getByRole("heading", { name: "Basic Prompting" }).click();

await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
timeout: 100000,
});

await page.getByTestId("icon-ChevronLeft").first().click();

await page.getByTestId("checkbox-component").first().click();
await page.getByTestId("icon-Trash2").click();
await page.getByText("Delete").last().click();

await page.waitForTimeout(1000);
await page.getByText("Selected items deleted successfully").isVisible();
});

test("search flows", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
Expand Down
Loading