From 2f3d54ab7562f2a4297041be72655b851f665c6f Mon Sep 17 00:00:00 2001 From: Yozhef Date: Sun, 16 Jan 2022 22:21:14 +0200 Subject: [PATCH 01/10] feat: remove custom error if service not found and create response standart dto --- src/Check/CheckInterface.php | 7 +- src/Check/DoctrineCheck.php | 24 ++---- src/Check/EnvironmentCheck.php | 9 +- src/Check/StatusUpCheck.php | 6 +- src/Controller/HealthController.php | 2 +- src/Dto/Response.php | 61 ++++++++++++++ src/Exception/AbstractException.php | 40 --------- src/Exception/ParameterExceptionInterface.php | 13 --- src/Exception/ServiceNotFoundException.php | 15 ---- .../Controller/HealthControllerTest.php | 64 ++++++++++----- .../Unit/Check/StatusUpCheckTest.php | 21 ----- .../Mock/AbstractPlatformMock.php | 2 +- .../{Integration => }/Mock/ConnectionMock.php | 2 +- .../Mock/EntityManagerMock.php | 2 +- .../Mock/ExecuteQueryMock.php | 2 +- .../Unit/Check/DoctrineCheckTest.php | 82 +++++++++++-------- .../Unit/Check/EnvironmentCheckTest.php | 29 +++++-- tests/Unit/Check/StatusUpCheckTest.php | 27 ++++++ 18 files changed, 227 insertions(+), 181 deletions(-) create mode 100644 src/Dto/Response.php delete mode 100644 src/Exception/AbstractException.php delete mode 100644 src/Exception/ParameterExceptionInterface.php delete mode 100644 src/Exception/ServiceNotFoundException.php delete mode 100644 tests/Integration/Unit/Check/StatusUpCheckTest.php rename tests/{Integration => }/Mock/AbstractPlatformMock.php (80%) rename tests/{Integration => }/Mock/ConnectionMock.php (82%) rename tests/{Integration => }/Mock/EntityManagerMock.php (73%) rename tests/{Integration => }/Mock/ExecuteQueryMock.php (64%) rename tests/{Integration => }/Unit/Check/DoctrineCheckTest.php (58%) rename tests/{Integration => }/Unit/Check/EnvironmentCheckTest.php (66%) create mode 100644 tests/Unit/Check/StatusUpCheckTest.php diff --git a/src/Check/CheckInterface.php b/src/Check/CheckInterface.php index d9227b7..3adc343 100644 --- a/src/Check/CheckInterface.php +++ b/src/Check/CheckInterface.php @@ -4,10 +4,9 @@ namespace SymfonyHealthCheckBundle\Check; +use SymfonyHealthCheckBundle\Dto\Response; + interface CheckInterface { - /** - * @return array - */ - public function check(): array; + public function check(): Response; } diff --git a/src/Check/DoctrineCheck.php b/src/Check/DoctrineCheck.php index 95016e2..60d14e0 100644 --- a/src/Check/DoctrineCheck.php +++ b/src/Check/DoctrineCheck.php @@ -5,12 +5,12 @@ namespace SymfonyHealthCheckBundle\Check; use Symfony\Component\DependencyInjection\ContainerInterface; -use SymfonyHealthCheckBundle\Exception\ServiceNotFoundException; +use SymfonyHealthCheckBundle\Dto\Response; use Throwable; class DoctrineCheck implements CheckInterface { - private const CHECK_RESULT_KEY = 'connection'; + private const CHECK_RESULT_NAME = 'doctrine'; private ContainerInterface $container; @@ -19,35 +19,25 @@ public function __construct(ContainerInterface $container) $this->container = $container; } - /** - * @throws ServiceNotFoundException - */ - public function check(): array + public function check(): Response { - $result = ['name' => 'doctrine']; - if ($this->container->has('doctrine.orm.entity_manager') === false) { - throw new ServiceNotFoundException( - 'Entity Manager Not Found.', - [ - 'class' => 'doctrine.orm.entity_manager', - ] - ); + return new Response(self::CHECK_RESULT_NAME, false, 'Entity Manager Not Found.'); } $entityManager = $this->container->get('doctrine.orm.entity_manager'); if ($entityManager === null) { - throw new ServiceNotFoundException('Entity Manager Not Found.'); + return new Response(self::CHECK_RESULT_NAME, false, 'Entity Manager Not Found.'); } try { $con = $entityManager->getConnection(); $con->executeQuery($con->getDatabasePlatform()->getDummySelectSQL())->free(); } catch (Throwable $e) { - return array_merge($result, [self::CHECK_RESULT_KEY => false]); + return new Response(self::CHECK_RESULT_NAME, false, $e->getMessage()); } - return array_merge($result, [self::CHECK_RESULT_KEY => true]); + return new Response(self::CHECK_RESULT_NAME, true, 'ok'); } } diff --git a/src/Check/EnvironmentCheck.php b/src/Check/EnvironmentCheck.php index d99e685..7d323f5 100644 --- a/src/Check/EnvironmentCheck.php +++ b/src/Check/EnvironmentCheck.php @@ -5,6 +5,7 @@ namespace SymfonyHealthCheckBundle\Check; use Symfony\Component\DependencyInjection\ContainerInterface; +use SymfonyHealthCheckBundle\Dto\Response; use Throwable; class EnvironmentCheck implements CheckInterface @@ -18,16 +19,14 @@ public function __construct(ContainerInterface $container) $this->container = $container; } - public function check(): array + public function check(): Response { - $result = ['name' => self::CHECK_RESULT_KEY]; - try { $env = $this->container->getParameter('kernel.environment'); } catch (Throwable $e) { - return array_merge($result, [self::CHECK_RESULT_KEY => 'Could not determine']); + return new Response(self::CHECK_RESULT_KEY, false, 'Could not determine'); } - return array_merge($result, [self::CHECK_RESULT_KEY => $env]); + return new Response(self::CHECK_RESULT_KEY, true, 'ok', [$env]); } } diff --git a/src/Check/StatusUpCheck.php b/src/Check/StatusUpCheck.php index 5272e44..6c124ae 100644 --- a/src/Check/StatusUpCheck.php +++ b/src/Check/StatusUpCheck.php @@ -4,10 +4,12 @@ namespace SymfonyHealthCheckBundle\Check; +use SymfonyHealthCheckBundle\Dto\Response; + class StatusUpCheck implements CheckInterface { - public function check(): array + public function check(): Response { - return ['status' => 'up']; + return new Response('status', true, 'up'); } } diff --git a/src/Controller/HealthController.php b/src/Controller/HealthController.php index d7a6e89..f13f53e 100644 --- a/src/Controller/HealthController.php +++ b/src/Controller/HealthController.php @@ -33,7 +33,7 @@ public function healthCheckAction(): JsonResponse { $resultHealthCheck = []; foreach ($this->healthChecks as $healthCheck) { - $resultHealthCheck[] = $healthCheck->check(); + $resultHealthCheck[] = $healthCheck->check()->toArray(); } return new JsonResponse($resultHealthCheck, Response::HTTP_OK); diff --git a/src/Dto/Response.php b/src/Dto/Response.php new file mode 100644 index 0000000..76a201b --- /dev/null +++ b/src/Dto/Response.php @@ -0,0 +1,61 @@ +name = $name; + $this->result = $result; + $this->message = $message; + $this->params = $params; + } + + public function getName(): string + { + return $this->name; + } + + public function getResult(): bool + { + return $this->result; + } + + public function getMessage(): string + { + return $this->message; + } + + /** + * @return mixed[] + */ + public function getParams(): array + { + return $this->params; + } + + public function toArray(): array + { + return [ + 'name' => $this->getName(), + 'result' => $this->getResult(), + 'message' => $this->getMessage(), + 'params' => $this->getParams(), + ]; + } +} diff --git a/src/Exception/AbstractException.php b/src/Exception/AbstractException.php deleted file mode 100644 index 4bd350a..0000000 --- a/src/Exception/AbstractException.php +++ /dev/null @@ -1,40 +0,0 @@ - $parameters - * @param int $code - * @param Throwable|null $previous - */ - public function __construct( - string $message, - array $parameters, - int $code = 0, - Throwable $previous = null - ) { - $this->parameters = $parameters; - - parent::__construct($message, $code, $previous); - } - - public function getParameters(): array - { - return $this->parameters; - } -} diff --git a/src/Exception/ParameterExceptionInterface.php b/src/Exception/ParameterExceptionInterface.php deleted file mode 100644 index d0866f7..0000000 --- a/src/Exception/ParameterExceptionInterface.php +++ /dev/null @@ -1,13 +0,0 @@ -healthCheckAction(); self::assertSame(200, $response->getStatusCode()); - self::assertSame(json_encode([['status' => 'up']]), $response->getContent()); + self::assertSame( + json_encode([[ + 'name' => 'status', 'result' => true, 'message' => 'up', 'params' => [], + ]]), + $response->getContent() + ); } public function testEnvironmentCheckCouldNotDetermine(): void @@ -45,31 +49,32 @@ public function testEnvironmentCheckCouldNotDetermine(): void self::assertSame(200, $response->getStatusCode()); self::assertSame( - json_encode([['name' => 'environment', 'environment' => 'Could not determine']]), + json_encode([[ + 'name' => 'environment', + 'result' => false, + 'message' => 'Could not determine', + 'params' => [] + ]]), $response->getContent() ); } public function testDoctrineCheckServiceNotFoundException(): void { - self::expectException(ServiceNotFoundException::class); - $healthController = new HealthController(); $healthController->addHealthCheck(new DoctrineCheck(new ContainerBuilder())); - $healthController->healthCheckAction(); - } - - public function testDoctrineCheckGetParameters(): void - { - $healthController = new HealthController(); - $healthController->addHealthCheck(new DoctrineCheck(new ContainerBuilder())); - - try { - $healthController->healthCheckAction(); - } catch (ServiceNotFoundException $exception) { - self::assertSame(["class" => "doctrine.orm.entity_manager"], $exception->getParameters()); - } + $response = $healthController->healthCheckAction(); + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([[ + 'name' => 'doctrine', + 'result' => false, + 'message' => 'Entity Manager Not Found.', + 'params' => [] + ]]), + $response->getContent() + ); } public function testTwoCheckSuccess(): void @@ -82,7 +87,19 @@ public function testTwoCheckSuccess(): void self::assertSame(200, $response->getStatusCode()); self::assertSame( - json_encode([['status' => 'up'], ['name' => 'environment', 'environment' => 'Could not determine']]), + json_encode([ + [ + 'name' => 'status', + 'result' => true, + 'message' => 'up', + 'params' => [], + ], + [ + 'name' => 'environment', + 'result' => false, + 'message' => 'Could not determine', + 'params' => [] + ]]), $response->getContent() ); } @@ -95,7 +112,14 @@ public function testEnvironmentCheckSuccess(): void self::assertSame(200, $response->getStatusCode()); self::assertSame( - json_encode([['name' => 'environment', 'environment' => 'testing']]), + json_encode([ + [ + 'name' => 'environment', + 'result' => true, + 'message' => 'ok', + 'params' => ['testing'] + ] + ]), $response->getContent() ); } diff --git a/tests/Integration/Unit/Check/StatusUpCheckTest.php b/tests/Integration/Unit/Check/StatusUpCheckTest.php deleted file mode 100644 index 6c26e82..0000000 --- a/tests/Integration/Unit/Check/StatusUpCheckTest.php +++ /dev/null @@ -1,21 +0,0 @@ -check(); - - self::assertIsArray($result); - self::assertNotEmpty($result); - self::assertArrayHasKey('status', $result); - self::assertSame('up', $result['status']); - } -} diff --git a/tests/Integration/Mock/AbstractPlatformMock.php b/tests/Mock/AbstractPlatformMock.php similarity index 80% rename from tests/Integration/Mock/AbstractPlatformMock.php rename to tests/Mock/AbstractPlatformMock.php index d1515ac..2a0b49e 100644 --- a/tests/Integration/Mock/AbstractPlatformMock.php +++ b/tests/Mock/AbstractPlatformMock.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Mock; +namespace SymfonyHealthCheckBundle\Tests\Mock; class AbstractPlatformMock { diff --git a/tests/Integration/Mock/ConnectionMock.php b/tests/Mock/ConnectionMock.php similarity index 82% rename from tests/Integration/Mock/ConnectionMock.php rename to tests/Mock/ConnectionMock.php index cb45951..0fb4a3e 100644 --- a/tests/Integration/Mock/ConnectionMock.php +++ b/tests/Mock/ConnectionMock.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Mock; +namespace SymfonyHealthCheckBundle\Tests\Mock; class ConnectionMock { diff --git a/tests/Integration/Mock/EntityManagerMock.php b/tests/Mock/EntityManagerMock.php similarity index 73% rename from tests/Integration/Mock/EntityManagerMock.php rename to tests/Mock/EntityManagerMock.php index 0e6fd55..8d096de 100644 --- a/tests/Integration/Mock/EntityManagerMock.php +++ b/tests/Mock/EntityManagerMock.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Mock; +namespace SymfonyHealthCheckBundle\Tests\Mock; class EntityManagerMock { diff --git a/tests/Integration/Mock/ExecuteQueryMock.php b/tests/Mock/ExecuteQueryMock.php similarity index 64% rename from tests/Integration/Mock/ExecuteQueryMock.php rename to tests/Mock/ExecuteQueryMock.php index a445ddf..f38cbb4 100644 --- a/tests/Integration/Mock/ExecuteQueryMock.php +++ b/tests/Mock/ExecuteQueryMock.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Mock; +namespace SymfonyHealthCheckBundle\Tests\Mock; class ExecuteQueryMock { diff --git a/tests/Integration/Unit/Check/DoctrineCheckTest.php b/tests/Unit/Check/DoctrineCheckTest.php similarity index 58% rename from tests/Integration/Unit/Check/DoctrineCheckTest.php rename to tests/Unit/Check/DoctrineCheckTest.php index b837bd1..0ac7969 100644 --- a/tests/Integration/Unit/Check/DoctrineCheckTest.php +++ b/tests/Unit/Check/DoctrineCheckTest.php @@ -2,29 +2,17 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Unit\Check; +namespace SymfonyHealthCheckBundle\Tests\Unit\Check; use Exception; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use SymfonyHealthCheckBundle\Check\DoctrineCheck; -use SymfonyHealthCheckBundle\Check\StatusUpCheck; -use SymfonyHealthCheckBundle\Exception\ServiceNotFoundException; -use SymfonyHealthCheckBundle\Tests\Integration\Mock\ConnectionMock; -use SymfonyHealthCheckBundle\Tests\Integration\Mock\EntityManagerMock; +use SymfonyHealthCheckBundle\Tests\Mock\ConnectionMock; +use SymfonyHealthCheckBundle\Tests\Mock\EntityManagerMock; class DoctrineCheckTest extends TestCase { - public function testStatusUpCheckSuccess(): void - { - $result = (new StatusUpCheck())->check(); - - self::assertIsArray($result); - self::assertNotEmpty($result); - self::assertArrayHasKey('status', $result); - self::assertSame('up', $result['status']); - } - public function testDoctrineHasNotFoundException(): void { $container = $this->createMock(ContainerInterface::class); @@ -34,12 +22,22 @@ public function testDoctrineHasNotFoundException(): void ->with('doctrine.orm.entity_manager') ->willReturn(false); - $this->expectException(ServiceNotFoundException::class); - $this->expectExceptionMessage('Entity Manager Not Found.'); - $doctrine = new DoctrineCheck($container); - $doctrine->check(); + $result = $doctrine->check()->toArray(); + + self::assertIsArray($result); + self::assertNotEmpty($result); + + self::assertArrayHasKey('name', $result); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + + self::assertSame('doctrine', $result['name']); + self::assertFalse($result['result']); + self::assertSame('Entity Manager Not Found.', $result['message']); + self::assertIsArray($result['params']); } public function testDoctrineGetNotFoundException(): void @@ -56,12 +54,22 @@ public function testDoctrineGetNotFoundException(): void ->with('doctrine.orm.entity_manager') ->willReturn(null); - $this->expectException(ServiceNotFoundException::class); - $this->expectExceptionMessage('Entity Manager Not Found.'); - $doctrine = new DoctrineCheck($container); - $doctrine->check(); + $result = $doctrine->check()->toArray(); + + self::assertIsArray($result); + self::assertNotEmpty($result); + + self::assertArrayHasKey('name', $result); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + + self::assertSame('doctrine', $result['name']); + self::assertFalse($result['result']); + self::assertSame('Entity Manager Not Found.', $result['message']); + self::assertIsArray($result['params']); } public function testDoctrineSuccess(): void @@ -81,15 +89,20 @@ public function testDoctrineSuccess(): void $doctrine = new DoctrineCheck($container); - $result = $doctrine->check(); + $result = $doctrine->check()->toArray(); self::assertIsArray($result); self::assertNotEmpty($result); + self::assertArrayHasKey('name', $result); - self::assertArrayHasKey('connection', $result); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + self::assertSame('doctrine', $result['name']); - self::assertIsBool($result['connection']); - self::assertTrue($result['connection']); + self::assertTrue($result['result']); + self::assertSame('ok', $result['message']); + self::assertIsArray($result['params']); } public function testDoctrineFailPing(): void @@ -106,7 +119,7 @@ public function testDoctrineFailPing(): void $connectionMock ->method('getDatabasePlatform') ->with() - ->will(self::throwException(new Exception())); + ->will(self::throwException(new Exception('failed getDatabasePlatform'))); $container ->method('has') @@ -120,14 +133,19 @@ public function testDoctrineFailPing(): void $doctrine = new DoctrineCheck($container); - $result = $doctrine->check(); + $result = $doctrine->check()->toArray(); self::assertIsArray($result); self::assertNotEmpty($result); + self::assertArrayHasKey('name', $result); - self::assertArrayHasKey('connection', $result); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + self::assertSame('doctrine', $result['name']); - self::assertIsBool($result['connection']); - self::assertFalse($result['connection']); + self::assertFalse($result['result']); + self::assertSame('failed getDatabasePlatform', $result['message']); + self::assertIsArray($result['params']); } } diff --git a/tests/Integration/Unit/Check/EnvironmentCheckTest.php b/tests/Unit/Check/EnvironmentCheckTest.php similarity index 66% rename from tests/Integration/Unit/Check/EnvironmentCheckTest.php rename to tests/Unit/Check/EnvironmentCheckTest.php index 24da47e..6fd42a9 100644 --- a/tests/Integration/Unit/Check/EnvironmentCheckTest.php +++ b/tests/Unit/Check/EnvironmentCheckTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SymfonyHealthCheckBundle\Tests\Integration\Unit\Check; +namespace SymfonyHealthCheckBundle\Tests\Unit\Check; use Exception; use PHPUnit\Framework\MockObject\MockObject; @@ -29,14 +29,22 @@ public function testStatusUpCheckSuccess(): void ->with('kernel.environment') ->willReturn('test'); - $result = (new EnvironmentCheck($this->container))->check(); + + $result = (new EnvironmentCheck($this->container))->check()->toArray(); + self::assertIsArray($result); self::assertNotEmpty($result); + self::assertArrayHasKey('name', $result); - self::assertArrayHasKey('environment', $result); - self::assertSame('test', $result['environment']); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + self::assertSame('environment', $result['name']); + self::assertTrue($result['result']); + self::assertSame('ok', $result['message']); + self::assertIsArray($result['params']); } public function testStatusUpCheckFail(): void @@ -46,13 +54,20 @@ public function testStatusUpCheckFail(): void ->with('kernel.environment') ->will(self::throwException(new Exception())); - $result = (new EnvironmentCheck($this->container))->check(); + $result = (new EnvironmentCheck($this->container))->check()->toArray(); self::assertIsArray($result); self::assertNotEmpty($result); + self::assertArrayHasKey('name', $result); - self::assertArrayHasKey('environment', $result); - self::assertSame('Could not determine', $result['environment']); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + + self::assertSame('environment', $result['name']); + self::assertFalse($result['result']); + self::assertSame('Could not determine', $result['message']); + self::assertEmpty($result['params']); } } diff --git a/tests/Unit/Check/StatusUpCheckTest.php b/tests/Unit/Check/StatusUpCheckTest.php new file mode 100644 index 0000000..4c39de6 --- /dev/null +++ b/tests/Unit/Check/StatusUpCheckTest.php @@ -0,0 +1,27 @@ +check()->toArray(); + + self::assertIsArray($result); + self::assertNotEmpty($result); + self::assertArrayHasKey('name', $result); + self::assertArrayHasKey('result', $result); + self::assertArrayHasKey('message', $result); + self::assertArrayHasKey('params', $result); + self::assertSame('status', $result['name']); + self::assertTrue($result['result']); + self::assertSame('up', $result['message']); + self::assertEmpty($result['params']); + } +} From 0c8d1ee33495fb05a06bc4d9530f51c29a8c2874 Mon Sep 17 00:00:00 2001 From: Yozhef Date: Sun, 16 Jan 2022 22:22:35 +0200 Subject: [PATCH 02/10] feat: update read me how create custom check --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f057ce9..03f9196 100644 --- a/README.md +++ b/README.md @@ -109,13 +109,13 @@ declare(strict_types=1); namespace YourProject\Check; +use SymfonyHealthCheckBundle\Dto\Response; + class CustomCheck implements CheckInterface { - private const CHECK_RESULT_KEY = 'customConnection'; - - public function check(): array + public function check(): Response { - return [self::CHECK_RESULT_KEY => true]; + return new Response('status', true, 'up'); } } ``` From 4811e0b3b0705aea3dc6e98287246814111d1e8c Mon Sep 17 00:00:00 2001 From: Yozhef Date: Sun, 16 Jan 2022 22:24:42 +0200 Subject: [PATCH 03/10] feat: add return type to response method toArray --- src/Dto/Response.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Dto/Response.php b/src/Dto/Response.php index 76a201b..b961b3b 100644 --- a/src/Dto/Response.php +++ b/src/Dto/Response.php @@ -48,7 +48,10 @@ public function getParams(): array { return $this->params; } - + + /** + * @return mixed[] + */ public function toArray(): array { return [ From 94c85ee681e5c0a86e75088f2f9862e79d29c421 Mon Sep 17 00:00:00 2001 From: Yozhef Date: Sun, 16 Jan 2022 22:26:16 +0200 Subject: [PATCH 04/10] feat: add return type to response method toArray --- src/Dto/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dto/Response.php b/src/Dto/Response.php index b961b3b..7df9b4e 100644 --- a/src/Dto/Response.php +++ b/src/Dto/Response.php @@ -48,7 +48,7 @@ public function getParams(): array { return $this->params; } - + /** * @return mixed[] */ From 218ecbb7d38d7fdc48f2c013f198ef62654d282e Mon Sep 17 00:00:00 2001 From: Yozhef Date: Sun, 16 Jan 2022 22:51:28 +0200 Subject: [PATCH 05/10] feat: add new route ping --- src/Controller/PingController.php | 41 ++++++ src/DependencyInjection/Configuration.php | 7 + .../SymfonyHealthCheckExtension.php | 8 ++ src/Resources/config/controller.xml | 3 + src/Resources/config/routes.xml | 6 + .../Controller/PingControllerTest.php | 134 ++++++++++++++++++ .../DependencyInjection/ConfigurationTest.php | 47 +++++- .../Fixtures/filled_bundle_config.yaml | 2 + .../SymfonyHealthCheckExtensionTest.php | 19 ++- 9 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 src/Controller/PingController.php create mode 100644 tests/Integration/Controller/PingControllerTest.php diff --git a/src/Controller/PingController.php b/src/Controller/PingController.php new file mode 100644 index 0000000..09a9ab2 --- /dev/null +++ b/src/Controller/PingController.php @@ -0,0 +1,41 @@ + + */ + private array $checks = []; + + public function addHealthCheck(CheckInterface $check): void + { + $this->checks[] = $check; + } + + /** + * @Route( + * path="/ping", + * name="ping", + * methods={"GET"} + * ) + */ + public function pingAction(): JsonResponse + { + $pingCheck = []; + foreach ($this->checks as $healthCheck) { + $pingCheck[] = $healthCheck->check()->toArray(); + } + + return new JsonResponse($pingCheck, Response::HTTP_OK); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6126746..91ff9a5 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -28,6 +28,13 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->end() + ->arrayNode('ping_checks') + ->prototype('array') + ->children() + ->scalarNode('id')->cannotBeEmpty()->end() + ->end() + ->end() + ->end() ->end() ; diff --git a/src/DependencyInjection/SymfonyHealthCheckExtension.php b/src/DependencyInjection/SymfonyHealthCheckExtension.php index e7b4664..8c06d8e 100644 --- a/src/DependencyInjection/SymfonyHealthCheckExtension.php +++ b/src/DependencyInjection/SymfonyHealthCheckExtension.php @@ -10,6 +10,7 @@ use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use SymfonyHealthCheckBundle\Controller\HealthController; +use SymfonyHealthCheckBundle\Controller\PingController; class SymfonyHealthCheckExtension extends Extension { @@ -40,9 +41,16 @@ private function loadHealthChecks( $loader->load('health_checks.xml'); $healthCheckCollection = $container->findDefinition(HealthController::class); + foreach ($config['health_checks'] as $healthCheckConfig) { $healthCheckDefinition = new Reference($healthCheckConfig['id']); $healthCheckCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]); } + + $pingCollection = $container->findDefinition(PingController::class); + foreach ($config['ping_checks'] as $healthCheckConfig) { + $healthCheckDefinition = new Reference($healthCheckConfig['id']); + $pingCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]); + } } } diff --git a/src/Resources/config/controller.xml b/src/Resources/config/controller.xml index 8a5e1bb..6607053 100644 --- a/src/Resources/config/controller.xml +++ b/src/Resources/config/controller.xml @@ -9,5 +9,8 @@ + + + diff --git a/src/Resources/config/routes.xml b/src/Resources/config/routes.xml index f735f9e..41a2704 100644 --- a/src/Resources/config/routes.xml +++ b/src/Resources/config/routes.xml @@ -10,4 +10,10 @@ controller="SymfonyHealthCheckBundle\Controller\HealthController::healthCheckAction" methods="GET" /> + diff --git a/tests/Integration/Controller/PingControllerTest.php b/tests/Integration/Controller/PingControllerTest.php new file mode 100644 index 0000000..9dc42d1 --- /dev/null +++ b/tests/Integration/Controller/PingControllerTest.php @@ -0,0 +1,134 @@ +request('GET', '/ping'); + + $response = $client->getResponse(); + self::assertSame(200, $response->getStatusCode()); + self::assertSame(json_encode([]), $response->getContent()); + } + + public function testAddCheckStatusUpSuccess(): void + { + $pingController = new PingController(); + $pingController->addHealthCheck(new StatusUpCheck()); + + $response = $pingController->pingAction(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([[ + 'name' => 'status', 'result' => true, 'message' => 'up', 'params' => [], + ]]), + $response->getContent() + ); + } + + public function testEnvironmentCheckCouldNotDetermine(): void + { + $pingController = new PingController(); + $pingController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder())); + + $response = $pingController->pingAction(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([[ + 'name' => 'environment', + 'result' => false, + 'message' => 'Could not determine', + 'params' => [] + ]]), + $response->getContent() + ); + } + + public function testDoctrineCheckServiceNotFoundException(): void + { + $pingController = new PingController(); + $pingController->addHealthCheck(new DoctrineCheck(new ContainerBuilder())); + + $response = $pingController->pingAction(); + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([[ + 'name' => 'doctrine', + 'result' => false, + 'message' => 'Entity Manager Not Found.', + 'params' => [] + ]]), + $response->getContent() + ); + } + + public function testTwoCheckSuccess(): void + { + $pingController = new PingController(); + $pingController->addHealthCheck(new StatusUpCheck()); + $pingController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder())); + + $response = $pingController->pingAction(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([ + [ + 'name' => 'status', + 'result' => true, + 'message' => 'up', + 'params' => [], + ], + [ + 'name' => 'environment', + 'result' => false, + 'message' => 'Could not determine', + 'params' => [] + ]]), + $response->getContent() + ); + } + + public function testEnvironmentCheckSuccess(): void + { + $pingController = new PingController(); + $pingController->addHealthCheck(new EnvironmentCheck(static::bootKernel()->getContainer())); + $response = $pingController->pingAction(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame( + json_encode([ + [ + 'name' => 'environment', + 'result' => true, + 'message' => 'ok', + 'params' => ['testing'] + ] + ]), + $response->getContent() + ); + } + + public function testAddCheckFailed(): void + { + self::expectException(TypeError::class); + + $pingController = new PingController(); + $pingController->addHealthCheck(new PingController()); + } +} diff --git a/tests/Integration/DependencyInjection/ConfigurationTest.php b/tests/Integration/DependencyInjection/ConfigurationTest.php index 10cfcf4..b229060 100644 --- a/tests/Integration/DependencyInjection/ConfigurationTest.php +++ b/tests/Integration/DependencyInjection/ConfigurationTest.php @@ -13,20 +13,59 @@ final class ConfigurationTest extends TestCase public function testProcessConfigurationWithDefaultConfiguration(): void { $expectedBundleDefaultConfig = [ - 'health_checks' => [] + 'health_checks' => [], + 'ping_checks' => [], ]; self::assertSame($expectedBundleDefaultConfig, $this->processConfiguration([])); } - public function testProcessConfiguration(): void + public function testProcessConfigurationHealthChecks(): void { $expectedConfig = [ 'health_checks' => [ ['id' => 'symfony_health_check.doctrine_check'], - ] + ], + 'ping_checks' => [], + ]; + $new = ['health_checks' => [['id' => 'symfony_health_check.doctrine_check']], 'ping_checks' => []]; + + self::assertSame( + $expectedConfig, + $this->processConfiguration($new) + ); + } + + public function testProcessConfigurationPing(): void + { + $expectedConfig = [ + 'health_checks' => [], + 'ping_checks' => [ + ['id' => 'symfony_health_check.doctrine_check'] + ], + ]; + $new = ['health_checks' => [], 'ping_checks' => [['id' => 'symfony_health_check.doctrine_check']]]; + + self::assertSame( + $expectedConfig, + $this->processConfiguration($new) + ); + } + + public function testProcessConfigurationPingAndHealthChecks(): void + { + $expectedConfig = [ + 'health_checks' => [ + ['id' => 'symfony_health_check.doctrine_check'] + ], + 'ping_checks' => [ + ['id' => 'symfony_health_check.doctrine_check'] + ], + ]; + $new = [ + 'health_checks' => [['id' => 'symfony_health_check.doctrine_check']], + 'ping_checks' => [['id' => 'symfony_health_check.doctrine_check']] ]; - $new = ['health_checks' => [['id' => 'symfony_health_check.doctrine_check']]]; self::assertSame( $expectedConfig, diff --git a/tests/Integration/DependencyInjection/Fixtures/filled_bundle_config.yaml b/tests/Integration/DependencyInjection/Fixtures/filled_bundle_config.yaml index 17f5dea..2fec92c 100644 --- a/tests/Integration/DependencyInjection/Fixtures/filled_bundle_config.yaml +++ b/tests/Integration/DependencyInjection/Fixtures/filled_bundle_config.yaml @@ -1,3 +1,5 @@ symfony_health_check: health_checks: - id: symfony_health_check.doctrine_check + ping_checks: + - id: symfony_health_check.doctrine_check diff --git a/tests/Integration/DependencyInjection/SymfonyHealthCheckExtensionTest.php b/tests/Integration/DependencyInjection/SymfonyHealthCheckExtensionTest.php index 2b01939..9be03bd 100644 --- a/tests/Integration/DependencyInjection/SymfonyHealthCheckExtensionTest.php +++ b/tests/Integration/DependencyInjection/SymfonyHealthCheckExtensionTest.php @@ -9,6 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use SymfonyHealthCheckBundle\Controller\PingController; use SymfonyHealthCheckBundle\DependencyInjection\SymfonyHealthCheckExtension; use Throwable; use SymfonyHealthCheckBundle\Controller\HealthController; @@ -30,12 +31,28 @@ public function testWithEmptyConfig(): void } } + public function testWithEmptyConfigPing(): void + { + $container = $this->createContainerFromFixture('empty_bundle_config'); + + try { + $container->getDefinition('ping_checks'); + } catch (Throwable $exception) { + self::assertInstanceOf(ServiceNotFoundException::class, $exception); + self::assertSame( + 'You have requested a non-existent service "ping_checks".', + $exception->getMessage() + ); + } + } + public function testWithFullConfig(): void { $container = $this->createContainerFromFixture('filled_bundle_config'); - self::assertCount(5, $container->getDefinitions()); + self::assertCount(6, $container->getDefinitions()); self::assertArrayHasKey(HealthController::class, $container->getDefinitions()); + self::assertArrayHasKey(PingController::class, $container->getDefinitions()); self::assertArrayHasKey('symfony_health_check.doctrine_check', $container->getDefinitions()); self::assertArrayHasKey('symfony_health_check.environment_check', $container->getDefinitions()); self::assertArrayHasKey('symfony_health_check.status_up_check', $container->getDefinitions()); From 98aa7e0bc3853e7fe04fde45fe284486522915a8 Mon Sep 17 00:00:00 2001 From: Yozhef Date: Tue, 18 Jan 2022 16:43:25 +0200 Subject: [PATCH 06/10] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 03f9196..936433a 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Configurating health check - all available you can see [here](https://github.com symfony_health_check: health_checks: - id: symfony_health_check.doctrine_check + ping_checks: + - id: symfony_health_check.status_up_check ``` Create Symfony Health Check Bundle Routing Config: @@ -126,7 +128,7 @@ Then we add our custom health check to collection symfony_health_check: health_checks: - id: symfony_health_check.doctrine_check - - id: custom_health_check + - id: custom_health_check // custom service check id ``` How Change Route: From a4219eee226086b5053315cf4636b91a2456ca86 Mon Sep 17 00:00:00 2001 From: Yozhef Date: Tue, 18 Jan 2022 16:44:24 +0200 Subject: [PATCH 07/10] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 936433a..898d908 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,11 @@ health: path: /your/custom/url methods: GET controller: SymfonyHealthCheckBundle\Controller\HealthController::healthCheckAction + +ping: + path: /your/custom/url + methods: GET + controller: SymfonyHealthCheckBundle\Controller\PingController::pingAction ``` From 59f4bc886f3e6516a58fce00eb7d0e52fa93f47d Mon Sep 17 00:00:00 2001 From: Yozhef Date: Tue, 18 Jan 2022 17:20:04 +0200 Subject: [PATCH 08/10] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 898d908..39fca42 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ If you are using [symfony/security](https://symfony.com/doc/current/security.htm healthcheck: pattern: ^/health security: false + ping: + pattern: ^/ping + security: false ``` Step 4: Additional settings From bd2f8da5aa2e2a965d9a083ec5493b48119d364c Mon Sep 17 00:00:00 2001 From: Yozhef Date: Tue, 18 Jan 2022 17:42:56 +0200 Subject: [PATCH 09/10] feat: add upgrade doc --- UPGRADE-1.0.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 UPGRADE-1.0.md diff --git a/UPGRADE-1.0.md b/UPGRADE-1.0.md new file mode 100644 index 0000000..c15bebc --- /dev/null +++ b/UPGRADE-1.0.md @@ -0,0 +1,83 @@ +Upgrade Symfony Health Check Bundle V1.0.0 +================================= + +Step 1: Update the Symfony Health Check Bundle via Composer +---------------------------------- +```console +$ "macpaw/symfony-health-check-bundle": "^v1.0.0" +``` + +### Next, use Composer to download new versions of the libraries: +```console +$ composer update "macpaw/symfony-health-check-bundle" +``` + +###Dependency Errors + +If you get a dependency error, it may mean that you also need to upgrade other libraries that are dependencies of the libraries. To allow that, pass the --with-all-dependencies flag: +```console +$ composer update "macpaw/symfony-health-check-bundle" -with-all-dependencies +``` + +Step 2: Update the Symfony Health Check Bundle via Composer +---------------------------------- + +##Automatical: +Over time - and especially when you upgrade to a new version of a library - an updated version of the recipe may be available. These updates are usually minor - e.g. new comments in a configuration file - but it's a good idea to keep your files in sync with the recipes. + +Symfony Flex provides several commands to help upgrade your recipes. Be sure to commit any unrelated changes you're working on before starting: + +```console +$ composer recipes + + +$ composer recipes symfony/framework-bundle + + +$ composer recipes:install symfony/framework-bundle --force -v +``` + +The tricky part of this process is that the recipe "update" does not perform any intelligent "upgrading" of your code. Instead, the updates process re-installs the latest version of the recipe which means that your custom code will be overridden completely. After updating a recipe, you need to carefully choose which changes you want, and undo the rest. + +##Manual: + +### Old Config: +`config/packages/symfony_health_check.yaml` +```yaml +symfony_health_check: + health_checks: + - id: symfony_health_check.doctrine_check +``` + +### New Config: +```yaml +symfony_health_check: + health_checks: + - id: symfony_health_check.doctrine_check + ping_checks: + - id: symfony_health_check.status_up_check +``` + +Security Optional: +---------------------------------- +`config/packages/security.yaml` + +### Old Config: +```yaml + firewalls: + healthcheck: + pattern: ^/health + security: false +``` + +### New Config: +```yaml + firewalls: + healthcheck: + pattern: ^/health + security: false + ping: + pattern: ^/ping + security: false +``` + From a49b96c4f30f7f0b82fd5eb496d1567a55fb9178 Mon Sep 17 00:00:00 2001 From: Yozhef Date: Tue, 18 Jan 2022 17:45:10 +0200 Subject: [PATCH 10/10] feat: add upgrade doc --- UPGRADE-1.0.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UPGRADE-1.0.md b/UPGRADE-1.0.md index c15bebc..39d5b7a 100644 --- a/UPGRADE-1.0.md +++ b/UPGRADE-1.0.md @@ -22,7 +22,8 @@ $ composer update "macpaw/symfony-health-check-bundle" -with-all-dependencies Step 2: Update the Symfony Health Check Bundle via Composer ---------------------------------- -##Automatical: +## Automatical + Over time - and especially when you upgrade to a new version of a library - an updated version of the recipe may be available. These updates are usually minor - e.g. new comments in a configuration file - but it's a good idea to keep your files in sync with the recipes. Symfony Flex provides several commands to help upgrade your recipes. Be sure to commit any unrelated changes you're working on before starting: @@ -39,7 +40,7 @@ $ composer recipes:install symfony/framework-bundle --force -v The tricky part of this process is that the recipe "update" does not perform any intelligent "upgrading" of your code. Instead, the updates process re-installs the latest version of the recipe which means that your custom code will be overridden completely. After updating a recipe, you need to carefully choose which changes you want, and undo the rest. -##Manual: +## Manual: ### Old Config: `config/packages/symfony_health_check.yaml`