Rewrite the entire scheduled maintenance implementation

This commit is contained in:
James Brooks 2016-10-30 20:54:12 +00:00
parent a2cded299d
commit ebed68a7d8
57 changed files with 1989 additions and 512 deletions

View File

@ -1,73 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Incident;
final class ReportMaintenanceCommand
{
/**
* The maintenance name.
*
* @var string
*/
public $name;
/**
* The maintenance message.
*
* @var string
*/
public $message;
/**
* Whether to notify about the maintenance or not.
*
* @var bool
*/
public $notify;
/**
* Timestamp of when the maintenance is due to start.
*
* @var string
*/
public $timestamp;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'name' => 'required|string',
'message' => 'required|string',
'notify' => 'nullable|bool',
'timestamp' => 'required|string',
];
/**
* Create a new report maintenance command instance.
*
* @param string $name
* @param string $message
* @param bool $notify
* @param string $timestamp
*
* @return void
*/
public function __construct($name, $message, $notify, $timestamp)
{
$this->name = $name;
$this->message = $message;
$this->notify = $notify;
$this->timestamp = $timestamp;
}
}

View File

@ -0,0 +1,98 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Schedule;
/**
* This is the create schedule command.
*
* @author James Brooks <james@alt-three.com>
*/
final class CreateScheduleCommand
{
/**
* The schedule name.
*
* @var string
*/
public $name;
/**
* The schedule message.
*
* @var string
*/
public $message;
/**
* The schedule status.
*
* @var int
*/
public $status;
/**
* The schedule date.
*
* @var string
*/
public $scheduled_at;
/**
* The completed at date.
*
* @var string
*/
public $completed_at;
/**
* The components affected by the schedule.
*
* @var array
*/
public $components;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'name' => 'required|string',
'message' => 'nullable|string',
'status' => 'required|int|min:0|max:2',
'scheduled_at' => 'required|string',
'completed_at' => 'nullable|string',
'components' => 'required|array',
];
/**
* Create a new create schedule command instance.
*
* @param string $name
* @param string $message
* @param int $status
* @param string $scheduled_at
* @param string $completed_at
* @param array $components
*
* @return void
*/
public function __construct($name, $message, $status, $scheduled_at, $completed_at, array $components)
{
$this->name = $name;
$this->message = $message;
$this->status = $status;
$this->scheduled_at = $scheduled_at;
$this->completed_at = $completed_at;
$this->components = $components;
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Schedule;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the delete schedule command.
*
* @author James Brooks <james@alt-three.com>
*/
final class DeleteScheduleCommand
{
/**
* The schedule to delete.
*
* @var \CachetHQ\Cachet\Models\Schedule
*/
public $schedule;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'schedule' => 'required',
];
/**
* Create a new delete schedule command instance.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return void
*/
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
}

View File

