Status Update Committed the async “Add selected” flow: job-only handler, deterministic run reuse, sanitized failure tracking, observation updates, and the new BulkOperationService/Progress test coverage. All relevant tasks in tasks.md are marked done, and the checklist under requirements.md is fully satisfied (PASS). Ran ./vendor/bin/pint --dirty plus BackupSetPolicyPickerTableTest.php—all green. Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #59
5.5 KiB
| description |
|---|
| Task list for implementing Async “Add Policies” to Backup Set (052) |
Tasks: Async “Add Policies” to Backup Set (052)
Input: Design documents from /specs/052-async-add-policies/
Prerequisites: plan.md (required), spec.md (required)
Tests: Required (Pest), per spec.md (SC-001..SC-004).
Organization: Tasks are grouped by user story so each story can be implemented and tested independently.
Format: - [ ] T### [P?] [US#] Description (with file path)
- [P]: Can run in parallel (different files, no dependencies)
- [US#]: User story mapping (US1/US2/US3)
Path Conventions (Laravel)
- App code:
app/ - DB:
database/migrations/ - Filament admin:
app/Filament/Resources/ - Livewire tables:
app/Livewire/ - Tests (Pest):
tests/Feature/,tests/Unit/
Phase 1: Setup (Shared Infrastructure)
- T001 [P] Confirm existing run infra supports this operation (BulkOperationRun statuses + unique idempotency index) and document mapping queued↔pending, partial↔completed_with_errors in specs/052-async-add-policies/plan.md
- T002 [P] Decide and standardize taxonomy strings: operationType (
backup_set.add_policies), resource (backup_set), action (add_policies) in specs/052-async-add-policies/spec.md and plan.md
Phase 2: User Story 1 - Add selected policies without blocking (Priority: P1) 🎯 MVP
Goal: “Add selected” returns quickly and queues background work with an observable Run record.
Independent Test: Trigger the action and assert a job is queued and a run exists; no Graph/capture work occurs in-request.
Tests (write first) ⚠️
- T010 [P] [US1] Update/replace sync-path assertions in tests/Feature/Filament/BackupSetPolicyPickerTableTest.php to assert job dispatch + run creation
- T011 [P] [US1] Add fail-hard guard test to ensure no Graph calls occur during the bulk action (mock
App\\Services\\Graph\\GraphClientInterfaceand assert->never())
Implementation
- T020 [US1] Create queued job to add policies to a backup set in app/Jobs/AddPoliciesToBackupSetJob.php (uses run lifecycle + safe failures via BulkOperationService)
- T021 [US1] Update bulk action handler in app/Livewire/BackupSetPolicyPickerTable.php to create/reuse BulkOperationRun and dispatch AddPoliciesToBackupSetJob (no inline snapshot capture)
- T022 [US1] Emit “queued” notification with “View run” link on submit (Filament DB notification preferred) from app/Livewire/BackupSetPolicyPickerTable.php
- T023 [US1] Emit completion/failure DB notification from app/Jobs/AddPoliciesToBackupSetJob.php with a safe summary
Checkpoint: US1 complete — submit is non-blocking, job executes, run is visible.
Phase 3: User Story 2 - Double click / repeated submissions are deduplicated (Priority: P2)
Goal: Matching queued/running operations reuse the same run and do not enqueue duplicates.
Independent Test: Call the action twice for the same tenant + backup set + selection and assert only one run and one job dispatch.
Tests ⚠️
- T030 [P] [US2] Add idempotency test in tests/Feature/BackupSets/BackupSetAddPoliciesIdempotencyTest.php (or extend existing picker test) asserting one run + one queued job
Implementation
- T031 [US2] Implement deterministic selection hashing and idempotency key creation using app/Support/RunIdempotency.php (sorted policy ids + option flags), and reuse active runs via findActiveBulkOperationRun
- T032 [US2] Handle race conditions safely (unique index collisions) by recovering the existing run rather than failing the request
- T033 [US2] Ensure the action is always async (no
dispatchSyncpath for small selections) in app/Livewire/BackupSetPolicyPickerTable.php
Checkpoint: US2 complete — double clicks are safe and deduped.
Phase 4: User Story 3 - Failures are visible and safe (Priority: P3)
Goal: Failures are persisted safely and tenant isolation is enforced for run visibility.
Independent Test: Force failure paths and confirm safe failures persisted; cross-tenant access is forbidden.
Tests ⚠️
- T040 [P] [US3] Add tenant isolation test for the created run (403 cross-tenant) in tests/Feature/BackupSets/BackupSetAddPoliciesTenantIsolationTest.php (or extend tests/Feature/RunAuthorizationTenantIsolationTest.php)
- T041 [P] [US3] Add sanitization test: failure reason containing token-like content is stored as redacted (exercise BulkOperationService::sanitizeFailureReason)
Implementation
- T042 [US3] Ensure job records per-item failures with sanitized reasons and does not store raw Graph payloads in run failures or notifications (app/Jobs/AddPoliciesToBackupSetJob.php)
- T043 [US3] Record stable failure
reason_codevalues (per spec.md) alongside sanitized short text in run failures (app/Jobs/AddPoliciesToBackupSetJob.php and/or app/Services/BulkOperationService.php) - T044 [US3] Record “already in backup set” as
skipped(with reason_codealready_in_backup_set) and ensure counts match spec.md (app/Jobs/AddPoliciesToBackupSetJob.php) - T045 [US3] Ensure job processes all items (no circuit breaker abort) and run status reflects partial completion (app/Jobs/AddPoliciesToBackupSetJob.php)
Checkpoint: US3 complete — failures are safe and observable; tenant isolation holds.
Phase 5: Polish & Validation
- T050 [P] Run formatting on changed files with ./vendor/bin/pint --dirty
- T051 Run targeted tests: ./vendor/bin/sail artisan test --filter=BackupSetAddPolicies