From fdfebc18fb580b9009a58fbdf3fd1c5f48f58c8e Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 12 Jul 2016 19:29:45 +0100 Subject: [PATCH] Implement the system status api endpoint. Closes #1936 --- app/Composers/StatusPageComposer.php | 64 +++++++---------- .../Providers/IntegrationServiceProvider.php | 47 +++++++++++++ .../Controllers/Api/GeneralController.php | 13 ++++ app/Http/Routes/ApiRoutes.php | 1 + app/Integrations/Contracts/System.php | 27 +++++++ app/Integrations/Core/System.php | 70 +++++++++++++++++++ config/app.php | 1 + .../IntegrationServiceProviderTest.php | 31 ++++++++ 8 files changed, 217 insertions(+), 37 deletions(-) create mode 100644 app/Foundation/Providers/IntegrationServiceProvider.php create mode 100644 app/Integrations/Contracts/System.php create mode 100644 app/Integrations/Core/System.php create mode 100644 tests/Foundation/Providers/IntegrationServiceProviderTest.php diff --git a/app/Composers/StatusPageComposer.php b/app/Composers/StatusPageComposer.php index ddfed69b4f81..349fd24c9662 100644 --- a/app/Composers/StatusPageComposer.php +++ b/app/Composers/StatusPageComposer.php @@ -11,13 +11,38 @@ namespace CachetHQ\Cachet\Composers; +use CachetHQ\Cachet\Integrations\Core\System; use CachetHQ\Cachet\Models\Component; use CachetHQ\Cachet\Models\ComponentGroup; use CachetHQ\Cachet\Models\Incident; use Illuminate\Contracts\View\View; +/** + * This is the status page composer. + * + * @author James Brooks + */ class StatusPageComposer { + /** + * The system instance. + * + * @var \CachetHQ\Cachet\Integrations\Contracts\System + */ + protected $system; + + /** + * Create a new status page composer instance. + * + * @param \CachetHQ\Cachet\Integrations\Contracts\System $system + * + * @return void + */ + public function __construct(System $system) + { + $this->system = $system; + } + /** * Index page view composer. * @@ -27,42 +52,7 @@ class StatusPageComposer */ public function compose(View $view) { - $totalComponents = Component::enabled()->count(); - $majorOutages = Component::enabled()->status(4)->count(); - $isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false; - - // Default data - $withData = [ - 'system_status' => 'info', - 'system_message' => trans_choice('cachet.service.bad', $totalComponents), - 'favicon' => 'favicon-high-alert', - ]; - - if ($isMajorOutage) { - $withData = [ - 'system_status' => 'danger', - 'system_message' => trans_choice('cachet.service.major', $totalComponents), - 'favicon' => 'favicon-high-alert', - ]; - } elseif (Component::enabled()->notStatus(1)->count() === 0) { - // If all our components are ok, do we have any non-fixed incidents? - $incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get()->filter(function ($incident) { - return $incident->status > 0; - }); - $incidentCount = $incidents->count(); - - if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) { - $withData = [ - 'system_status' => 'success', - 'system_message' => trans_choice('cachet.service.good', $totalComponents), - 'favicon' => 'favicon', - ]; - } - } else { - if (Component::enabled()->whereIn('status', [2, 3])->count() > 0) { - $withData['favicon'] = 'favicon-medium-alert'; - } - } + $status = $this->system->getStatus(); // Scheduled maintenance code. $scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get(); @@ -72,7 +62,7 @@ public function compose(View $view) $componentGroups = ComponentGroup::whereIn('id', $usedComponentGroups)->orderBy('order')->get(); $ungroupedComponents = Component::enabled()->where('group_id', 0)->orderBy('order')->orderBy('created_at')->get(); - $view->with($withData) + $view->with($status) ->withComponentGroups($componentGroups) ->withUngroupedComponents($ungroupedComponents) ->withScheduledMaintenance($scheduledMaintenance); diff --git a/app/Foundation/Providers/IntegrationServiceProvider.php b/app/Foundation/Providers/IntegrationServiceProvider.php new file mode 100644 index 000000000000..4708ad3cef44 --- /dev/null +++ b/app/Foundation/Providers/IntegrationServiceProvider.php @@ -0,0 +1,47 @@ + + */ +class IntegrationServiceProvider extends ServiceProvider +{ + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->registerSystem(); + } + + /** + * Register the system class. + * + * @return void + */ + protected function registerSystem() + { + $this->app->singleton(SystemContract::class, function (Container $app) { + return new System(); + }); + } +} diff --git a/app/Http/Controllers/Api/GeneralController.php b/app/Http/Controllers/Api/GeneralController.php index 2fd7331eff08..9036ddd210b2 100644 --- a/app/Http/Controllers/Api/GeneralController.php +++ b/app/Http/Controllers/Api/GeneralController.php @@ -11,6 +11,7 @@ namespace CachetHQ\Cachet\Http\Controllers\Api; +use CachetHQ\Cachet\Integrations\Contracts\System; use CachetHQ\Cachet\Integrations\Releases; /** @@ -44,4 +45,16 @@ public function version() 'latest' => $latest, ])->item(CACHET_VERSION); } + + /** + * Get the system status message. + * + * @return \Illuminate\Http\JsonResponse + */ + public function status() + { + $system = app()->make(System::class)->getStatus(); + + return $this->item($system['system_message']); + } } diff --git a/app/Http/Routes/ApiRoutes.php b/app/Http/Routes/ApiRoutes.php index 20744edbb07d..c288b96bc410 100644 --- a/app/Http/Routes/ApiRoutes.php +++ b/app/Http/Routes/ApiRoutes.php @@ -33,6 +33,7 @@ public function map(Registrar $router) $router->group(['middleware' => ['auth.api']], function (Registrar $router) { $router->get('ping', 'GeneralController@ping'); $router->get('version', 'GeneralController@version'); + $router->get('status', 'GeneralController@status'); $router->get('components', 'ComponentController@getComponents'); $router->get('components/groups', 'ComponentGroupController@getGroups'); diff --git a/app/Integrations/Contracts/System.php b/app/Integrations/Contracts/System.php new file mode 100644 index 000000000000..f427b8e4dec6 --- /dev/null +++ b/app/Integrations/Contracts/System.php @@ -0,0 +1,27 @@ + + */ +interface System +{ + /** + * Get the entire system status. + * + * @return array + */ + public function getStatus(); +} diff --git a/app/Integrations/Core/System.php b/app/Integrations/Core/System.php new file mode 100644 index 000000000000..a44af6e8b4d5 --- /dev/null +++ b/app/Integrations/Core/System.php @@ -0,0 +1,70 @@ + + */ +class System implements SystemContract +{ + /** + * Get the entire system status. + * + * @return array + */ + public function getStatus() + { + $enabledScope = Component::enabled(); + $totalComponents = Component::enabled()->count(); + $majorOutages = Component::enabled()->status(4)->count(); + $isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false; + + // Default data + $status = [ + 'system_status' => 'info', + 'system_message' => trans_choice('cachet.service.bad', $totalComponents), + 'favicon' => 'favicon-high-alert', + ]; + + if ($isMajorOutage) { + $status = [ + 'system_status' => 'danger', + 'system_message' => trans_choice('cachet.service.major', $totalComponents), + 'favicon' => 'favicon-high-alert', + ]; + } elseif (Component::enabled()->notStatus(1)->count() === 0) { + // If all our components are ok, do we have any non-fixed incidents? + $incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get()->filter(function ($incident) { + return $incident->status > 0; + }); + $incidentCount = $incidents->count(); + + if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) { + $status = [ + 'system_status' => 'success', + 'system_message' => trans_choice('cachet.service.good', $totalComponents), + 'favicon' => 'favicon', + ]; + } + } elseif (Component::enabled()->whereIn('status', [2, 3])->count() > 0) { + $status['favicon'] = 'favicon-medium-alert'; + } + + return $status; + } +} diff --git a/config/app.php b/config/app.php index 3a5d4fd9d57a..7a8182ceac67 100644 --- a/config/app.php +++ b/config/app.php @@ -184,6 +184,7 @@ 'CachetHQ\Cachet\Foundation\Providers\ComposerServiceProvider', 'CachetHQ\Cachet\Foundation\Providers\ConsoleServiceProvider', 'CachetHQ\Cachet\Foundation\Providers\ConfigServiceProvider', + 'CachetHQ\Cachet\Foundation\Providers\IntegrationServiceProvider', 'CachetHQ\Cachet\Foundation\Providers\EventServiceProvider', 'CachetHQ\Cachet\Foundation\Providers\RepositoryServiceProvider', 'CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider', diff --git a/tests/Foundation/Providers/IntegrationServiceProviderTest.php b/tests/Foundation/Providers/IntegrationServiceProviderTest.php new file mode 100644 index 000000000000..e3018af4360b --- /dev/null +++ b/tests/Foundation/Providers/IntegrationServiceProviderTest.php @@ -0,0 +1,31 @@ + + */ +class IntegrationServiceProviderTest extends AbstractTestCase +{ + use ServiceProviderTrait; + + public function testSystemIsInjectable() + { + $this->assertIsInjectable(System::class); + } +}