diff --git a/src/backend/base/langflow/api/v1/published_flows.py b/src/backend/base/langflow/api/v1/published_flows.py index f93a8224d7f1..44a4633a1a92 100644 --- a/src/backend/base/langflow/api/v1/published_flows.py +++ b/src/backend/base/langflow/api/v1/published_flows.py @@ -569,9 +569,9 @@ async def get_flow_versions( current_user: CurrentActiveUser, ): """ - Get all published versions for a flow (for version dropdown). - Returns versions ordered by published_at ascending (v1, v2, v3...). - Active version is marked with active=True. + Get all flow versions for a flow (for version dropdown). + Returns versions from flow_version table ordered by created_at descending. + Uses status_name from flow_status table to show version status. No permission check - any authenticated user can view versions. """ # Check if flow exists @@ -582,10 +582,13 @@ async def get_flow_versions( detail="Flow not found", ) - # Get all versions for this original flow - query = select(PublishedFlowVersion).where( - PublishedFlowVersion.flow_id_cloned_from == flow_id - ).order_by(PublishedFlowVersion.published_at.asc()) # Oldest first (v1, v2, v3...) + # Get all versions for this original flow from flow_version table + query = ( + select(FlowVersion) + .where(FlowVersion.original_flow_id == flow_id) + .options(joinedload(FlowVersion.status)) + .order_by(FlowVersion.created_at.desc()) # Newest first + ) result = await session.exec(query) versions = result.all() @@ -593,16 +596,36 @@ async def get_flow_versions( if not versions: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="No published versions found for this flow", + detail="No versions found for this flow", ) - return [PublishedFlowVersionRead.model_validate(v, from_attributes=True) for v in versions] + # Convert FlowVersion to PublishedFlowVersionRead format + return [ + PublishedFlowVersionRead( + id=str(v.id), # UUID to string + version=v.version, + flow_id_cloned_to=str(v.version_flow_id) if v.version_flow_id else None, + flow_id_cloned_from=str(v.original_flow_id), + published_flow_id=None, # Not applicable for flow_version + flow_name=v.title or "", + flow_icon=v.agent_logo, + description=v.description, + tags=v.tags, + active=(v.status.status_name == FlowStatusEnum.PUBLISHED.value if v.status else False), + drafted=False, # Not used with flow_version + published_by=str(v.published_by) if v.published_by else str(v.submitted_by), + published_at=v.published_at.isoformat() if v.published_at else v.created_at.isoformat(), + created_at=v.created_at.isoformat(), + status_name=v.status.status_name if v.status else None, # Add status name + ) + for v in versions + ] @router.post("/revert/{flow_id}/{version_id}", response_model=RevertToVersionResponse, status_code=status.HTTP_200_OK) async def revert_to_version( flow_id: UUID, - version_id: int, + version_id: str, # Changed from int to str (UUID) session: DbSession, current_user: CurrentActiveUser, ): @@ -610,10 +633,20 @@ async def revert_to_version( Revert original flow to a specific version by cloning version's data. This REPLACES the original flow's data with the selected version's data. User can then edit and publish as a new version. + Uses flow_version table instead of published_flow_version. No permission check - any authenticated user can revert. """ - # Get the version record - version = await session.get(PublishedFlowVersion, version_id) + # Convert version_id string to UUID + try: + version_uuid = UUID(version_id) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid version ID format", + ) + + # Get the version record from flow_version table + version = await session.get(FlowVersion, version_uuid) if not version: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, @@ -621,7 +654,7 @@ async def revert_to_version( ) # Validate version belongs to this flow - if version.flow_id_cloned_from != flow_id: + if version.original_flow_id != flow_id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Version does not belong to this flow", @@ -636,7 +669,13 @@ async def revert_to_version( ) # Get the versioned flow data - versioned_flow = await session.get(Flow, version.flow_id_cloned_to) + if not version.version_flow_id: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Versioned flow data not found", + ) + + versioned_flow = await session.get(Flow, version.version_flow_id) if not versioned_flow: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, @@ -652,27 +691,16 @@ async def revert_to_version( session.add(original_flow) - # Update drafted flags: set all versions to drafted=False for this flow - await session.exec( - update(PublishedFlowVersion) - .where(PublishedFlowVersion.flow_id_cloned_from == flow_id) - .values(drafted=False) - ) - - # Set the reverted version as drafted - version.drafted = True - session.add(version) - await session.commit() await session.refresh(original_flow) - logger.info(f"Reverted flow {flow_id} to version {version.version} (version_id: {version_id}), marked as drafted") + logger.info(f"Reverted flow {flow_id} to version {version.version} (version_id: {version_id})") return RevertToVersionResponse( message=f"Successfully reverted to version {version.version}", version=version.version, flow_id=flow_id, - cloned_flow_id=version.flow_id_cloned_to, + cloned_flow_id=version.version_flow_id, ) diff --git a/src/backend/base/langflow/services/database/models/published_flow_version/model.py b/src/backend/base/langflow/services/database/models/published_flow_version/model.py index fdb0890217ee..138de5bef133 100644 --- a/src/backend/base/langflow/services/database/models/published_flow_version/model.py +++ b/src/backend/base/langflow/services/database/models/published_flow_version/model.py @@ -106,13 +106,14 @@ class PublishedFlowVersion(PublishedFlowVersionBase, table=True): # type: ignor class PublishedFlowVersionRead(PublishedFlowVersionBase): """Schema for reading published flow version.""" - id: int - flow_id_cloned_to: UUID + id: int | str # int for published_flow_version, str (UUID) for flow_version + flow_id_cloned_to: UUID | None flow_id_cloned_from: UUID - published_flow_id: UUID + published_flow_id: UUID | None published_by: UUID created_at: datetime drafted: bool + status_name: str | None = None # Status from flow_status table (when using flow_version) class RevertToVersionResponse(SQLModel): diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/version-dropdown.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/version-dropdown.tsx index 2297b6943e5f..f141323f9562 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/version-dropdown.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/version-dropdown.tsx @@ -119,7 +119,7 @@ export default function VersionDropdown({
{version.version} - {version.active && ( + {version.status_name === "Published" && ( Published @@ -129,13 +129,6 @@ export default function VersionDropdown({ {moment(version.published_at).fromNow()}
- - {version.drafted && ( - - )} ))} diff --git a/src/frontend/src/controllers/API/queries/flow-versions/use-cancel-submission.ts b/src/frontend/src/controllers/API/queries/flow-versions/use-cancel-submission.ts new file mode 100644 index 000000000000..94dd45ae06e5 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/flow-versions/use-cancel-submission.ts @@ -0,0 +1,48 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { api } from "../../api"; +import { getURL } from "../../helpers/constants"; +import type { FlowVersionRead } from "./use-get-pending-reviews"; + +export const useCancelSubmission = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (versionId: string) => { + const response = await api.post( + `${getURL("FLOW_VERSIONS")}/cancel/${versionId}` + ); + return response.data; + }, + onSuccess: async () => { + // Invalidate all relevant queries + // Use refetchType: "all" to ensure immediate refetch of all matching queries + await queryClient.invalidateQueries({ + queryKey: ["pending-reviews"], + refetchType: "all" + }); + await queryClient.invalidateQueries({ + queryKey: ["flow-versions-by-status", "Submitted"], + refetchType: "all" + }); + await queryClient.invalidateQueries({ + queryKey: ["flow-versions-by-status", "Draft"], + refetchType: "all" + }); + await queryClient.invalidateQueries({ + queryKey: ["my-submissions"], + refetchType: "all" + }); + // Invalidate flow-latest-status to update toolbar button state + await queryClient.invalidateQueries({ + queryKey: ["flow-latest-status"], + refetchType: "all" + }); + // Invalidate flows to update the locked state (flow is unlocked on cancellation) + // Force refetch to ensure the flow locked state is updated immediately + await queryClient.refetchQueries({ + queryKey: ["useGetRefreshFlowsQuery"], + type: "all" + }); + }, + }); +}; diff --git a/src/frontend/src/controllers/API/queries/published-flows/use-get-flow-versions.ts b/src/frontend/src/controllers/API/queries/published-flows/use-get-flow-versions.ts index 04487798fde3..2daa7058a2d4 100644 --- a/src/frontend/src/controllers/API/queries/published-flows/use-get-flow-versions.ts +++ b/src/frontend/src/controllers/API/queries/published-flows/use-get-flow-versions.ts @@ -3,11 +3,11 @@ import { api } from "../../api"; import { getURL } from "../../helpers/constants"; export interface PublishedFlowVersion { - id: number; + id: number | string; // number for old published_flow_version, string (UUID) for flow_version version: string; - flow_id_cloned_to: string; + flow_id_cloned_to: string | null; flow_id_cloned_from: string; - published_flow_id: string; + published_flow_id: string | null; flow_name: string; flow_icon: string | null; description: string | null; @@ -17,6 +17,7 @@ export interface PublishedFlowVersion { published_by: string; published_at: string; created_at: string; + status_name?: string | null; // Status from flow_status table (Published, Approved, etc.) } export const useGetFlowVersions = (flowId: string | undefined) => { diff --git a/src/frontend/src/controllers/API/queries/published-flows/use-revert-to-version.ts b/src/frontend/src/controllers/API/queries/published-flows/use-revert-to-version.ts index 03e17a4319df..2ab19ae00d09 100644 --- a/src/frontend/src/controllers/API/queries/published-flows/use-revert-to-version.ts +++ b/src/frontend/src/controllers/API/queries/published-flows/use-revert-to-version.ts @@ -5,7 +5,7 @@ import useAlertStore from "@/stores/alertStore"; export interface RevertToVersionParams { flowId: string; - versionId: number; + versionId: number | string; // number for old published_flow_version, string (UUID) for flow_version } export interface RevertToVersionResponse {