import hashlib import json from dataclasses import dataclass from datetime import datetime from typing import Any from django.db import transaction from commerce.models import SourceRecord, SourceSystem @dataclass(frozen=True) class IngestResult: record: SourceRecord created: bool changed: bool def payload_hash(payload: dict[str, Any]) -> str: canonical = json.dumps( payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"), default=str, ) return hashlib.sha256(canonical.encode("utf-8")).hexdigest() @transaction.atomic def ingest_source_record( *, source: SourceSystem, entity_type: str, external_id: str, payload: dict[str, Any], source_updated_at: datetime | None = None, ) -> IngestResult: """Idempotently retain the latest payload and report whether it changed.""" digest = payload_hash(payload) existing = ( SourceRecord.objects.select_for_update() .filter( source=source, entity_type=entity_type, external_id=str(external_id), ) .first() ) if existing is not None: changed = ( existing.payload_hash != digest or existing.source_updated_at != source_updated_at ) if changed: existing.payload = payload existing.payload_hash = digest existing.source_updated_at = source_updated_at existing.save( update_fields=[ "payload", "payload_hash", "source_updated_at", "received_at", ] ) else: existing.save(update_fields=["received_at"]) return IngestResult(record=existing, created=False, changed=changed) record = SourceRecord.objects.create( source=source, entity_type=entity_type, external_id=str(external_id), payload=payload, payload_hash=digest, source_updated_at=source_updated_at, ) return IngestResult(record=record, created=True, changed=True)