feat: implement sync notifications for Obsidian plugin

- Added functionality to create and update notifications during the Obsidian sync process.
- Improved handling of sync completion and failure notifications.
- Updated connector naming convention in various locations for consistency.
This commit is contained in:
Anish Sarkar 2026-04-22 06:38:51 +05:30
parent 3b7f27cff9
commit 4a75603d4f
4 changed files with 134 additions and 8 deletions

View file

@ -41,6 +41,7 @@ from app.schemas.obsidian_plugin import (
SyncAckItem,
SyncBatchRequest,
)
from app.services.notification_service import NotificationService
from app.services.obsidian_plugin_indexer import (
delete_note,
get_manifest,
@ -68,6 +69,103 @@ def _build_handshake() -> dict[str, object]:
return {"capabilities": list(OBSIDIAN_CAPABILITIES)}
def _connector_type_value(connector: SearchSourceConnector) -> str:
connector_type = connector.connector_type
if hasattr(connector_type, "value"):
return str(connector_type.value)
return str(connector_type)
async def _start_obsidian_sync_notification(
session: AsyncSession,
*,
user: User,
connector: SearchSourceConnector,
total_count: int,
):
"""Create/update the rolling inbox item for Obsidian plugin sync.
Obsidian sync is continuous and batched, so we keep one stable
operation_id per connector instead of creating a new notification per batch.
"""
handler = NotificationService.connector_indexing
operation_id = f"obsidian_sync_connector_{connector.id}"
connector_name = connector.name or "Obsidian"
notification = await handler.find_or_create_notification(
session=session,
user_id=user.id,
operation_id=operation_id,
title=f"Syncing: {connector_name}",
message="Syncing from Obsidian plugin",
search_space_id=connector.search_space_id,
initial_metadata={
"connector_id": connector.id,
"connector_name": connector_name,
"connector_type": _connector_type_value(connector),
"sync_stage": "processing",
"indexed_count": 0,
"failed_count": 0,
"total_count": total_count,
"source": "obsidian_plugin",
},
)
return await handler.update_notification(
session=session,
notification=notification,
status="in_progress",
metadata_updates={
"sync_stage": "processing",
"total_count": total_count,
},
)
async def _finish_obsidian_sync_notification(
session: AsyncSession,
*,
notification,
indexed: int,
failed: int,
):
"""Mark the rolling Obsidian sync inbox item complete or failed."""
handler = NotificationService.connector_indexing
connector_name = notification.notification_metadata.get("connector_name", "Obsidian")
if failed > 0 and indexed == 0:
title = f"Failed: {connector_name}"
message = (
f"Sync failed: {failed} file(s) failed"
if failed > 1
else "Sync failed: 1 file failed"
)
status_value = "failed"
stage = "failed"
else:
title = f"Ready: {connector_name}"
if failed > 0:
message = f"Partially synced: {indexed} file(s) synced, {failed} failed."
elif indexed == 0:
message = "Already up to date!"
elif indexed == 1:
message = "Now searchable! 1 file synced."
else:
message = f"Now searchable! {indexed} files synced."
status_value = "completed"
stage = "completed"
await handler.update_notification(
session=session,
notification=notification,
title=title,
message=message,
status=status_value,
metadata_updates={
"indexed_count": indexed,
"failed_count": failed,
"sync_stage": stage,
},
)
async def _resolve_vault_connector(
session: AsyncSession,
*,
@ -188,7 +286,7 @@ def _build_config(
def _display_name(vault_name: str) -> str:
return f"Obsidian \u2014 {vault_name}"
return f"Obsidian - {vault_name}"
@router.post("/connect", response_model=ConnectResponse)
@ -335,6 +433,18 @@ async def obsidian_sync(
connector = await _resolve_vault_connector(
session, user=user, vault_id=payload.vault_id
)
notification = None
try:
notification = await _start_obsidian_sync_notification(
session, user=user, connector=connector, total_count=len(payload.notes)
)
except Exception:
logger.warning(
"obsidian sync notification start failed connector=%s user=%s",
connector.id,
user.id,
exc_info=True,
)
items: list[SyncAckItem] = []
indexed = 0
@ -362,6 +472,22 @@ async def obsidian_sync(
SyncAckItem(path=note.path, status="error", error=str(exc)[:300])
)
if notification is not None:
try:
await _finish_obsidian_sync_notification(
session,
notification=notification,
indexed=indexed,
failed=failed,
)
except Exception:
logger.warning(
"obsidian sync notification finish failed connector=%s user=%s",
connector.id,
user.id,
exc_info=True,
)
return SyncAck(
vault_id=payload.vault_id,
indexed=indexed,