Model/infrastructure/sqs_client.py
Jun-te Kim 7b00a33cd2 infrastructure: typed S3/SQS clients (S3Client, CsvS3Client, SqsClient, Address2UprnQueueClient)
Slice 3/6 of the postcode_splitter refactor (Hestia-Homes/Model#1101).
Introduces a thin typed infrastructure layer wrapping boto3 for the AWS
side of the splitter. S3Client/SqsClient are bucket-/queue-bound byte
adapters; CsvS3Client subclasses S3Client to round-trip CSV row dicts
via the existing parse_s3_uri helper in utils/s3.py; Address2UprnQueueClient
subclasses SqsClient to publish the typed {task_id, sub_task_id, s3_uri}
fan-out body the downstream consumer expects. moto[s3,sqs] is pulled into
test.requirements.txt and the new tests/infrastructure/ suite exercises
each client against the moto backend (S3 round-trip, CSV round-trip,
SQS send + body inspection, typed publish + body inspection). pyright
--strict is clean on the new modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:12:21 +00:00

28 lines
943 B
Python

import json
from typing import Any
class SqsClient:
"""Thin typed wrapper around a boto3 SQS client bound to one queue URL.
The body is JSON-serialised here so callers can pass plain dictionaries
instead of constructing message strings themselves. Typed publish
helpers (e.g. :class:`Address2UprnQueueClient`) build on this contract.
"""
def __init__(self, boto_sqs_client: Any, queue_url: str) -> None:
self._client = boto_sqs_client
self._queue_url = queue_url
@property
def queue_url(self) -> str:
return self._queue_url
def send(self, body: dict[str, Any]) -> str:
"""JSON-serialise ``body`` and send it. Returns the SQS ``MessageId``."""
response: dict[str, Any] = self._client.send_message(
QueueUrl=self._queue_url,
MessageBody=json.dumps(body),
)
message_id: str = response["MessageId"]
return message_id