diff --git a/src/backend/base/langflow/api/v1/projects.py b/src/backend/base/langflow/api/v1/projects.py index f987434f171f..0e0f9d07736c 100644 --- a/src/backend/base/langflow/api/v1/projects.py +++ b/src/backend/base/langflow/api/v1/projects.py @@ -268,7 +268,7 @@ async def read_project( try: # Check if pagination is explicitly requested by the user (both page and size provided) if page is not None and size is not None: - stmt = select(Flow).where(Flow.folder_id == project_id) + stmt = select(Flow).where(Flow.folder_id == project_id, Flow.user_id == current_user.id) if Flow.updated_at is not None: stmt = stmt.order_by(Flow.updated_at.desc()) # type: ignore[attr-defined] diff --git a/src/backend/base/langflow/initial_setup/setup.py b/src/backend/base/langflow/initial_setup/setup.py index 1d91ea3c5bca..d3dcbc6e5d7b 100644 --- a/src/backend/base/langflow/initial_setup/setup.py +++ b/src/backend/base/langflow/initial_setup/setup.py @@ -62,10 +62,9 @@ def update_projects_components_with_latest_component_versions(project_data, all_types_dict): # Flatten the all_types_dict for easy access - all_types_dict_flat = {} - for category in all_types_dict.values(): - for key, component in category.items(): - all_types_dict_flat[key] = component + all_types_dict_flat = { + key: component for category in all_types_dict.values() for key, component in category.items() + } node_changes_log = defaultdict(list) project_data_copy = deepcopy(project_data) diff --git a/src/backend/tests/unit/api/v1/test_projects.py b/src/backend/tests/unit/api/v1/test_projects.py index e18048ea1ab2..09ccc2c6675e 100644 --- a/src/backend/tests/unit/api/v1/test_projects.py +++ b/src/backend/tests/unit/api/v1/test_projects.py @@ -1545,6 +1545,49 @@ async def test_read_project_error_handling_consistency(self, client: AsyncClient f"Error message should mention 'not found' for params: {params}" ) + async def test_read_project_paginated_flows_match_current_user_visibility( + self, client: AsyncClient, logged_in_headers, basic_case + ): + project_response = await client.post("api/v1/projects/", json=basic_case, headers=logged_in_headers) + assert project_response.status_code == status.HTTP_201_CREATED + project_id = project_response.json()["id"] + + own_flow_payload = { + "name": "Owned flow", + "description": "Visible to the project owner", + "data": {}, + "folder_id": project_id, + } + own_flow_response = await client.post("api/v1/flows/", json=own_flow_payload, headers=logged_in_headers) + assert own_flow_response.status_code == status.HTTP_201_CREATED + own_flow_id = own_flow_response.json()["id"] + + other_user_flow_id = uuid4() + async with session_scope() as session: + flow_create = FlowCreate( + id=other_user_flow_id, + name="Other user's flow", + description="Should stay hidden from paginated project reads", + data={}, + folder_id=project_id, + user_id=uuid4(), + ) + flow = Flow.model_validate(flow_create, from_attributes=True) + session.add(flow) + await session.commit() + + response = await client.get( + f"api/v1/projects/{project_id}?page=1&size=10", + headers=logged_in_headers, + ) + assert response.status_code == status.HTTP_200_OK + + result = response.json() + flow_ids = {flow["id"] for flow in result["flows"]["items"]} + + assert own_flow_id in flow_ids + assert str(other_user_flow_id) not in flow_ids + async def test_download_file_starter_project(client: AsyncClient, logged_in_headers, active_user, json_flow): """Test downloading a project with multiple flows. diff --git a/src/lfx/src/lfx/_assets/component_index.json b/src/lfx/src/lfx/_assets/component_index.json index f7b6e163cade..d4bca9343923 100644 --- a/src/lfx/src/lfx/_assets/component_index.json +++ b/src/lfx/src/lfx/_assets/component_index.json @@ -73431,7 +73431,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "lfx", @@ -73579,7 +73579,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "googleapiclient", @@ -73740,7 +73740,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "langchain_core", @@ -73869,7 +73869,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "langchain_google_community", @@ -73998,7 +73998,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "googleapiclient", @@ -74288,7 +74288,7 @@ }, { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "langchain_google_genai", @@ -74648,7 +74648,7 @@ "dependencies": [ { "name": "google", - "version": "2.30.0" + "version": "2.8.0" }, { "name": "google_auth_oauthlib", @@ -113845,7 +113845,7 @@ }, { "name": "google", - "version": "2.30.0" + "version": "2.8.0" } ], "total_dependencies": 3 @@ -114229,7 +114229,7 @@ }, { "name": "google", - "version": "2.30.0" + "version": "2.8.0" } ], "total_dependencies": 3 @@ -118487,6 +118487,6 @@ "num_components": 359, "num_modules": 97 }, - "sha256": "dcb9a6e44e2405967f6fd4881e5bc5d3748bb507b3a0e57fc90c9b86c2536730", + "sha256": "adb51913c419daf5f3f496820e732dc9d7d562f0f0963f11a760f39fe8aaecd8", "version": "0.3.1" } \ No newline at end of file