From 6f0a2d1c5ed9aab7d0e81f24946663c61a29f317 Mon Sep 17 00:00:00 2001 From: Kevin Duret Date: Fri, 13 Mar 2020 11:45:31 +0100 Subject: [PATCH] enh(api): update service graph endpoints (#8413) Refs: MON-4792 --- .../Centreon/Monitoring.Resource.yml | 8 + config/routes/Centreon/monitoring/metric.yaml | 18 ++ doc/API/centreon-api-v2.yaml | 4 +- .../Monitoring/MetricController.php | 94 +++++++++ .../MonitoringResourceController.php | 41 +++- src/Centreon/Domain/Monitoring/Resource.php | 48 +++++ .../Monitoring/MetricControllerTest.php | 193 +++++++++++++++++- 7 files changed, 398 insertions(+), 8 deletions(-) diff --git a/config/packages/serializer/Centreon/Monitoring.Resource.yml b/config/packages/serializer/Centreon/Monitoring.Resource.yml index 7ad78dda7a6..d93d3c08bb9 100644 --- a/config/packages/serializer/Centreon/Monitoring.Resource.yml +++ b/config/packages/serializer/Centreon/Monitoring.Resource.yml @@ -68,6 +68,14 @@ Centreon\Domain\Monitoring\Resource: type: string groups: - 'resource_main' + statusGraphEndpoint: + type: string + groups: + - 'resource_main' + performanceGraphEndpoint: + type: string + groups: + - 'resource_main' severity: type: Centreon\Domain\Monitoring\ResourceSeverity groups: diff --git a/config/routes/Centreon/monitoring/metric.yaml b/config/routes/Centreon/monitoring/metric.yaml index 3f6591e2184..14ab625f64b 100644 --- a/config/routes/Centreon/monitoring/metric.yaml +++ b/config/routes/Centreon/monitoring/metric.yaml @@ -19,3 +19,21 @@ monitoring.metric.getServiceStatus: end: '\S+' controller: 'Centreon\Application\Controller\Monitoring\MetricController::getServiceStatus' condition: "request.attributes.get('version') >= 2.0" + +monitoring.metric.getServicePerformanceMetrics: + methods: GET + path: /monitoring/hosts/{hostId}/services/{serviceId}/metrics/performance + requirements: + hostId: '\d+' + serviceId: '\d+' + controller: 'Centreon\Application\Controller\Monitoring\MetricController::getServicePerformanceMetrics' + condition: "request.attributes.get('version') >= 2.0" + +monitoring.metric.getServiceStatusMetrics: + methods: GET + path: /monitoring/hosts/{hostId}/services/{serviceId}/metrics/status + requirements: + hostId: '\d+' + serviceId: '\d+' + controller: 'Centreon\Application\Controller\Monitoring\MetricController::getServiceStatusMetrics' + condition: "request.attributes.get('version') >= 2.0" diff --git a/doc/API/centreon-api-v2.yaml b/doc/API/centreon-api-v2.yaml index 9b6157f0bd8..a0788c48e29 100644 --- a/doc/API/centreon-api-v2.yaml +++ b/doc/API/centreon-api-v2.yaml @@ -2489,9 +2489,9 @@ components: type: string description: "URL of the status chart linked to the resource" example: "/monitoring/hosts/1/services/2/metrics/status" - metrics_graph_endpoint: + performance_graph_endpoint: type: string - description: "URL of the metrics chart linked to the resource" + description: "URL of the performance chart linked to the resource" example: "/monitoring/hosts/1/services/2/metrics/performance" duration: type: string diff --git a/src/Centreon/Application/Controller/Monitoring/MetricController.php b/src/Centreon/Application/Controller/Monitoring/MetricController.php index 142c8ebd9b6..797ccfc5ada 100644 --- a/src/Centreon/Application/Controller/Monitoring/MetricController.php +++ b/src/Centreon/Application/Controller/Monitoring/MetricController.php @@ -26,7 +26,9 @@ use Centreon\Domain\Monitoring\Metric\Interfaces\MetricServiceInterface; use Centreon\Domain\Monitoring\Interfaces\MonitoringServiceInterface; use Centreon\Domain\Monitoring\Service; +use Centreon\Domain\RequestParameters\Interfaces\RequestParametersInterface; use Centreon\Domain\Exception\EntityNotFoundException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use FOS\RestBundle\View\View; /** @@ -88,6 +90,36 @@ private function findService(int $hostId, int $serviceId): Service return $service; } + /** + * Validate and extract start/end dates from request parameters + * + * @param RequestParametersInterface $requestParameters + * @return array + * @example [new \Datetime('yesterday'), new \Datetime('today')] + * @throws NotFoundHttpException + * @throws \LogicException + */ + private function extractDatesFromRequestParameters(RequestParametersInterface $requestParameters): array + { + $start = $requestParameters->getExtraParameter('start') ?: '1 day ago'; + $end = $requestParameters->getExtraParameter('end') ?: 'now'; + + foreach (['start' => $start, 'end' => $end] as $param => $value) { + if (false === strtotime($value)) { + throw new NotFoundHttpException(sprintf('Invalid date given for parameter "%s".', $param)); + } + } + + $start = new \DateTime($start); + $end = new \DateTime($end); + + if ($start >= $end) { + throw new \RangeException('End date must be greater than start date.'); + } + + return [$start, $end]; + } + /** * Entry point to get service metrics * @@ -151,4 +183,66 @@ public function getServiceStatus( return $this->view($status); } + + /** + * Entry point to get service performance metrics + * + * @param int $hostId + * @param int $serviceId + * @return View + * @throws \Exception + */ + public function getServicePerformanceMetrics( + RequestParametersInterface $requestParameters, + int $hostId, + int $serviceId + ): View { + $this->denyAccessUnlessGrantedForApiRealtime(); + + list($start, $end) = $this->extractDatesFromRequestParameters($requestParameters); + + /** + * @var $contact Contact + */ + $contact = $this->getUser(); + + $service = $this->findService($hostId, $serviceId); + + $metrics = $this->metricService + ->filterByContact($contact) + ->findMetricsByService($service, $start, $end); + + return $this->view($metrics); + } + + /** + * Entry point to get service status metrics + * + * @param int $hostId + * @param int $serviceId + * @return View + * @throws \Exception + */ + public function getServiceStatusMetrics( + RequestParametersInterface $requestParameters, + int $hostId, + int $serviceId + ): View { + $this->denyAccessUnlessGrantedForApiRealtime(); + + list($start, $end) = $this->extractDatesFromRequestParameters($requestParameters); + + /** + * @var $contact Contact + */ + $contact = $this->getUser(); + + $service = $this->findService($hostId, $serviceId); + + $status = $this->metricService + ->filterByContact($contact) + ->findStatusByService($service, $start, $end); + + return $this->view($status); + } } diff --git a/src/Centreon/Application/Controller/MonitoringResourceController.php b/src/Centreon/Application/Controller/MonitoringResourceController.php index 4d854160779..e8b96b00654 100644 --- a/src/Centreon/Application/Controller/MonitoringResourceController.php +++ b/src/Centreon/Application/Controller/MonitoringResourceController.php @@ -33,6 +33,7 @@ use Centreon\Domain\Monitoring\Serializer\ResourceExclusionStrategy; use Centreon\Domain\Monitoring\Resource; use Centreon\Domain\Monitoring\ResourceFilter; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * Resource APIs for the Unified View page @@ -65,12 +66,19 @@ class MonitoringResourceController extends AbstractController */ protected $resource; + /** + * @var UrlGeneratorInterface + */ + protected $router; + /** * @param ResourceServiceInterface $resource + * @param UrlGeneratorInterface $router */ - public function __construct(ResourceServiceInterface $resource) + public function __construct(ResourceServiceInterface $resource, UrlGeneratorInterface $router) { $this->resource = $resource; + $this->router = $router; } /** @@ -132,9 +140,36 @@ public function list( $context->addExclusionStrategy(new ResourceExclusionStrategy()); + $resources = $this->resource->filterByContact($this->getUser()) + ->findResources($filter); + + foreach ($resources as $resource) { + if ($resource->getParent() != null) { + $parameters = [ + 'hostId' => $resource->getParent()->getId(), + 'serviceId' => $resource->getId(), + ]; + + // set service performance graph endpoint from metrics controller + $resource->setPerformanceGraphEndpoint( + $this->router->generate( + 'monitoring.metric.getServicePerformanceMetrics', + $parameters + ) + ); + + // set service status graph endpoint from metrics controller + $resource->setStatusGraphEndpoint( + $this->router->generate( + 'monitoring.metric.getServiceStatusMetrics', + $parameters + ) + ); + } + } + return $this->view([ - 'result' => $this->resource->filterByContact($this->getUser()) - ->findResources($filter), + 'result' => $resources, 'meta' => $requestParameters->toArray(), ])->setContext($context); } diff --git a/src/Centreon/Domain/Monitoring/Resource.php b/src/Centreon/Domain/Monitoring/Resource.php index f1b514d5e8d..6aa538358e4 100644 --- a/src/Centreon/Domain/Monitoring/Resource.php +++ b/src/Centreon/Domain/Monitoring/Resource.php @@ -98,6 +98,16 @@ class Resource */ private $acknowledgementEndpoint; + /** + * @var string|null + */ + private $statusGraphEndpoint; + + /** + * @var string|null + */ + private $performanceGraphEndpoint; + /** * @var \Centreon\Domain\Monitoring\ResourceSeverity|null */ @@ -399,6 +409,44 @@ public function setAcknowledgementEndpoint(string $acknowledgementEndpoint): sel return $this; } + /** + * @return string|null + */ + public function getStatusGraphEndpoint(): ?string + { + return $this->statusGraphEndpoint; + } + + /** + * @param string $statusGraphEndpoint + * @return \Centreon\Domain\Monitoring\Resource + */ + public function setStatusGraphEndpoint(string $statusGraphEndpoint): self + { + $this->statusGraphEndpoint = $statusGraphEndpoint; + + return $this; + } + + /** + * @return string|null + */ + public function getPerformanceGraphEndpoint(): ?string + { + return $this->performanceGraphEndpoint; + } + + /** + * @param string $performanceGraphEndpoint + * @return \Centreon\Domain\Monitoring\Resource + */ + public function setPerformanceGraphEndpoint(string $performanceGraphEndpoint): self + { + $this->performanceGraphEndpoint = $performanceGraphEndpoint; + + return $this; + } + /** * @return \Centreon\Domain\Monitoring\ResourceSeverity|null */ diff --git a/tests/php/Centreon/Application/Controller/Monitoring/MetricControllerTest.php b/tests/php/Centreon/Application/Controller/Monitoring/MetricControllerTest.php index abda3e47439..df38335ff34 100644 --- a/tests/php/Centreon/Application/Controller/Monitoring/MetricControllerTest.php +++ b/tests/php/Centreon/Application/Controller/Monitoring/MetricControllerTest.php @@ -28,9 +28,11 @@ use Centreon\Domain\Exception\EntityNotFoundException; use Centreon\Domain\Monitoring\Metric\Interfaces\MetricServiceInterface; use Centreon\Domain\Monitoring\Interfaces\MonitoringServiceInterface; +use Centreon\Domain\RequestParameters\Interfaces\RequestParametersInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use FOS\RestBundle\View\View; use Psr\Container\ContainerInterface; use PHPUnit\Framework\TestCase; @@ -51,6 +53,8 @@ class MetricControllerTest extends TestCase protected $container; + protected $requestParameters; + protected function setUp() { $this->adminContact = (new Contact()) @@ -88,11 +92,11 @@ protected function setUp() ->method('isGranted') ->willReturn(true); $token = $this->createMock(TokenInterface::class); - $token->expects($this->exactly(2)) + $token->expects($this->any()) ->method('getUser') ->willReturn('admin'); $tokenStorage = $this->createMock(TokenStorageInterface::class); - $tokenStorage->expects($this->exactly(2)) + $tokenStorage->expects($this->any()) ->method('getToken') ->willReturn($token); @@ -100,7 +104,7 @@ protected function setUp() $this->container->expects($this->any()) ->method('has') ->willReturn(true); - $this->container->expects($this->exactly(3)) + $this->container->expects($this->any()) ->method('get') ->withConsecutive( [$this->equalTo('security.authorization_checker')], @@ -108,6 +112,8 @@ protected function setUp() [$this->equalTo('security.token_storage')] ) ->willReturnOnConsecutiveCalls($authorizationChecker, $tokenStorage, $tokenStorage); + + $this->requestParameters = $this->createMock(RequestParametersInterface::class); } /** @@ -239,4 +245,185 @@ public function testGetServiceStatusSucceed() View::create($this->status, null, []) ); } + + /** + * test getServicePerformanceMetrics with not found host + */ + public function testGetServicePerformanceMetricsNotFoundHost() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn(null); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(EntityNotFoundException::class); + $this->expectExceptionMessage('Host 1 not found'); + $metricController->getServiceMetrics(1, 1, $this->start, $this->end); + } + + /** + * test getServicePerformanceMetrics with not found service + */ + public function testGetServicePerformanceMetricsNotFoundService() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn($this->host); + $this->monitoringService->expects($this->once()) + ->method('findOneService') + ->willReturn(null); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(EntityNotFoundException::class); + $this->expectExceptionMessage('Service 1 not found'); + $metricController->getServicePerformanceMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServicePerformanceMetrics with wrong start date format + */ + public function testGetServicePerformanceMetricsWrongStartDate() + { + $this->requestParameters->expects($this->exactly(2)) + ->method('getExtraParameter') + ->willReturnOnConsecutiveCalls('wrong format', '2020-02-18T12:00:00'); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(NotFoundHttpException::class); + $this->expectExceptionMessage('Invalid date given for parameter "start".'); + $metricController->getServicePerformanceMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServicePerformanceMetrics with wrong end date format + */ + public function testGetServicePerformanceMetricsWrongEndDate() + { + $this->requestParameters->expects($this->exactly(2)) + ->method('getExtraParameter') + ->willReturnOnConsecutiveCalls('2020-02-18T00:00:00', 'wrong format'); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(NotFoundHttpException::class); + $this->expectExceptionMessage('Invalid date given for parameter "end".'); + $metricController->getServicePerformanceMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServicePerformanceMetrics with start date greater than end date + */ + public function testGetServicePerformanceMetricsWrongDateRange() + { + $this->requestParameters->expects($this->exactly(2)) + ->method('getExtraParameter') + ->willReturnOnConsecutiveCalls('2020-02-18T12:00:00', '2020-02-18T00:00:00'); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(\RangeException::class); + $this->expectExceptionMessage('End date must be greater than start date.'); + $metricController->getServicePerformanceMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServicePerformanceMetrics which succeed + */ + public function testGetServicePerformanceMetricsSucceed() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn($this->host); + $this->monitoringService->expects($this->once()) + ->method('findOneService') + ->willReturn($this->service); + $this->metricService->expects($this->once()) + ->method('filterByContact') + ->willReturn($this->metricService); + $this->metricService->expects($this->once()) + ->method('findMetricsByService') + ->willReturn($this->metrics); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $metrics = $metricController->getServicePerformanceMetrics($this->requestParameters, 1, 1); + $this->assertEquals( + $metrics, + View::create($this->metrics, null, []) + ); + } + + /** + * test getServiceStatusMetrics with not found host + */ + public function testGetServiceStatusMetricsNotFoundHost() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn(null); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(EntityNotFoundException::class); + $this->expectExceptionMessage('Host 1 not found'); + $metricController->getServiceStatusMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServiceStatusMetrics with not found service + */ + public function testGetServiceStatusMetricsNotFoundService() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn($this->host); + $this->monitoringService->expects($this->once()) + ->method('findOneService') + ->willReturn(null); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $this->expectException(EntityNotFoundException::class); + $this->expectExceptionMessage('Service 1 not found'); + $metricController->getServiceStatusMetrics($this->requestParameters, 1, 1); + } + + /** + * test getServiceStatusMetrics which succeed + */ + public function testGetServiceStatusMetricsSucceed() + { + $this->monitoringService->expects($this->once()) + ->method('findOneHost') + ->willReturn($this->host); + $this->monitoringService->expects($this->once()) + ->method('findOneService') + ->willReturn($this->service); + $this->metricService->expects($this->once()) + ->method('filterByContact') + ->willReturn($this->metricService); + $this->metricService->expects($this->once()) + ->method('findStatusByService') + ->willReturn($this->status); + + $metricController = new MetricController($this->metricService, $this->monitoringService); + $metricController->setContainer($this->container); + + $status = $metricController->getServiceStatusMetrics($this->requestParameters, 1, 1); + $this->assertEquals( + $status, + View::create($this->status, null, []) + ); + } }