Migration from the Legacy API
This page maps each method of the deprecated Client abstract base class (from ecmind_blue_client.client) to its equivalent in the current ECM API.
The old API was built around raw Job objects and untyped dict results.
The new API uses typed model classes, fluent query builders, and structured 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)
The SyncPoolClient manages a connection pool automatically.
Use AsyncPoolClient in asyncio contexts (FastAPI, etc.).
2. Method mapping
2.1. lol_query() → ecm.dms.select_lol()
lol_query() executed paginated flat-list (LOL) queries and yielded untyped dict rows.
The direct equivalent in the new API is ecm.dms.select_lol(), which uses the same LOL format but returns typed model instances.
-
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"])
The new API also supports hierarchical (HOL) queries via ecm.dms.select().
HOL returns richer object graphs with parent/child relationships and is the recommended default
for most use cases.
|
For static type checking, declare the model class explicitly instead of using 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 — flat list, same format as lol_query()
for folder in ecm.dms.select_lol(InvoiceFolder).where(
InvoiceFolder.Year == 2024
).stream():
print(folder.Title, folder.Year)
# HOL — hierarchical, recommended for most queries
for folder in ecm.dms.select(InvoiceFolder).where(
InvoiceFolder.Year == 2024
).order_by(InvoiceFolder.Year.DESC).stream():
print(folder.Title, folder.Year)
See ecm.dms.select_lol() and ecm.dms.select().
2.2. xml_import() → ecm.dms.insert() / update() / upsert()
xml_import() combined insert and update logic via action0/action1 parameters.
The new API splits these into separate, clearly named methods.
2.2.1. Insert
-
Legacy
-
ECM API
from ecmind_blue_client.const import ImportActions
client.xml_import(
"InvoiceFolder",
search_fields={},
import_fields={"Title": "Invoice 2024", "Year": "2024"},
action0=ImportActions.INSERT,
action1=ImportActions.ERROR,
)
folder_id, type_id = ecm.dms.insert(InvoiceFolder(Title="Invoice 2024", Year=2024))
# or get the object back directly:
folder = ecm.dms.insert_and_get(InvoiceFolder(Title="Invoice 2024", Year=2024))
2.2.2. Insert or update (upsert)
-
Legacy
-
ECM API
from ecmind_blue_client.const import ImportActions
client.xml_import(
"InvoiceFolder",
search_fields={"Title": "Invoice 2024"},
import_fields={"Year": "2024"},
action0=ImportActions.INSERT,
action1=ImportActions.UPDATE,
)
object_id, type_id, hits, action = (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.execute()
)
2.2.3. Multiple hits
When the search finds more than one matching object, the default behaviour is to return
an error.
Use .action_multiple() to handle that case explicitly:
from ecmind_blue_client.const import ImportActions
object_id, type_id, hits, action = (
ecm.dms.upsert(InvoiceFolder(Title="Invoice 2024", Year=2024))
.search(InvoiceFolder.Title == "Invoice 2024")
.action_multiple(ImportActions.UPDATE) # update all matches
.execute()
)
The main_type, variant_parent_id, and options parameters of xml_import() have
no direct equivalent in the high-level upsert builder.
Use ecm.execute() as a low-level escape hatch for operations that require these
parameters.
|
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)
See ecm.dms.get().
2.4. store_in_cache() / store_in_cache_by_id() → ecm.dms.files()
store_in_cache() and store_in_cache_by_id() returned raw ResultFile objects.
ecm.dms.files() unifies both methods and returns structured file metadata.
-
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. With file conversion (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)
# or by plain object ID:
files = ecm.dms.files(42, convert=StoreInCacheByIdConversion.PDF)
StoreInCacheByIdConversion supports NONE (default), PDF (main types 1–4), and
MULTIPAGE_TIFF (TIFF main types 2–3 merged into one file).
Additional parameters not present in the legacy API: when_cold_then_tiff (return
COLD/ASCII files as TIFF) and add_annotations (burn annotations into images).
The checkout parameter of store_in_cache() has no equivalent in ecm.dms.files().
Use ecm.execute() with the raw job if checkout behaviour is required.
|
See ecm.dms.files().
2.5. get_object_type_by_id() → ecm.dms.get_object_type_by_id()
Both APIs expose this method with the same signature.
-
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"])
See ecm.db.select().
2.7. execute() — low-level escape hatch
Both APIs retain a raw execute() method for jobs not yet covered by the high-level API.
-
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. User context switching
The legacy API passed context_user as a parameter to individual methods.
The new API uses a context manager that injects the user into every operation inside the block.
-
Legacy
-
ECM API
client.xml_import(..., context_user="john")
with ecm.impersonate("john") as ecm_john:
ecm_john.dms.insert(InvoiceFolder(Title="Invoice 2024"))
4. New capabilities in the ECM API
The following methods are available in ecm.dms but have no equivalent in the legacy
Client base class.
4.1. Delete, move, copy
# Delete (soft delete by default; hard_delete=True bypasses the recycle bin)
ecm.dms.delete(folder)
ecm.dms.delete(folder, hard_delete=True, delete_cascading=True)
# Move a document to a different register
ecm.dms.move(document, folder_id=target_folder, register_id=target_register)
# Copy a folder including all children
ecm.dms.copy(folder, folder_id=target_folder, copy_cascading=True)
# Create a linked copy of a document (second location, shared index record)
ecm.dms.copy(document, folder_id=target_folder, register_id=target_register, link_document=True)
4.2. Advanced query options (ecm.dms.select())
ecm.dms.select() exposes HOL query modifiers with no legacy equivalent:
# Include rights, base parameters, and file properties in the response
results = (
ecm.dms.select(InvoiceFolder)
.where(InvoiceFolder.Year == 2024)
.rights()
.base_params()
.execute()
)
# Hierarchical traversal
results = ecm.dms.select(InvoiceFolder).with_children().execute()
results = ecm.dms.select(InvoiceFolder).with_parents().execute()
# Include document variants
results = ecm.dms.select(InvoiceDocument).variants().execute()
# Query the recycle bin
results = ecm.dms.select(InvoiceFolder).garbage_mode().execute()