feat(sync-utils): add logging delegation method to BaseSyncUtils

This commit is contained in:
bsiggel
2026-03-08 18:38:48 +00:00
parent 91ae2947e5
commit 7fd6eed86d
2 changed files with 663 additions and 187 deletions

View File

@@ -1,129 +1,662 @@
# Documentation Index - Motia III
# BitByLaw Motia III - Developer Guide
## Getting Started
> **For AI Assistants**: This document contains all critical patterns, conventions, and best practices. Read this first to understand the codebase structure and ensure consistency.
**Quick Navigation:**
- [Core Concepts](#core-concepts) - System architecture and patterns
- [Step Development](#step-development-best-practices) - How to create new steps
- [Services](#service-layer-patterns) - Reusable business logic
- [Integrations](#external-integrations) - xAI, EspoCRM, Advoware
- [Testing & Debugging](#testing-and-debugging)
---
## Project Status
**Migration:** ✅ 100% Complete (21/21 Steps migrated from Motia v0.17 → Motia III v1.0-RC)
**New to the project?** Start here:
1. [README.md](../README.md) - Project Overview & Quick Start
2. [MIGRATION_STATUS.md](../MIGRATION_STATUS.md) - Migration Progress (100% Complete!)
3. [MIGRATION_COMPLETE_ANALYSIS.md](../MIGRATION_COMPLETE_ANALYSIS.md) - Complete Migration Analysis
2. [MIGRATION_GUIDE.md](../MIGRATION_GUIDE.md) - Complete migration patterns
3. [ARCHITECTURE.md](ARCHITECTURE.md) - System design and architecture
## Migration to Motia III
---
**Status: ✅ 100% Complete (21/21 Steps migrated)**
## Core Concepts
- **[MIGRATION_GUIDE.md](../MIGRATION_GUIDE.md)** - Complete migration patterns
- Old Motia v0.17 → Motia III v1.0-RC
- TypeScript + Python Hybrid → Pure Python
- Configuration changes, trigger patterns, API differences
- **[MIGRATION_STATUS.md](../MIGRATION_STATUS.md)** - Current migration status
- **[MIGRATION_COMPLETE_ANALYSIS.md](../MIGRATION_COMPLETE_ANALYSIS.md)** - Complete analysis
### System Overview
## Core Documentation
**Architecture:**
- **Pure Python** (Motia III)
- **Event-Driven** with queue-based async processing
- **Redis-backed** distributed locking and caching
- **REST APIs** for webhooks and proxies
### For Developers
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System design and architecture
- Components, Data Flow, Event-Driven Design
- Updated for Motia III patterns
**Key Components:**
1. **Steps** (`steps/`) - Business logic handlers (HTTP, Queue, Cron)
2. **Services** (`services/`) - Shared API clients and utilities
3. **Configuration** (`iii-config.yaml`) - System setup
### For Operations
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Production deployment (if available)
- Installation, systemd, nginx, Monitoring
- **[CONFIGURATION.md](CONFIGURATION.md)** - Environment configuration (if available)
- All environment variables, secrets management
## Component Documentation
### Steps (Business Logic)
**Advoware Proxy** ([Module README](../steps/advoware_proxy/README.md)):
Universal HTTP proxy for Advoware API with automatic authentication.
- GET, POST, PUT, DELETE proxies
- HMAC-512 authentication
- Redis token caching
**Calendar Sync** ([Module README](../steps/advoware_cal_sync/README.md)):
Bidirectional sync between Advoware appointments and Google Calendar.
- `calendar_sync_cron_step.py` - Auto trigger (every 15 min)
- `calendar_sync_api_step.py` - Manual trigger endpoint
- `calendar_sync_all_step.py` - Employee cascade handler
- `calendar_sync_event_step.py` - Per-employee sync logic (1053 lines)
**VMH Integration** ([Module README](../steps/vmh/README.md)):
Webhooks and bidirectional sync between EspoCRM and Advoware.
- **Beteiligte Sync** (Bidirectional EspoCRM ↔ Advoware)
- Cron job (every 15 min)
- Event handlers for create/update/delete
- **Webhooks** (6 endpoints)
- Beteiligte: create, update, delete
- Bankverbindungen: create, update, delete
### Services
Service modules providing API clients and business logic:
- `advoware_service.py` - Advoware API client (HMAC-512 auth, token caching)
- `espocrm.py` - EspoCRM API client
- `advoware.py` - Legacy Advoware service (deprecated)
- Sync utilities and mappers
## Motia III Patterns
### Step Configuration
**Old (Motia v0.17):**
```python
config = {
'type': 'api', # or 'event', 'cron'
'name': 'MyStep',
'method': 'POST',
'path': '/my-step',
'emits': ['my-event'],
'subscribes': ['other-event']
}
**Data Flow:**
```
Webhook (HTTP) → Queue Event → Event Handler → External APIs
↓ ↓
Redis Lock Service Layer
(EspoCRM, Advoware, xAI)
```
**New (Motia III):**
---
## Step Development Best Practices
### File Naming Convention
**CRITICAL: Always use `_step.py` suffix!**
```
✅ CORRECT:
steps/vmh/webhook/document_create_api_step.py
steps/vmh/document_sync_event_step.py
steps/vmh/beteiligte_sync_cron_step.py
❌ WRONG:
steps/vmh/document_handler.py # Missing _step.py
steps/vmh/sync.py # Missing _step.py
```
**Naming Pattern:**
- **Webhooks**: `{entity}_{action}_api_step.py`
- Examples: `beteiligte_create_api_step.py`, `document_update_api_step.py`
- **Event Handlers**: `{entity}_sync_event_step.py`
- Examples: `document_sync_event_step.py`, `beteiligte_sync_event_step.py`
- **Cron Jobs**: `{entity}_sync_cron_step.py`
- Examples: `beteiligte_sync_cron_step.py`, `calendar_sync_cron_step.py`
### Step Template
**Complete step template with all required patterns:**
```python
from motia import http, queue, cron
"""Module-level docstring describing the step's purpose"""
from typing import Dict, Any
from motia import FlowContext, http, queue, cron, ApiRequest, ApiResponse
config = {
'name': 'MyStep',
'flows': ['my-flow'],
'triggers': [
http('POST', '/my-step') # or queue('topic') or cron('0 */15 * * * *')
"name": "Clear Human-Readable Name",
"description": "Brief description of what this step does",
"flows": ["flow-name"], # Logical grouping
"triggers": [
# Pick ONE trigger type:
http("POST", "/path/to/endpoint"), # For webhooks
# queue("topic.name"), # For event handlers
# cron("0 */15 * * * *"), # For scheduled jobs
],
'enqueues': ['my-event']
"enqueues": ["next.topic"], # Topics this step emits (optional)
}
async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
"""
Handler docstring explaining:
- What triggers this handler
- What it does
- What events it emits
"""
try:
# 1. Log entry
ctx.logger.info("=")="=" * 80)
ctx.logger.info(f"🔄 STEP STARTED: {config['name']}")
ctx.logger.info("=" * 80)
# 2. Extract and validate input
payload = request.body
# 3. Business logic
# ...
# 4. Enqueue events if needed
await ctx.enqueue({
'topic': 'next.step',
'data': {
'entity_id': entity_id,
'action': 'create'
}
})
# 5. Log success
ctx.logger.info("✅ Step completed successfully")
# 6. Return response
return ApiResponse(
status_code=200,
body={'success': True}
)
except Exception as e:
# Always log errors with context
ctx.logger.error(f"❌ Error in {config['name']}: {e}")
ctx.logger.error(f"Payload: {request.body}")
return ApiResponse(
status_code=500,
body={'success': False, 'error': str(e)}
)
```
### Handler Signatures
### Handler Signatures by Trigger Type
**HTTP Trigger:**
**HTTP Trigger (Webhooks, APIs):**
```python
from motia import ApiRequest, ApiResponse, FlowContext
async def handler(request: ApiRequest, ctx: FlowContext) -> ApiResponse:
# Access: request.body, request.query_params, request.path_params
# Enqueue: await ctx.enqueue(topic='...', data={...})
# Log: ctx.logger.info('...')
return ApiResponse(status=200, body={...})
return ApiResponse(status_code=200, body={...})
```
**Queue Trigger:**
**Queue Trigger (Event Handlers):**
```python
async def handler(input_data: dict, ctx: FlowContext) -> None:
# Process queue data
await ctx.enqueue(topic='next-step', data={...})
from motia import FlowContext
async def handler(event_data: Dict[str, Any], ctx: FlowContext) -> None:
# Process event_data
# No return value
pass
```
**Cron Trigger:**
**Cron Trigger (Scheduled Jobs):**
```python
from motia import FlowContext
async def handler(input_data: None, ctx: FlowContext) -> None:
# Cron jobs receive no input
ctx.logger.info('Cron triggered')
# No return value
pass
```
### Key Differences from Old Motia
### Logging Best Practices
**ALWAYS use `ctx.logger`, NEVER use `print()` or module-level `logger`:**
```python
# ✅ CORRECT: Context-aware logging
ctx.logger.info("Processing started")
ctx.logger.debug(f"Data: {data}")
ctx.logger.warn("Skipping invalid entry")
ctx.logger.error(f"Failed: {e}")
# ❌ WRONG: Direct print or module logger
print("Processing started") # Not visible in iii logs
logger.info("Processing started") # Loses context
```
**Log Levels:**
- `info()` - Normal operations (start, success, counts)
- `debug()` - Detailed data dumps (payloads, responses)
- `warn()` - Non-critical issues (skipped items, fallbacks)
- `error()` - Failures requiring attention
**Log Structure:**
```python
# Section headers with visual separators
ctx.logger.info("=" * 80)
ctx.logger.info("🔄 SYNC HANDLER STARTED")
ctx.logger.info("=" * 80)
ctx.logger.info(f"Entity Type: {entity_type}")
ctx.logger.info(f"Action: {action.upper()}")
ctx.logger.info(f"Document ID: {entity_id}")
ctx.logger.info("=" * 80)
# Use emojis for visual scanning
ctx.logger.info("📥 Downloading file...")
ctx.logger.info("✅ Downloaded 1024 bytes")
ctx.logger.error("❌ Upload failed")
ctx.logger.warn("⚠️ No collections found")
```
### Event Topics Naming
**Pattern:** `{module}.{entity}.{action}`
```
✅ Examples:
vmh.document.create
vmh.document.update
vmh.document.delete
vmh.beteiligte.create
calendar.sync.employee
advoware.proxy.response
❌ Avoid:
document-create # Use dots, not dashes
DocumentCreate # Use lowercase
create_document # Wrong order
```
---
## Service Layer Patterns
### Service Base Class Pattern
**All services should follow this pattern:**
```python
"""Service docstring"""
import logging
from typing import Optional
from services.logging_utils import get_service_logger
logger = logging.getLogger(__name__)
class MyService:
"""Service for interacting with External API"""
def __init__(self, context=None):
"""
Initialize service.
Args:
context: Optional Motia FlowContext for logging
"""
self.context = context
self.logger = get_service_logger('my_service', context)
self._session = None
# Load config from env
self.api_key = os.getenv('MY_API_KEY', '')
if not self.api_key:
raise ValueError("MY_API_KEY not configured")
self.logger.info("MyService initialized")
def _log(self, message: str, level: str = 'info') -> None:
"""Internal logging helper"""
log_func = getattr(self.logger, level, self.logger.info)
log_func(message)
async def _get_session(self):
"""Lazy session initialization"""
if self._session is None or self._session.closed:
self._session = aiohttp.ClientSession()
return self._session
async def close(self) -> None:
"""Cleanup resources"""
if self._session and not self._session.closed:
await self._session.close()
```
### Sync Utilities Pattern
**For bidirectional sync operations, inherit from `BaseSyncUtils`:**
```python
from services.sync_utils_base import BaseSyncUtils
class MyEntitySync(BaseSyncUtils):
"""Sync utilities for MyEntity"""
def _get_lock_key(self, entity_id: str) -> str:
"""Required: Define lock key pattern"""
return f"sync_lock:myentity:{entity_id}"
async def should_sync(self, entity: Dict) -> Tuple[bool, str]:
"""
Decide if sync is needed.
Returns:
(needs_sync: bool, reason: str)
"""
# Implementation
pass
```
**Base class provides:**
- `_log()` - Context-aware logging
- `_acquire_redis_lock()` - Distributed locking
- `_release_redis_lock()` - Lock cleanup
- `self.espocrm` - EspoCRM API client
- `self.redis` - Redis client
- `self.context` - Motia context
- `self.logger` - Integration logger
---
## External Integrations
### xAI Collections Integration
**Status:** ✅ Fully Implemented
**Service:** `services/xai_service.py`
**Environment Variables:**
```bash
XAI_API_KEY=xai-... # For file uploads (api.x.ai)
XAI_MANAGEMENT_KEY=xai-token-... # For collections (management-api.x.ai)
```
**Usage Example:**
```python
from services.xai_service import XAIService
xai = XAIService(ctx)
# Upload file
file_id = await xai.upload_file(
file_content=bytes_data,
filename="document.pdf",
mime_type="application/pdf"
)
# Add to collection
await xai.add_to_collection("collection_id", file_id)
# Add to multiple collections
added = await xai.add_to_collections(["col1", "col2"], file_id)
# Remove from collection
await xai.remove_from_collection("collection_id", file_id)
```
**Architecture:**
- Files are uploaded ONCE to Files API (`api.x.ai/v1/files`)
- Same `file_id` can be added to MULTIPLE collections
- Removing from collection does NOT delete the file (may be used elsewhere)
- Hash-based change detection prevents unnecessary reuploads
**Document Sync Flow:**
```
1. EspoCRM Webhook → vmh.document.{create|update|delete}
2. Document Sync Handler:
a. Acquire distributed lock (prevents duplicate syncs)
b. Load document from EspoCRM
c. Check if sync needed:
- dateiStatus = "Neu" or "Geändert" → SYNC
- Hash changed → SYNC
- Entity has xAI collections → SYNC
d. Download file from EspoCRM
e. Calculate MD5 hash
f. Upload to xAI (or reuse existing file_id)
g. Add to required collections
h. Update EspoCRM metadata (xaiFileId, xaiCollections, xaiSyncedHash)
i. Release lock
3. Delete: Remove from collections (keep file)
```
**EspoCRM Fields (CDokumente):**
```python
{
'xaiId': 'file-abc123', # xAI file_id
'xaiCollections': ['col1', 'col2'], # Array of collection IDs
'xaiSyncedHash': 'abc123def456', # MD5 at last sync
'fileStatus': 'synced', # Status: neu, geändert, synced
'md5sum': 'abc123def456', # Current file hash
'sha256': 'def456...', # SHA-256 (optional)
}
```
### EspoCRM Integration
**Service:** `services/espocrm.py`
**Environment Variables:**
```bash
ESPOCRM_API_BASE_URL=https://crm.bitbylaw.com/api/v1
ESPOCRM_API_KEY=your-api-key
ESPOCRM_API_TIMEOUT_SECONDS=30
```
**Usage:**
```python
from services.espocrm import EspoCRMAPI
espocrm = EspoCRMAPI(ctx)
# Get entity
entity = await espocrm.get_entity('CDokumente', entity_id)
# Update entity
await espocrm.update_entity('CDokumente', entity_id, {
'xaiId': file_id,
'fileStatus': 'synced'
})
# List entities
result = await espocrm.list_entities(
'CDokumente',
where=[{'type': 'equals', 'attribute': 'fileStatus', 'value': 'neu'}],
select='id,name,fileStatus',
max_size=50
)
# Download attachment
file_bytes = await espocrm.download_attachment(attachment_id)
```
### Advoware Integration
**Service:** `services/advoware_service.py`
**Authentication:** HMAC-512 with token caching
**Environment Variables:**
```bash
ADVOWARE_API_BASE_URL=https://api.advoware.de
ADVOWARE_API_KEY=your-key
ADVOWARE_API_SECRET=your-secret
REDIS_HOST=localhost
REDIS_PORT=6379
```
**Proxy Endpoints:**
- `GET /advoware/proxy?endpoint={path}` - Proxy GET requests
- `POST /advoware/proxy` - Proxy POST requests
- `PUT /advoware/proxy` - Proxy PUT requests
- `DELETE /advoware/proxy` - Proxy DELETE requests
---
## Testing and Debugging
### Start System
```bash
# Start iii Engine
cd /opt/motia-iii/bitbylaw
/opt/bin/iii -c iii-config.yaml
# Start iii Console (Web UI)
/opt/bin/iii-console --enable-flow --host 0.0.0.0 --port 3113 \
--engine-host 192.168.67.233 --engine-port 3111 --ws-port 3114
```
### Check Registered Steps
```bash
curl http://localhost:3111/_console/functions | python3 -m json.tool
```
### Test HTTP Endpoint
```bash
# Test document webhook
curl -X POST "http://localhost:3111/vmh/webhook/document/create" \
-H "Content-Type: application/json" \
-d '[{"id": "test123", "entityType": "CDokumente"}]'
# Test advoware proxy
curl "http://localhost:3111/advoware/proxy?endpoint=employees"
```
### Manually Trigger Cron
```bash
curl -X POST "http://localhost:3111/_console/cron/trigger" \
-H "Content-Type: application/json" \
-d '{"function_id": "steps::VMH Beteiligte Sync Cron::trigger::0"}'
```
### View Logs
```bash
# Live logs via journalctl
journalctl -u motia-iii -f
# Search for specific step
journalctl --since "today" | grep -i "document sync"
# Check for errors
tail -100 /opt/motia-iii/bitbylaw/iii_new.log | grep -i error
```
### Common Issues
**Step not showing up:**
1. Check file naming: Must end with `_step.py`
2. Check for import errors: `grep -i "importerror\|traceback" iii.log`
3. Verify `config` dict is present
4. Restart iii engine
**Redis connection failed:**
- Check `REDIS_HOST` and `REDIS_PORT` environment variables
- Verify Redis is running: `redis-cli ping`
- Service will work without Redis but with warnings
**AttributeError '_log' not found:**
- Ensure service inherits from `BaseSyncUtils` OR
- Implement `_log()` method manually
---
## Key Patterns Summary
### ✅ DO
- **Always** use `_step.py` suffix for step files
- **Always** use `ctx.logger` for logging (never `print`)
- **Always** wrap handlers in try/except with error logging
- **Always** use visual separators in logs (`"=" * 80`)
- **Always** return `ApiResponse` from HTTP handlers
- **Always** document what events a step emits
- **Always** use distributed locks for sync operations
- **Always** calculate hashes for change detection
### ❌ DON'T
- **Don't** use module-level `logger` in steps
- **Don't** forget `async` on handler functions
- **Don't** use blocking I/O (use `aiohttp`, not `requests`)
- **Don't** return values from queue/cron handlers
- **Don't** hardcode credentials (use environment variables)
- **Don't** skip lock cleanup in `finally` blocks
- **Don't** use `print()` for logging
---
## Module Documentation
---
## Module Documentation
### Steps
**Advoware Proxy** ([Module README](../steps/advoware_proxy/README.md))
- Universal HTTP proxy with HMAC-512 authentication
- Endpoints: GET, POST, PUT, DELETE
- Redis token caching
**Calendar Sync** ([Module README](../steps/advoware_cal_sync/README.md))
- Bidirectional Advoware ↔ Google Calendar sync
- Cron: Every 15 minutes
- API trigger: `/advoware/calendar/sync`
**VMH Integration** ([Module README](../steps/vmh/README.md))
- EspoCRM ↔ Advoware bidirectional sync
- Webhooks: Beteiligte, Bankverbindungen, Documents
- xAI Collections integration for documents
### Services
| Service | Purpose | Config |
|---------|---------|--------|
| `xai_service.py` | xAI file uploads & collections | `XAI_API_KEY`, `XAI_MANAGEMENT_KEY` |
| `espocrm.py` | EspoCRM REST API client | `ESPOCRM_API_BASE_URL`, `ESPOCRM_API_KEY` |
| `advoware_service.py` | Advoware API with HMAC auth | `ADVOWARE_API_KEY`, `ADVOWARE_API_SECRET` |
| `document_sync_utils.py` | Document sync logic | Redis connection |
| `beteiligte_sync_utils.py` | Beteiligte sync logic | Redis connection |
| `sync_utils_base.py` | Base class for sync utils | - |
---
## Quick Reference
### Environment Variables
**Required:**
```bash
# EspoCRM
ESPOCRM_API_BASE_URL=https://crm.bitbylaw.com/api/v1
ESPOCRM_API_KEY=your-key
# Advoware
ADVOWARE_API_BASE_URL=https://api.advoware.de
ADVOWARE_API_KEY=your-key
ADVOWARE_API_SECRET=your-secret
# xAI
XAI_API_KEY=xai-...
XAI_MANAGEMENT_KEY=xai-token-...
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB_ADVOWARE_CACHE=1
```
**Optional:**
```bash
ESPOCRM_API_TIMEOUT_SECONDS=30
ESPOCRM_METADATA_TTL_SECONDS=300
```
### File Structure
```
bitbylaw/
├── iii-config.yaml # Motia III configuration
├── pyproject.toml # Python dependencies (uv)
├── steps/ # Business logic
│ ├── advoware_proxy/
│ ├── advoware_cal_sync/
│ └── vmh/
│ ├── webhook/ # HTTP webhook handlers
│ │ ├── *_create_api_step.py
│ │ ├── *_update_api_step.py
│ │ └── *_delete_api_step.py
│ ├── *_sync_event_step.py # Queue event handlers
│ └── *_sync_cron_step.py # Scheduled jobs
├── services/ # Shared services
│ ├── xai_service.py
│ ├── espocrm.py
│ ├── advoware_service.py
│ ├── *_sync_utils.py
│ ├── sync_utils_base.py
│ ├── logging_utils.py
│ └── exceptions.py
├── docs/ # Documentation
│ ├── INDEX.md # This file
│ ├── ARCHITECTURE.md
│ └── DOCUMENT_SYNC_XAI_STATUS.md
└── tests/ # Test scripts
└── test_xai_collections_api.py
```
### Motia III vs Old Motia
| Old Motia v0.17 | Motia III v1.0-RC |
|-----------------|-------------------|
@@ -132,115 +665,53 @@ async def handler(input_data: None, ctx: FlowContext) -> None:
| `type: 'cron'` | `triggers: [cron()]` |
| `context.emit()` | `ctx.enqueue()` |
| `emits: [...]` | `enqueues: [...]` |
| `subscribes: [...]` | Moved to trigger: `queue('topic')` |
| 5-field cron | 6-field cron (prepend seconds) |
| `subscribes: [...]` | `triggers: [queue('topic')]` |
| 5-field cron | 6-field cron (seconds first) |
| `context.logger` | `ctx.logger` |
| Motia Workbench | iii Console |
| Node.js + Python | Pure Python |
## Documentation Structure
### Cron Syntax
**6 fields (Motia III):** `second minute hour day month weekday`
```
docs/
├── INDEX.md # This file
├── ARCHITECTURE.md # System design (Motia III)
└── advoware/
└── (optional API specs)
steps/{module}/
├── README.md # Module overview
└── {step_name}_step.py # Step implementation
services/
└── {service_name}.py # Service implementations
MIGRATION_GUIDE.md # Complete migration guide
MIGRATION_STATUS.md # Migration progress
MIGRATION_COMPLETE_ANALYSIS.md # Final analysis
0 */15 * * * * # Every 15 minutes
0 0 */6 * * * # Every 6 hours
0 0 2 * * * # Daily at 2 AM
0 30 9 * * 1-5 # Monday-Friday at 9:30 AM
```
## Quick Reference
---
### Common Tasks
## Additional Resources
| Task | Documentation |
|------|---------------|
| Understand migration | [MIGRATION_GUIDE.md](../MIGRATION_GUIDE.md) |
| Check migration status | [MIGRATION_STATUS.md](../MIGRATION_STATUS.md) |
| Understand architecture | [ARCHITECTURE.md](ARCHITECTURE.md) |
| Calendar sync overview | [steps/advoware_cal_sync/README.md](../steps/advoware_cal_sync/README.md) |
| Proxy API usage | [steps/advoware_proxy/README.md](../steps/advoware_proxy/README.md) |
| VMH sync details | [steps/vmh/README.md](../steps/vmh/README.md) |
### Code Locations
| Component | Location | Documentation |
|-----------|----------|---------------|
| API Proxy Steps | `steps/advoware_proxy/` | [README](../steps/advoware_proxy/README.md) |
| Calendar Sync Steps | `steps/advoware_cal_sync/` | [README](../steps/advoware_cal_sync/README.md) |
| VMH Steps | `steps/vmh/` | [README](../steps/vmh/README.md) |
| Advoware Service | `services/advoware_service.py` | (in-code docs) |
| Configuration | `iii-config.yaml` | System config |
| Environment | `.env` or systemd | Environment variables |
## Running the System
### Start iii Engine
```bash
cd /opt/motia-iii/bitbylaw
/opt/bin/iii -c iii-config.yaml
```
### Start iii Console (Web UI)
```bash
/opt/bin/iii-console --enable-flow --host 0.0.0.0 --port 3113 \
--engine-host 192.168.67.233 --engine-port 3111 --ws-port 3114
```
### Access Web Console
Open browser: `http://localhost:3113/`
### Check Registered Steps
```bash
curl http://localhost:3111/_console/functions | python3 -m json.tool
```
## Testing
### Test HTTP Step
```bash
# Calendar sync API
curl -X POST "http://localhost:3111/advoware/calendar/sync" \
-H "Content-Type: application/json" \
-d '{"kuerzel": "PB"}'
# Advoware proxy
curl "http://localhost:3111/advoware/proxy?endpoint=employees"
```
### Trigger Cron Manually
```bash
curl -X POST "http://localhost:3111/_console/cron/trigger" \
-H "Content-Type: application/json" \
-d '{"function_id": "steps::Calendar Sync Cron Job::trigger::0"}'
```
### Check Logs
View logs in iii Console or via API:
```bash
curl "http://localhost:3111/_console/logs"
```
## External Resources
### Documentation
- [MIGRATION_GUIDE.md](../MIGRATION_GUIDE.md) - v0.17 → v1.0 migration
- [MIGRATION_STATUS.md](../MIGRATION_STATUS.md) - Migration progress
- [ARCHITECTURE.md](ARCHITECTURE.md) - System design
- [DOCUMENT_SYNC_XAI_STATUS.md](DOCUMENT_SYNC_XAI_STATUS.md) - xAI integration details
### External Resources
- [Motia III Documentation](https://iii.dev)
- [Python SDK](https://pypi.org/project/motia/)
- [Google Calendar API](https://developers.google.com/calendar)
- [xAI API Docs](https://docs.x.ai/)
- [EspoCRM API](https://docs.espocrm.com/development/api/)
- [Redis Documentation](https://redis.io/documentation)
## Support
### Support & Troubleshooting
- **Migration Questions**: Check [MIGRATION_GUIDE.md](../MIGRATION_GUIDE.md)
- **Runtime Issues**: Check iii Console logs
- **Step Not Showing**: Verify import errors in logs
- **Redis Issues**: Check Redis connection in `services/`
| Issue | Solution |
|-------|----------|
| Step not registered | Check `_step.py` suffix, restart iii engine |
| Import errors | Check logs: `grep -i importerror iii.log` |
| Redis unavailable | Service works with warnings, check `REDIS_HOST` |
| `'_log' not found` | Inherit from `BaseSyncUtils` or implement `_log()` |
| Webhook not triggering | Verify endpoint in iii Console, check EspoCRM config |
| xAI upload fails | Verify `XAI_API_KEY` and `XAI_MANAGEMENT_KEY` |
---
**Last Updated:** 2026-03-08
**Migration Status:** ✅ Complete
**xAI Integration:** ✅ Implemented

View File

@@ -42,6 +42,11 @@ class BaseSyncUtils:
"Distributed Locking deaktiviert - Race Conditions möglich!"
)
def _log(self, message: str, level: str = 'info') -> None:
"""Delegate logging to the logger with optional level"""
log_func = getattr(self.logger, level, self.logger.info)
log_func(message)
def _get_lock_key(self, entity_id: str) -> str:
"""
Erzeugt Redis Lock-Key für eine Entity