from __future__ import annotations from typing import Any from django.db import transaction from django.utils import timezone from .client import CatalogPlugin from .lifecycle import LifecycleRequest from .models import LifecycleAudit, ManagedPlugin from .redaction import redact_data, redact_text class DjangoLifecycleRepository: def has_other_pending_mutation(self, slug: str, audit_id: int) -> bool: return LifecycleAudit.objects.filter( slug=slug, dry_run=False, status=LifecycleAudit.Status.HANDED_OFF, ).exclude(pk=audit_id).exists() def create_audit(self, request: LifecycleRequest, actor_id: int | None) -> int: audit = LifecycleAudit.objects.create( actor_id=actor_id, slug=request.slug, action=request.action, requested_version=request.version, dry_run=request.dry_run, request_data={ "slug": request.slug, "action": request.action, "version": request.version, "dry_run": request.dry_run, }, ) return audit.pk def mark_running(self, audit_id: int) -> None: LifecycleAudit.objects.filter(pk=audit_id).update( status=LifecycleAudit.Status.RUNNING, started=timezone.now(), error="", ) def mark_success(self, audit_id: int, result: dict[str, Any]) -> None: LifecycleAudit.objects.filter(pk=audit_id).update( status=LifecycleAudit.Status.SUCCEEDED, result_data=redact_data(result), completed=timezone.now(), ) def mark_handed_off(self, audit_id: int, operation_id: str, result: dict[str, Any]) -> None: LifecycleAudit.objects.filter(pk=audit_id).update( status=LifecycleAudit.Status.HANDED_OFF, external_operation_id=operation_id, result_data=redact_data(result), ) def mark_failed(self, audit_id: int, error: str, result: dict[str, Any]) -> None: LifecycleAudit.objects.filter(pk=audit_id).update( status=LifecycleAudit.Status.FAILED, error=redact_text(error, limit=8_000), result_data=redact_data(result), completed=timezone.now(), ) @transaction.atomic def update_status(self, plugin: CatalogPlugin, **values: Any) -> None: safe_values = { "name": plugin.name, "package_name": plugin.package_name, "import_name": plugin.import_name, "repository_url": plugin.repository_url, "available_version": plugin.latest_version, "last_checked": timezone.now(), **values, } if "last_error" in safe_values: safe_values["last_error"] = redact_text(safe_values["last_error"], limit=4_000) obj = ManagedPlugin.objects.select_for_update().filter(slug=plugin.slug).first() if obj is None: ManagedPlugin.objects.create(slug=plugin.slug, **safe_values) else: for key, value in safe_values.items(): setattr(obj, key, value) obj.save(update_fields=tuple(safe_values) + ("updated",))