🐛 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:
parent
6799007514
commit
0339072079
4 changed files with 80 additions and 22 deletions
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue