- Use searchable multi-select with select all/clear\n- Track bulk progress per policy type\n- Update tests and spec tasks/quickstart
141 lines
6.3 KiB
Markdown
141 lines
6.3 KiB
Markdown
# Implementation Plan: Inventory Sync Button (046)
|
|
|
|
**Branch**: `046-inventory-sync-button` | **Date**: 2026-01-08 | **Spec**: `specs/046-inventory-sync-button/spec.md`
|
|
**Input**: Feature specification from `specs/046-inventory-sync-button/spec.md`
|
|
|
|
## Summary
|
|
|
|
Add a “Run Inventory Sync” action in the Inventory area that dispatches a queued job, records a new `InventorySyncRun` attributed to the initiating user, and provides visibility via:
|
|
|
|
- Filament database notifications
|
|
- Existing bottom-right progress widget (powered by `BulkOperationRun` + `BulkOperationProgress`)
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4.15
|
|
**Primary Dependencies**: Laravel 12, Filament v4, Livewire v3
|
|
**Storage**: PostgreSQL (JSONB used for run payload + error context)
|
|
**Testing**: Pest v4 (Laravel test runner)
|
|
**Target Platform**: Containerized (Laravel Sail locally), deployed via Dokploy
|
|
**Project Type**: Web application (Laravel + Filament admin panel)
|
|
**Performance Goals**: UI action should enqueue quickly (<2s perceived) and never block on Graph calls
|
|
**Constraints**: Tenant-isolated, idempotent start semantics, observable background execution, queue workers may be down
|
|
**Scale/Scope**: Single-tenant interactive action; one run per tenant/selection at a time (locks/concurrency)
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- Inventory-first: This feature refreshes Inventory (“last observed”) and does not touch snapshots/backups.
|
|
- Read/write separation: No Graph writes. Only reads + local DB writes (run records + inventory items).
|
|
- Graph contract path: Inventory sync already uses `GraphClientInterface`; this feature does not add new Graph endpoints.
|
|
- Deterministic capabilities: Uses `PolicyTypeResolver` + `config('tenantpilot.supported_policy_types')` to derive default selection.
|
|
- Tenant isolation: Uses `Tenant::current()` for all run creation and progress tracking.
|
|
- Automation: Uses existing inventory locks/concurrency; runs are observable via `InventorySyncRun` and `BulkOperationRun`.
|
|
- Data minimization: Inventory sync continues to store metadata + sanitized meta JSON; no secrets in notifications/logs.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/046-inventory-sync-button/
|
|
├── plan.md
|
|
├── research.md
|
|
├── data-model.md
|
|
├── quickstart.md
|
|
├── contracts/
|
|
│ └── internal-actions.md
|
|
└── tasks.md # Phase 2 output (/speckit.tasks) - not created here
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
|
|
```text
|
|
app/
|
|
├── Filament/
|
|
│ └── Pages/
|
|
│ └── InventoryLanding.php # add header action “Run Inventory Sync”
|
|
├── Jobs/
|
|
│ └── RunInventorySyncJob.php # new queued job
|
|
├── Models/
|
|
│ ├── InventorySyncRun.php # add user relationship (nullable)
|
|
│ └── BulkOperationRun.php # reused by progress widget
|
|
├── Services/
|
|
│ ├── BulkOperationService.php # reused for progress + audit
|
|
│ ├── Intune/
|
|
│ │ └── AuditLogger.php # reused for inventory audit trail
|
|
│ └── Inventory/
|
|
│ └── InventorySyncService.php # add entrypoint to run sync attributed to user
|
|
|
|
database/migrations/
|
|
└── xxxx_xx_xx_xxxxxx_add_user_id_to_inventory_sync_runs_table.php
|
|
|
|
resources/views/
|
|
└── filament/pages/inventory-landing.blade.php # may need to show action if not already in header
|
|
|
|
tests/Feature/
|
|
└── Inventory/InventorySyncButtonTest.php # new Pest feature test
|
|
```
|
|
|
|
**Structure Decision**: Web application, implemented in Filament page actions + Laravel queued job.
|
|
|
|
## Phase 0 Output (Research)
|
|
|
|
Completed in `specs/046-inventory-sync-button/research.md`.
|
|
|
|
Key decisions used in this plan:
|
|
- Reuse existing bottom-right progress widget by creating a `BulkOperationRun` (`resource=inventory`, `action=sync`).
|
|
- Authorize using existing tenant role capability: `User::canSyncTenant(Tenant::current())`.
|
|
- Default selection = “full inventory” (all supported policy types, foundations + dependencies enabled).
|
|
|
|
## Phase 1 Output (Design)
|
|
|
|
Completed in:
|
|
- `specs/046-inventory-sync-button/data-model.md`
|
|
- `specs/046-inventory-sync-button/contracts/internal-actions.md`
|
|
|
|
## Phase 2 Plan (Implementation Outline)
|
|
|
|
### 1) Data model: store initiator on run record
|
|
|
|
- Add nullable `user_id` foreign key to `inventory_sync_runs`.
|
|
- Add `InventorySyncRun::user()` relationship.
|
|
- Backfill is not required (existing rows can remain `null`).
|
|
|
|
### 2) Job orchestration: queued execution + progress tracking
|
|
|
|
- Add `RunInventorySyncJob` that:
|
|
- Loads the tenant + user + `BulkOperationRun`.
|
|
- Marks the `BulkOperationRun` as running (`BulkOperationService::start`).
|
|
- Runs inventory sync via `InventorySyncService` in a way that sets `InventorySyncRun.user_id`.
|
|
- Records bulk progress as a single-item operation:
|
|
- success: `recordSuccess` + `complete`
|
|
- failure/skip: `recordFailure`/`recordSkippedWithReason` + `complete` (or `fail` for hard failures)
|
|
- Sends Filament database notifications to the initiating user on completion/failure.
|
|
|
|
### 3) UI: Inventory “Run Inventory Sync” action
|
|
|
|
- Add a header action on `InventoryLanding`:
|
|
- Visible/authorized only if `auth()->user()` is a `User` and `canSyncTenant(Tenant::current())`.
|
|
- On click:
|
|
- Compute default selection payload.
|
|
- Pre-flight check: if a matching `InventorySyncRun` is currently `running` (same tenant + selection hash), show an informational notification and do not dispatch.
|
|
- Create a `BulkOperationRun` via `BulkOperationService::createRun(...)` with `total_items=1`.
|
|
- Send a “started” Filament DB notification with the “check bottom right progress bar” copy.
|
|
- Dispatch `RunInventorySyncJob` with identifiers.
|
|
|
|
### 4) Audit trail
|
|
|
|
- Use `AuditLogger` to emit at minimum:
|
|
- `inventory.sync.dispatched`
|
|
- terminal event keyed by outcome: `inventory.sync.completed|inventory.sync.failed|inventory.sync.skipped`
|
|
|
|
### 5) Tests (Pest)
|
|
|
|
- New feature test covering:
|
|
- authorized user can dispatch a sync from the Inventory page and it creates a `BulkOperationRun`.
|
|
- `RunInventorySyncJob` sets `InventorySyncRun.user_id`.
|
|
- unauthorized user cannot see/execute the action.
|
|
- concurrency case: when a run is already `running`, a second dispatch is prevented.
|