From 183a8c28d989765866231cfb6ee88b4bdf102bc9 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 14 May 2025 06:19:34 -0700 Subject: [PATCH] fix: Enhance DB queries with async pagination and improve test reliability (#8040) Co-authored-by: Lucas Oliveira Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/backend/base/langflow/api/v1/projects.py | 4 ++-- src/backend/base/langflow/api/v2/files.py | 6 +++--- .../extended/features/files-page.spec.ts | 21 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/backend/base/langflow/api/v1/projects.py b/src/backend/base/langflow/api/v1/projects.py index ab417140b..5626e61b6 100644 --- a/src/backend/base/langflow/api/v1/projects.py +++ b/src/backend/base/langflow/api/v1/projects.py @@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFil from fastapi.encoders import jsonable_encoder from fastapi.responses import StreamingResponse from fastapi_pagination import Params -from fastapi_pagination.ext.sqlmodel import paginate +from fastapi_pagination.ext.sqlmodel import apaginate from sqlalchemy import or_, update from sqlalchemy.orm import selectinload from sqlmodel import select @@ -152,7 +152,7 @@ async def read_project( stmt = stmt.where(Flow.is_component == False) # noqa: E712 if search: stmt = stmt.where(Flow.name.like(f"%{search}%")) # type: ignore[attr-defined] - paginated_flows = await paginate(session, stmt, params=params) + paginated_flows = await apaginate(session, stmt, params=params) return FolderWithPaginatedFlows(folder=FolderRead.model_validate(project), flows=paginated_flows) diff --git a/src/backend/base/langflow/api/v2/files.py b/src/backend/base/langflow/api/v2/files.py index 2d53b344a..f24d7b96a 100644 --- a/src/backend/base/langflow/api/v2/files.py +++ b/src/backend/base/langflow/api/v2/files.py @@ -11,7 +11,7 @@ from zoneinfo import ZoneInfo from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from fastapi.responses import StreamingResponse -from sqlmodel import String, cast, select +from sqlmodel import String, cast, col, select from langflow.api.schemas import UploadFileResponse from langflow.api.utils import CurrentActiveUser, DbSession @@ -173,7 +173,7 @@ async def delete_files_batch( """Delete multiple files by their IDs.""" try: # Fetch all files from the DB - stmt = select(UserFile).where(UserFile.id in file_ids, UserFile.user_id == current_user.id) + stmt = select(UserFile).where(col(UserFile.id).in_(file_ids), col(UserFile.user_id) == current_user.id) results = await session.exec(stmt) files = results.all() @@ -206,7 +206,7 @@ async def download_files_batch( """Download multiple files as a zip file by their IDs.""" try: # Fetch all files from the DB - stmt = select(UserFile).where(UserFile.id in file_ids, UserFile.user_id == current_user.id) + stmt = select(UserFile).where(col(UserFile.id).in_(file_ids), col(UserFile.user_id) == current_user.id) results = await session.exec(stmt) files = results.all() diff --git a/src/frontend/tests/extended/features/files-page.spec.ts b/src/frontend/tests/extended/features/files-page.spec.ts index bd6f3121a..3f1c1a5f4 100644 --- a/src/frontend/tests/extended/features/files-page.spec.ts +++ b/src/frontend/tests/extended/features/files-page.spec.ts @@ -8,7 +8,7 @@ import { generateRandomFilename } from "../../utils/generate-filename"; // Configure tests to run serially with a delay between each test test( "should navigate to files page and show empty state", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { await awaitBootstrapTest(page, { skipModal: true }); @@ -49,7 +49,7 @@ test( test( "should upload file using upload button", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { const fileName = generateRandomFilename(); const testFilePath = path.join(__dirname, "../../assets/test-file.txt"); @@ -94,7 +94,7 @@ test( test( "should upload file using drag and drop", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { const fileName = generateRandomFilename(); @@ -150,7 +150,7 @@ test( test( "should upload multiple files with different types", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { const fileNames = { txt: generateRandomFilename(), @@ -222,7 +222,7 @@ test( test( "should search uploaded files", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { const fileNames = { txt: generateRandomFilename(), @@ -321,7 +321,7 @@ test( test( "should handle bulk actions for multiple files", - { tag: ["@release", "@files"] }, + { tag: ["@release", "@components"] }, async ({ page }) => { const fileNames = { txt: generateRandomFilename(), @@ -421,13 +421,13 @@ test( const download = await downloadPromise; // Verify the download was initiated - expect(download).toBeTruthy(); + await expect(download).toBeTruthy(); // Check for success message const downloadSuccessMessage = await page.getByText( /Files? downloaded successfully/, ); - expect(downloadSuccessMessage).toBeTruthy(); + await expect(downloadSuccessMessage).toBeTruthy(); // Select both files (checkbox on the grid) @@ -453,13 +453,14 @@ test( const deleteSuccessMessage = await page.getByText( "Files deleted successfully", ); - expect(deleteSuccessMessage).toBeTruthy(); + await expect(deleteSuccessMessage).toBeTruthy(); + await page.waitForTimeout(500); // Verify the deleted files are no longer visible const remainingFileCount = (await page.getByText(fileNames.py + ".py").count()) + (await page.getByText(fileNames.txt + ".txt").count()) + (await page.getByText(fileNames.json + ".json").count()); - expect(remainingFileCount).toBe(1); + await expect(remainingFileCount).toBe(1); }, );