- Implemented comprehensive tests for the Kommunikation Sync functionality, covering base64 encoding, marker parsing, creation, type detection, and integration scenarios. - Added a verification script to check for unique IDs in Advoware communications, ensuring stability and integrity of the IDs. - Created utility scripts for code validation, notification testing, and PUT response detail analysis to enhance development and testing processes. - Updated README with details on new tools and their usage.
262 lines
8.9 KiB
Python
262 lines
8.9 KiB
Python
"""
|
||
Deep-Dive: Suche nach versteckten ID-Feldern
|
||
|
||
Die Relationships emailAddresses/phoneNumbers existieren (kein 404),
|
||
aber wir bekommen 403 Forbidden.
|
||
|
||
Möglichkeiten:
|
||
1. IDs sind in emailAddressData versteckt (vielleicht als 'id' Feld?)
|
||
2. Es gibt ein separates ID-Array
|
||
3. IDs sind in einem anderen Format gespeichert
|
||
4. Admin-API-Key hat nicht genug Rechte
|
||
"""
|
||
|
||
import asyncio
|
||
import json
|
||
from services.espocrm import EspoCRMAPI
|
||
|
||
TEST_BETEILIGTE_ID = '68e4af00172be7924'
|
||
|
||
|
||
class SimpleContext:
|
||
class Logger:
|
||
def info(self, msg): print(f"[INFO] {msg}")
|
||
def error(self, msg): print(f"[ERROR] {msg}")
|
||
def warning(self, msg): print(f"[WARN] {msg}")
|
||
def debug(self, msg): pass
|
||
|
||
def __init__(self):
|
||
self.logger = self.Logger()
|
||
|
||
|
||
def print_section(title):
|
||
print("\n" + "="*70)
|
||
print(title)
|
||
print("="*70)
|
||
|
||
|
||
async def inspect_email_data_structure():
|
||
"""Schaue sehr genau in emailAddressData/phoneNumberData"""
|
||
print_section("DEEP INSPECTION: emailAddressData Structure")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
entity = await espo.get_entity('CBeteiligte', TEST_BETEILIGTE_ID)
|
||
|
||
email_data = entity.get('emailAddressData', [])
|
||
|
||
print(f"\n📧 emailAddressData hat {len(email_data)} Einträge\n")
|
||
|
||
for i, email in enumerate(email_data):
|
||
print(f"[{i+1}] RAW Type: {type(email)}")
|
||
print(f" Keys: {list(email.keys())}")
|
||
print(f" JSON:\n")
|
||
print(json.dumps(email, indent=4, ensure_ascii=False))
|
||
|
||
# Prüfe ob 'id' Feld vorhanden ist
|
||
if 'id' in email:
|
||
print(f"\n ✅ ID GEFUNDEN: {email['id']}")
|
||
else:
|
||
print(f"\n ❌ Kein 'id' Feld")
|
||
|
||
# Prüfe alle Felder auf ID-ähnliche Werte
|
||
print(f"\n Alle Werte:")
|
||
for key, value in email.items():
|
||
print(f" {key:20s} = {value}")
|
||
print()
|
||
|
||
|
||
async def test_raw_api_call():
|
||
"""Mache rohe API-Calls um zu sehen was wirklich zurückkommt"""
|
||
print_section("RAW API CALL: Direkt ohne Wrapper")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
# Test 1: Normale Entity-Abfrage
|
||
print(f"\n1️⃣ GET /CBeteiligte/{TEST_BETEILIGTE_ID}")
|
||
result1 = await espo.api_call(f'CBeteiligte/{TEST_BETEILIGTE_ID}')
|
||
|
||
# Zeige nur Email-relevante Felder
|
||
email_fields = {k: v for k, v in result1.items() if 'email' in k.lower()}
|
||
print(json.dumps(email_fields, indent=2, ensure_ascii=False))
|
||
|
||
# Test 2: Mit maxDepth Parameter (falls EspoCRM das unterstützt)
|
||
print(f"\n2️⃣ GET mit maxDepth=2")
|
||
try:
|
||
result2 = await espo.api_call(
|
||
f'CBeteiligte/{TEST_BETEILIGTE_ID}',
|
||
params={'maxDepth': '2'}
|
||
)
|
||
email_fields2 = {k: v for k, v in result2.items() if 'email' in k.lower()}
|
||
print(json.dumps(email_fields2, indent=2, ensure_ascii=False))
|
||
except Exception as e:
|
||
print(f" ❌ Error: {e}")
|
||
|
||
# Test 3: Select nur emailAddressData
|
||
print(f"\n3️⃣ GET mit select=emailAddressData")
|
||
result3 = await espo.api_call(
|
||
f'CBeteiligte/{TEST_BETEILIGTE_ID}',
|
||
params={'select': 'emailAddressData'}
|
||
)
|
||
print(json.dumps(result3, indent=2, ensure_ascii=False))
|
||
|
||
|
||
async def search_for_link_table():
|
||
"""Suche nach EntityEmailAddress oder EntityPhoneNumber Link-Tables"""
|
||
print_section("SUCHE: Link-Tables")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
# In EspoCRM gibt es manchmal Link-Tables wie "EntityEmailAddress"
|
||
link_table_names = [
|
||
'EntityEmailAddress',
|
||
'EntityPhoneNumber',
|
||
'ContactEmailAddress',
|
||
'ContactPhoneNumber',
|
||
'CBeteiligteEmailAddress',
|
||
'CBeteiligtePhoneNumber'
|
||
]
|
||
|
||
for table_name in link_table_names:
|
||
print(f"\n🔍 Teste: {table_name}")
|
||
try:
|
||
result = await espo.api_call(table_name, params={'maxSize': 3})
|
||
print(f" ✅ Existiert! Total: {result.get('total', 'unknown')}")
|
||
if result.get('list'):
|
||
print(f" Beispiel:")
|
||
print(json.dumps(result['list'][0], indent=6, ensure_ascii=False))
|
||
except Exception as e:
|
||
error_msg = str(e)
|
||
if '404' in error_msg:
|
||
print(f" ❌ 404 - Existiert nicht")
|
||
elif '403' in error_msg:
|
||
print(f" ⚠️ 403 - Existiert aber kein Zugriff")
|
||
else:
|
||
print(f" ❌ {error_msg}")
|
||
|
||
|
||
async def test_update_with_ids():
|
||
"""Test: Kann ich beim UPDATE IDs setzen?"""
|
||
print_section("TEST: Update mit IDs")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
print(f"\n💡 Idee: Vielleicht kann man beim UPDATE IDs mitgeben")
|
||
print(f" und EspoCRM erstellt dann die Verknüpfung?\n")
|
||
|
||
# Hole aktuelle Daten
|
||
entity = await espo.get_entity('CBeteiligte', TEST_BETEILIGTE_ID)
|
||
current_emails = entity.get('emailAddressData', [])
|
||
|
||
print(f"Aktuelle Emails:")
|
||
for email in current_emails:
|
||
print(f" • {email.get('emailAddress')}")
|
||
|
||
# Versuche ein Update mit expliziter ID
|
||
print(f"\n🧪 Teste: Füge 'id' Feld zu emailAddressData hinzu")
|
||
|
||
test_emails = []
|
||
for email in current_emails:
|
||
email_copy = email.copy()
|
||
# Generiere eine Test-ID (oder verwende eine echte wenn wir eine finden)
|
||
email_copy['id'] = f"test-id-{hash(email['emailAddress']) % 100000}"
|
||
test_emails.append(email_copy)
|
||
print(f" • {email['emailAddress']:40s} → id={email_copy['id']}")
|
||
|
||
print(f"\n⚠️ ACHTUNG: Würde jetzt UPDATE machen mit:")
|
||
print(json.dumps({'emailAddressData': test_emails}, indent=2, ensure_ascii=False))
|
||
print(f"\n→ NICHT ausgeführt (zu riskant ohne Backup)")
|
||
|
||
|
||
async def check_database_or_config():
|
||
"""Prüfe ob es Config/Settings gibt die IDs aktivieren"""
|
||
print_section("ESPOCRM CONFIG: ID-Unterstützung")
|
||
|
||
context = SimpleContext()
|
||
espo = EspoCRMAPI(context)
|
||
|
||
print(f"\n📋 Hole App-Informationen:")
|
||
try:
|
||
# EspoCRM hat oft einen /App endpoint
|
||
app_info = await espo.api_call('App/user')
|
||
|
||
# Zeige nur relevante Felder
|
||
if app_info:
|
||
relevant = ['acl', 'preferences', 'settings']
|
||
for key in relevant:
|
||
if key in app_info:
|
||
print(f"\n{key}:")
|
||
# Suche nach Email/Phone-relevanten Einstellungen
|
||
data = app_info[key]
|
||
if isinstance(data, dict):
|
||
email_phone_settings = {k: v for k, v in data.items()
|
||
if 'email' in k.lower() or 'phone' in k.lower()}
|
||
if email_phone_settings:
|
||
print(json.dumps(email_phone_settings, indent=2, ensure_ascii=False))
|
||
else:
|
||
print(" (keine Email/Phone-spezifischen Einstellungen)")
|
||
except Exception as e:
|
||
print(f" ❌ Error: {e}")
|
||
|
||
# Prüfe Settings
|
||
print(f"\n📋 System Settings:")
|
||
try:
|
||
settings = await espo.api_call('Settings')
|
||
if settings:
|
||
email_phone_settings = {k: v for k, v in settings.items()
|
||
if 'email' in k.lower() or 'phone' in k.lower()}
|
||
if email_phone_settings:
|
||
print(json.dumps(email_phone_settings, indent=2, ensure_ascii=False))
|
||
except Exception as e:
|
||
print(f" ❌ Error: {e}")
|
||
|
||
|
||
async def main():
|
||
print("\n" + "="*70)
|
||
print("DEEP DIVE: SUCHE NACH PHONENUMBER/EMAILADDRESS IDs")
|
||
print("="*70)
|
||
|
||
try:
|
||
# Sehr detaillierte Inspektion
|
||
await inspect_email_data_structure()
|
||
|
||
# Rohe API-Calls
|
||
await test_raw_api_call()
|
||
|
||
# Link-Tables
|
||
await search_for_link_table()
|
||
|
||
# Update-Test (ohne tatsächlich zu updaten)
|
||
await test_update_with_ids()
|
||
|
||
# Config
|
||
await check_database_or_config()
|
||
|
||
print_section("FAZIT")
|
||
|
||
print("\n🎯 Mögliche Szenarien:")
|
||
print("\n1️⃣ IDs existieren NICHT in emailAddressData")
|
||
print(" → Wert-basiertes Matching notwendig")
|
||
print(" → Hybrid-Strategie (primary-Flag)")
|
||
|
||
print("\n2️⃣ IDs existieren aber sind versteckt/nicht zugänglich")
|
||
print(" → API-Rechte müssen erweitert werden")
|
||
print(" → Admin muss emailAddresses/phoneNumbers Relationship freigeben")
|
||
|
||
print("\n3️⃣ IDs können beim UPDATE gesetzt werden")
|
||
print(" → Wir könnten eigene IDs generieren")
|
||
print(" → Advoware-ID direkt als EspoCRM-ID nutzen")
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ Fehler: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|