4 Commits

3 changed files with 29 additions and 3 deletions

View File

@@ -1 +1 @@
{"task_id": "31188f42-8544-8074-bad3-d3e1b9b4051f", "token": "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", "readme_path": "connector-superoffice/README.md", "session_start_time": "2026-02-25T18:08:58.087419"} {"task_id": "31188f42-8544-8074-bad3-d3e1b9b4051f", "token": "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", "readme_path": "connector-superoffice/README.md", "session_start_time": "2026-02-26T07:18:12.570241"}

View File

@@ -201,6 +201,32 @@ Um die Zertifizierung für den SuperOffice App Store zu erhalten, mussten kritis
* **Lösung:** Umstellung auf **`PATCH`**. Wir senden nun nur noch die *tatsächlich geänderten Felder* (Delta). * **Lösung:** Umstellung auf **`PATCH`**. Wir senden nun nur noch die *tatsächlich geänderten Felder* (Delta).
* **Implementierung:** Der Worker baut nun ein `patch_payload` (z.B. `{'Position': {'Id': 123}}`) und nutzt den dedizierten PATCH-Endpunkt. Dies wurde explizit von SuperOffice für die Zertifizierung gefordert. * **Implementierung:** Der Worker baut nun ein `patch_payload` (z.B. `{'Position': {'Id': 123}}`) und nutzt den dedizierten PATCH-Endpunkt. Dies wurde explizit von SuperOffice für die Zertifizierung gefordert.
### 11. Production Environment (Live Feb 26, 2026)
Nach erfolgreicher Zertifizierung durch SuperOffice wurde der Connector auf die Produktionsumgebung umgestellt.
* **Tenant:** `Cust26720`
* **Environment:** `online3` (zuvor `sod`)
* **Endpoint:** `https://online3.superoffice.com/Cust26720/api/v1`
* **Authentication:** Umstellung auf Produktions-Client-ID und -Secret.
**Wichtig:** SuperOffice nutzt Load-Balancing. Die Subdomain (`online3`) kann sich theoretisch ändern. Die Anwendung prüft dies dynamisch, aber die Basis-Konfiguration sollte den aktuellen Tenant-Status widerspiegeln.
### 12. Lessons Learned: Production Migration (Feb 26, 2026)
Der Wechsel von der Staging-Umgebung (`sod`) zur Produktion (`onlineX`) brachte spezifische technische Hürden mit sich:
1. **Globaler Token-Endpunkt:**
* **Problem:** Mandantenspezifische Subdomains (wie `online3.superoffice.com`) akzeptieren oft keine OAuth-Anfragen oder liefern leere Antworten.
* **Lösung:** Für den Token-Refresh muss zwingend der globale Endpunkt **`https://online.superoffice.com/login/common/oauth/tokens`** verwendet werden, unabhängig davon, auf welchem Cluster der Mandant liegt.
2. **DNS-Präfixe (app- vs. direkt):**
* **Problem:** In der Staging-Umgebung lautet der API-Host meist `app-sod.superoffice.com`. In der Produktion wird das `app-` Präfix oft nicht verwendet oder führt zu Zertifikatsfehlern.
* **Lösung:** Der `SuperOfficeClient` wurde so flexibilisiert, dass er in der Produktion direkt auf `{env}.superoffice.com` zugreift.
3. **Refresh Token Lebenszyklus:**
* Ein Refresh Token ist an die **Client ID** gebunden. Beim Wechsel der App-Umgebung (Staging -> Produktion) muss zwingend ein neuer Refresh Token über den Auth-Flow generiert werden.
## Appendix: The "First Sentence" Prompt ## Appendix: The "First Sentence" Prompt
This is the core logic used to generate the company-specific opener. This is the core logic used to generate the company-specific opener.

View File

@@ -28,7 +28,7 @@ class SuperOfficeClient:
self.access_token = None self.access_token = None
return return
self.base_url = f"https://app-{self.env}.superoffice.com/{self.cust_id}/api/v1" self.base_url = f"https://{self.env}.superoffice.com/{self.cust_id}/api/v1"
self.access_token = self._refresh_access_token() self.access_token = self._refresh_access_token()
if not self.access_token: if not self.access_token:
@@ -43,7 +43,7 @@ class SuperOfficeClient:
def _refresh_access_token(self): def _refresh_access_token(self):
"""Refreshes and returns a new access token.""" """Refreshes and returns a new access token."""
url = f"https://{self.env}.superoffice.com/login/common/oauth/tokens" url = "https://online.superoffice.com/login/common/oauth/tokens"
logger.debug(f"DEBUG: Refresh URL: '{url}' (Env: '{self.env}')") logger.debug(f"DEBUG: Refresh URL: '{url}' (Env: '{self.env}')")
data = { data = {