issue-facade integration
This commit is contained in:
parent
c2b7cc64d6
commit
a907ed6f74
16 changed files with 642 additions and 95 deletions
|
|
@ -1,28 +1,111 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from issue_tracker.core.models import Issue, IssueState, Label, Priority
|
||||
|
||||
from .issue_backends import gitea_configured, local_backend, remote_backend
|
||||
|
||||
|
||||
class IssueAdapter(ABC):
|
||||
def aufgabe_zu_issue(aufgabe) -> Issue:
|
||||
"""Konvertiert eine Aufgabe in ein issue-facade Issue-Objekt."""
|
||||
prioritaet_map = {1: Priority.HIGH, 2: Priority.MEDIUM, 3: Priority.LOW}
|
||||
labels = [
|
||||
Label(name='task'),
|
||||
Label(name=f'priority:{prioritaet_map.get(aufgabe.prioritaet, Priority.MEDIUM).value}'),
|
||||
]
|
||||
if aufgabe.typ:
|
||||
labels.append(Label(name=aufgabe.typ))
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
return Issue(
|
||||
id='',
|
||||
number=0,
|
||||
title=aufgabe.titel,
|
||||
description=aufgabe.beschreibung or '',
|
||||
state=IssueState.OPEN,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
labels=labels,
|
||||
)
|
||||
|
||||
|
||||
def lokales_issue_erstellen(aufgabe) -> dict:
|
||||
"""
|
||||
Adapter-Basisklasse. Jeder externe Issue-Tracker implementiert diese
|
||||
Schnittstelle. Registrierung via ISSUE_ADAPTERS-Dict in settings.
|
||||
Legt ein Issue im lokalen SQLite-Backend an.
|
||||
Gibt {'issue_facade_id', 'issue_key', 'sync_status'} zurück.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def create_issue(self, aufgabe) -> dict:
|
||||
"""Legt ein Issue im externen System an. Gibt {'url', 'key'} zurück."""
|
||||
|
||||
@abstractmethod
|
||||
def fetch_status(self, external_issue) -> str:
|
||||
"""Liest den aktuellen Status aus dem externen System."""
|
||||
|
||||
@abstractmethod
|
||||
def close_issue(self, external_issue) -> None:
|
||||
"""Schließt das Issue im externen System."""
|
||||
issue = aufgabe_zu_issue(aufgabe)
|
||||
with local_backend() as b:
|
||||
created = b.create_issue(issue)
|
||||
return {
|
||||
'issue_facade_id': str(created.id),
|
||||
'issue_key': f'#{created.number}',
|
||||
'sync_status': created.state.value,
|
||||
}
|
||||
|
||||
|
||||
def get_adapter(system: str) -> 'IssueAdapter | None':
|
||||
"""Gibt den registrierten Adapter für `system` zurück, oder None."""
|
||||
from django.conf import settings
|
||||
adapters = getattr(settings, 'ISSUE_ADAPTERS', {})
|
||||
cls = adapters.get(system)
|
||||
return cls() if cls else None
|
||||
def an_gitea_delegieren(aufgabe, external_issue) -> dict:
|
||||
"""
|
||||
Schiebt ein lokales Issue nach Gitea.
|
||||
Gibt {'issue_facade_backend', 'issue_facade_id', 'issue_url', 'issue_key', 'sync_status'} zurück.
|
||||
Wirft ValueError wenn Gitea nicht konfiguriert ist.
|
||||
"""
|
||||
with remote_backend() as b:
|
||||
if b is None:
|
||||
raise ValueError('Gitea nicht konfiguriert (ISSUE_FACADE_GITEA fehlt in settings)')
|
||||
issue = aufgabe_zu_issue(aufgabe)
|
||||
created = b.create_issue(issue)
|
||||
url = ''
|
||||
if created.sync_metadata:
|
||||
url = created.sync_metadata.get('url', '')
|
||||
return {
|
||||
'issue_facade_backend': 'gitea',
|
||||
'issue_facade_id': str(created.number),
|
||||
'issue_url': url,
|
||||
'issue_key': f'#{created.number}',
|
||||
'sync_status': created.state.value,
|
||||
}
|
||||
|
||||
|
||||
def status_synchronisieren(external_issue) -> str:
|
||||
"""
|
||||
Liest den aktuellen Status aus dem konfigurierten Backend.
|
||||
Gibt den neuen sync_status-String zurück.
|
||||
"""
|
||||
from django.utils import timezone as dj_tz
|
||||
|
||||
backend_ctx = (
|
||||
remote_backend() if external_issue.issue_facade_backend == 'gitea'
|
||||
else local_backend()
|
||||
)
|
||||
with backend_ctx as b:
|
||||
if b is None:
|
||||
raise ValueError('Backend nicht verfügbar')
|
||||
issue = b.get_issue(external_issue.issue_facade_id)
|
||||
|
||||
neuer_status = issue.state.value if issue else 'error'
|
||||
external_issue.sync_status = neuer_status
|
||||
external_issue.letzter_sync = dj_tz.now()
|
||||
external_issue.save(update_fields=['sync_status', 'letzter_sync'])
|
||||
return neuer_status
|
||||
|
||||
|
||||
def issue_schliessen(external_issue) -> None:
|
||||
"""Setzt das Issue im Backend auf CLOSED."""
|
||||
backend_ctx = (
|
||||
remote_backend() if external_issue.issue_facade_backend == 'gitea'
|
||||
else local_backend()
|
||||
)
|
||||
with backend_ctx as b:
|
||||
if b is None:
|
||||
return
|
||||
issue = b.get_issue(external_issue.issue_facade_id)
|
||||
if issue:
|
||||
issue.state = IssueState.CLOSED
|
||||
b.update_issue(issue)
|
||||
external_issue.sync_status = 'closed'
|
||||
external_issue.save(update_fields=['sync_status'])
|
||||
|
||||
|
||||
def get_adapter(system: str):
|
||||
"""Rückwärtskompatible Stub — gibt None zurück (kein manuelles Adapter-Dict mehr)."""
|
||||
return None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue