Files
MrBlake f36d6be511
CI / php-store (push) Waiting to run
CI / python-components (push) Waiting to run
feat: add NetBox plugin store
2026-08-24 20:51:25 +02:00

87 lines
3.2 KiB
Python

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",))