|
1 | | -from common.clients import logger |
2 | | -from common.clients import STREAM_NAME |
| 1 | +from typing import Any, Dict |
| 2 | + |
| 3 | +from common.clients import logger, STREAM_NAME |
3 | 4 | from common.log_decorator import logging_decorator |
4 | 5 | from common.aws_lambda_event import AwsLambdaEvent |
5 | 6 | from exceptions.id_sync_exception import IdSyncException |
6 | 7 | from record_processor import process_record |
7 | | -''' |
8 | | -Lambda function handler for processing SQS events.Lambda for ID Sync. Fired by SQS |
9 | | -''' |
10 | 8 |
|
| 9 | +""" |
| 10 | +- Parses the incoming AWS event into `AwsLambdaEvent` and iterate its `records`. |
| 11 | +- Delegate each record to `process_record` and collect `nhs_number` from each result. |
| 12 | +- If any record has status == "error" raise `IdSyncException` with aggregated nhs_numbers. |
| 13 | +- Any unexpected error is wrapped into `IdSyncException(message="Error processing id_sync event")`. |
| 14 | +""" |
11 | 15 |
|
12 | 16 | @logging_decorator(prefix="id_sync", stream_name=STREAM_NAME) |
13 | | -def handler(event_data, _): |
14 | | - |
| 17 | +def handler(event_data: Dict[str, Any], _context) -> Dict[str, Any]: |
15 | 18 | try: |
16 | 19 | logger.info("id_sync handler invoked") |
17 | 20 | event = AwsLambdaEvent(event_data) |
18 | | - record_count = len(event.records) |
19 | | - if record_count > 0: |
20 | | - logger.info("id_sync processing event with %d records", record_count) |
21 | | - error_count = 0 |
22 | | - nhs_numbers = [] |
23 | | - for record in event.records: |
24 | | - record_result = process_record(record) |
25 | | - nhs_numbers.append(record_result["nhs_number"]) |
26 | | - if record_result["status"] == "error": |
27 | | - error_count += 1 |
28 | | - if error_count > 0: |
29 | | - raise IdSyncException(message=f"Processed {record_count} records with {error_count} errors", |
30 | | - nhs_numbers=nhs_numbers) |
31 | | - |
32 | | - else: |
33 | | - response = {"status": "success", |
34 | | - "message": f"Successfully processed {record_count} records", |
35 | | - "nhs_numbers": nhs_numbers} |
36 | | - else: |
37 | | - response = {"status": "success", "message": "No records found in event"} |
| 21 | + records = event.records |
| 22 | + |
| 23 | + if not records: |
| 24 | + return {"status": "success", "message": "No records found in event"} |
| 25 | + |
| 26 | + logger.info("id_sync processing event with %d records", len(records)) |
| 27 | + |
| 28 | + # Process records in order. Let any unexpected exception bubble to the outer handler |
| 29 | + # so tests that expect a wrapped IdSyncException keep working. |
| 30 | + results = [process_record(record) for record in records] |
| 31 | + nhs_numbers = [result["nhs_number"] for result in results] |
| 32 | + error_count = sum(1 for result in results if result.get("status") == "error") |
| 33 | + |
| 34 | + if error_count: |
| 35 | + raise IdSyncException(message=f"Processed {len(records)} records with {error_count} errors", |
| 36 | + nhs_numbers=nhs_numbers) |
| 37 | + |
| 38 | + response = {"status": "success", |
| 39 | + "message": f"Successfully processed {len(records)} records", |
| 40 | + "nhs_numbers": nhs_numbers} |
| 41 | + |
38 | 42 | logger.info("id_sync handler completed: %s", response) |
39 | 43 | return response |
| 44 | + |
40 | 45 | except IdSyncException as e: |
| 46 | + # Preserve domain exceptions but ensure they're logged |
41 | 47 | logger.exception(f"id_sync error: {e.message}") |
42 | | - raise e |
43 | | - except Exception as e: |
| 48 | + raise |
| 49 | + except Exception: |
44 | 50 | msg = "Error processing id_sync event" |
45 | 51 | logger.exception(msg) |
46 | | - raise IdSyncException(message=msg, exception=e) |
| 52 | + # Raise a domain exception with a predictable message for callers/tests |
| 53 | + raise IdSyncException(message=msg) |
0 commit comments