186 lines
6.4 KiB
Markdown
186 lines
6.4 KiB
Markdown
# Quickstart: 109 — Tenant Review Pack Export v1
|
|
|
|
**Date**: 2026-02-23
|
|
**Branch**: `109-review-pack-export`
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Sail services running (`vendor/bin/sail up -d`)
|
|
- Database migrated to latest
|
|
- At least one workspace with a connected tenant
|
|
- Tenant has stored reports (permission_posture and/or entra.admin_roles) and findings
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Foundation (Data Layer + Config)
|
|
|
|
**Goal:** Database table, model, factory, enum additions, config entries.
|
|
|
|
1. **Migration**: `create_review_packs_table`
|
|
- See [data-model.md](data-model.md) for full schema
|
|
- Includes partial unique index on `(workspace_id, tenant_id, fingerprint)` for non-expired/failed packs
|
|
|
|
2. **Model**: `ReviewPack`
|
|
- Uses `DerivesWorkspaceIdFromTenant`
|
|
- Casts: summary/options as array, generated_at/expires_at as datetime
|
|
- Relationships: workspace, tenant, operationRun, initiator
|
|
- Scopes: ready, expired, pastRetention, forTenant, latestReady
|
|
|
|
3. **Factory**: `ReviewPackFactory` with states: queued, generating, ready, failed, expired
|
|
|
|
4. **Enum**: Add `ReviewPackGenerate` to `OperationRunType`
|
|
|
|
5. **Enum**: Add `ReviewPackStatus` (standalone enum at `App\Support\ReviewPackStatus`)
|
|
|
|
6. **Config**:
|
|
- `config/filesystems.php` → `exports` disk
|
|
- `config/tenantpilot.php` → `review_pack` section
|
|
|
|
7. **Capabilities**: Add `REVIEW_PACK_VIEW` and `REVIEW_PACK_MANAGE` to `Capabilities.php`
|
|
|
|
8. **Badge**: Add `ReviewPackStatus` to `BadgeDomain` + create `ReviewPackStatusBadge` mapper
|
|
|
|
**Run**: `vendor/bin/sail artisan migrate && vendor/bin/sail artisan test --compact --filter=ReviewPack`
|
|
|
|
---
|
|
|
|
### Phase 2: Service Layer + Job
|
|
|
|
**Goal:** Generation logic, ZIP assembly, fingerprint computation.
|
|
|
|
1. **Service**: `ReviewPackService`
|
|
- `generate(Tenant, User, array $options): ReviewPack` — orchestrates creation
|
|
- `computeFingerprint(Tenant, array $options): string` — deterministic hash
|
|
- `generateDownloadUrl(ReviewPack): string` — signed URL
|
|
- `findExistingPack(Tenant, string $fingerprint): ?ReviewPack` — dedupe check
|
|
|
|
2. **Job**: `GenerateReviewPackJob`
|
|
- Collects data from StoredReport, Finding, Tenant, OperationRun
|
|
- Builds file map → assembles ZIP → stores on exports disk
|
|
- Updates ReviewPack status + metadata
|
|
- Sends notification on completion/failure
|
|
|
|
3. **Notification**: `ReviewPackStatusNotification`
|
|
- Database channel
|
|
- Ready: includes view page link
|
|
- Failed: includes sanitized reason
|
|
|
|
**Run**: `vendor/bin/sail artisan test --compact --filter=GenerateReviewPack`
|
|
|
|
---
|
|
|
|
### Phase 3: Download Controller
|
|
|
|
**Goal:** Signed URL download endpoint.
|
|
|
|
1. **Controller**: `ReviewPackDownloadController`
|
|
- Single `__invoke` method
|
|
- Validates pack exists + status is ready
|
|
- Streams file with proper headers (Content-Type, Content-Disposition, SHA256)
|
|
|
|
2. **Route**: `GET /admin/review-packs/{reviewPack}/download`
|
|
- Named `admin.review-packs.download`
|
|
- Middleware: `signed`
|
|
|
|
**Run**: `vendor/bin/sail artisan test --compact --filter=ReviewPackDownload`
|
|
|
|
---
|
|
|
|
### Phase 4: Filament UI
|
|
|
|
**Goal:** Resource, view page, dashboard widget.
|
|
|
|
1. **Resource**: `ReviewPackResource`
|
|
- List page: table with status badge, generated_at, expires_at, file_size
|
|
- Header action: "Generate Pack" (modal with PII/operations toggles)
|
|
- Row actions: Download (ready), Expire (destructive + confirmation)
|
|
- Empty state: "No review packs yet" + "Generate first pack" CTA
|
|
- Filters: status, date range
|
|
|
|
2. **View Page**: `ViewReviewPack`
|
|
- Infolist layout with sections
|
|
- Header actions: Download, Regenerate
|
|
- Summary display, data freshness table, options used
|
|
|
|
3. **Widget**: `TenantReviewPackCard`
|
|
- Shows latest pack status + metadata
|
|
- Actions: Generate, Download (conditional)
|
|
|
|
**Run**: `vendor/bin/sail artisan test --compact --filter=ReviewPackResource`
|
|
|
|
---
|
|
|
|
### Phase 5: Prune Command + Schedule
|
|
|
|
**Goal:** Retention automation.
|
|
|
|
1. **Command**: `tenantpilot:review-pack:prune`
|
|
- Marks expired, deletes files, optional hard-delete
|
|
- `--hard-delete` flag for DB row removal after grace period
|
|
|
|
2. **Schedule**: Wire in `routes/console.php`
|
|
- `daily()` + `withoutOverlapping()`
|
|
|
|
3. **AlertRule cleanup**: Remove `sla_due` from dropdown options
|
|
|
|
**Run**: `vendor/bin/sail artisan test --compact --filter="ReviewPackPrune|AlertRule"`
|
|
|
|
---
|
|
|
|
### Phase 6: Integration Tests
|
|
|
|
**Goal:** End-to-end coverage.
|
|
|
|
1. RBAC enforcement tests (404 for non-members, 403 for insufficient caps)
|
|
2. Fingerprint dedupe test (duplicate generation → reuse)
|
|
3. Active-run dedupe test (concurrent generation → rejection)
|
|
4. Prune test (expired packs → status update + file deletion)
|
|
5. Download test (signed URL → file stream with correct headers)
|
|
6. Empty state + widget display tests
|
|
|
|
**Run**: `vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/`
|
|
|
|
---
|
|
|
|
## File Inventory (New Files)
|
|
|
|
```
|
|
database/migrations/XXXX_create_review_packs_table.php
|
|
app/Models/ReviewPack.php
|
|
database/factories/ReviewPackFactory.php
|
|
app/Support/ReviewPackStatus.php
|
|
app/Support/Badges/Mappers/ReviewPackStatusBadge.php
|
|
app/Services/ReviewPackService.php
|
|
app/Jobs/GenerateReviewPackJob.php
|
|
app/Notifications/ReviewPackStatusNotification.php
|
|
app/Http/Controllers/ReviewPackDownloadController.php
|
|
app/Filament/Resources/ReviewPackResource.php
|
|
app/Filament/Resources/ReviewPackResource/Pages/ListReviewPacks.php
|
|
app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php
|
|
app/Filament/Widgets/TenantReviewPackCard.php
|
|
app/Console/Commands/PruneReviewPacksCommand.php
|
|
tests/Feature/ReviewPack/ReviewPackGenerationTest.php
|
|
tests/Feature/ReviewPack/ReviewPackDownloadTest.php
|
|
tests/Feature/ReviewPack/ReviewPackRbacTest.php
|
|
tests/Feature/ReviewPack/ReviewPackPruneTest.php
|
|
tests/Feature/ReviewPack/ReviewPackResourceTest.php
|
|
tests/Feature/ReviewPack/ReviewPackWidgetTest.php
|
|
```
|
|
|
|
## Modified Files
|
|
|
|
```
|
|
app/Support/OperationRunType.php — add ReviewPackGenerate case
|
|
app/Support/Auth/Capabilities.php — add REVIEW_PACK_VIEW, REVIEW_PACK_MANAGE
|
|
app/Support/Badges/BadgeDomain.php — add ReviewPackStatus case
|
|
config/filesystems.php — add exports disk
|
|
config/tenantpilot.php — add review_pack section
|
|
routes/web.php — add download route
|
|
routes/console.php — add prune schedule entry
|
|
app/Filament/Resources/AlertRuleResource.php — hide sla_due option
|
|
```
|