develop #2
@@ -41,6 +41,7 @@ from app.core.note_summarizer import generate_note_summary
|
|||||||
from app.db import get_session
|
from app.db import get_session
|
||||||
from app.integrations import encrypt_token
|
from app.integrations import encrypt_token
|
||||||
from app.models import CloudScoutConfig, ScoutRunLog, LocalScoutConfig
|
from app.models import CloudScoutConfig, ScoutRunLog, LocalScoutConfig
|
||||||
|
from app.scouts.connectors.registry import get_connector
|
||||||
from app.schemas import (
|
from app.schemas import (
|
||||||
CloudScoutCreateRequest,
|
CloudScoutCreateRequest,
|
||||||
CloudScoutResponse,
|
CloudScoutResponse,
|
||||||
@@ -375,6 +376,46 @@ async def delete_cloud_scout(
|
|||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/cloud/{scout_id}/gmail-labels")
|
||||||
|
async def list_gmail_labels(
|
||||||
|
scout_id: str,
|
||||||
|
db: AsyncSession = Depends(get_session),
|
||||||
|
current_user: UserProfile = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
scout = await db.get(CloudScoutConfig, scout_id)
|
||||||
|
if scout is None or scout.user_id != current_user.id:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND, "Scout not found")
|
||||||
|
try:
|
||||||
|
connector = get_connector("gmail")
|
||||||
|
except KeyError:
|
||||||
|
return []
|
||||||
|
return await connector.list_labels(scout)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/cloud/{scout_id}/gmail-disconnect", response_model=CloudScoutResponse)
|
||||||
|
async def disconnect_gmail(
|
||||||
|
scout_id: str,
|
||||||
|
db: AsyncSession = Depends(get_session),
|
||||||
|
current_user: UserProfile = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
scout = await db.get(CloudScoutConfig, scout_id)
|
||||||
|
if scout is None or scout.user_id != current_user.id:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND, "Scout not found")
|
||||||
|
try:
|
||||||
|
connector = get_connector("gmail")
|
||||||
|
await connector.stop_watch(scout)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
scout.oauth_token_encrypted = None
|
||||||
|
scout.gmail_history_id = None
|
||||||
|
scout.gmail_watch_expires_at = None
|
||||||
|
scout.gmail_address = None
|
||||||
|
scout.enabled = False
|
||||||
|
await db.commit()
|
||||||
|
await db.refresh(scout)
|
||||||
|
return _to_cloud_response(scout)
|
||||||
|
|
||||||
|
|
||||||
# ── Gmail OAuth setup (scout-specific) ───────────────────────────────────────
|
# ── Gmail OAuth setup (scout-specific) ───────────────────────────────────────
|
||||||
|
|
||||||
# Scopes required for Gmail scout connectivity.
|
# Scopes required for Gmail scout connectivity.
|
||||||
@@ -556,7 +597,6 @@ async def scout_gmail_oauth_callback(
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
# Attempt to set up Gmail push watch so we start receiving Pub/Sub notifications.
|
# Attempt to set up Gmail push watch so we start receiving Pub/Sub notifications.
|
||||||
from app.scouts.connectors.registry import get_connector
|
|
||||||
try:
|
try:
|
||||||
connector = get_connector("gmail")
|
connector = get_connector("gmail")
|
||||||
await connector.setup_watch(scout)
|
await connector.setup_watch(scout)
|
||||||
|
|||||||
@@ -104,3 +104,46 @@ async def test_delete_cloud_scout():
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
listing = (await client.get("/api/v1/scouts/cloud", headers=_auth_headers())).json()
|
listing = (await client.get("/api/v1/scouts/cloud", headers=_auth_headers())).json()
|
||||||
assert all(r["id"] != sid for r in listing)
|
assert all(r["id"] != sid for r in listing)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_gmail_labels_route_returns_labels():
|
||||||
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
|
created = (await client.post(
|
||||||
|
"/api/v1/scouts/cloud",
|
||||||
|
json={"name": "L", "provider": "gmail"},
|
||||||
|
headers=_auth_headers(),
|
||||||
|
)).json()
|
||||||
|
sid = created["id"]
|
||||||
|
|
||||||
|
with patch("app.api.routes.scouts.get_connector") as mock_get:
|
||||||
|
mock_get.return_value.list_labels = AsyncMock(return_value=[{"id": "INBOX", "name": "INBOX"}])
|
||||||
|
resp = await client.get(f"/api/v1/scouts/cloud/{sid}/gmail-labels", headers=_auth_headers())
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.json() == [{"id": "INBOX", "name": "INBOX"}]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_gmail_disconnect_clears_token():
|
||||||
|
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||||
|
created = (await client.post(
|
||||||
|
"/api/v1/scouts/cloud",
|
||||||
|
json={"name": "D", "provider": "gmail"},
|
||||||
|
headers=_auth_headers(),
|
||||||
|
)).json()
|
||||||
|
sid = created["id"]
|
||||||
|
# mark it connected directly in the DB
|
||||||
|
async with _TestSessionLocal() as session:
|
||||||
|
row = await session.get(CloudScoutConfig, sid)
|
||||||
|
row.oauth_token_encrypted = "blob"
|
||||||
|
row.gmail_address = "a@b.com"
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
with patch("app.api.routes.scouts.get_connector") as mock_get:
|
||||||
|
mock_get.return_value.stop_watch = AsyncMock()
|
||||||
|
resp = await client.post(f"/api/v1/scouts/cloud/{sid}/gmail-disconnect", headers=_auth_headers())
|
||||||
|
assert resp.status_code == 200
|
||||||
|
body = resp.json()
|
||||||
|
assert body["oauth_connected"] is False
|
||||||
|
assert body["gmail_address"] is None
|
||||||
|
assert body["enabled"] is False
|
||||||
|
|||||||
Reference in New Issue
Block a user