Active CRUD Operations
Relevant source files
The following files were used as context for generating this wiki page:
Active CRUD Operations in the flext-ldap library provide a high-level service layer for interacting with LDAP directories. This system coordinates standard Create, Read, Update, and Delete actions while implementing advanced patterns such as idempotent "upserts" and batch processing. It acts as a bridge between the underlying protocol adapters and the domain logic, ensuring that all operations are type-safe, normalized, and error-resilient. Sources: src/flext_ldap/services/operations.py:1-25, src/flext_ldap/protocols.py:133-210
The architecture relies on the Railway-Oriented Programming (ROP) pattern, where operations return result objects (r) instead of raising exceptions. This ensures consistent error handling across the service, with failures capturing detailed context for audit and forensic analysis. Sources: src/flext_ldap/services/operations.py:27-40, tests/unit/test_operations.py:1-20
Service Architecture
The core of the operation logic resides in the FlextLdapOperations class, which inherits from FlextLdapAdapterHost. It delegates low-level protocol concerns to an adapter (typically Ldap3Adapter) while managing high-level business rules like DN normalization and entry comparison. Sources: src/flext_ldap/services/operations.py:46-60
Component Relationship
The following diagram illustrates how the operations service interacts with adapters and models:
The operations service maintains a lazy-initialized _UpsertHandler to manage complex idempotent write flows. Sources: src/flext_ldap/services/operations.py:80-95, src/flext_ldap/protocols.py:214-250
Core CRUD Operations
Create (Add)
The add method creates new entries. It supports cross-server entry conversion, ensuring that if an entry was originally from a different server type (e.g., OUD vs OpenLDAP), it is normalized for the target server before the operation. Sources: src/flext_ldap/services/operations.py:257-300
Read (Search)
Search operations utilize SearchOptions models to define scope, filters, and attribute limits. Before searching, the Base DN is normalized using u.Ldif.norm() to ensure consistency regardless of the input format. Sources: src/flext_ldap/services/operations.py:532-570, src/flext_ldap/_models/ldap.py:65-115
| Search Option | Type | Default | Description |
|---|---|---|---|
base_dn | str | Required | The starting point for the search. |
scope | str | SUBTREE | Search depth (BASE, ONE_LEVEL, SUBTREE). |
filter_str | str | (objectClass=*) | LDAP filter for entry selection. |
attributes | list | None | Specific attributes to return. |
size_limit | int | 0 | Maximum number of entries (0 = unlimited). |
Sources: src/flext_ldap/_models/ldap.py:65-80, tests/unit/test_models_search.py:27-40
Update (Modify)
The modify method applies a change set to an existing entry. Changes follow the ldap3 dictionary format, specifying whether to ADD, DELETE, or REPLACE attribute values. Sources: src/flext_ldap/services/operations.py:488-518
Delete
Deletes identify entries by DN. The service ensures the DN is validated and converted into a proper DN model before calling the adapter. Sources: src/flext_ldap/services/operations.py:433-470
Idempotent Upsert Logic
The "Upsert" operation implements a sophisticated "Add-or-Modify" pattern. This ensures that the system can be run repeatedly without causing duplicate entries or unnecessary LDAP writes. Sources: src/flext_ldap/services/operations.py:100-150
Upsert Workflow
The logic follows an optimistic "Add-then-Compare" flow:
Key Logic for Existing Entries
When an entry exists (detected via LDAP error 68), the _UpsertHandler performs a base_scope search. It compares the existing entry against the target state while ignoring operational attributes like modifyTimestamp. Sources: src/flext_ldap/services/operations.py:155-200, tests/unit/test_utilities.py:300-330
Batch Operations
The batch_upsert method processes sequences of entries, providing real-time progress tracking and failure management. Sources: src/flext_ldap/services/operations.py:315-350
Batch Configuration and Stats
Batch operations are governed by SyncPhaseConfig, which controls retry behavior and error handling. Sources: src/flext_ldap/_models/ldap.py:180-195
# Batch Statistics Model
class LdapBatchStats(m.BaseModel):
synced: int = 0 # Added + Modified
failed: int = 0 # Operation errors
skipped: int = 0 # No changes needed
Sources: src/flext_ldap/_models/ldap.py:133-145, src/flext_ldap/services/operations.py:397-420
Error Resilience
- Retry Mechanism: Retries are supported for specific transient error patterns (e.g., "session terminated"). Sources: src/flext_ldap/services/operations.py:585-610
- Stop on Error: If
stop_on_erroris enabled, the batch halts immediately upon a single entry failure, otherwise it continues and reports final aggregated statistics. Sources: src/flext_ldap/services/operations.py:345-365, tests/unit/test_operations.py:115-130
Summary
Active CRUD Operations in flext-ldap provide a robust, type-safe interface for directory management. By centralizing logic for normalization, idempotent writes, and batch processing, the library ensures high data integrity and auditability. The use of ROP result patterns and comprehensive models allows for clear execution paths and deterministic error handling in enterprise environments. Sources: src/flext_ldap/services/operations.py:1-40, src/flext_ldap/_models/ldap.py:250-280