wiki/c-work/2-build/2026-04-msft-google-calendar-contacts.md
2026-04-29 00:35:17 +02:00

9.5 KiB

MSFT- und Google-Connector: CalendarAdapter + ContactsAdapter, plus Reconnect-Button

Beschreibung und Kontext

Der Infomaniak-Connector liefert seit dem 2026-04-28 die drei Services kDrive, Calendar und Contacts. Die analogen Services fuer Microsoft und Google fehlten bislang in der UDB. Ziel dieses Builds: vier neue ServiceAdapter (MSFT-Calendar, MSFT-Contacts, Google-Calendar, Google-Contacts), so dass die UDB pro Connection einen einheitlichen Service-Pool zeigt und der Agent fuer Calendar/Contacts gegen alle drei Provider gleichermassen arbeiten kann.

Zusaetzliche Anforderung: ein Reconnect-Button im Frontend. Bestehende MSFT-/Google-Connections wurden mit einem kleineren Scope-Set autorisiert. Beim Hinzufuegen neuer Scopes (Calendars.Read, Contacts.Read, calendar.readonly, contacts.readonly) liefern die Provider auf einer Re-Authorisierung sonst still dieselben alten Tokens, weil sie include_granted_scopes (Google) bzw. prompt=select_account (MSFT) default-maessig nutzen -- die neuen Scopes wuerden nie das Tokenset erreichen.

Architektur

  • oauthProviderConfig.googleDataScopes += calendar.readonly, contacts.readonly
  • oauthProviderConfig.msftDataScopes += Calendars.Read, Contacts.Read
  • connectorMsft.CalendarAdapter (neu)
    • Endpoints: me/calendars, me/calendars/{id}/events
    • Pagination via @odata.nextLink (Helper _stripGraphBase aus dem Modul)
    • Pfad: "" -> Calendars; "/{calendarId}" -> Events
    • Download: synthetisches RFC-5545 VCALENDAR/VEVENT (Graph hat keinen $value-Endpoint fuer Events)
  • connectorMsft.ContactsAdapter (neu)
    • Endpoints: me/contactFolders, me/contactFolders/{id}/contacts, plus virtueller Default-Folder default -> me/contacts
    • Download: vCard 3.0 (N/FN/ORG/TITLE/EMAIL/TEL/ADR/NOTE)
  • Helper: _eventToIcs, _contactToVcard, _safeFileName, _personLabel, _icsEscape, _icsDateTime (alle modullokal)
  • connectorMsft.MsftConnector._SERVICE_MAP += "calendar" + "contact"
  • connectorGoogle.CalendarAdapter (neu)
    • Endpoints: users/me/calendarList, calendars/{id}/events?singleEvents=true&orderBy=startTime
    • Download: .ics aus Event-Detail synthetisiert
    • Search: events?q=... per Calendar-ID (Default primary)
  • connectorGoogle.ContactsAdapter (neu)
    • Endpoints: contactGroups + virtueller all -> people/me/connections
    • Gruppen-Mitglieder via contactGroups/{id} -> memberResourceNames -> people:batchGet?resourceNames=...&personFields=...
    • Search: people:searchContacts
    • Download: vCard 3.0 aus personFields=names,emailAddresses,phoneNumbers, organizations,addresses,biographies,memberships
  • connectorGoogle.GoogleConnector._SERVICE_MAP += "calendar" + "contact"
  • routeFeatureWorkspace und routeFeatureGraphicalEditor Service-Label-/Icon-Maps += kdrive, calendar, contact (so dass die UDB sinnvolle Bezeichnungen anzeigt -- vorher war Infomaniak-kDrive ungelabelt durchgerutscht)

Reconnect-Flow

  • routeDataConnections.connect_service (POST /api/connections/{id}/connect) akzeptiert optionalen Body {"reauth": true} und haengt &reauth=1 an die zurueckgegebene auth_url.
  • routeSecurityMsft.auth_connect setzt bei reauth=1 prompt=consent (statt select_account / login). Das erzwingt den Microsoft-Consent-Screen und liefert die neuen Scopes nach.
  • routeSecurityGoogle.auth_connect droppt bei reauth=1 include_granted_scopes=true (default-true macht Google "vergesslich" fuer neu hinzugekommene Scopes -- ohne diesen Drop blieben die neuen Scopes still aussen vor).
  • ClickUp ignoriert den Param (FastAPI laesst unbekannte Query-Args durchrutschen) -- das ist OK, weil ClickUp keine Scope-Erweiterung hat.
  • Frontend:
    • connectionApi.connectService(id, reauth?) haengt den Body an.
    • useConnections.connectWithPopup(id, reauth?) reicht durch (auch der separate useOAuthConnect-Hook).
    • ConnectionsPage hat ein neues customAction reconnect mit FaSyncAlt-Icon, eigenem reconnectingConnections-Set und visible-Filter status === 'active' && authority in {msft, google, clickup}. Der Refresh-Button bleibt fuer Token-Refresh ohne Re-Consent erhalten.