@ -0,0 +1,110 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Schedule;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the update schedule command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateScheduleCommand
{
/**
* The schedule to update.
*
* @param \CachetHQ\Cachet\Models\Schedule
*/
public $schedule;
/**
* The schedule name.
*
* @var string
*/
public $name;
/**
* The schedule message.
*
* @var string
*/
public $message;
/**
* The schedule status.
*
* @var int
*/
public $status;
/**
* The schedule date.
*
* @var string
*/
public $scheduled_at;
/**
* The completed at date.
*
* @var string
*/
public $completed_at;
/**
* The components affected by the schedule.
*
* @var array
*/
public $components;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'schedule' => 'required',
'name' => 'nullable|string',
'message' => 'nullable|string',
'status' => 'nullable|int|min:0|max:2',
'scheduled_at' => 'nullable|string',
'completed_at' => 'nullable|string',
'components' => 'nullable|array',
];
/**
* Create a new update schedule command instance.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
* @param string $name
* @param string $message
* @param int $status
* @param string $scheduled_at
* @param string $completed_at
* @param array $components
*
* @return void
*/
public function __construct(Schedule $schedule, $name, $message, $status, $scheduled_at, $completed_at, array $components = [])
{
$this->schedule = $schedule;
$this->name = $name;
$this->message = $message;
$this->status = $status;
$this->scheduled_at = $scheduled_at;
$this->completed_at = $completed_at;
$this->components = $components;
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\Incident;
use CachetHQ\Cachet\Models\Incident;
final class MaintenanceWasScheduledEvent implements IncidentEventInterface
{
/**
* The incident that has been reported.
*
* @var \CachetHQ\Cachet\Models\Incident
*/
public $incident;
/**
* Create a new maintenance has scheduled event instance.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return void
*/
public function __construct(Incident $incident)
{
$this->incident = $incident;
}
}

View File

@ -0,0 +1,24 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Bus\Events\EventInterface;
/**
* This is the schedule event interface.
*
* @author James Brooks <james@alt-three.com>
*/
interface ScheduleEventInterface extends EventInterface
{
//
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the schedule was created event class.
*
* @author James Brooks <james@alt-three.com>
*/
final class ScheduleWasCreatedEvent implements ScheduleEventInterface
{
/**
* The schedule that has been created.
*
* @var \CachetHQ\Cachet\Models\Schedule
*/
public $schedule;
/**
* Create a new schedule was created event instance.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return void
*/
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the schedule was removed event class.
*
* @author James Brooks <james@alt-three.com>
*/
final class ScheduleWasRemovedEvent implements ScheduleEventInterface
{
/**
* The schedule that has been removed.
*
* @var \CachetHQ\Cachet\Models\Schedule
*/
public $schedule;
/**
* Create a new schedule was removed event instance.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return void
*/
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the schedule was updated event class.
*
* @author James Brooks <james@alt-three.com>
*/
final class ScheduleWasUpdatedEvent implements ScheduleEventInterface
{
/**
* The schedule that has been updated.
*
* @var \CachetHQ\Cachet\Models\Schedule
*/
public $schedule;
/**
* Create a new schedule was updated event instance.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return void
*/
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
}

View File

@ -1,66 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Incident;
use CachetHQ\Cachet\Bus\Commands\Incident\ReportMaintenanceCommand;
use CachetHQ\Cachet\Bus\Events\Incident\MaintenanceWasScheduledEvent;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\Incident;
class ReportMaintenanceCommandHandler
{
/**
* The date factory instance.
*
* @var \CachetHQ\Cachet\Dates\DateFactory
*/
protected $dates;
/**
* Create a new report maintenance command handler instance.
*
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
*
* @return void
*/
public function __construct(DateFactory $dates)
{
$this->dates = $dates;
}
/**
* Handle the report maintenance command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Incident\ReportMaintenanceCommand $command
*
* @return \CachetHQ\Cachet\Models\Incident
*/
public function handle(ReportMaintenanceCommand $command)
{
$scheduledAt = $this->dates->create('d/m/Y H:i', $command->timestamp);
$maintenanceEvent = Incident::create([
'name' => $command->name,
'message' => $command->message,
'scheduled_at' => $scheduledAt,
'status' => 0,
'visible' => 1,
'stickied' => false,
]);
$maintenanceEvent->notify = (bool) $command->notify;
event(new MaintenanceWasScheduledEvent($maintenanceEvent));
return $maintenanceEvent;
}
}

View File

@ -0,0 +1,90 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Schedule;
use CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasCreatedEvent;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the create schedule command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class CreateScheduleCommandHandler
{
/**
* The date factory instance.
*
* @var \CachetHQ\Cachet\Dates\DateFactory
*/
protected $dates;
/**
* Create a new update schedule command handler instance.
*
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
*
* @return void
*/
public function __construct(DateFactory $dates)
{
$this->dates = $dates;
}
/**
* Handle the create schedule command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand $command
*
* @return \CachetHQ\Cachet\Models\Schedule
*/
public function handle(CreateScheduleCommand $command)
{
$schedule = Schedule::create($this->filter($command));
event(new ScheduleWasCreatedEvent($schedule));
return $schedule;
}
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand $command
*
* @return array
*/
protected function filter(CreateScheduleCommand $command)
{
$scheduledAt = $this->dates->create('Y-m-d H:i', $command->scheduled_at);
if ($completedAt = $command->completed_at) {
$completedAt = $this->dates->create('Y-m-d H:i', $command->completed_at);
}
$params = [
'name' => $command->name,
'message' => $command->message,
'status' => $command->status,
'scheduled_at' => $scheduledAt,
'completed_at' => $completedAt,
];
$availableParams = array_filter($params, function ($val) {
return $val !== null;
});
return $availableParams;
}
}

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Schedule;
use CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasRemovedEvent;
/**
* This is the delete schedule command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteScheduleCommandHandler
{
/**
* Handle the delete schedule command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand $command
*
* @return void
*/
public function handle(DeleteScheduleCommand $command)
{
$schedule = $command->schedule;
event(new ScheduleWasRemovedEvent($schedule));
$schedule->delete();
}
}

View File

@ -0,0 +1,92 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Schedule;
use CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasUpdatedEvent;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\Schedule;
/**
* This is the update schedule command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateScheduleCommandHandler
{
/**
* The date factory instance.
*
* @var \CachetHQ\Cachet\Dates\DateFactory
*/
protected $dates;
/**
* Create a new update schedule command handler instance.
*
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
*
* @return void
*/
public function __construct(DateFactory $dates)
{
$this->dates = $dates;
}
/**
* Handle the update schedule command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand $command
*
* @return \CachetHQ\Cachet\Models\Schedule
*/
public function handle(UpdateScheduleCommand $command)
{
$schedule = $command->schedule;
$schedule->update($this->filter($command));
event(new ScheduleWasUpdatedEvent($schedule));
return $schedule;
}
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand $command
*
* @return array
*/
protected function filter(UpdateScheduleCommand $command)
{
$params = [
'name' => $command->name,
'message' => $command->message,
'status' => $command->status,
];
if ($scheduleddAt = $command->scheduled_at) {
$params['scheduled_at'] = $this->dates->create('Y-m-d H:i', $scheduledAt);
}
if ($completedAt = $command->completed_at) {
$params['completed_at'] = $this->dates->create('Y-m-d H:i', $completedAt);
}
$availableParams = array_filter($params, function ($val) {
return $val !== null;
});
return $availableParams;
}
}

View File

@ -9,15 +9,20 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Events\Incident;
namespace CachetHQ\Cachet\Bus\Handlers\Events\Schedule;
use CachetHQ\Cachet\Bus\Events\Incident\MaintenanceWasScheduledEvent;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleEventInterface;
use CachetHQ\Cachet\Models\Subscriber;
use Illuminate\Contracts\Mail\MailQueue;
use Illuminate\Mail\Message;
use McCool\LaravelAutoPresenter\Facades\AutoPresenter;
class SendMaintenanceEmailNotificationHandler
/**
* This is the send schedule event notification handler.
*
* @author James Brooks <james@alt-three.com>
*/
class SendScheduleEmailNotificationHandler
{
/**
* The mailer instance.
@ -50,21 +55,12 @@ class SendMaintenanceEmailNotificationHandler
/**
* Handle the event.
*
* @param \CachetHQ\Cachet\Bus\Events\MaintenanceWasScheduledEvent $event
* @param \CachetHQ\Cachet\Bus\Events\Schedule\ScheduleEventInterface $event
*
* @return void
*/
public function handle(MaintenanceWasScheduledEvent $event)
public function handle(ScheduleEventInterface $event)
{
if (!$event->incident->notify) {
return false;
}
// Only send emails for public incidents.
if ($event->incident->visible === 0) {
return;
}
// First notify all global subscribers.
$globalSubscribers = $this->subscriber->isVerified()->isGlobal()->get();
@ -72,10 +68,6 @@ class SendMaintenanceEmailNotificationHandler
$this->notify($event, $subscriber);
}
if (!$event->incident->component) {
return;
}
$notified = $globalSubscribers->pluck('id')->all();
// Notify the remaining component specific subscribers.
@ -95,12 +87,12 @@ class SendMaintenanceEmailNotificationHandler
/**
* Send notification to subscriber.
*
* @param \CachetHQ\Cachet\Bus\Events\MaintenanceWasScheduledEvent $event
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
* @param \CachetHQ\Cachet\Bus\Events\Schedule\ScheduleEventInterface $event
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function notify(MaintenanceWasScheduledEvent $event, $subscriber)
public function notify(ScheduleEventInterface $event, $subscriber)
{
$incident = AutoPresenter::decorate($event->incident);
$component = AutoPresenter::decorate($event->incident->component);

View File

@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Models\Subscriber;
use Illuminate\Contracts\View\View;
@ -34,9 +35,10 @@ class DashboardComposer
*/
public function compose(View $view)
{
$view->withIncidentCount(Incident::notScheduled()->count());
$view->withComponentCount(Component::count());
$view->withIncidentCount(Incident::count());
$view->withIncidentTemplateCount(IncidentTemplate::count());
$view->withComponentCount(Component::all()->count());
$view->withScheduleCount(Schedule::count());
$view->withSubscriberCount(Subscriber::isVerified()->count());
}
}

View File

@ -11,7 +11,7 @@
namespace CachetHQ\Cachet\Composers\Modules;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\Schedule;
use Illuminate\Contracts\View\View;
/**
@ -31,7 +31,7 @@ class ScheduledComposer
*/
public function compose(View $view)
{
$scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get();
$scheduledMaintenance = Schedule::futureSchedules()->orderBy('scheduled_at')->get();
$view->withScheduledMaintenance($scheduledMaintenance);
}

View File

@ -33,9 +33,10 @@ class StickiedComposer
*/
public function compose(View $view)
{
$stickiedIncidents = Incident::stickied()->orderBy('scheduled_at', 'desc')->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {
$stickiedIncidents = Incident::stickied()->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {
return app(DateFactory::class)->make($incident->is_scheduled ? $incident->scheduled_at : $incident->occurred_at)->toDateString();
});
$view->withStickiedIncidents($stickiedIncidents);
}
}

View File

@ -18,6 +18,7 @@ use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Cachet\Settings\Repository;
@ -89,6 +90,7 @@ class DemoSeederCommand extends Command
$this->seedIncidentTemplates();
$this->seedMetricPoints();
$this->seedMetrics();
$this->seedSchedules();
$this->seedSettings();
$this->seedSubscribers();
$this->seedUsers();
@ -207,7 +209,6 @@ EINCIDENT;
'message' => 'We\'re investigating an issue with our monkeys not performing as they should be.',
'status' => Incident::INVESTIGATING,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
'stickied' => false,
'occurred_at' => Carbon::now(),
@ -217,7 +218,6 @@ EINCIDENT;
'message' => 'Unresolved incidents are left without a **Fixed** update.',
'status' => Incident::INVESTIGATING,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
'stickied' => false,
'occurred_at' => Carbon::now(),
@ -332,6 +332,27 @@ EINCIDENT;
}
}
/**
* Seed the schedules table.
*
* @return void
*/
protected function seedSchedules()
{
$defaultSchedules = [
[
'name' => 'Demo resets every half hour!',
'message' => 'You can schedule downtime for _your_ service!',
'status' => Schedule::UPCOMING,
'scheduled_at' => (new DateTime())->add(new DateInterval('PT2H')),
],
];
foreach ($defaultSchedules as $schedule) {
Schedule::create($schedule);
}
}
/**
* Seed the settings table.
*

View File

@ -69,9 +69,6 @@ class EventServiceProvider extends ServiceProvider
'CachetHQ\Cachet\Bus\Events\Incident\IncidentWasRemovedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Incident\MaintenanceWasScheduledEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\Incident\SendMaintenanceEmailNotificationHandler',
],
'CachetHQ\Cachet\Bus\Events\Invite\InviteWasClaimedEvent' => [
//
],
@ -93,6 +90,15 @@ class EventServiceProvider extends ServiceProvider
'CachetHQ\Cachet\Bus\Events\Metric\MetricWasUpdatedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasCreatedEvent' => [
// 'CachetHQ\Cachet\Bus\Handlers\Events\Schedule\SendScheduleEmailNotificationHandler',
],
'CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasRemovedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasUpdatedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasSubscribedEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\Subscriber\SendSubscriberVerificationEmailHandler',
],

View File

@ -69,6 +69,7 @@ class RouteServiceProvider extends ServiceProvider
$router->model('incident_update', 'CachetHQ\Cachet\Models\IncidentUpdate');
$router->model('metric', 'CachetHQ\Cachet\Models\Metric');
$router->model('metric_point', 'CachetHQ\Cachet\Models\MetricPoint');
$router->model('schedule', 'CachetHQ\Cachet\Models\Schedule');
$router->model('setting', 'CachetHQ\Cachet\Models\Setting');
$router->model('subscriber', 'CachetHQ\Cachet\Models\Subscriber');
$router->model('subscription', 'CachetHQ\Cachet\Models\Subscription');

View File

@ -0,0 +1,128 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand;
use CachetHQ\Cachet\Models\Schedule;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the schedule controller.
*
* @author James Brooks <james@alt-three.com>
*/
class ScheduleController extends AbstractApiController
{
/**
* Return all schedules.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getSchedules()
{
$schedule = Schedule::whereRaw('1 = 1');
if ($sortBy = Binput::get('sort')) {
$direction = Binput::has('order') && Binput::get('order') == 'desc';
$schedule->sort($sortBy, $direction);
}
$schedule = $schedule->paginate(Binput::get('per_page', 20));
return $this->paginator($schedule, Request::instance());
}
/**
* Return a single schedule.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\Http\JsonResponse
*/
public function getSchedule(Schedule $schedule)
{
return $this->item($schedule);
}
/**
* Create a new schedule.
*
* @return \Illuminate\Http\JsonResponse
*/
public function postSchedule()
{
try {
$schedule = dispatch(new CreateScheduleCommand(
Binput::get('name'),
Binput::get('message'),
Binput::get('status'),
Binput::get('scheduled_at'),
Binput::get('completed_at'),
Binput::get('components', [])
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($schedule);
}
/**
* Update a schedule.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\Http\JsonResponse
*/
public function putSchedule(Schedule $schedule)
{
try {
$schedule = dispatch(new UpdateScheduleCommand(
$schedule,
Binput::get('name'),
Binput::get('message'),
Binput::get('status'),
Binput::get('scheduled_at'),
Binput::get('completed_at'),
Binput::get('components', [])
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($schedule);
}
/**
* Delete a schedule.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteSchedule(Schedule $schedule)
{
try {
dispatch(new DeleteScheduleCommand($schedule));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->noContent();
}
}

View File

@ -127,7 +127,7 @@ class DashboardController extends Controller
*/
protected function getIncidents()
{
$allIncidents = Incident::notScheduled()->whereBetween('occurred_at', [
$allIncidents = Incident::whereBetween('occurred_at', [
$this->startDate->copy()->subDays(30)->format('Y-m-d').' 00:00:00',
$this->startDate->format('Y-m-d').' 23:59:59',
])->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {

View File

@ -57,22 +57,6 @@ class IncidentController extends Controller
{
$this->auth = $auth;
$this->subMenu = [
'incidents' => [
'title' => trans('dashboard.incidents.incidents'),
'url' => cachet_route('dashboard.incidents'),
'icon' => 'ion-android-checkmark-circle',
'active' => true,
],
'schedule' => [
'title' => trans('dashboard.schedule.schedule'),
'url' => cachet_route('dashboard.schedule'),
'icon' => 'ion-android-calendar',
'active' => false,
],
];
View::share('sub_menu', $this->subMenu);
View::share('sub_title', trans('dashboard.incidents.title'));
}
@ -83,7 +67,7 @@ class IncidentController extends Controller
*/
public function showIncidents()
{
$incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get();
$incidents = Incident::orderBy('created_at', 'desc')->get();
return View::make('dashboard.incidents.index')
->withPageTitle(trans('dashboard.incidents.incidents').' - '.trans('dashboard.dashboard'))

View File

@ -12,16 +12,20 @@
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Incident\ReportMaintenanceCommand;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\Schedule;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;
use Illuminate\Support\MessageBag;
use Jenssegers\Date\Date;
/**
* This is the schedule controller class.
*
* @author James Brooks <james@alt-three.com>
*/
class ScheduleController extends Controller
{
/**
@ -38,23 +42,7 @@ class ScheduleController extends Controller
*/
public function __construct()
{
$this->subMenu = [
'incidents' => [
'title' => trans('dashboard.incidents.incidents'),
'url' => cachet_route('dashboard.incidents'),
'icon' => 'ion-android-checkmark-circle',
'active' => false,
],
'schedule' => [
'title' => trans('dashboard.schedule.schedule'),
'url' => cachet_route('dashboard.schedule'),
'icon' => 'ion-android-calendar',
'active' => true,
],
];
View::share('sub_menu', $this->subMenu);
View::share('sub_title', trans('dashboard.incidents.title'));
View::share('sub_title', trans('dashboard.schedule.title'));
}
/**
@ -64,7 +52,7 @@ class ScheduleController extends Controller
*/
public function showIndex()
{
$schedule = Incident::scheduled()->orderBy('created_at')->get();
$schedule = Schedule::orderBy('created_at')->get();
return View::make('dashboard.schedule.index')
->withPageTitle(trans('dashboard.schedule.schedule').' - '.trans('dashboard.dashboard'))
@ -86,18 +74,20 @@ class ScheduleController extends Controller
}
/**
* Creates a new scheduled maintenance "incident".
* Creates a new scheduled maintenance.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function addScheduleAction()
{
try {
$incident = dispatch(new ReportMaintenanceCommand(
dispatch(new CreateScheduleCommand(
Binput::get('name'),
Binput::get('message'),
Binput::get('notify'),
Binput::get('scheduled_at')
Binput::get('status', Schedule::UPCOMING),
Binput::get('scheduled_at'),
Binput::get('completed_at'),
Binput::get('components', [])
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.schedule.create')
@ -113,11 +103,11 @@ class ScheduleController extends Controller
/**
* Shows the edit schedule maintenance form.
*
* @param \CachetHQ\Cachet\Models\Incident $schedule
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\View\View
*/
public function showEditSchedule(Incident $schedule)
public function showEditSchedule(Schedule $schedule)
{
$incidentTemplates = IncidentTemplate::all();
@ -130,30 +120,22 @@ class ScheduleController extends Controller
/**
* Updates the given incident.
*
* @param \CachetHQ\Cachet\Models\Incident $schedule
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\Http\RedirectResponse
*/
public function editScheduleAction(Incident $schedule)
public function editScheduleAction(Schedule $schedule)
{
$scheduleData = Binput::get('incident');
// Parse the schedule date.
$scheduledAt = app(DateFactory::class)->create('d/m/Y H:i', $scheduleData['scheduled_at']);
if ($scheduledAt->isPast()) {
$messageBag = new MessageBag();
$messageBag->add('scheduled_at', trans('validation.date', ['attribute' => 'scheduled time you supplied']));
return cachet_redirect('dashboard.schedule.edit', [$schedule->id])->withErrors($messageBag);
}
$scheduleData['scheduled_at'] = $scheduledAt;
// Bypass the incident.status field.
$scheduleData['status'] = 0;
try {
$schedule->update($scheduleData);
$schedule = dispatch(new UpdateScheduleCommand(
$schedule,
Binput::get('name', null),
Binput::get('message', null),
Binput::get('status', null),
Binput::get('scheduled_at', null),
Binput::get('completed_at', null),
Binput::get('components', [])
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.schedule.edit', [$schedule->id])
->withInput(Binput::all())
@ -168,13 +150,13 @@ class ScheduleController extends Controller
/**
* Deletes a given schedule.
*
* @param \CachetHQ\Cachet\Models\Incident $schedule
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteScheduleAction(Incident $schedule)
public function deleteScheduleAction(Schedule $schedule)
{
$schedule->delete();
dispatch(new DeleteScheduleCommand($schedule));
return cachet_redirect('dashboard.schedule')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.schedule.delete.success')));

View File

@ -17,6 +17,7 @@ use CachetHQ\Cachet\Http\Controllers\Api\AbstractApiController;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Repositories\Metric\MetricRepository;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
@ -84,11 +85,11 @@ class StatusPageController extends AbstractApiController
$incidentVisibility = Auth::check() ? 0 : 1;
$allIncidents = Incident::notScheduled()->where('visible', '>=', $incidentVisibility)->whereBetween('occurred_at', [
$allIncidents = Incident::where('visible', '>=', $incidentVisibility)->whereBetween('occurred_at', [
$startDate->copy()->subDays($daysToShow)->format('Y-m-d').' 00:00:00',
$startDate->format('Y-m-d').' 23:59:59',
])->orderBy('scheduled_at', 'desc')->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {
return app(DateFactory::class)->make($incident->is_scheduled ? $incident->scheduled_at : $incident->occurred_at)->toDateString();
])->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {
return app(DateFactory::class)->make($incident->occurred_at)->toDateString();
});
// Add in days that have no incidents
@ -111,7 +112,7 @@ class StatusPageController extends AbstractApiController
->withDaysToShow($daysToShow)
->withAllIncidents($allIncidents)
->withCanPageForward((bool) $today->gt($startDate))
->withCanPageBackward(Incident::notScheduled()->where('occurred_at', '<', $startDate->format('Y-m-d'))->count() > 0)
->withCanPageBackward(Incident::where('occurred_at', '<', $startDate->format('Y-m-d'))->count() > 0)
->withPreviousDate($startDate->copy()->subDays($daysToShow)->toDateString())
->withNextDate($startDate->copy()->addDays($daysToShow)->toDateString());
}
@ -125,8 +126,19 @@ class StatusPageController extends AbstractApiController
*/
public function showIncident(Incident $incident)
{
return View::make('single-incident')
->withIncident($incident);
return View::make('single-incident')->withIncident($incident);
}
/**
* Show a single schedule.
*
* @param \CachetHQ\Cachet\Models\Schedule $schedule
*
* @return \Illuminate\View\View
*/
public function showSchedule(Schedule $schedule)
{
return View::make('single-schedule')->withSchedule($schedule);
}
/**

View File

@ -59,6 +59,9 @@ class ApiRoutes
$router->get('metrics', 'MetricController@getMetrics');
$router->get('metrics/{metric}', 'MetricController@getMetric');
$router->get('metrics/{metric}/points', 'MetricController@getMetricPoints');
$router->get('schedules', 'ScheduleController@getSchedules');
$router->get('schedules/{schedule}', 'ScheduleController@getSchedule');
});
$router->group(['middleware' => ['auth.api:true']], function (Registrar $router) {
@ -70,6 +73,7 @@ class ApiRoutes
$router->post('incidents/{incident}/updates', 'IncidentUpdateController@postIncidentUpdate');
$router->post('metrics', 'MetricController@postMetrics');
$router->post('metrics/{metric}/points', 'MetricPointController@postMetricPoints');
$router->post('schedules', 'ScheduleController@postSchedule');
$router->post('subscribers', 'SubscriberController@postSubscribers');
$router->put('components/groups/{component_group}', 'ComponentGroupController@putGroup');
@ -78,6 +82,7 @@ class ApiRoutes
$router->put('incidents/{incident}/updates/{update}', 'IncidentUpdateController@putIncidentUpdate');
$router->put('metrics/{metric}', 'MetricController@putMetric');
$router->put('metrics/{metric}/points/{metric_point}', 'MetricPointController@putMetricPoint');
$router->put('schedules/{schedule}', 'ScheduleController@putSchedule');
$router->delete('components/groups/{component_group}', 'ComponentGroupController@deleteGroup');
$router->delete('components/{component}', 'ComponentController@deleteComponent');
@ -85,6 +90,7 @@ class ApiRoutes
$router->delete('incidents/{incident}/updates/{update}', 'IncidentUpdateController@deleteIncidentUpdate');
$router->delete('metrics/{metric}', 'MetricController@deleteMetric');
$router->delete('metrics/{metric}/points/{metric_point}', 'MetricPointController@deleteMetricPoint');
$router->delete('schedules/{schedule}', 'ScheduleController@deleteSchedule');
$router->delete('subscribers/{subscriber}', 'SubscriberController@deleteSubscriber');
$router->delete('subscriptions/{subscription}', 'SubscriberController@deleteSubscription');
});

View File

@ -56,15 +56,15 @@ class ScheduleRoutes
'uses' => 'ScheduleController@addScheduleAction',
]);
$router->get('{incident}', [
$router->get('{schedule}', [
'as' => 'get:dashboard.schedule.edit',
'uses' => 'ScheduleController@showEditSchedule',
]);
$router->post('{incident}', [
$router->post('{schedule}', [
'as' => 'post:dashboard.schedule.edit',
'uses' => 'ScheduleController@editScheduleAction',
]);
$router->delete('{incident}', [
$router->delete('{schedule}', [
'as' => 'delete:dashboard.schedule.delete',
'uses' => 'ScheduleController@deleteScheduleAction',
]);

View File

@ -49,6 +49,11 @@ class StatusPageRoutes
'uses' => 'StatusPageController@showIncident',
]);
$router->get('schedules/{schedule}', [
'as' => 'get:schedule',
'uses' => 'StatusPageController@showSchedule',
]);
$router->get('metrics/{metric}', [
'as' => 'get:metric',
'uses' => 'StatusPageController@getMetrics',

View File

@ -49,7 +49,7 @@ class System implements SystemContract
];
} elseif ($enabledScope->notStatus(1)->count() === 0) {
// If all our components are ok, do we have any non-fixed incidents?
$incidents = Incident::notScheduled()->orderBy('occurred_at', 'desc')->get()->filter(function ($incident) {
$incidents = Incident::orderBy('occurred_at', 'desc')->get()->filter(function ($incident) {
return $incident->status > 0;
});
$incidentCount = $incidents->count();

View File

@ -15,7 +15,6 @@ use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\IncidentPresenter;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -75,11 +74,10 @@ class Incident extends Model implements HasPresenter
* @var string[]
*/
protected $casts = [
'visible' => 'int',
'stickied' => 'bool',
'scheduled_at' => 'date',
'occurred_at' => 'date',
'deleted_at' => 'date',
'visible' => 'int',
'stickied' => 'bool',
'occurred_at' => 'date',
'deleted_at' => 'date',
];
/**
@ -94,7 +92,6 @@ class Incident extends Model implements HasPresenter
'visible',
'stickied',
'message',
'scheduled_at',
'occurred_at',
'created_at',
'updated_at',
@ -194,44 +191,6 @@ class Incident extends Model implements HasPresenter
return $query->where('stickied', '=', true);
}
/**
* Finds all scheduled incidents (maintenance).
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeScheduled(Builder $query)
{
return $query->where('status', '=', 0)->where('scheduled_at', '>=', Carbon::now());
}
/**
* Finds all non-scheduled incidents.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNotScheduled(Builder $query)
{
return $query->where('status', '>', 0)->orWhere(function ($query) {
$query->where('status', '=', 0)->where(function ($query) {
$query->whereNull('scheduled_at')->orWhere('scheduled_at', '<=', Carbon::now());
});
});
}
/**
* Returns whether the "incident" is scheduled or not.
*
* @return bool
*/
public function getIsScheduledAttribute()
{
return $this->getOriginal('scheduled_at') !== null;
}
/**
* Is the incident resolved?
*

162
app/Models/Schedule.php Normal file
View File

@ -0,0 +1,162 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\SchedulePresenter;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use McCool\LaravelAutoPresenter\HasPresenter;
class Schedule extends Model implements HasPresenter
{
use SearchableTrait, SortableTrait, ValidatingTrait;
/**
* The upcoming status.
*
* @var int
*/
const UPCOMING = 0;
/**
* The in progress status.
*
* @var int
*/
const IN_PROGRESS = 1;
/**
* The complete status.
*
* @var int
*/
const COMPLETE = 2;
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'name' => 'string',
'message' => 'string',
'status' => 'int',
'scheduled_at' => 'date',
'completed_at' => 'date',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = [
'name',
'message',
'status',
'scheduled_at',
'completed_at',
'created_at',
'updated_at',
];
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'name' => 'required|string',
'message' => 'nullable|string',
'status' => 'required|int|between:0,2',
];
/**
* The searchable fields.
*
* @var string[]
*/
protected $searchable = [
'id',
'name',
'status',
];
/**
* The sortable fields.
*
* @var string[]
*/
protected $sortable = [
'id',
'name',
'status',
'scheduled_at',
'completed_at',
'created_at',
'updated_at',
];
/**
* The relations to eager load on every query.
*
* @var string[]
*/
protected $with = ['components'];
/**
* Scopes schedules to those in the future.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeFutureSchedules($query)
{
return $query->whereIn('status', [self::UPCOMING, self::IN_PROGRESS])->where('scheduled_at', '>=', Carbon::now());
}
/**
* Scopes schedules to those in the past.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePastSchedules($query)
{
return $query->where('status', '<', self::COMPLETE)->where('scheduled_at', '<=', Carbon::now());
}
/**
* Get the components relation.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function components()
{
return $this->hasMany(ScheduleComponent::class);
}
/**
* Get the presenter class.
*
* @return string
*/
public function getPresenterClass()
{
return SchedulePresenter::class;
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use Illuminate\Database\Eloquent\Model;
class ScheduleComponent extends Model
{
use ValidatingTrait;
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'schedule_id' => 'int',
'component_id' => 'int',
'component_status' => 'int',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = [
'schedule_id',
'component_id',
'component_status',
];
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'schedule_id' => 'required|int',
'component_id' => 'required|int',
'component_status' => 'required|int',
];
/**
* Get the schedule relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function schedule()
{
return $this->belongsTo(Schedule::class);
}
/**
* Get the component relation.
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function component()
{
return $this->hasOne(Component::class);
}
}

View File

@ -157,56 +157,6 @@ class IncidentPresenter extends BasePresenter implements Arrayable
return $this->dates->make($this->wrappedObject->created_at)->toISO8601String();
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->toDateTimeString();
}
/**
* Present diff for humans date time.
*
* @return string
*/
public function scheduled_at_diff()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->diffForHumans();
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at_formatted()
{
return ucfirst($this->dates->make($this->wrappedObject->scheduled_at)->format($this->incidentDateFormat()));
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at_iso()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->toISO8601String();
}
/**
* Formats the scheduled_at time ready to be used by bootstrap-datetimepicker.
*
* @return string
*/
public function scheduled_at_datetimepicker()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->format('d/m/Y H:i');
}
/**
* Returns a formatted timestamp for use within the timeline.
*
@ -214,10 +164,6 @@ class IncidentPresenter extends BasePresenter implements Arrayable
*/
public function timestamp_formatted()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_formatted;
}
return $this->occurred_at_formatted;
}
@ -228,10 +174,6 @@ class IncidentPresenter extends BasePresenter implements Arrayable
*/
public function timestamp_iso()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_iso;
}
return $this->occurred_at_iso;
}
@ -352,7 +294,6 @@ class IncidentPresenter extends BasePresenter implements Arrayable
'latest_icon' => $this->latest_icon(),
'permalink' => $this->permalink(),
'duration' => $this->duration(),
'scheduled_at' => $this->scheduled_at(),
'occurred_at' => $this->occurred_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),

View File

@ -0,0 +1,261 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Presenters;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Contracts\Support\Arrayable;
use McCool\LaravelAutoPresenter\BasePresenter;
/**
* This is the schedule presenter class.
*
* @author James Brooks <james@alt-three.com>
*/
class SchedulePresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* The date factory instance.
*
* @var \CachetHQ\Cachet\Dates\DateFactory
*/
protected $dates;
/**
* Create a new presenter.
*
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
* @param \CachetHQ\Cachet\Models\Schedule $resource
*
* @return void
*/
public function __construct(DateFactory $dates, Schedule $resource)
{
$this->dates = $dates;
parent::__construct($resource);
}
/**
* Renders the message from Markdown into HTML.
*
* @return string
*/
public function formattedMessage()
{
return Markdown::convertToHtml($this->wrappedObject->message);
}
/**
* Present diff for humans date time.
*
* @return string
*/
public function created_at_diff()
{
return $this->dates->make($this->wrappedObject->created_at)->diffForHumans();
}
/**
* Present formatted date time.
*
* @return string
*/
public function created_at_formatted()
{
return ucfirst($this->dates->make($this->wrappedObject->created_at)->format($this->incidentDateFormat()));
}
/**
* Formats the created_at time ready to be used by bootstrap-datetimepicker.
*
* @return string
*/
public function created_at_datetimepicker()
{
return $this->dates->make($this->wrappedObject->created_at)->format('Y-m-d H:i');
}
/**
* Present formatted date time.
*
* @return string
*/
public function created_at_iso()
{
return $this->dates->make($this->wrappedObject->created_at)->toISO8601String();
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->toDateTimeString();
}
/**
* Present diff for humans date time.
*
* @return string
*/
public function scheduled_at_diff()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->diffForHumans();
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at_formatted()
{
return ucfirst($this->dates->make($this->wrappedObject->scheduled_at)->format($this->incidentDateFormat()));
}
/**
* Present formatted date time.
*
* @return string
*/
public function scheduled_at_iso()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->toISO8601String();
}
/**
* Formats the scheduled_at time ready to be used by bootstrap-datetimepicker.
*
* @return string
*/
public function scheduled_at_datetimepicker()
{
return $this->dates->make($this->wrappedObject->scheduled_at)->format('Y-m-d H:i');
}
/**
* Returns a formatted timestamp for use within the timeline.
*
* @return string
*/
public function timestamp_formatted()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_formatted;
}
return $this->created_at_formatted;
}
/**
* Present formatted date time.
*
* @return string
*/
public function completed_at()
{
return $this->dates->make($this->wrappedObject->completed_at)->toDateTimeString();
}
/**
* Present diff for humans date time.
*
* @return string
*/
public function completed_at_diff()
{
return $this->dates->make($this->wrappedObject->completed_at)->diffForHumans();
}
/**
* Present formatted date time.
*
* @return string
*/
public function completed_at_formatted()
{
return ucfirst($this->dates->make($this->wrappedObject->completed_at)->format($this->incidentDateFormat()));
}
/**
* Present formatted date time.
*
* @return string
*/
public function completed_at_iso()
{
return $this->dates->make($this->wrappedObject->completed_at)->toISO8601String();
}
/**
* Formats the completed_at time ready to be used by bootstrap-datetimepicker.
*
* @return string
*/
public function completed_at_datetimepicker()
{
return $this->dates->make($this->wrappedObject->completed_at)->format('Y-m-d H:i');
}
/**
* Return the iso timestamp for use within the timeline.
*
* @return string
*/
public function timestamp_iso()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_iso;
}
return $this->completed_at_iso;
}
/**
* Returns a human readable version of the status.
*
* @return string
*/
public function human_status()
{
// return trans('cachet.incidents.status.'.$this->wrappedObject->status);
// TODO: Refactor into translations.
switch ($this->wrappedObject->status) {
case 0: return 'Upcoming';
case 1: return 'In Progress';
case 2: return 'Complete';
}
}
/**
* Convert the presenter instance to an array.
*
* @return string[]
*/
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'human_status' => $this->human_status(),
'scheduled_at' => $this->scheduled_at(),
'completed_at' => $this->completed_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
]);
}
}

View File

@ -16,6 +16,7 @@ use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Models\Setting;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\Subscription;
@ -89,6 +90,15 @@ $factory->define(MetricPoint::class, function ($faker) {
];
});
$factory->define(Schedule::class, function ($faker) {
return [
'name' => $faker->sentence(),
'message' => $faker->paragraph(),
'status' => Schedule::UPCOMING,
'scheduled_at' => Carbon::now()->addDays(7),
];
});
$factory->define(Setting::class, function ($faker) {
return [
'name' => 'app_name',

View File

@ -28,6 +28,7 @@ class CreateSchedulesTable extends Migration
$table->longText('message')->nullable()->default(null);
$table->tinyInteger('status')->unsigned()->default(0);
$table->timestamp('scheduled_at');
$table->timestamp('completed_at')->nullable()->default(null);
$table->timestamps();
});
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class AlterTableIncidentsRemoveScheduledColumns extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// We need a better way of handling data migrations...
DB::update('INSERT INTO schedules (name, message, scheduled_at, created_at, updated_at) SELECT name, message, scheduled_at, created_at, updated_at FROM incidents WHERE scheduled_at IS NOT NULL');
DB::table('incidents')->whereNotNull('scheduled_at')->delete();
Schema::table('incidents', function (Blueprint $table) {
$table->dropColumn('scheduled_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('incidents', function (Blueprint $table) {
$table->timestamp('scheduled_at')->after('user_id')->nullable()->default(null);
});
}
}

View File

@ -36,7 +36,6 @@ return [
'scheduled_at' => ', scheduled :timestamp',
'posted' => 'Posted :timestamp',
'status' => [
0 => 'Scheduled', // TODO: Hopefully remove this.
1 => 'Investigating',
2 => 'Identified',
3 => 'Watching',
@ -44,6 +43,15 @@ return [
],
],
// Schedule
'schedules' => [
'status' => [
0 => 'Upcoming',
1 => 'In Progress',
2 => 'Complete',
],
],
// Service Status
'service' => [
'good' => '[0,1] System operational|[2,Inf] All systems are operational',

View File

@ -51,7 +51,6 @@ return [
'component' => 'Component',
'message' => 'Message',
'message-help' => 'You may also use Markdown.',
'scheduled_at' => 'When to schedule the maintenance for?',
'occurred_at' => 'When did this incident occur?',
'notify_subscribers' => 'Notify subscribers?',
'visibility' => 'Incident Visibility',
@ -67,6 +66,20 @@ return [
],
],
'schedules' => [
'name' => 'Name',
'status' => 'Status',
'message' => 'Message',
'message-help' => 'You may also use Markdown.',
'scheduled_at' => 'When is this maintenance scheduled for?',
'completed_at' => 'When did this maintenance complete?',
'templates' => [
'name' => 'Name',
'template' => 'Template',
'twig' => 'Incident Templates can make use of the <a href="http://twig.sensiolabs.org/" target="_blank">Twig</a> templating language.',
],
],
// Components form fields
'components' => [
'name' => 'Name',

View File

@ -22,7 +22,7 @@
<span>{{ trans('dashboard.dashboard') }}</span>
</a>
</li>
<li {!! set_active('dashboard/incidents*') !!} {!! set_active('dashboard/schedule*') !!}>
<li {!! set_active('dashboard/incidents*') !!}>
<a href="{{ cachet_route('dashboard.incidents') }}">
<i class="ion ion-ios-information-outline"></i>
<span>{{ trans('dashboard.incidents.incidents') }}</span>
@ -36,6 +36,13 @@
<span class="label label-info">{{ $incident_template_count }}</span>
</a>
</li>
<li {!! set_active('dashboard/schedule*') !!}>
<a href="{{ cachet_route('dashboard.schedule') }}">
<i class="ion ion-android-calendar"></i>
<span>{{ trans('dashboard.schedule.schedule') }}</span>
<span class="label label-info">{{ $schedule_count }}</span>
</a>
</li>
<li {!! set_active('dashboard/components*') !!}>
<a href="{{ cachet_route('dashboard.components') }}">
<i class="ion ion-ios-browsers-outline"></i>

View File

@ -16,11 +16,10 @@
@include('dashboard.partials.errors')
<form class="form-vertical" name="ScheduleForm" role="form" method="POST" autocomplete="off">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="visible" value="1">
<fieldset>
@if($incident_templates->count() > 0)
<div class="form-group">
<label for="incident-template">{{ trans('forms.incidents.templates.template') }}</label>
<label for="incident-template">{{ trans('forms.schedules.templates.template') }}</label>
<select class="form-control" name="template">
<option selected></option>
@foreach($incident_templates as $tpl)
@ -30,27 +29,32 @@
</div>
@endif
<div class="form-group">
<label for="incident-name">{{ trans('forms.incidents.name') }}</label>
<input type="text" class="form-control" name="name" id="incident-name" required value="{{ Binput::old('incident.name') }}" placeholder="{{ trans('forms.incidents.name') }}">
<label for="incident-name">{{ trans('forms.schedules.name') }}</label>
<input type="text" class="form-control" name="name" id="incident-name" required value="{{ Binput::old('name') }}" placeholder="{{ trans('forms.schedules.name') }}">
</div>
<div class="form-group">
<label>{{ trans('forms.incidents.message') }}</label>
<label>{{ trans('forms.schedules.status') }}</label><br>
@foreach(trans('cachet.schedules.status') as $id => $status)
<label class="radio-inline">
<input type="radio" name="status" value="{{ $id }}" {{ $id === 0 ? 'checked="checked"' : null }}>
{{ $status }}
</label>
@endforeach
</div>
<div class="form-group">
<label>{{ trans('forms.schedules.message') }}</label>
<div class="markdown-control">
<textarea name="message" class="form-control autosize" rows="5" required placeholder="{{ trans('forms.incidents.message') }}">{{ Binput::old('incident.message') }}</textarea>
<textarea name="message" class="form-control autosize" rows="5" required placeholder="{{ trans('forms.schedules.message') }}">{{ Binput::old('message') }}</textarea>
</div>
</div>
<div class="form-group">
<label>{{ trans('forms.incidents.scheduled_at') }}</label>
<input type="text" name="scheduled_at" class="form-control" rel="datepicker" required placeholder="{{ trans('forms.incidents.scheduled_at') }}">
<label>{{ trans('forms.schedules.scheduled_at') }}</label>
<input type="text" name="scheduled_at" class="form-control" rel="datepicker-custom" data-date-format="YYYY-MM-DD HH:mm" required placeholder="{{ trans('forms.schedules.scheduled_at') }}">
</div>
@if(subscribers_enabled())
<div class="checkbox">
<label>
<input type="checkbox" name="notify" value="1" checked="{{ Binput::old('incident.message', 'checked') }}">
{{ trans('forms.incidents.notify_subscribers') }}
</label>
<div class="form-group">
<label>{{ trans('forms.schedules.scheduled_at') }}</label>
<input type="text" name="completed_at" class="form-control" rel="datepicker-custom" data-date-format="YYYY-MM-DD HH:mm" placeholder="{{ trans('forms.schedules.scheduled_at') }}">
</div>
@endif
</fieldset>
<div class="form-group">

View File

@ -16,11 +16,11 @@
@include('dashboard.partials.errors')
<form class="form-vertical" name="ScheduleForm" role="form" method='POST' autocomplete="off">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="incident[visible]" value="1">
<input type="hidden" name="visible" value="1">
<fieldset>
@if($incident_templates->count() > 0)
<div class="form-group">
<label for="incident-template">{{ trans('forms.incidents.templates.template') }}</label>
<label for="incident-template">{{ trans('forms.schedules.templates.template') }}</label>
<select class="form-control" name="template">
<option selected></option>
@foreach($incident_templates as $tpl)
@ -30,18 +30,31 @@
</div>
@endif
<div class="form-group">
<label for="incident-name">{{ trans('forms.incidents.name') }}</label>
<input type="text" class="form-control" name="incident[name]" id="incident-name" required value="{{ $schedule->name }}" placeholder="{{ trans('forms.incidents.name') }}">
<label for="incident-name">{{ trans('forms.schedules.name') }}</label>
<input type="text" class="form-control" name="name" id="incident-name" required value="{{ $schedule->name }}" placeholder="{{ trans('forms.schedules.name') }}">
</div>
<div class="form-group">
<label>{{ trans('forms.incidents.message') }}</label>
<label>{{ trans('forms.schedules.status') }}</label><br>
@foreach(trans('cachet.schedules.status') as $id => $status)
<label class="radio-inline">
<input type="radio" name="status" value="{{ $id }}" {{ $id === $schedule->status ? 'checked="checked"' : null }}>
{{ $status }}
</label>
@endforeach
</div>
<div class="form-group">
<label>{{ trans('forms.schedules.message') }}</label>
<div class='markdown-control'>
<textarea name="incident[message]" class="form-control autosize" rows="5" required>{{ $schedule->message }}</textarea>
<textarea name="message" class="form-control autosize" rows="5" required>{{ $schedule->message }}</textarea>
</div>
</div>
<div class="form-group">
<label>{{ trans('forms.incidents.scheduled_at') }}</label>
<input type="text" name="incident[scheduled_at]" class="form-control" rel="datepicker" value="{{ $schedule->scheduled_at_datetimepicker }}" required placeholder="{{ trans('forms.incidents.scheduled_at') }}">
<label>{{ trans('forms.schedules.scheduled_at') }}</label>
<input type="text" name="scheduled_at" class="form-control" rel="datepicker-custom" data-date-format="YYYY-MM-DD HH:mm" value="{{ $schedule->scheduled_at_datetimepicker }}" required placeholder="{{ trans('forms.schedules.scheduled_at') }}">
</div>
<div class="form-group">
<label>{{ trans('forms.schedules.completed_at') }}</label>
<input type="text" name="completed_at" class="form-control" rel="datepicker-custom" data-date-format="YYYY-MM-DD HH:mm" value="{{ $schedule->completed_at_datetimepicker }}" placeholder="{{ trans('forms.schedules.completed_at') }}">
</div>
</fieldset>

View File

@ -26,7 +26,7 @@
<div class="col-xs-6">
<strong>{{ $incident->name }}</strong>
<br>
{{ trans('dashboard.schedule.scheduled_at', ['timestamp' => $incident->scheduled_at_iso]) }}
{{ trans('dashboard.schedule.scheduled_at', ['timestamp' => $incident->scheduled_at_formatted]) }}
@if($incident->message)
<p><small>{{ Str::words($incident->message, 5) }}</small></p>
@endif

View File

@ -1,14 +1,20 @@
<div class="timeline schedule">
<div class="panel panel-info">
<div class="panel panel-default">
<div class="panel-heading">
<strong>{{ trans('cachet.incidents.scheduled') }}</strong>
</div>
<div class="list-group">
@foreach($scheduled_maintenance as $schedule)
<div class="list-group-item">
<a href="{{ cachet_route('schedule', $schedule) }}" class="list-group-item">
<strong>{{ $schedule->name }}</strong> <small class="date"><abbr class="timeago" data-toggle="tooltip" data-placement="right" title="{{ $schedule->scheduled_at_formatted }}" data-timeago="{{ $schedule->scheduled_at_iso }}"></abbr></small>
{!! $schedule->formattedMessage !!}
</div>
@if($schedule->components->count() > 0)
<hr>
@foreach($schedule->components as $affected_component)
<span class="label label-primary">{{ $affected_component->component->name }}</span>
@endforeach
@endif
</a>
@endforeach
</div>
</div>

View File

@ -0,0 +1,35 @@
@extends('layout.master')
@section('bodyClass', 'no-padding')
@section('outer-content')
@include('partials.nav')
@stop
@section('content')
<h1>{{ $schedule->name }}</h1>
<div class="timeline">
<div class="content-wrapper">
<div class="moment first">
<div class="row event clearfix">
<div class="col-sm-1">
<div class="status-icon status-{{ $schedule->status }}" data-toggle="tooltip" title="{{ $schedule->human_status }}" data-placement="left">
<i class="icon ion-android-calendar"></i>
</div>
</div>
<div class="col-xs-10 col-xs-offset-2 col-sm-11 col-sm-offset-0">
<div class="panel panel-message incident">
<div class="panel-heading">
<strong>{{ $schedule->name }}</strong>{{ trans("cachet.incidents.scheduled_at", ["timestamp" => $schedule->scheduled_at_diff]) }}
</div>
<div class="panel-body">
{!! $schedule->formattedMessage !!}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@stop

View File

@ -0,0 +1,87 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Api;
/**
* This is the schedule test class.
*
* @author James Brooks <james@alt-three.com>
*/
class ScheduleTest extends AbstractApiTestCase
{
public function testGetSchedules()
{
$schedules = factory('CachetHQ\Cachet\Models\Schedule', 3)->create();
$this->get('/api/v1/schedules');
$this->assertResponseOk();
$this->seeJson(['id' => $schedules[0]->id]);
$this->seeJson(['id' => $schedules[1]->id]);
$this->seeJson(['id' => $schedules[2]->id]);
}
public function testGetSchedule()
{
$schedule = factory('CachetHQ\Cachet\Models\Schedule')->create();
$this->get('/api/v1/schedules/'.$schedule->id);
$this->assertResponseOk();
$this->seeJson($schedule->toArray());
}
public function testCreateSchedule()
{
$this->beUser();
$schedule = [
'name' => 'Test Schedule',
'message' => 'Foo bar, baz.',
'status' => 1,
'scheduled_at' => date('Y-m-d H:i'),
];
$this->post('/api/v1/schedules/', $schedule);
$this->assertResponseOk();
$this->seeJson(array_forget($schedule, 'scheduled_at'));
}
public function testUpdateSchedule()
{
$this->beUser();
$schedule = factory('CachetHQ\Cachet\Models\Schedule')->create();
$this->put('/api/v1/schedules/'.$schedule->id, [
'name' => 'Updated schedule',
]);
$this->assertResponseOk();
$this->seeJson(['name' => 'Updated schedule']);
}
public function testDeleteSchedule()
{
$this->beUser();
factory('CachetHQ\Cachet\Models\Schedule')->create();
$this->delete('/api/v1/schedules/1');
$this->assertResponseStatus(204);
}
}

View File

@ -1,57 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\Incident;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Incident\ReportMaintenanceCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Incident\ReportMaintenanceCommandHandler;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the add incident command test class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class ReportMaintenanceCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'name' => 'Test',
'message' => 'Foo bar baz',
'notify' => false,
'timestamp' => '2020-12-30 00:00:01',
];
$object = new ReportMaintenanceCommand(
$params['name'],
$params['message'],
$params['notify'],
$params['timestamp']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return ReportMaintenanceCommandHandler::class;
}
}

View File

@ -0,0 +1,59 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\Schedule;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Schedule\CreateScheduleCommandHandler;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the create schedule command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class CreateScheduleCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'name' => 'Test',
'message' => 'Foo',
'status' => 1,
'scheduled_at' => date('Y-m-d H:i'),
'completed_at' => date('Y-m-d H:i'),
'components' => [],
];
$object = new CreateScheduleCommand(
$params['name'],
$params['message'],
$params['status'],
$params['scheduled_at'],
$params['completed_at'],
$params['components']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return CreateScheduleCommandHandler::class;
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\Schedule;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Schedule\DeleteScheduleCommandHandler;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the create schedule command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteScheduleCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'schedule' => new Schedule(),
];
$object = new DeleteScheduleCommand(
$params['schedule']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return DeleteScheduleCommandHandler::class;
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\Schedule;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Schedule\UpdateScheduleCommandHandler;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the create schedule command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateScheduleCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'schedule' => new Schedule(),
'name' => 'Foo',
'message' => 'Bar',
'status' => 1,
'scheduled_at' => date('Y-m-d H:i'),
'completed_at' => date('Y-m-d H:i'),
'components' => [],
];
$object = new UpdateScheduleCommand(
$params['schedule'],
$params['name'],
$params['message'],
$params['status'],
$params['scheduled_at'],
$params['completed_at'],
$params['components']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return UpdateScheduleCommandHandler::class;
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Events\Incident;
use CachetHQ\Cachet\Bus\Events\Incident\MaintenanceWasScheduledEvent;
use CachetHQ\Cachet\Models\Incident;
/**
* This is the maintenance was scheduled event test class.
*
* @author James Brooks <james@alt-three.com>
*/
class MaintenanceWasScheduledEventTest extends AbstractIncidentEventTestCase
{
protected function objectHasHandlers()
{
return true;
}
protected function getObjectAndParams()
{
$params = ['incident' => new Incident()];
$object = new MaintenanceWasScheduledEvent($params['incident']);
return compact('params', 'object');
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Events\Schedule;
use AltThree\TestBench\EventTrait;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleEventInterface;
use CachetHQ\Tests\Cachet\AbstractTestCase;
abstract class AbstractScheduleEventTestCase extends AbstractTestCase
{
use EventTrait;
protected function getEventInterfaces()
{
return [ScheduleEventInterface::class];
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasCreatedEvent;
use CachetHQ\Cachet\Models\Schedule;
class ScheduleWasAddedEventTest extends AbstractScheduleEventTestCase
{
protected function objectHasHandlers()
{
return false;
}
protected function getObjectAndParams()
{
$params = ['schedule' => new Schedule()];
$object = new ScheduleWasCreatedEvent($params['schedule']);
return compact('params', 'object');
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasRemovedEvent;
use CachetHQ\Cachet\Models\Schedule;
class ScheduleWasRemovedEventTest extends AbstractScheduleEventTestCase
{
protected function objectHasHandlers()
{
return false;
}
protected function getObjectAndParams()
{
$params = ['schedule' => new Schedule()];
$object = new ScheduleWasRemovedEvent($params['schedule']);
return compact('params', 'object');
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Events\Schedule;
use CachetHQ\Cachet\Bus\Events\Schedule\ScheduleWasUpdatedEvent;
use CachetHQ\Cachet\Models\Schedule;
class ScheduleWasUpdatedEventTest extends AbstractScheduleEventTestCase
{
protected function objectHasHandlers()
{
return false;
}
protected function getObjectAndParams()
{
$params = ['schedule' => new Schedule()];
$object = new ScheduleWasUpdatedEvent($params['schedule']);
return compact('params', 'object');
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Models;
use AltThree\TestBench\ValidationTrait;
use CachetHQ\Cachet\Models\ScheduleComponent;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the schedule component model test class.
*
* @author James Brooks <james@alt-three.com>
*/
class ScheduleComponentTest extends AbstractTestCase
{
use ValidationTrait;
public function testValidation()
{
$this->checkRules(new ScheduleComponent());
}
}

View File

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Models;
use AltThree\TestBench\ValidationTrait;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the Schedule model test class.
*
* @author James Brooks <james@alt-three.com>
*/
class ScheduleTest extends AbstractTestCase
{
use ValidationTrait;
public function testValidation()
{
$this->checkRules(new Schedule());
}
}