diff --git a/src/Actions/Incident/SetLatestStatusIncident.php b/src/Actions/Incident/SetLatestStatusIncident.php new file mode 100644 index 00000000..1a57df8c --- /dev/null +++ b/src/Actions/Incident/SetLatestStatusIncident.php @@ -0,0 +1,35 @@ +updates()->latest()->limit(1)->first(); + + if(is_null($update) && $incident->status === IncidentStatusEnum::unknown) { + return $incident; + } + + $newStatus = $update->status ?? IncidentStatusEnum::unknown; + + if($newStatus === $incident->status) { + return $incident; + } + + + $incident->update(['status' => $newStatus]); + IncidentUpdated::dispatch($incident); + return $incident->fresh(); + } +} diff --git a/src/Actions/Update/CreateUpdate.php b/src/Actions/Update/CreateUpdate.php index 75f8ecd7..f97c09f8 100644 --- a/src/Actions/Update/CreateUpdate.php +++ b/src/Actions/Update/CreateUpdate.php @@ -4,6 +4,7 @@ use Cachet\Data\Requests\IncidentUpdate\CreateIncidentUpdateRequestData; use Cachet\Data\Requests\ScheduleUpdate\CreateScheduleUpdateRequestData; +use Cachet\Events\Incidents\IncidentUpdated; use Cachet\Models\Incident; use Cachet\Models\Schedule; use Cachet\Models\Update; @@ -19,7 +20,10 @@ public function handle(Incident|Schedule $resource, CreateIncidentUpdateRequestD $resource->updates()->save($update); - // @todo Dispatch notification that incident was updated. + if($resource instanceof Incident) { + $resource->update(['status' => $update->status]); + IncidentUpdated::dispatch($resource); + } return $update; } diff --git a/src/Filament/Resources/IncidentResource.php b/src/Filament/Resources/IncidentResource.php index eecb9f1e..231e667a 100644 --- a/src/Filament/Resources/IncidentResource.php +++ b/src/Filament/Resources/IncidentResource.php @@ -25,7 +25,6 @@ class IncidentResource extends Resource protected static ?string $model = Incident::class; protected static ?string $navigationIcon = 'cachet-incident'; - public static function form(Form $form): Form { return $form diff --git a/src/Filament/Resources/UpdateResource/RelationManagers/UpdatesRelationManager.php b/src/Filament/Resources/UpdateResource/RelationManagers/UpdatesRelationManager.php index 7048a5b8..08235d3d 100644 --- a/src/Filament/Resources/UpdateResource/RelationManagers/UpdatesRelationManager.php +++ b/src/Filament/Resources/UpdateResource/RelationManagers/UpdatesRelationManager.php @@ -2,12 +2,20 @@ namespace Cachet\Filament\Resources\UpdateResource\RelationManagers; +use Cachet\Actions\Incident\SetLatestStatusIncident; +use Cachet\Actions\Update\CreateUpdate as CreateUpdateAction; +use Cachet\Data\Requests\IncidentUpdate\CreateIncidentUpdateRequestData; +use Cachet\Data\Requests\ScheduleUpdate\CreateScheduleUpdateRequestData; use Cachet\Enums\IncidentStatusEnum; +use Cachet\Models\Incident; +use Cachet\Models\Schedule; use Filament\Forms; use Filament\Forms\Form; +use Filament\Notifications\Notification; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Table; +use PHPUnit\Runner\ErrorException; class UpdatesRelationManager extends RelationManager { @@ -84,16 +92,65 @@ public function table(Table $table): Table ->options(IncidentStatusEnum::class), ]) ->headerActions([ - Tables\Actions\CreateAction::make(), + Tables\Actions\CreateAction::make() + ->action(function (CreateUpdateAction $createUpdate, array $data) { + /** @var Schedule|Incident $resource */ + $resource = $this->getOwnerRecord(); + $requestData = match (get_class($resource)) { + Schedule::class => CreateScheduleUpdateRequestData::from($data), + Incident::class => CreateIncidentUpdateRequestData::from($data), + default => throw new ErrorException('Request data resource mismatch') + }; + + $createUpdate->handle($resource, $requestData); + + if($resource instanceof Incident) { + Notification::make() + ->title(__('cachet::incident.record_update.success_title', ['name' => $resource->name])) + ->body(__('cachet::incident.record_update.success_body')) + ->success() + ->send(); + + // Somehow the events aren't working to update the parent + // So then let's just refresh the page + redirect(request()->header('Referer')); + } + }), ]) ->actions([ - Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make(), + Tables\Actions\EditAction::make() + ->after(function(SetLatestStatusIncident $latestStatusIncident) { + $this->afterChanged($latestStatusIncident); + }), + Tables\Actions\DeleteAction::make() + ->after(function(SetLatestStatusIncident $latestStatusIncident) { + $this->afterChanged($latestStatusIncident); + }), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\DeleteBulkAction::make()->after(function(SetLatestStatusIncident $latestStatusIncident) { + $this->afterChanged($latestStatusIncident); + }), ]), ]); } + + protected function afterChanged(?SetLatestStatusIncident $latestStatusIncident = null): void + { + /** @var Schedule|Incident $resource */ + $resource = $this->getOwnerRecord(); + if(is_null($latestStatusIncident)) { + $latestStatusIncident = app()->make(SetLatestStatusIncident::class); + } + + if ($resource instanceof Incident) { + $latestStatusIncident->handle($resource); + + // Somehow the events aren't working to update the parent + // So then let's just refresh the page + redirect(request()->header('Referer')); + } + + } } diff --git a/src/Http/Controllers/Api/IncidentUpdateController.php b/src/Http/Controllers/Api/IncidentUpdateController.php index e62937de..519d5754 100644 --- a/src/Http/Controllers/Api/IncidentUpdateController.php +++ b/src/Http/Controllers/Api/IncidentUpdateController.php @@ -2,6 +2,7 @@ namespace Cachet\Http\Controllers\Api; +use Cachet\Actions\Incident\SetLatestStatusIncident; use Cachet\Actions\Update\CreateUpdate; use Cachet\Actions\Update\DeleteUpdate; use Cachet\Actions\Update\EditUpdate; @@ -79,11 +80,19 @@ public function show(Incident $incident, Update $update) /** * Update Incident Update */ - public function update(EditIncidentUpdateRequestData $data, Incident $incident, Update $update, EditUpdate $editUpdateAction) + public function update( + EditIncidentUpdateRequestData $data, + Incident $incident, + Update $update, + EditUpdate $editUpdateAction, + SetLatestStatusIncident $latestStatusIncident, + ) { $this->guard('incident-updates.manage'); $editUpdateAction->handle($update, $data); + $latestStatusIncident->handle($incident); + return UpdateResource::make($update->fresh()); } @@ -91,11 +100,17 @@ public function update(EditIncidentUpdateRequestData $data, Incident $incident, /** * Delete Incident Update */ - public function destroy(Incident $incident, Update $update, DeleteUpdate $deleteUpdateAction) + public function destroy( + Incident $incident, + Update $update, + DeleteUpdate $deleteUpdateAction, + SetLatestStatusIncident $latestStatusIncident, + ) { $this->guard('incident-updates.delete'); $deleteUpdateAction->handle($update); + $latestStatusIncident->handle($incident); return response()->noContent(); } diff --git a/tests/Feature/Api/IncidentUpdateTest.php b/tests/Feature/Api/IncidentUpdateTest.php index e3f7b79b..a0bd394e 100644 --- a/tests/Feature/Api/IncidentUpdateTest.php +++ b/tests/Feature/Api/IncidentUpdateTest.php @@ -166,7 +166,9 @@ it('can create an incident update', function () { Sanctum::actingAs(User::factory()->create(), ['incident-updates.manage']); - $incident = Incident::factory()->create(); + $incident = Incident::factory()->create([ + 'status' => IncidentStatusEnum::unknown->value, + ]); $response = postJson("/status/api/incidents/{$incident->id}/updates", [ 'status' => IncidentStatusEnum::identified->value, @@ -178,6 +180,9 @@ 'status' => IncidentStatusEnum::identified->value, 'message' => 'This is a test message.', ]); + $this->assertDatabaseHas('incidents', [ + 'status' => IncidentStatusEnum::identified->value, + ]); }); it('cannot update an incident update if not authenticated', function () { @@ -208,6 +213,7 @@ $incidentUpdate = Update::factory()->forIncident()->create(); $data = [ + 'status' => IncidentStatusEnum::fixed->value, 'message' => 'This is an updated message.', ]; @@ -216,9 +222,12 @@ $response->assertOk(); $this->assertDatabaseHas('updates', [ 'id' => $incidentUpdate->id, - 'status' => $incidentUpdate->status, ...$data, ]); + $this->assertDatabaseHas('incidents', [ + 'id' => $incidentUpdate->updateable->getKey(), + 'status' => $data['status'], + ]); }); it('cannot delete an incident update if not authenticated', function () { @@ -242,14 +251,47 @@ it('can delete an incident update', function () { Sanctum::actingAs(User::factory()->create(), ['incident-updates.delete']); - $incidentUpdate = Update::factory()->forIncident()->create(); + $incident = Incident::factory()->create(['status' => IncidentStatusEnum::watching->value]); + $incidentUpdateFirst = Update::factory()->forIncident($incident)->create([ + 'status' => IncidentStatusEnum::investigating + ]); + $incidentUpdate = Update::factory()->forIncident($incident)->create([ + 'status' => IncidentStatusEnum::watching, + ]); $response = deleteJson("/status/api/incidents/{$incidentUpdate->updateable_id}/updates/{$incidentUpdate->id}"); $response->assertNoContent(); $this->assertDatabaseMissing('updates', [ + 'id' => $incidentUpdate->id, 'updateable_type' => Relation::getMorphAlias(Incident::class), 'updateable_id' => $incidentUpdate->updateable_id, ]); + + $this->assertDatabaseHas('incidents', [ + 'id' => $incidentUpdateFirst->updateable_id, + 'status' => $incidentUpdateFirst->status->value, + ]); +}); + +it('can delete an incident first update', function () { + Sanctum::actingAs(User::factory()->create(), ['incident-updates.delete']); + + $incident = Incident::factory()->create(['status' => IncidentStatusEnum::watching->value]); + $incidentUpdate = Update::factory()->forIncident($incident)->create([ + 'status' => IncidentStatusEnum::watching, + ]); + + $response = deleteJson("/status/api/incidents/{$incidentUpdate->updateable_id}/updates/{$incidentUpdate->id}"); + $response->assertNoContent(); + $this->assertDatabaseMissing('updates', [ + 'updateable_type' => Relation::getMorphAlias(Incident::class), + 'updateable_id' => $incidentUpdate->updateable_id, + ]); + + $this->assertDatabaseHas('incidents', [ + 'id' => $incidentUpdate->updateable_id, + 'status' => IncidentStatusEnum::unknown->value, + ]); }); it('cannot delete an incident update from another incident', function () {