Skip to content

Commit

Permalink
Merge pull request #63 from oparshyna/feat/add_custom_error_status_code
Browse files Browse the repository at this point in the history
feat: add custom response error code
  • Loading branch information
Yozhef committed Oct 3, 2023
2 parents d7c56c9 + 22eda82 commit 55cebe8
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
run:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
Expand Down
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ symfony_health_check:
ping_checks:
- id: symfony_health_check.status_up_check
```
Change response code:
- default response code is 200.
- determine your custom response code in case of some check fails (Response code must be a valid HTTP status code)
```yaml
symfony_health_check:
health_checks:
- id: symfony_health_check.doctrine_check
ping_checks:
- id: symfony_health_check.status_up_check
ping_error_response_code: 500
health_error_response_code: 404
```

Create Symfony Health Check Bundle Routing Config:
----------------------------------
Expand Down Expand Up @@ -141,12 +153,12 @@ You can change the default behavior with a light configuration, remember to retu
health:
path: /your/custom/url
methods: GET
controller: SymfonyHealthCheckBundle\Controller\HealthController::healthCheckAction
controller: SymfonyHealthCheckBundle\Controller\HealthController::check

ping:
path: /your/custom/url
methods: GET
controller: SymfonyHealthCheckBundle\Controller\PingController::pingAction
controller: SymfonyHealthCheckBundle\Controller\PingController::check

```

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"require-dev": {
"ext-json": "*",
"phpstan/phpstan": "1.9.*",
"squizlabs/php_codesniffer": "3.5.*",
"squizlabs/php_codesniffer": "3.7.*",
"symfony/phpunit-bridge": "^3.4 || ^4.1.12 || ^5.0 || ^6.0",
"phpunit/phpunit": "^8.5 || ^9.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
Expand Down
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
parameters:
ignoreErrors:
-
message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#'
message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::variableNode\(\).#'
count: 1
path: ./src/DependencyInjection
-
Expand Down
3 changes: 3 additions & 0 deletions src/Check/DoctrineCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public function check(): Response
return new Response(self::CHECK_RESULT_NAME, false, 'Entity Manager Not Found.');
}

/**
* @var object|null $entityManager
*/
$entityManager = $this->container->get('doctrine.orm.entity_manager');

if ($entityManager === null) {
Expand Down
64 changes: 64 additions & 0 deletions src/Controller/BaseController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace SymfonyHealthCheckBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use SymfonyHealthCheckBundle\Check\CheckInterface;

abstract class BaseController extends AbstractController
{
/**
* @var array<CheckInterface>
*/
private array $checks = [];
private ?int $customResponseCode = null;

abstract public function check(): JsonResponse;

public function addHealthCheck(CheckInterface $check): void
{
$this->checks[] = $check;
}

public function setCustomResponseCode(?int $code): void
{
$this->customResponseCode = $code;
}

protected function checkAction(): JsonResponse
{
$checkResult = $this->performCheck();
$responseCode = $this->determineResponseCode($checkResult);

return new JsonResponse($checkResult, $responseCode);
}

protected function performCheck(): array
{
return array_map(
fn($healthCheck) => $healthCheck->check()->toArray(),
$this->checks
);
}

protected function determineResponseCode(array $results): int
{
$code = $this->customResponseCode;
$responseCode = Response::HTTP_OK;

if (null !== $code) {
foreach ($results as $result) {
if (!$result['result']) {
$responseCode = $code;
break;
}
}
}

return $responseCode;
}
}
24 changes: 3 additions & 21 deletions src/Controller/HealthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,20 @@

namespace SymfonyHealthCheckBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use SymfonyHealthCheckBundle\Check\CheckInterface;

final class HealthController extends AbstractController
final class HealthController extends BaseController
{
/**
* @var array<CheckInterface>
*/
private array $healthChecks = [];

public function addHealthCheck(CheckInterface $healthCheck): void
{
$this->healthChecks[] = $healthCheck;
}

/**
* @Route(
* path="/health",
* name="health",
* methods={"GET"}
* )
*/
public function healthCheckAction(): JsonResponse
public function check(): JsonResponse
{
$resultHealthCheck = [];
foreach ($this->healthChecks as $healthCheck) {
$resultHealthCheck[] = $healthCheck->check()->toArray();
}

return new JsonResponse($resultHealthCheck, Response::HTTP_OK);
return $this->checkAction();
}
}
24 changes: 3 additions & 21 deletions src/Controller/PingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,20 @@

namespace SymfonyHealthCheckBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use SymfonyHealthCheckBundle\Check\CheckInterface;

final class PingController extends AbstractController
final class PingController extends BaseController
{
/**
* @var array<CheckInterface>
*/
private array $checks = [];

public function addHealthCheck(CheckInterface $check): void
{
$this->checks[] = $check;
}

/**
* @Route(
* path="/ping",
* name="ping",
* methods={"GET"}
* )
*/
public function pingAction(): JsonResponse
public function check(): JsonResponse
{
$pingCheck = [];
foreach ($this->checks as $healthCheck) {
$pingCheck[] = $healthCheck->check()->toArray();
}

return new JsonResponse($pingCheck, Response::HTTP_OK);
return $this->checkAction();
}
}
19 changes: 19 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\HttpFoundation\Response;

class Configuration implements ConfigurationInterface
{
Expand All @@ -21,6 +22,24 @@ public function getConfigTreeBuilder(): TreeBuilder

$root
->children()
->variableNode('ping_error_response_code')
->defaultValue(null)
->validate()
->ifTrue(function ($value) {
return $value !== null && !array_key_exists($value, Response::$statusTexts);
})
->thenInvalid('The ping_error_response_code must be valid HTTP status code or null.')
->end()
->end()
->variableNode('health_error_response_code')
->defaultValue(null)
->validate()
->ifTrue(function ($value) {
return $value !== null && !array_key_exists($value, Response::$statusTexts);
})
->thenInvalid('The health_error_response_code must be valid HTTP status code or null.')
->end()
->end()
->arrayNode('health_checks')
->prototype('array')
->children()
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/SymfonyHealthCheckExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ private function loadHealthChecks(
foreach ($config['health_checks'] as $healthCheckConfig) {
$healthCheckDefinition = new Reference($healthCheckConfig['id']);
$healthCheckCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]);
$healthCheckCollection->addMethodCall('setCustomResponseCode', [$config['health_error_response_code']]);
}

$pingCollection = $container->findDefinition(PingController::class);
foreach ($config['ping_checks'] as $healthCheckConfig) {
$healthCheckDefinition = new Reference($healthCheckConfig['id']);
$pingCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]);
$pingCollection->addMethodCall('setCustomResponseCode', [$config['ping_error_response_code']]);
}
}
}
4 changes: 2 additions & 2 deletions src/Resources/config/routes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<route
id="health"
path="/health"
controller="SymfonyHealthCheckBundle\Controller\HealthController::healthCheckAction"
controller="SymfonyHealthCheckBundle\Controller\HealthController::check"
methods="GET"
/>
<route
id="ping"
path="/ping"
controller="SymfonyHealthCheckBundle\Controller\PingController::pingAction"
controller="SymfonyHealthCheckBundle\Controller\PingController::check"
methods="GET"
/>
</routes>
1 change: 0 additions & 1 deletion src/SymfonyHealthCheckBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@

