🐛 fix(schema.py): set count field in ListComponentResponseModel as Optional with default value of 0 to handle cases where count is not provided

🐛 fix(service.py): import logger from loguru module to fix logger import error
✨ feat(service.py): add support for metadata in _get method to handle additional metadata returned in API response
✨ feat(service.py): add support for metadata in query_components method to handle additional metadata returned in API response
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-11-16 16:27:27 -03:00
commit d460b05a6c
2 changed files with 32 additions and 21 deletions

View file

@ -49,7 +49,7 @@ class ListComponentResponse(BaseModel):
class ListComponentResponseModel(BaseModel): class ListComponentResponseModel(BaseModel):
count: int count: Optional[int] = 0
authorized: bool authorized: bool
results: Optional[List[ListComponentResponse]] results: Optional[List[ListComponentResponse]]

View file

@ -1,9 +1,11 @@
import json import json
from typing import TYPE_CHECKING, Any, Dict, List, Optional from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
from uuid import UUID from uuid import UUID
import httpx import httpx
from httpx import HTTPError, HTTPStatusError from httpx import HTTPError, HTTPStatusError
from loguru import logger
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.store.schema import ( from langflow.services.store.schema import (
CreateComponentResponse, CreateComponentResponse,
@ -13,7 +15,6 @@ from langflow.services.store.schema import (
StoreComponentCreate, StoreComponentCreate,
) )
from langflow.services.store.utils import process_tags_for_post, update_components_with_user_data from langflow.services.store.utils import process_tags_for_post, update_components_with_user_data
from loguru import logger
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
@ -29,7 +30,9 @@ async def user_data_context(store_service: "StoreService", api_key: Optional[str
# Fetch and set user data to the context variable # Fetch and set user data to the context variable
if api_key: if api_key:
try: try:
user_data = await store_service._get(f"{store_service.base_url}/users/me", api_key, params={"fields": "id"}) user_data, _ = await store_service._get(
f"{store_service.base_url}/users/me", api_key, params={"fields": "id"}
)
if isinstance(user_data, list): if isinstance(user_data, list):
user_data = user_data[0] user_data = user_data[0]
user_data_var.set(user_data) user_data_var.set(user_data)
@ -77,7 +80,7 @@ class StoreService(Service):
async def _get( async def _get(
self, url: str, api_key: Optional[str] = None, params: Optional[Dict[str, Any]] = None self, url: str, api_key: Optional[str] = None, params: Optional[Dict[str, Any]] = None
) -> List[Dict[str, Any]]: ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
"""Utility method to perform GET requests.""" """Utility method to perform GET requests."""
if api_key: if api_key:
headers = {"Authorization": f"Bearer {api_key}"} headers = {"Authorization": f"Bearer {api_key}"}
@ -91,10 +94,15 @@ class StoreService(Service):
raise exc raise exc
except Exception as exc: except Exception as exc:
raise ValueError(f"GET failed: {exc}") raise ValueError(f"GET failed: {exc}")
result = response.json()["data"] json_response = response.json()
result = json_response["data"]
metadata = {}
if "meta" in json_response:
metadata = json_response["meta"]
if isinstance(result, dict): if isinstance(result, dict):
return [result] return [result], metadata
return result return result, metadata
async def call_webhook(self, api_key: str, webhook_url: str, component_id: UUID) -> None: async def call_webhook(self, api_key: str, webhook_url: str, component_id: UUID) -> None:
# The webhook is a POST request with the data in the body # The webhook is a POST request with the data in the body
@ -128,7 +136,7 @@ class StoreService(Service):
api_key = api_key if use_api_key else None api_key = api_key if use_api_key else None
results = await self._get(self.components_url, api_key, params) results, _ = await self._get(self.components_url, api_key, params)
return int(results[0].get("count", 0)) return int(results[0].get("count", 0))
@staticmethod @staticmethod
@ -200,11 +208,12 @@ class StoreService(Service):
fields: Optional[List[str]] = None, fields: Optional[List[str]] = None,
filter_conditions: Optional[List[Dict[str, Any]]] = None, filter_conditions: Optional[List[Dict[str, Any]]] = None,
use_api_key: Optional[bool] = False, use_api_key: Optional[bool] = False,
) -> List[ListComponentResponse]: ) -> Tuple[List[ListComponentResponse], Dict[str, Any]]:
params: Dict[str, Any] = { params: Dict[str, Any] = {
"page": page, "page": page,
"limit": limit, "limit": limit,
"fields": ",".join(fields) if fields else ",".join(self.default_fields), "fields": ",".join(fields) if fields else ",".join(self.default_fields),
"meta": "filter_count", # !This is DEPRECATED so we should remove it ASAP
} }
# ?aggregate[count]=likes # ?aggregate[count]=likes
@ -221,7 +230,7 @@ class StoreService(Service):
# so we don't need to risk passing an invalid api_key # so we don't need to risk passing an invalid api_key
# and getting 401 # and getting 401
api_key = api_key if use_api_key else None api_key = api_key if use_api_key else None
results = await self._get(self.components_url, api_key, params) results, metadata = await self._get(self.components_url, api_key, params)
if isinstance(results, dict): if isinstance(results, dict):
results = [results] results = [results]
results_objects = [ListComponentResponse(**component) for component in results] results_objects = [ListComponentResponse(**component) for component in results]
@ -229,7 +238,7 @@ class StoreService(Service):
# for component in results_objects: # for component in results_objects:
# if component.tags: # if component.tags:
# component.tags = [tags_id.tags_id for tags_id in component.tags] # component.tags = [tags_id.tags_id for tags_id in component.tags]
return results_objects return results_objects, metadata
async def get_liked_by_user_components(self, component_ids: List[UUID], api_key: str) -> List[str]: async def get_liked_by_user_components(self, component_ids: List[UUID], api_key: str) -> List[str]:
# Get fields id # Get fields id
@ -249,7 +258,7 @@ class StoreService(Service):
} }
), ),
} }
results = await self._get(self.components_url, api_key, params) results, _ = await 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 # Which of the components is parent of the user's components
@ -268,7 +277,7 @@ class StoreService(Service):
} }
), ),
} }
results = await self._get(self.components_url, api_key, params) results, _ = await self._get(self.components_url, api_key, params)
return [result["id"] for result in results] return [result["id"] for result in results]
async def download(self, api_key: str, component_id: UUID) -> DownloadComponentResponse: async def download(self, api_key: str, component_id: UUID) -> DownloadComponentResponse:
@ -276,7 +285,7 @@ class StoreService(Service):
params = {"fields": ",".join(["id", "name", "description", "data", "is_component"])} params = {"fields": ",".join(["id", "name", "description", "data", "is_component"])}
if not self.download_webhook_url: if not self.download_webhook_url:
raise ValueError("DOWNLOAD_WEBHOOK_URL is not set") raise ValueError("DOWNLOAD_WEBHOOK_URL is not set")
component = await self._get(url, api_key, params) component, _ = await self._get(url, api_key, params)
await self.call_webhook(api_key, self.download_webhook_url, component_id) await self.call_webhook(api_key, self.download_webhook_url, component_id)
return DownloadComponentResponse(**component) return DownloadComponentResponse(**component)
@ -311,7 +320,7 @@ class StoreService(Service):
async def get_tags(self) -> List[Dict[str, Any]]: async def get_tags(self) -> List[Dict[str, Any]]:
url = f"{self.base_url}/items/tags" url = f"{self.base_url}/items/tags"
params = {"fields": ",".join(["id", "name"])} params = {"fields": ",".join(["id", "name"])}
tags = await self._get(url, api_key=None, params=params) tags, _ = await self._get(url, api_key=None, params=params)
return tags return tags
async def get_user_likes(self, api_key: str) -> List[Dict[str, Any]]: async def get_user_likes(self, api_key: str) -> List[Dict[str, Any]]:
@ -319,7 +328,7 @@ class StoreService(Service):
params = { params = {
"fields": ",".join(["id", "likes"]), "fields": ",".join(["id", "likes"]),
} }
likes = await self._get(url, api_key, params) likes, _ = await self._get(url, api_key, params)
return likes return likes
async def get_component_likes_count(self, component_id: str, api_key: Optional[str] = None) -> int: async def get_component_likes_count(self, component_id: str, api_key: Optional[str] = None) -> int:
@ -328,7 +337,7 @@ class StoreService(Service):
params = { params = {
"fields": ",".join(["id", "count(liked_by)"]), "fields": ",".join(["id", "count(liked_by)"]),
} }
result = await self._get(url, api_key=api_key, params=params) result, _ = await self._get(url, api_key=api_key, params=params)
if len(result) == 0: if len(result) == 0:
raise ValueError("Component not found") raise ValueError("Component not found")
likes = result[0]["liked_by_count"] likes = result[0]["liked_by_count"]
@ -399,7 +408,7 @@ class StoreService(Service):
result: List[ListComponentResponse] = [] result: List[ListComponentResponse] = []
authorized = False authorized = False
try: try:
result = await self.query_components( result, metadata = await self.query_components(
api_key=store_api_Key, api_key=store_api_Key,
page=page, page=page,
limit=limit, limit=limit,
@ -407,13 +416,15 @@ class StoreService(Service):
filter_conditions=filter_conditions, filter_conditions=filter_conditions,
use_api_key=liked or filter_by_user, use_api_key=liked or filter_by_user,
) )
if metadata:
comp_count = metadata.get("filter_count", 0)
except HTTPStatusError as exc: except HTTPStatusError as exc:
if exc.response.status_code == 403: if exc.response.status_code == 403:
raise ValueError("You are not authorized to access this public resource") raise ValueError("You are not authorized to access this public resource")
elif exc.response.status_code == 401: elif exc.response.status_code == 401:
raise ValueError("You are not authorized to access this resource. Please check your API key.") raise ValueError("You are not authorized to access this resource. Please check your API key.")
try: try:
if result: if result and not metadata:
if len(result) >= limit: if len(result) >= limit:
comp_count = await self.count_components( comp_count = await self.count_components(
api_key=store_api_Key, api_key=store_api_Key,
@ -422,7 +433,7 @@ class StoreService(Service):
) )
else: else:
comp_count = len(result) comp_count = len(result)
else: elif not metadata:
comp_count = 0 comp_count = 0
except HTTPStatusError as exc: except HTTPStatusError as exc:
if exc.response.status_code == 403: if exc.response.status_code == 403: