Răsfoiți Sursa

reload endpoint clears service_api client caches

POST /reload is the user's 'my view drifted from reality' button, but it
only flushed store-side CalDAV caches. A canceled upstream reservation
could live in the ServiceAPI singleton's month/detail caches indefinitely
(no TTL; _had_bookings only guards the all-bookings-vanished direction),
surviving credential reloads and periodic syncs until a server restart.

Add ServiceAPI.clear_caches() and call it from /reload alongside the
store clear; report both counts in the response.
ZiB 3 luni în urmă
părinte
comite
0a0441f215
3 a modificat fișierele cu 28 adăugiri și 4 ștergeri
  1. 8 4
      room_cal/api/_routes/reload.py
  2. 9 0
      room_cal/service_api/client.py
  3. 11 0
      tests/test_api.py

+ 8 - 4
room_cal/api/_routes/reload.py

@@ -6,10 +6,14 @@ router = APIRouter()
 
 @router.post('/reload', tags=['system'])
 async def reload():
-  """Clear all CalDAV event caches so the next request re-fetches from the server.
+  """Drop all in-memory caches so the next request re-reads reality.
 
-  Config is re-read from disk on every call already; this only flushes the
-  in-memory event cache maintained by Calendar instances.
+  Config is re-read from disk on every call already; this flushes the store's
+  CalDAV event caches and every service client's calendar/detail caches, so a
+  user who suspects their view has drifted (e.g. an upstream cancellation not
+  showing) gets a fully fresh fetch without restarting the server.
   """
+  from room_cal.service_api import ServiceAPI
   count = clear_caches()
-  return {'cleared_calendars': count}
+  services_count = ServiceAPI.get().clear_caches()
+  return {'cleared_calendars': count, 'cleared_services': services_count}

+ 9 - 0
room_cal/service_api/client.py

@@ -95,6 +95,15 @@ class ServiceAPI:
       self._clients.pop(key, None)
       self._disabled[key] = str(e)
 
+  def clear_caches(self) -> int:
+    """Clear every loaded client's data caches; returns the number of clients cleared."""
+    cleared = 0
+    for client in self._clients.values():
+      if hasattr(client, 'clear_cache'):
+        client.clear_cache()
+        cleared += 1
+    return cleared
+
   def client(self, key: str) -> ServiceClient:
     """Return the platform client for `key`, typed as ServiceClient."""
     try:

+ 11 - 0
tests/test_api.py

@@ -112,10 +112,19 @@ async def test_action_dispatch_unknown_action(client):
 @pytest.mark.asyncio
 async def test_reload_clears_caches(client):
   import room_cal.store as _cal
+  from room_cal.service_api import ServiceAPI
   cal = _cal.get_calendar()
   cal._event_cache[('fake_key',)] = []
   cal._cached_url = 'http://example.com/cal'
 
+  class _StubClient:
+    cleared = False
+    def clear_cache(self):
+      self.cleared = True
+
+  stub = _StubClient()
+  ServiceAPI.set(ServiceAPI(clients={'stub': stub}))
+
   with patch('room_cal.api.get_user', return_value=_AUTHED):
     r = await client.post('/reload')
 
@@ -125,3 +134,5 @@ async def test_reload_clears_caches(client):
   assert data['cleared_calendars'] >= 1
   assert cal._event_cache == {}
   assert cal._cached_url is None
+  assert data['cleared_services'] == 1
+  assert stub.cleared