What Implements tenant-scoped backup scheduling end-to-end: schedules CRUD, minute-based dispatch, queued execution, run history, manual “Run now/Retry”, retention (keep last N), and auditability. Key changes Filament UI: Backup Schedules resource with tenant scoping + SEC-002 role gating. Scheduler + queue: tenantpilot:schedules:dispatch command wired in scheduler (runs every minute), creates idempotent BackupScheduleRun records and dispatches jobs. Execution: RunBackupScheduleJob syncs policies, creates immutable backup sets, updates run status, writes audit logs, applies retry/backoff mapping, and triggers retention. Run history: Relation manager + “View” modal rendering run details. UX polish: row actions grouped; bulk actions grouped (run now / retry / delete). Bulk dispatch writes DB notifications (shows in notifications panel). Validation: policy type hard-validation on save; unknown policy types handled safely at runtime (skipped/partial). Tests: comprehensive Pest coverage for CRUD/scoping/validation, idempotency, job outcomes, error mapping, retention, view modal, run-now/retry notifications, bulk delete (incl. operator forbidden). Files / Areas Filament: BackupScheduleResource.php and app/Filament/Resources/BackupScheduleResource/* Scheduling/Jobs: app/Console/Commands/TenantpilotDispatchBackupSchedules.php, app/Jobs/RunBackupScheduleJob.php, app/Jobs/ApplyBackupScheduleRetentionJob.php, console.php Models/Migrations: app/Models/BackupSchedule.php, app/Models/BackupScheduleRun.php, database/migrations/backup_schedules, backup_schedule_runs Notifications: BackupScheduleRunDispatchedNotification.php Specs: specs/032-backup-scheduling-mvp/* (tasks/checklist/quickstart updates) How to test (Sail) Run tests: ./vendor/bin/sail artisan test tests/Feature/BackupScheduling Run formatter: ./vendor/bin/sail php ./vendor/bin/pint --dirty Apply migrations: ./vendor/bin/sail artisan migrate Manual dispatch: ./vendor/bin/sail artisan tenantpilot:schedules:dispatch Notes Uses DB notifications for queued UI actions to ensure they appear in the notifications panel even under queue fakes in tests. Checklist gate for 032 is PASS; tasks updated accordingly. Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #34
205 lines
6.4 KiB
YAML
205 lines
6.4 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: TenantPilot Backup Scheduling (Spec 032)
|
|
version: "0.1"
|
|
description: |
|
|
Conceptual contract for Backup Scheduling MVP. TenantPilot uses Filament/Livewire;
|
|
these endpoints describe behavior for review/testing and future API alignment.
|
|
servers:
|
|
- url: https://{host}
|
|
variables:
|
|
host:
|
|
default: example.local
|
|
|
|
paths:
|
|
/tenants/{tenantId}/backup-schedules:
|
|
get:
|
|
summary: List backup schedules for a tenant
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BackupSchedule'
|
|
post:
|
|
summary: Create a backup schedule
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupScheduleCreate'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupSchedule'
|
|
'422':
|
|
description: Validation error (e.g. unknown policy_types)
|
|
|
|
/tenants/{tenantId}/backup-schedules/{scheduleId}:
|
|
patch:
|
|
summary: Update a backup schedule
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/ScheduleId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupScheduleUpdate'
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupSchedule'
|
|
'422':
|
|
description: Validation error
|
|
delete:
|
|
summary: Delete (or disable) a schedule
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/ScheduleId'
|
|
responses:
|
|
'204':
|
|
description: Deleted
|
|
|
|
/tenants/{tenantId}/backup-schedules/{scheduleId}/run-now:
|
|
post:
|
|
summary: Trigger a run immediately
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/ScheduleId'
|
|
responses:
|
|
'202':
|
|
description: Accepted (run created and job dispatched)
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupScheduleRun'
|
|
|
|
/tenants/{tenantId}/backup-schedules/{scheduleId}/retry:
|
|
post:
|
|
summary: Create a new run as retry
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/ScheduleId'
|
|
responses:
|
|
'202':
|
|
description: Accepted
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BackupScheduleRun'
|
|
|
|
/tenants/{tenantId}/backup-schedules/{scheduleId}/runs:
|
|
get:
|
|
summary: List runs for a schedule
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/ScheduleId'
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/BackupScheduleRun'
|
|
|
|
components:
|
|
parameters:
|
|
TenantId:
|
|
name: tenantId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
ScheduleId:
|
|
name: scheduleId
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
|
|
schemas:
|
|
BackupSchedule:
|
|
type: object
|
|
required: [id, tenant_id, name, is_enabled, timezone, frequency, time_of_day, policy_types, retention_keep_last]
|
|
properties:
|
|
id: { type: integer }
|
|
tenant_id: { type: integer }
|
|
name: { type: string }
|
|
is_enabled: { type: boolean }
|
|
timezone: { type: string, example: "Europe/Berlin" }
|
|
frequency: { type: string, enum: [daily, weekly] }
|
|
time_of_day: { type: string, example: "02:00:00" }
|
|
days_of_week:
|
|
type: array
|
|
nullable: true
|
|
items: { type: integer, minimum: 1, maximum: 7 }
|
|
policy_types:
|
|
type: array
|
|
items: { type: string }
|
|
description: Must be keys from config('tenantpilot.supported_policy_types').
|
|
include_foundations: { type: boolean }
|
|
retention_keep_last: { type: integer, minimum: 1 }
|
|
last_run_at: { type: string, format: date-time, nullable: true }
|
|
last_run_status: { type: string, nullable: true }
|
|
next_run_at: { type: string, format: date-time, nullable: true }
|
|
|
|
BackupScheduleCreate:
|
|
allOf:
|
|
- $ref: '#/components/schemas/BackupScheduleUpdate'
|
|
- type: object
|
|
required: [name, timezone, frequency, time_of_day, policy_types]
|
|
|
|
BackupScheduleUpdate:
|
|
type: object
|
|
properties:
|
|
name: { type: string }
|
|
is_enabled: { type: boolean }
|
|
timezone: { type: string }
|
|
frequency: { type: string, enum: [daily, weekly] }
|
|
time_of_day: { type: string }
|
|
days_of_week:
|
|
type: array
|
|
nullable: true
|
|
items: { type: integer, minimum: 1, maximum: 7 }
|
|
policy_types:
|
|
type: array
|
|
items: { type: string }
|
|
include_foundations: { type: boolean }
|
|
retention_keep_last: { type: integer, minimum: 1 }
|
|
|
|
BackupScheduleRun:
|
|
type: object
|
|
required: [id, backup_schedule_id, tenant_id, scheduled_for, status]
|
|
properties:
|
|
id: { type: integer }
|
|
backup_schedule_id: { type: integer }
|
|
tenant_id: { type: integer }
|
|
scheduled_for: { type: string, format: date-time }
|
|
started_at: { type: string, format: date-time, nullable: true }
|
|
finished_at: { type: string, format: date-time, nullable: true }
|
|
status: { type: string, enum: [running, success, partial, failed, canceled, skipped] }
|
|
summary:
|
|
type: object
|
|
additionalProperties: true
|
|
error_code: { type: string, nullable: true }
|
|
error_message: { type: string, nullable: true }
|
|
backup_set_id: { type: integer, nullable: true }
|