class SymfonyHealthCheckBundle extends Bundle
{

}
47 changes: 42 additions & 5 deletions tests/Integration/Controller/HealthControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testAddCheckStatusUpSuccess(): void
$healthController = new HealthController();
$healthController->addHealthCheck(new StatusUpCheck());

$response = $healthController->healthCheckAction();
$response = $healthController->check();

self::assertSame(200, $response->getStatusCode());
self::assertSame(
Expand All @@ -45,7 +45,7 @@ public function testEnvironmentCheckCouldNotDetermine(): void
$healthController = new HealthController();
$healthController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder()));

$response = $healthController->healthCheckAction();
$response = $healthController->check();

self::assertSame(200, $response->getStatusCode());
self::assertSame(
Expand All @@ -64,7 +64,7 @@ public function testDoctrineCheckServiceNotFoundException(): void
$healthController = new HealthController();
$healthController->addHealthCheck(new DoctrineCheck(new ContainerBuilder()));

$response = $healthController->healthCheckAction();
$response = $healthController->check();
self::assertSame(200, $response->getStatusCode());
self::assertSame(
json_encode([[
Expand All @@ -83,7 +83,7 @@ public function testTwoCheckSuccess(): void
$healthController->addHealthCheck(new StatusUpCheck());
$healthController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder()));

$response = $healthController->healthCheckAction();
$response = $healthController->check();

self::assertSame(200, $response->getStatusCode());
self::assertSame(
Expand All @@ -108,7 +108,7 @@ public function testEnvironmentCheckSuccess(): void
{
$healthController = new HealthController();
$healthController->addHealthCheck(new EnvironmentCheck(static::bootKernel()->getContainer()));
$response = $healthController->healthCheckAction();
$response = $healthController->check();

self::assertSame(200, $response->getStatusCode());
self::assertSame(
Expand All @@ -131,4 +131,41 @@ public function testAddCheckFailed(): void
$healthController = new HealthController();
$healthController->addHealthCheck(new HealthController());
}

public function testCustomErrorCodeIfOneOfChecksIsFalse(): void
{
$healthController = new HealthController();
$healthController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder()));
$healthController->setCustomResponseCode(500);

$response = $healthController->check();

self::assertSame(500, $response->getStatusCode());
self::assertSame(
json_encode([[
'name' => 'environment',
'result' => false,
'message' => 'Could not determine',
'params' => []
]]),
$response->getContent()
);
}

public function testCustomErrorCodeDoesNotAffectSuccessResponse(): void
{
$healthController = new HealthController();
$healthController->addHealthCheck(new StatusUpCheck());
$healthController->setCustomResponseCode(500);

$response = $healthController->check();

self::assertSame(200, $response->getStatusCode());
self::assertSame(
json_encode([[
'name' => 'status', 'result' => true, 'message' => 'up', 'params' => [],
]]),
$response->getContent()
);
}
}

0 comments on commit 55cebe8

Please sign in to comment.