Summary
This PR implements Spec 049 – Backup/Restore Job Orchestration: all critical Backup/Restore execution paths are job-only, idempotent, tenant-scoped, and observable via run records + DB notifications (Phase 1). The UI no longer performs heavy Graph work inside request/Filament actions for these flows.
Why
We want predictable UX and operations at MSP scale:
• no timeouts / long-running requests
• reproducible run state + per-item results
• safe error persistence (no secrets / no token leakage)
• strict tenant isolation + auditability for write paths
What changed
Foundational (Runs + Idempotency + Observability)
• Added a shared RunIdempotency helper (dedupe while queued/running).
• Added a read-only BulkOperationRuns surface (list + view) for status/progress.
• Added DB notifications for run status changes (with “View run” link).
US1 – Policy “Capture snapshot” is job-only
• Policy detail “Capture snapshot” now:
• creates/reuses a run (dedupe key: tenant + policy.capture_snapshot + policy DB id)
• dispatches a queued job
• returns immediately with notification + link to run detail
• Graph capture work moved fully into the job; request path stays Graph-free.
US3 – Restore runs orchestration is job-only + safe
• Live restore execution is queued and updates RestoreRun status/progress.
• Per-item outcomes are persisted deterministically (per internal DB record).
• Audit logging is written for live restore.
• Preview/dry-run is enforced as read-only (no writes).
Tenant isolation / authorization (non-negotiable)
• Run list/view/start are tenant-scoped and policy-guarded (cross-tenant access => 403, not 404).
• Explicit Pest tests cover cross-tenant denial and start authorization.
Tests / Verification
• ./vendor/bin/pint --dirty
• Targeted suite (examples):
• policy capture snapshot queued + idempotency tests
• restore orchestration + audit logging + preview read-only tests
• run authorization / tenant isolation tests
Notes / Scope boundaries
• Phase 1 UX = DB notifications + run detail page. A global “progress widget” is tracked as Phase 2 and not required for merge.
• Resilience/backoff is tracked in tasks but can be iterated further after merge.
Review focus
• Dedupe behavior for queued/running runs (reuse vs create-new)
• Tenant scoping & policy gates for all run surfaces
• Restore safety: audit event + preview no-writes
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #56
Feature 048: Backup/Restore UI Graph-Safety (Phase 1)
Dieses PR entfernt Microsoft Graph Calls aus UI-Renderpfaden (Filament/Livewire mount/render/options/typeahead/labels) in den kritischen Backup/Restore Screens und fügt Fail-Hard Guard Tests hinzu, die regressionssicher verhindern, dass UI-Rendering wieder Graph aufruft.
⸻
Motivation
Backup/Restore UI wurde teilweise “fragil”, weil UI-Komponenten (z.B. Group Typeahead/Option Labels) Graph/Entra direkt beim Rendern triggern konnten. Das führt zu:
• langsamen/unstabilen Seiten (429/Timeout/Permissions)
• schwer reproduzierbaren UI-Fehlern im MSP-Scale
• unnötiger Kopplung von “Page render” an Graph-Verfügbarkeit
Ziel: UI muss DB-only rendern; Graph darf nur in Jobs/Run-Execution stattfinden.
⸻
Scope / Changes
1) Restore Wizard: Entfernt Graph-Typeahead & Label-Resolution
• Group Mapping ist jetzt DB-only:
• manuelle GUID Eingabe / Skip
• GUID Validation
• Helper Text, wo die Object ID zu finden ist
• Keine Graph calls mehr in options() / getOptionLabelUsing() / typeahead beim Rendern.
2) Fail-Hard Guard Tests (Graph-Safety)
• Neue Test-Infrastruktur: FailHardGraphClient (GraphClientInterface darf nicht aufgerufen werden)
• Guard Tests als Pest Feature Tests (HTTP GET):
• Backup Sets Index rendert mit fail-hard Graph client
• Restore Wizard Route rendert mit fail-hard Graph client
• Assertions:
• 200 OK
• plus stable UI marker string
• Masking/Fallback Format ist deterministisch: Unresolved (…<last8>)
3) Spec/Plan/Tasks/Checklist
• Spec 048 aktualisiert, Tasks abgehakt
• requirements.md Checklist Gate: PASS
⸻
Out of Scope / Non-Goals
• Kein Umbau der “Execution”-Actions zu Jobs (Capture snapshot, Restore rerun, Dry-Run execution etc.) → eigener Folge-Spec (Phase 2).
• Keine Entra Group Name Resolution (separates Group-Inventory/Cache Feature).
• Keine neuen Tabellen/Migrations in Phase 1.
⸻
How to verify (manual)
Mit absichtlich kaputtem Tenant/Auth (Graph failt):
1. Öffne Backups & Restore → Backup Sets
✅ muss laden (UI render DB-only)
2. Öffne Restore Runs → Create Restore Run (Wizard)
✅ muss laden, kein Group-Typeahead mehr
3. Starte eine Restore Operation
❌ darf fehlschlagen (Graph kaputt) – wichtig ist: Render bleibt stabil, Run zeigt Fehler sauber pro Item.
⸻
Tests / Validation
Executed:
• ./vendor/bin/pint --dirty ✅
• ./vendor/bin/sail artisan test tests/Feature/Filament/BackupSetGraphSafetyTest.php tests/Feature/Filament/RestoreWizardGraphSafetyTest.php ✅
• (optional) Combined targeted suite ✅
⸻
Notes
• This PR intentionally focuses on UI Graph-Safety only.
• Any future reintroduction of Graph search/typeahead in UI must go through contracts first and be executed asynchronously, never in UI render paths.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #55
Wichtige Änderungen:
- Eine neue "Restore via Wizard"-Aktion wurde der PolicyVersion-Tabelle hinzugefügt.
- Diese Aktion ermöglicht die Erstellung eines Einzelposten-BackupSets aus dem ausgewählten
Policy-Version-Snapshot.
- Der CreateRestoreRun Wizard unterstützt nun das Vorbefüllen seiner Formularfelder basierend auf
Abfrageparametern, was eine nahtlose Übergabe von der PolicyVersion-Aktion ermöglicht.
- Umfassende Feature-Tests wurden hinzugefügt, um die korrekte Funktionalität und Integration dieses
neuen Workflows sicherzustellen.
- Die specs/011-restore-run-wizard/tasks.md wurde aktualisiert, um den Abschluss von Aufgabe T023
widerzuspiegeln.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #17
Problem: Restore nutzt bisher den Snapshot aus dem BackupSet (BackupItem). Wenn der Snapshot “unvollständig”/nicht der gewünschte Stand ist, landen nach Restore nur wenige Admin-Template-Settings in Intune.
Lösung:
Neue Action “Restore to Intune” direkt an einer konkreten PolicyVersion (inkl. Dry-Run Toggle) → reproduzierbarer Rollback auf exakt diese Version.
Restore-UI zeigt jetzt PolicyVersion-Nummer (version: X) in der Item-Auswahl + BackupSet Items Tabelle hat eine Version-Spalte.
Implementierung:
RestoreService::executeFromPolicyVersion() erzeugt dafür einen kleinen, temporären BackupSet+BackupItem aus der Version und startet einen normalen RestoreRun.
Pest-Test: PolicyVersionRestoreToIntuneTest.php
Specs/TODO:
Offene Follow-ups sind dokumentiert in tasks.md unter “Open TODOs (Follow-up)”.
QA (GUI):
Inventory → Policies → <Policy> → Versions → Restore to Intune (erst Dry-Run, dann Execute)
Backups & Restore → Restore Runs → Create (bei Items steht version: X)
Backups & Restore → Backup Sets → <Set> (Version-Spalte)
Tests: PolicyVersionRestoreToIntuneTest.php
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #13
Resolves assignment filter names when Graph stores filter IDs at assignment root.
Tracks assignment fetch success/failure and shows clearer UI states for versions.
Adds scope tag fallback display in backup set items.
Restored versions now capture applied assignments consistently.
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #8