Skip to content

Commit 112e38d

Browse files
authored
feat: Enhance async capabilities and integrate httpx for non-blocking requests (#40)
1 parent b892faa commit 112e38d

File tree

13 files changed

+1846
-940
lines changed

13 files changed

+1846
-940
lines changed

backend/api/endpoints/gallery.py

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
)
1212
from typing import Dict, List, Optional, Any
1313
from fastapi.responses import StreamingResponse
14+
import asyncio
1415
import io
1516
import re
1617
import os
@@ -85,7 +86,8 @@ async def get_gallery_images(
8586
tag_list = [tag.strip() for tag in tags.split(",") if tag.strip()]
8687

8788
# Query Cosmos DB for images only
88-
result = cosmos_service.query_assets(
89+
result = await asyncio.to_thread(
90+
cosmos_service.query_assets,
8991
media_type="image", # Images only
9092
folder_path=folder_path,
9193
tags=tag_list,
@@ -183,7 +185,8 @@ async def get_gallery_videos(
183185
tag_list = [tag.strip() for tag in tags.split(",") if tag.strip()]
184186

185187
# Query Cosmos DB for videos only
186-
result = cosmos_service.query_assets(
188+
result = await asyncio.to_thread(
189+
cosmos_service.query_assets,
187190
media_type="video", # Videos only
188191
folder_path=folder_path,
189192
tags=tag_list,
@@ -310,7 +313,8 @@ async def _get_gallery_items_from_cosmos(
310313
) -> GalleryResponse:
311314
"""Get gallery items from CosmosDB metadata with standardized analysis structure"""
312315
try:
313-
result = cosmos_service.query_assets(
316+
result = await asyncio.to_thread(
317+
cosmos_service.query_assets,
314318
folder_path=folder_path,
315319
limit=limit,
316320
offset=offset,
@@ -479,8 +483,9 @@ async def upload_asset(
479483

480484
cosmos_metadata = AssetMetadataCreateRequest(**cosmos_data)
481485

482-
cosmos_service.create_asset_metadata(
483-
cosmos_metadata.dict(exclude_unset=True)
486+
await asyncio.to_thread(
487+
cosmos_service.create_asset_metadata,
488+
cosmos_metadata.dict(exclude_unset=True),
484489
)
485490
except Exception as cosmos_error:
486491
# Log error but don't fail the upload
@@ -549,7 +554,9 @@ async def delete_asset(
549554
# Delete from Cosmos DB first (if available)
550555
if cosmos_service:
551556
try:
552-
cosmos_service.delete_asset_metadata(asset_id, media_type_str)
557+
await asyncio.to_thread(
558+
cosmos_service.delete_asset_metadata, asset_id, media_type_str
559+
)
553560
except Exception as cosmos_error:
554561
import logging
555562

@@ -558,7 +565,9 @@ async def delete_asset(
558565
f"Failed to delete Cosmos DB metadata: {cosmos_error}")
559566

560567
# Delete from Azure Blob Storage
561-
success = azure_storage_service.delete_asset(blob_name, container_name)
568+
success = await asyncio.to_thread(
569+
azure_storage_service.delete_asset, blob_name, container_name
570+
)
562571

563572
if not success:
564573
raise HTTPException(status_code=404, detail="Asset not found")
@@ -598,8 +607,8 @@ async def get_asset_content(
598607
if media_type == MediaType.IMAGE
599608
else settings.AZURE_BLOB_VIDEO_CONTAINER
600609
)
601-
content, content_type = azure_storage_service.get_asset_content(
602-
blob_name, container_name
610+
content, content_type = await asyncio.to_thread(
611+
azure_storage_service.get_asset_content, blob_name, container_name
603612
)
604613

605614
if not content:
@@ -675,7 +684,9 @@ async def health_check(
675684
settings.AZURE_BLOB_VIDEO_CONTAINER,
676685
]
677686
for container in containers:
678-
azure_storage_service._ensure_container_exists(container)
687+
await asyncio.to_thread(
688+
azure_storage_service._ensure_container_exists, container
689+
)
679690

680691
health_status["services"]["azure_blob_storage"] = {
681692
"status": "healthy",
@@ -692,7 +703,7 @@ async def health_check(
692703
# Check Cosmos DB
693704
if cosmos_service:
694705
try:
695-
cosmos_health = cosmos_service.health_check()
706+
cosmos_health = await asyncio.to_thread(cosmos_service.health_check)
696707
health_status["services"]["cosmos_db"] = cosmos_health
697708

698709
if cosmos_health["status"] != "healthy":
@@ -757,14 +768,18 @@ async def metadata_service_status(
757768
if cosmos_service:
758769
try:
759770
# Test basic connectivity
760-
cosmos_health = cosmos_service.health_check()
771+
cosmos_health = await asyncio.to_thread(cosmos_service.health_check)
761772

762773
# Test query capabilities
763-
test_query_result = cosmos_service.query_assets(limit=1, offset=0)
774+
test_query_result = await asyncio.to_thread(
775+
cosmos_service.query_assets, limit=1, offset=0
776+
)
764777

765778
# Test search capabilities
766779
try:
767-
search_result = cosmos_service.search_assets("test", limit=1)
780+
search_result = await asyncio.to_thread(
781+
cosmos_service.search_assets, "test", limit=1
782+
)
768783
search_available = True
769784
except:
770785
search_available = False
@@ -847,11 +862,14 @@ async def list_folders(
847862

848863
# Get folders from Cosmos DB
849864
media_type_str = media_type.value if media_type else None
850-
result = cosmos_service.get_all_folders(media_type=media_type_str)
865+
result = await asyncio.to_thread(
866+
cosmos_service.get_all_folders, media_type=media_type_str
867+
)
851868

852869
# Filter out root folder from the results and build hierarchy for UI
853-
filtered_folders = [folder for folder in result['folders'] if folder != '/' and folder.strip()]
854-
870+
filtered_folders = [
871+
folder for folder in result['folders'] if folder != '/' and folder.strip()]
872+
855873
# Build folder hierarchy for UI
856874
folder_hierarchy = {}
857875
for folder_path in filtered_folders:
@@ -883,7 +901,7 @@ async def create_folder(
883901
):
884902
"""
885903
Create a folder placeholder in CosmosDB for immediate navbar visibility.
886-
904+
887905
This creates a special folder metadata record so the folder appears immediately
888906
in the navigation, even before any assets are added to it.
889907
"""
@@ -918,13 +936,19 @@ async def create_folder(
918936
if cosmos_service:
919937
try:
920938
# Check if folder placeholder already exists
921-
existing_placeholder = cosmos_service.container.query_items(
922-
query="SELECT * FROM c WHERE c.doc_type = 'folder_placeholder' AND c.folder_path = @folder_path",
923-
parameters=[{"name": "@folder_path", "value": normalized_folder}],
924-
enable_cross_partition_query=True
939+
existing_placeholder = await asyncio.to_thread(
940+
lambda: list(
941+
cosmos_service.container.query_items(
942+
query="SELECT * FROM c WHERE c.doc_type = 'folder_placeholder' AND c.folder_path = @folder_path",
943+
parameters=[
944+
{"name": "@folder_path", "value": normalized_folder}
945+
],
946+
enable_cross_partition_query=True,
947+
)
948+
)
925949
)
926-
927-
if not list(existing_placeholder):
950+
951+
if not existing_placeholder:
928952
# Create folder placeholder record
929953
folder_placeholder = {
930954
"id": f"folder_{uuid.uuid4().hex[:12]}",
@@ -937,14 +961,19 @@ async def create_folder(
937961
"is_placeholder": True,
938962
"asset_count": 0
939963
}
940-
941-
cosmos_service.create_asset_metadata(folder_placeholder)
942-
logger.info(f"Created folder placeholder for: {folder_path}")
964+
965+
await asyncio.to_thread(
966+
cosmos_service.create_asset_metadata, folder_placeholder
967+
)
968+
logger.info(
969+
f"Created folder placeholder for: {folder_path}")
943970
else:
944-
logger.info(f"Folder placeholder already exists for: {folder_path}")
945-
971+
logger.info(
972+
f"Folder placeholder already exists for: {folder_path}")
973+
946974
except Exception as e:
947-
logger.warning(f"Failed to create folder placeholder in CosmosDB: {e}")
975+
logger.warning(
976+
f"Failed to create folder placeholder in CosmosDB: {e}")
948977
# Continue anyway - folder will still work when assets are added
949978

950979
# Return success

0 commit comments

Comments
 (0)