Migration von der alten API

Diese Seite zeigt, wie die Methoden der veralteten abstrakten Client-Basisklasse (aus ecmind_blue_client.client) auf die aktuelle ECM-API umgestellt werden.

Die alte API basierte auf rohen Job-Objekten und untypisierten dict-Ergebnissen. Die neue API nutzt typisierte Modellklassen, fluent Query Builder und strukturierte Exceptions.

1. Setup

  • Legacy

  • ECM API

from ecmind_blue_client.tcp_client import Connection

client = Connection(host, port, "App", user, password, ssl)
result = client.execute(job)
from ecmind_blue_client.pool import SyncPoolClient
from ecmind_blue_client.ecm import ECM

client = SyncPoolClient(servers="host:4000:1", username=user, password=password)
ecm = ECM(client)

Der SyncPoolClient verwaltet den Verbindungspool automatisch. Für asyncio-Kontexte (FastAPI etc.) wird der AsyncPoolClient verwendet.

2. Methodenübersicht

2.1. lol_query()ecm.dms.select_lol()

lol_query() führte paginierte Flachlisten-Abfragen (LOL) aus und lieferte untypisierte dict-Zeilen. Das direkte Äquivalent in der neuen API ist ecm.dms.select_lol() — gleicher LOL-Format, aber mit typisierten Modellinstanzen.

  • Legacy

  • ECM API

from ecmind_blue_client.query_condition_group import QueryConditionGroup

results = list(client.lol_query(
    "InvoiceFolder",
    conditions=QueryConditionGroup("InvoiceFolder", [("Year", "=", "2024")]),
    result_fields=["Title", "Year"],
    page_size=100,
))
for row in results:
    print(row["Title"], row["Year"])
from ecmind_blue_client.ecm.model import make_folder_model

InvoiceFolder = make_folder_model("InvoiceFolder")

for folder in ecm.dms.select_lol(InvoiceFolder).where(
    InvoiceFolder["Year"] == 2024
).stream():
    print(folder["Title"], folder["Year"])
Die neue API unterstützt zusätzlich hierarchische (HOL) Abfragen über ecm.dms.select(). HOL liefert reichere Objektgraphen mit Eltern-Kind-Beziehungen und ist für die meisten Anwendungsfälle die empfohlene Standardmethode.

Für statische Typprüfung empfiehlt sich die explizite Modelldeklaration statt make_folder_model:

from ecmind_blue_client.ecm.model import ECMFolderModel, ECMField

class InvoiceFolder(ECMFolderModel):
    _internal_name_ = "InvoiceFolder"
    Title: ECMField[str]
    Year: ECMField[int]

# LOL — Flachliste, gleicher Aufbau wie lol_query()
for folder in ecm.dms.select_lol(InvoiceFolder).where(
    InvoiceFolder.Year == 2024
).stream():
    print(folder.Title, folder.Year)

# HOL — hierarchisch, für die meisten Abfragen empfohlen
for folder in ecm.dms.select(InvoiceFolder).where(
    InvoiceFolder.Year == 2024
).order_by(InvoiceFolder.Year.DESC).stream():
    print(folder.Title, folder.Year)

2.2. xml_import()ecm.dms.insert() / update() / upsert()

xml_import() kombinierte Insert- und Update-Logik über action0/action1-Parameter. Die neue API trennt diese in klar benannte Methoden.

2.2.1. Insert

  • Legacy

  • ECM API

from ecmind_blue_client.const import ImportActions

client.xml_import(
    "InvoiceFolder",
    search_fields={},
    import_fields={"Title": "Rechnung 2024", "Year": "2024"},
    action0=ImportActions.INSERT,
    action1=ImportActions.ERROR,
)
folder_id, type_id = ecm.dms.insert(InvoiceFolder(Title="Rechnung 2024", Year=2024))
# oder Objekt direkt zurückgeben:
folder = ecm.dms.insert_and_get(InvoiceFolder(Title="Rechnung 2024", Year=2024))

2.2.2. Insert oder Update (Upsert)

  • Legacy

  • ECM API

from ecmind_blue_client.const import ImportActions

client.xml_import(
    "InvoiceFolder",
    search_fields={"Title": "Rechnung 2024"},
    import_fields={"Year": "2024"},
    action0=ImportActions.INSERT,
    action1=ImportActions.UPDATE,
)
object_id, type_id, hits, action = (
    ecm.dms.upsert(InvoiceFolder(Title="Rechnung 2024", Year=2024))
    .search(InvoiceFolder.Title == "Rechnung 2024")
    .execute()
)

2.2.3. Mehrere Treffer (action_multiple)

Wenn die Suche mehr als ein übereinstimmendes Objekt findet, wird standardmäßig ein Fehler zurückgegeben. Mit .action_multiple() kann dieser Fall explizit behandelt werden:

from ecmind_blue_client.const import ImportActions

object_id, type_id, hits, action = (
    ecm.dms.upsert(InvoiceFolder(Title="Rechnung 2024", Year=2024))
    .search(InvoiceFolder.Title == "Rechnung 2024")
    .action_multiple(ImportActions.UPDATE)  # alle Treffer aktualisieren
    .execute()
)
Die Parameter main_type, variant_parent_id und options von xml_import() haben kein direktes Äquivalent im High-Level-Upsert-Builder. Für Operationen, die diese Parameter benötigen, steht ecm.execute() als Low-Level-Escape-Hatch zur Verfügung.

2.3. get_object_details()ecm.dms.get()

  • Legacy

  • ECM API

