"""Lambda function module for detect_errors.""" from typing import Any, Dict, List, Optional import config logger = config.app_logger def handler(event: Dict, context: Any) -> Dict: """Detect errors handler. Called after the SetTrackMetadata Map state. The SFN merges the Map output into the SM context via ``"ResultPath": "$.track_errors"``, so the event always has this shape:: { "correlation_id": "...", "project": { ... }, "product": { ... }, "tracks": [ ... ], "label_participants": [ ... ], "track_errors": [ null, {"Error": "...", "Cause": "..."}, null ] } Optionally, an upstream step failure (e.g. create_product) may have set ``errors`` on the context via ``"ResultPath": "$.errors"``:: { ...context..., "errors": {"Error": "...", "Cause": "..."} } All errors are collected into a list and set as ``event["errors"]``. When there are no errors the key is absent. ``track_errors`` is always removed from the output. ``Cause`` is always a JSON-encoded string (AWS SFN behaviour) - stored as-is, never parsed. Args: event: SM context dict with ``track_errors`` merged in by the SFN. context: Lambda execution context (unused). Returns: dict: SM context with ``errors`` list attached, or without it. """ logger.info(f'Triggered detect_errors: {event}') errors: List[Dict] = [] # Upstream step failure -- "ResultPath": "$.errors" produces a single dict. upstream_error: Optional[Dict] = event.pop('errors', None) if upstream_error: errors.append(upstream_error) # Per-track failures -- "ResultPath": "$.track_errors" produces a list # where each item is null (success) or {"Error": ..., "Cause": ...}. track_errors: List = event.pop('track_errors', None) or [] for item in track_errors: if item and item.get('Error') and item.get('Cause'): errors.append(item) if errors: logger.warning(f'Detected {len(errors)} error(s): {errors}') event['errors'] = errors return event