🐛 fix(store.py): add missing import for user_data_context and update_components_with_user_data functions

✨ feat(store.py): add support for user_data_context context manager to fetch and set user data for requests made inside the context
✨ feat(store.py): add support for in_user_collection attribute in ListComponentResponse schema to indicate if a component is in the user's collection
🐛 fix(store.py): fix get_liked_by_user_components function to use user_data_var context variable instead of making the request multiple times
🐛 fix(store.py): fix get_components_in_users_collection function to use user_data_var context variable instead of making the request multiple times
✨ feat(store.py): add update_components_with_user_data function to update components with user data (liked_by_user and in_users_collection attributes)
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-11-01 09:31:50 -03:00
commit 0339072079
4 changed files with 80 additions and 22 deletions

View file

@ -18,7 +18,8 @@ from langflow.services.store.schema import (
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from datetime import datetime 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"]) 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), store_api_Key: Optional[str] = Depends(get_optional_user_store_api_key),
): ):
try: try:
result = store_service.query_components( with user_data_context(store_api_Key, store_service):
api_key=store_api_Key, result = store_service.query_components(
page=page, api_key=store_api_Key,
limit=limit, page=page,
filter_by_user=filter_by_user, limit=limit,
) filter_by_user=filter_by_user,
)
if not store_api_Key: if not store_api_Key:
return result return result
# Now, from the result, we need to get the components # Now, from the result, we need to get the components
# the user likes and set the liked_by_user to True # the user likes and set the liked_by_user to True
liked_by_user_ids = store_service.get_liked_by_user_components( result = update_components_with_user_data(result, store_service, store_api_Key)
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
return result return result
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc)) raise HTTPException(status_code=400, detail=str(exc))

View file

@ -43,6 +43,7 @@ class ListComponentResponse(BaseModel):
description: Optional[str] description: Optional[str]
liked_by_count: Optional[int] liked_by_count: Optional[int]
liked_by_user: Optional[bool] liked_by_user: Optional[bool]
in_user_collection: Optional[bool]
is_component: Optional[bool] is_component: Optional[bool]
metadata: Optional[dict] metadata: Optional[dict]
user_created: Optional[dict] user_created: Optional[dict]

View file

@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, List, Dict, Any, Optional, Union
import httpx import httpx
from httpx import HTTPError from httpx import HTTPError
from langflow.services.database.models import user
from langflow.services.store.schema import ( from langflow.services.store.schema import (
ComponentResponse, ComponentResponse,
DownloadComponentResponse, DownloadComponentResponse,
@ -17,6 +18,27 @@ from langflow.services.store.utils import process_tags_for_post
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.settings.service import SettingsService 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): class StoreService(Service):
@ -45,6 +67,11 @@ class StoreService(Service):
"metadata", "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( def _get(
self, url: str, api_key: str, params: Dict[str, Any] = None self, url: str, api_key: str, params: Dict[str, Any] = None
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
@ -175,9 +202,7 @@ class StoreService(Service):
raise ValueError("No API key provided") raise ValueError("No API key provided")
if filter_by_user and api_key: if filter_by_user and api_key:
user_data = self._get( user_data = user_data_var.get()
f"{self.base_url}/users/me", api_key, params={"fields": "id"}
)
params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}}) params["filter"] = json.dumps({"user_created": {"_eq": user_data["id"]}})
else: else:
params["filter"] = params["filter"] = json.dumps( params["filter"] = params["filter"] = json.dumps(
@ -193,14 +218,12 @@ class StoreService(Service):
return results_objects return results_objects
def get_liked_by_user_components( 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]: ) -> List[UUID]:
# Get fields id # Get fields id
# filter should be "id is in component_ids AND liked_by directus_users_id token is api_key" # filter should be "id is in component_ids AND liked_by directus_users_id token is api_key"
# return the ids # return the ids
user_data = self._get( user_data = user_data_var.get()
f"{self.base_url}/users/me", api_key, params={"fields": "id"}
)
params = { params = {
"fields": "id", "fields": "id",
"filter": json.dumps( "filter": json.dumps(
@ -215,6 +238,26 @@ class StoreService(Service):
results = self._get(self.components_url, api_key, params) results = self._get(self.components_url, api_key, params)
return [result["id"] for result in results] 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: def download(self, api_key: str, component_id: str) -> DownloadComponentResponse:
url = f"{self.components_url}/{component_id}" url = f"{self.components_url}/{component_id}"
params = { params = {

View file

@ -3,3 +3,22 @@ def process_tags_for_post(component_dict):
if tags and all(isinstance(tag, str) for tag in tags): if tags and all(isinstance(tag, str) for tag in tags):
component_dict["tags"] = [{"tags_id": tag} for tag in tags] component_dict["tags"] = [{"tags_id": tag} for tag in tags]
return component_dict 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