Fokus und kritische Details

  • Pagination: MSFT Calendar/Contacts paginieren bei $top<=100; wir ziehen @odata.nextLink durch wie OutlookAdapter und stoppen bei effectiveLimit.
  • iCal-Bauen: Wir ueberlassen das nicht einer Library, weil RFC-5545 fuer einzelne VEVENT-Faelle trivial ist und der Overhead einer Dependency (icalendar) nicht gerechtfertigt ist. Escape und DTSTAMP/DTSTART/DTEND in UTC.
  • vCard-Bauen: vCard 3.0 weil universell von Outlook/Google/macOS Contacts akzeptiert. ADR-Felder: leere Felder werden als leere Slots gerendert (nicht weggelassen), damit das Format gueltig bleibt.
  • Default-Contacts-Folder: Outlook hat einen unsichtbaren Default-Ordner -- den simulieren wir mit dem Pseudo-Folder default, der intern auf me/contacts mappt. Ohne diesen Eintrag waere der Default-Ordner aus der UDB nicht erreichbar.
  • People-API Group-Resolution: contactGroups/{id} liefert keine vollstaendigen Person-Records, nur memberResourceNames. Wir batchen diese in 200er-Chunks via people:batchGet. Limit fuer Resource-Names pro Batch ist 200 (Google API spec).
  • Reconnect ohne Token-Vernichtung: Wir loeschen die alte Token nicht -- der Auth-Callback ueberschreibt sie atomar nach erfolgreichem Code-Exchange. Bei abgebrochenem Reconnect bleibt der bisherige Token gueltig.

Entscheidungen

  • Calendar/Contacts read-only: Wir nehmen Calendars.Read / calendar.readonly / Contacts.Read / contacts.readonly -- nicht ReadWrite. Der UDB-Use-Case ist ausschliesslich Lesen + Suche + Download. Schreibrechte werden bei einem konkreten Bedarf separat diskutiert (mehr Scopes erhoehen Consent-Aufwand und Risk-Profile unnoetig).
  • Reconnect statt eigenem Endpoint: Wir spendieren keinen separaten /reconnect-Endpoint, sondern einen Body-Param am bestehenden /connect. Vorteil: ein einziger Authorisierungspfad, identische Callback-Behandlung, kein doppeltes State-Management.
  • include_granted_scopes Drop bei Google: Default true ist freundlich fuer Token-Reuse, aber die offizielle Google-Doku warnt ausdruecklich, dass es bei Scope-Erweiterung ueberraschend Tokens ohne neue Scopes ausstellt. false bei Reconnect ist die robustere Wahl.
  • prompt=consent bei MSFT: Microsoft erkennt zusaetzliche Scopes zwar normalerweise selbststaendig und zeigt einen Consent-Screen, aber das ist nur best-effort. Mit prompt=consent ist es garantiert.
  • vCard 3.0 statt 4.0: 4.0 hat noch immer schlechte Outlook- Kompatibilitaet beim Import; 3.0 ist der Common-Denominator.
  • .ics per Hand bauen statt iCalendar-Lib: Eine externe Dependency lohnt fuer einen einzigen VEVENT je Download nicht.

Umsetzungs-Checkliste

  • oauthProviderConfig.py: googleDataScopes + msftDataScopes ergaenzt
  • connectorMsft.CalendarAdapter (browse/download/search)
  • connectorMsft.ContactsAdapter (browse/download/search)
  • connectorMsft._SERVICE_MAP: calendar, contact registriert
  • connectorMsft._eventToIcs, _contactToVcard, Helpers
  • connectorGoogle.CalendarAdapter (browse/download/search)
  • connectorGoogle.ContactsAdapter (browse/download/search inkl. Group-Resolution)
  • connectorGoogle._SERVICE_MAP: calendar, contact registriert
  • connectorGoogle._googleEventToIcs, _googlePersonToVcard, Helpers
  • routeFeatureWorkspace Service-Label/Icon-Maps erweitert
  • routeFeatureGraphicalEditor Service-Label/Icon-Maps erweitert
  • routeDataConnections.connect_service: optionaler reauth-Body
  • routeSecurityMsft.auth_connect: reauth -> prompt=consent
  • routeSecurityGoogle.auth_connect: reauth -> drop include_granted_scopes
  • Frontend connectionApi.connectService(id, reauth?)
  • Frontend useConnections.connectWithPopup(id, reauth?) (beide Hooks)
  • Frontend ConnectionsPage: Reconnect-Button mit FaSyncAlt-Icon und eigenem Loading-Set
  • Manuelle Verifikation: existierende MSFT-Connection neu durchklicken (Reconnect), Calendar+Contacts in der UDB verifizieren; existierende Google-Connection genauso
  • Manuelle Verifikation: Calendar-Download liefert eine importierbare .ics (Outlook-Test); Contacts-Download liefert eine importierbare .vcf
  • Doc-Sync: wiki/b-reference/auth-and-rbac.md, wiki/b-reference/connectors.md aktualisieren (neue Scopes, Reconnect-Pfad), Topics-Liste pruefen

Akzeptanzkriterien

  1. Bestehende MSFT-/Google-Connection -> Reconnect-Button -> nach Consent erscheinen Calendar und Contacts als zusaetzliche Service-Knoten in der UDB.
  2. Calendar-Browse listet die User-Calendars; Klick auf einen Calendar listet die juengsten Events; Download eines Events liefert eine .ics mit DTSTART/DTEND/SUMMARY/LOCATION.
  3. Contacts-Browse listet ContactFolders (MSFT) bzw. ContactGroups (Google); Klick listet die enthaltenen Contacts; Download liefert eine .vcf mit FN, ORG, EMAIL, TEL.
  4. Frischer Connect (neue MSFT-Connection) zieht die neuen Scopes ohne Reconnect (weil sie im initialen msftDataScopes / googleDataScopes enthalten sind).
  5. ClickUp- und FTP-Connections sind unbeeintraechtigt; bei Infomaniak ist der Reconnect-Button korrekt nicht sichtbar (Authority-Whitelist).