from ecmind_blue_client.const import SystemFields

details = client.get_object_details(
    "InvoiceFolder",
    object_id=42,
    system_fields=[SystemFields.OBJECT_ID],
)
print(details["Title"])
folder = ecm.dms.get(InvoiceFolder, object_id=42)
print(folder.Title)
print(folder.system.id)

Siehe ecm.dms.get().

2.4. store_in_cache() / store_in_cache_by_id()ecm.dms.files()

store_in_cache() und store_in_cache_by_id() lieferten rohe ResultFile-Objekte. ecm.dms.files() vereint beide Methoden und gibt strukturierte Datei-Metadaten zurück.

  • Legacy

  • ECM API

files = client.store_in_cache(object_id=42)
for f in files:
    data = f.bytes()
files = ecm.dms.files(document)
for f in files:
    data = f.bytes()

2.4.1. Mit Dateikonvertierung (store_in_cache_by_id)

  • Legacy

  • ECM API

from ecmind_blue_client.const import StoreInCacheByIdConversion

files = client.store_in_cache_by_id(
    object_id=42, convert=StoreInCacheByIdConversion.PDF
)
from ecmind_blue_client.const import StoreInCacheByIdConversion

files = ecm.dms.files(document, convert=StoreInCacheByIdConversion.PDF)
# oder per Objekt-ID:
files = ecm.dms.files(42, convert=StoreInCacheByIdConversion.PDF)

StoreInCacheByIdConversion unterstützt NONE (Standard), PDF (Haupttypen 1–4) und MULTIPAGE_TIFF (TIFF-Haupttypen 2–3 werden zu einer einzigen Datei zusammengeführt). Zusätzliche Parameter, die in der Legacy-API nicht vorhanden waren: when_cold_then_tiff (COLD/ASCII-Dateien als TIFF zurückgeben) und add_annotations (Annotationen einbrennen).

Der checkout-Parameter von store_in_cache() hat kein Äquivalent in ecm.dms.files(). Für Checkout-Funktionalität kann ecm.execute() mit dem rohen Job verwendet werden.

2.5. get_object_type_by_id()ecm.dms.get_object_type_by_id()

Beide APIs bieten diese Methode mit identischer Signatur.

  • Legacy

  • ECM API

type_id = client.get_object_type_by_id(object_id=42)
type_id = ecm.dms.get_object_type_by_id(object_id=42)

2.6. execute_sql()ecm.db.select()

  • Legacy

  • ECM API

rows = client.execute_sql("SELECT * FROM invoices WHERE year = ?", 2024)
for row in rows:
    print(row["id"], row["title"])
result = ecm.db.select("SELECT * FROM invoices WHERE year = ?", 2024)
for row in result.rows:
    print(row["id"], row["title"])

2.7. execute() — Low-Level-Escape-Hatch

Beide APIs behalten eine rohe execute()-Methode für Jobs, die noch nicht durch die High-Level-API abgedeckt werden.

  • Legacy

  • ECM API

from ecmind_blue_client import Job

result = client.execute(Job("krn.GetServerInfo", Flags=0, Info=6))
print(result.values["Value"])
from ecmind_blue_client.rpc import Jobs

result = ecm.execute(Jobs.KRN_GETSERVERINFO, Flags=0, Info=6)
print(result.values["Value"])

3. Benutzerwechsel (Context-User)

Die alte API übergab context_user als Parameter an einzelne Methoden. Die neue API verwendet einen Context Manager, der den Benutzer in alle Operationen innerhalb des Blocks injiziert.

  • Legacy

  • ECM API

client.xml_import(..., context_user="john")
with ecm.impersonate("john") as ecm_john:
    ecm_john.dms.insert(InvoiceFolder(Title="Rechnung 2024"))

4. Neue Funktionen in der ECM API

Die folgenden Methoden sind in ecm.dms verfügbar, haben aber kein Äquivalent in der veralteten Client-Basisklasse.

4.1. Löschen, Verschieben, Kopieren

# Löschen (Soft-Delete standardmäßig; hard_delete=True umgeht den Papierkorb)
ecm.dms.delete(folder)
ecm.dms.delete(folder, hard_delete=True, delete_cascading=True)

# Dokument in ein anderes Register verschieben
ecm.dms.move(document, folder_id=target_folder, register_id=target_register)

# Ordner inklusive aller Kinder kopieren
ecm.dms.copy(folder, folder_id=target_folder, copy_cascading=True)

# Verknüpfte Kopie eines Dokuments erstellen (zweiter Ablagort, gemeinsamer Indexsatz)
ecm.dms.copy(document, folder_id=target_folder, register_id=target_register, link_document=True)

4.2. Erweiterte Abfrageoptionen (ecm.dms.select())

ecm.dms.select() bietet HOL-Abfragemodifikatoren ohne Legacy-Äquivalent:

# Rechte, Basisparameter und Datei-Metadaten in die Antwort einschließen
results = (
    ecm.dms.select(InvoiceFolder)
    .where(InvoiceFolder.Year == 2024)
    .rights()
    .base_params()
    .execute()
)

# Hierarchische Traversierung
results = ecm.dms.select(InvoiceFolder).with_children().execute()
results = ecm.dms.select(InvoiceFolder).with_parents().execute()

# Dokumentvarianten einschließen
results = ecm.dms.select(InvoiceDocument).variants().execute()

# Papierkorb abfragen
results = ecm.dms.select(InvoiceFolder).garbage_mode().execute()