diff --git a/src/backend/langflow/api/v1/store.py b/src/backend/langflow/api/v1/store.py index 5a1893629..c3491a7c5 100644 --- a/src/backend/langflow/api/v1/store.py +++ b/src/backend/langflow/api/v1/store.py @@ -18,7 +18,8 @@ from langflow.services.store.schema import ( from fastapi import APIRouter, Depends, HTTPException, Query from datetime import datetime -from langflow.services.store.service import StoreService +from langflow.services.store.service import StoreService, user_data_context +from langflow.services.store.utils import update_components_with_user_data router = APIRouter(prefix="/store", tags=["Components Store"]) @@ -78,26 +79,20 @@ def list_components( store_api_Key: Optional[str] = Depends(get_optional_user_store_api_key), ): try: - result = store_service.query_components( - api_key=store_api_Key, - page=page, - limit=limit, - filter_by_user=filter_by_user, - ) + with user_data_context(store_api_Key, store_service): + result = store_service.query_components( + api_key=store_api_Key, + page=page, + limit=limit, + filter_by_user=filter_by_user, + ) if not store_api_Key: return result # Now, from the result, we need to get the components # the user likes and set the liked_by_user to True - liked_by_user_ids = store_service.get_liked_by_user_components( - component_ids=[str(component.id) for component in result], - api_key=store_api_Key, - ) - # Now we need to set the liked_by_user attribute - for component in result: - component.liked_by_user = str(component.id) in liked_by_user_ids - + result = update_components_with_user_data(result, store_service, store_api_Key) return result except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) diff --git a/src/backend/langflow/services/store/schema.py b/src/backend/langflow/services/store/schema.py index 54c6ff91c..71722dded 100644 --- a/src/backend/langflow/services/store/schema.py +++ b/src/backend/langflow/services/store/schema.py @@ -43,6 +43,7 @@ class ListComponentResponse(BaseModel): description: Optional[str] liked_by_count: Optional[int] liked_by_user: Optional[bool] + in_user_collection: Optional[bool] is_component: Optional[bool] metadata: Optional[dict] user_created: Optional[dict] diff --git a/src/backend/langflow/services/store/service.py b/src/backend/langflow/services/store/service.py index df559fe82..08b9bbd29 100644 --- a/src/backend/langflow/services/store/service.py +++ b/src/backend/langflow/services/store/service.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, List, Dict, Any, Optional, Union import httpx from httpx import HTTPError +from langflow.services.database.models import user from langflow.services.store.schema import ( ComponentResponse, DownloadComponentResponse, @@ -17,6 +18,27 @@ from langflow.services.store.utils import process_tags_for_post if TYPE_CHECKING: from langflow.services.settings.service import SettingsService +from contextlib import contextmanager +from contextvars import ContextVar + +user_data_var: ContextVar[Optional[Dict[str, Any]]] = ContextVar( + "user_data", default=None +) + + +@contextmanager +def user_data_context(api_key: str, store_service: "StoreService"): + # Fetch and set user data to the context variable + if api_key: + user_data = store_service._get( + f"{store_service.base_url}/users/me", api_key, params={"fields": "id"} + ) + user_data_var.set(user_data) + try: + yield + finally: + # Clear the user data from the context variable + user_data_var.set(None) class StoreService(Service): @@ -45,6 +67,11 @@ class StoreService(Service): "metadata", ] + # Create a context manager that will use the api key to + # get the user data and all requests inside the context manager + # will make a property return that data + # Without making the request multiple times + def _get( self, url: str, api_key: str, params: Dict[str, Any] = None ) -> List[Dict[str, Any]]: @@ -175,9 +202,7 @@ class StoreService(Service): raise ValueError("No API key provided") if filter_by_user and api_key: - user_data = self._get( - f"{self.base_url}/users/me", api_key, params={"fields": "id"} - ) + user_data = user_data_var.get() params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}}) else: params["filter"] = params["filter"] = json.dumps( @@ -193,14 +218,12 @@ class StoreService(Service): return results_objects def get_liked_by_user_components( - self, component_ids: List[UUID], api_key: str + self, component_ids: List[UUID], api_key: str, user_data: Dict[str, Any] ) -> List[UUID]: # Get fields id # filter should be "id is in component_ids AND liked_by directus_users_id token is api_key" # return the ids - user_data = self._get( - f"{self.base_url}/users/me", api_key, params={"fields": "id"} - ) + user_data = user_data_var.get() params = { "fields": "id", "filter": json.dumps( @@ -215,6 +238,26 @@ class StoreService(Service): results = self._get(self.components_url, api_key, params) return [result["id"] for result in results] + # Which of the components is parent of the user's components + def get_components_in_users_collection( + self, component_ids: List[UUID], api_key: str + ): + user_data = user_data_var.get() + params = { + "fields": "id", + "filter": json.dumps( + { + "_and": [ + {"user_created": {"_eq": user_data["id"]}}, + {"parent": {"_in": component_ids}}, + ] + } + ), + } + results = self._get(self.components_url, api_key, params) + return [result["id"] for result in results] + + def download(self, api_key: str, component_id: str) -> DownloadComponentResponse: url = f"{self.components_url}/{component_id}" params = { diff --git a/src/backend/langflow/services/store/utils.py b/src/backend/langflow/services/store/utils.py index ab9ab0680..25024fd5f 100644 --- a/src/backend/langflow/services/store/utils.py +++ b/src/backend/langflow/services/store/utils.py @@ -3,3 +3,22 @@ def process_tags_for_post(component_dict): if tags and all(isinstance(tag, str) for tag in tags): component_dict["tags"] = [{"tags_id": tag} for tag in tags] return component_dict + + +def update_components_with_user_data(components, store_service, store_api_Key): + """ + Updates the components with the user data (liked_by_user and in_users_collection) + """ + liked_by_user_ids = store_service.get_liked_by_user_components( + component_ids=[str(component.id) for component in components], + api_key=store_api_Key, + ) + in_users_collection_ids = store_service.get_components_in_users_collection( + component_ids=[str(component.id) for component in components], + api_key=store_api_Key, + ) + # Now we need to set the liked_by_user attribute + for component in components: + component.liked_by_user = str(component.id) in liked_by_user_ids + component.in_users_collection = str(component.id) in in_users_collection_ids + return components