Skip to content

Commit bae04b1

Browse files
committed
feat: add custom response error code
1 parent d7c56c9 commit bae04b1

File tree

9 files changed

+188
-11
lines changed

9 files changed

+188
-11
lines changed

phpstan-baseline.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
parameters:
22
ignoreErrors:
33
-
4-
message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#'
4+
message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::variableNode\(\).#'
55
count: 1
66
path: ./src/DependencyInjection
77
-

src/Check/DoctrineCheck.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ public function check(): Response
2525
return new Response(self::CHECK_RESULT_NAME, false, 'Entity Manager Not Found.');
2626
}
2727

28+
/**
29+
* @var object|null $entityManager
30+
*/
2831
$entityManager = $this->container->get('doctrine.orm.entity_manager');
2932

3033
if ($entityManager === null) {

src/Controller/HealthController.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ final class HealthController extends AbstractController
1616
* @var array<CheckInterface>
1717
*/
1818
private array $healthChecks = [];
19+
private ?int $customResponseCode = null;
20+
21+
public function setCustomResponseCode(?int $code): void
22+
{
23+
$this->customResponseCode = $code;
24+
}
1925

2026
public function addHealthCheck(CheckInterface $healthCheck): void
2127
{
@@ -31,11 +37,22 @@ public function addHealthCheck(CheckInterface $healthCheck): void
3137
*/
3238
public function healthCheckAction(): JsonResponse
3339
{
34-
$resultHealthCheck = [];
35-
foreach ($this->healthChecks as $healthCheck) {
36-
$resultHealthCheck[] = $healthCheck->check()->toArray();
40+
$resultHealthCheck = array_map(
41+
fn($healthCheck) => $healthCheck->check()->toArray(),
42+
$this->healthChecks
43+
);
44+
45+
$code = $this->customResponseCode;
46+
47+
if (null !== $code) {
48+
foreach ($resultHealthCheck as $result) {
49+
if (!$result['result']) {
50+
$responseCode = $code;
51+
break;
52+
}
53+
}
3754
}
3855

39-
return new JsonResponse($resultHealthCheck, Response::HTTP_OK);
56+
return new JsonResponse($resultHealthCheck, $responseCode ?? Response::HTTP_OK);
4057
}
4158
}

src/Controller/PingController.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@ final class PingController extends AbstractController
1616
* @var array<CheckInterface>
1717
*/
1818
private array $checks = [];
19+
private ?int $customResponseCode = null;
1920

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

26+
public function setCustomResponseCode(?int $code): void
27+
{
28+
$this->customResponseCode = $code;
29+
}
30+
2531
/**
2632
* @Route(
2733
* path="/ping",
@@ -31,11 +37,22 @@ public function addHealthCheck(CheckInterface $check): void
3137
*/
3238
public function pingAction(): JsonResponse
3339
{
34-
$pingCheck = [];
35-
foreach ($this->checks as $healthCheck) {
36-
$pingCheck[] = $healthCheck->check()->toArray();
40+
$pingCheck = array_map(
41+
fn($healthCheck) => $healthCheck->check()->toArray(),
42+
$this->checks
43+
);
44+
45+
$code = $this->customResponseCode;
46+
47+
if (null !== $code) {
48+
foreach ($pingCheck as $result) {
49+
if (!$result['result']) {
50+
$responseCode = $code;
51+
break;
52+
}
53+
}
3754
}
3855

39-
return new JsonResponse($pingCheck, Response::HTTP_OK);
56+
return new JsonResponse($pingCheck, $responseCode ?? Response::HTTP_OK);
4057
}
4158
}

src/DependencyInjection/Configuration.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
88
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
99
use Symfony\Component\Config\Definition\ConfigurationInterface;
10+
use Symfony\Component\HttpFoundation\Response;
1011

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

2223
$root
2324
->children()
25+
->variableNode('ping_error_response_code')
26+
->defaultValue(null)
27+
->validate()
28+
->ifTrue(function ($value) {
29+
return $value !== null && !array_key_exists($value, Response::$statusTexts);
30+
})
31+
->thenInvalid('The ping_error_response_code must be valid HTTP status code or null.')
32+
->end()
33+
->end()
34+
->variableNode('health_error_response_code')
35+
->defaultValue(null)
36+
->validate()
37+
->ifTrue(function ($value) {
38+
return $value !== null && !array_key_exists($value, Response::$statusTexts);
39+
})
40+
->thenInvalid('The health_error_response_code must be valid HTTP status code or null.')
41+
->end()
42+
->end()
2443
->arrayNode('health_checks')
2544
->prototype('array')
2645
->children()

src/DependencyInjection/SymfonyHealthCheckExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ private function loadHealthChecks(
3737
foreach ($config['health_checks'] as $healthCheckConfig) {
3838
$healthCheckDefinition = new Reference($healthCheckConfig['id']);
3939
$healthCheckCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]);
40+
$healthCheckCollection->addMethodCall('setCustomResponseCode', [$config['health_error_response_code']]);
4041
}
4142

4243
$pingCollection = $container->findDefinition(PingController::class);
4344
foreach ($config['ping_checks'] as $healthCheckConfig) {
4445
$healthCheckDefinition = new Reference($healthCheckConfig['id']);
4546
$pingCollection->addMethodCall('addHealthCheck', [$healthCheckDefinition]);
47+
$pingCollection->addMethodCall('setCustomResponseCode', [$config['ping_error_response_code']]);
4648
}
4749
}
4850
}

tests/Integration/Controller/HealthControllerTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,41 @@ public function testAddCheckFailed(): void
131131
$healthController = new HealthController();
132132
$healthController->addHealthCheck(new HealthController());
133133
}
134+
135+
public function testCustomErrorCodeIfOneOfChecksIsFalse(): void
136+
{
137+
$healthController = new HealthController();
138+
$healthController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder()));
139+
$healthController->setCustomResponseCode(500);
140+
141+
$response = $healthController->healthCheckAction();
142+
143+
self::assertSame(500, $response->getStatusCode());
144+
self::assertSame(
145+
json_encode([[
146+
'name' => 'environment',
147+
'result' => false,
148+
'message' => 'Could not determine',
149+
'params' => []
150+
]]),
151+
$response->getContent()
152+
);
153+
}
154+
155+
public function testCustomErrorCodeDoesNotAffectSuccessResponse(): void
156+
{
157+
$healthController = new HealthController();
158+
$healthController->addHealthCheck(new StatusUpCheck());
159+
$healthController->setCustomResponseCode(500);
160+
161+
$response = $healthController->healthCheckAction();
162+
163+
self::assertSame(200, $response->getStatusCode());
164+
self::assertSame(
165+
json_encode([[
166+
'name' => 'status', 'result' => true, 'message' => 'up', 'params' => [],
167+
]]),
168+
$response->getContent()
169+
);
170+
}
134171
}

tests/Integration/Controller/PingControllerTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,51 @@ public function testTwoCheckSuccess(): void
104104
);
105105
}
106106

107+
public function testCustomErrorCodeIfOneOfChecksIsFalse(): void
108+
{
109+
$pingController = new PingController();
110+
$pingController->addHealthCheck(new StatusUpCheck());
111+
$pingController->addHealthCheck(new EnvironmentCheck(new ContainerBuilder()));
112+
$pingController->setCustomResponseCode(500);
113+
114+
$response = $pingController->pingAction();
115+
116+
self::assertSame(500, $response->getStatusCode());
117+
self::assertSame(
118+
json_encode([
119+
[
120+
'name' => 'status',
121+
'result' => true,
122+
'message' => 'up',
123+
'params' => [],
124+
],
125+
[
126+
'name' => 'environment',
127+
'result' => false,
128+
'message' => 'Could not determine',
129+
'params' => []
130+
]]),
131+
$response->getContent()
132+
);
133+
}
134+
135+
public function testCustomErrorCodeDoesNotAffectSuccessResponse(): void
136+
{
137+
$pingController = new PingController();
138+
$pingController->addHealthCheck(new StatusUpCheck());
139+
$pingController->setCustomResponseCode(500);
140+
141+
$response = $pingController->pingAction();
142+
143+
self::assertSame(200, $response->getStatusCode());
144+
self::assertSame(
145+
json_encode([[
146+
'name' => 'status', 'result' => true, 'message' => 'up', 'params' => [],
147+
]]),
148+
$response->getContent()
149+
);
150+
}
151+
107152
public function testEnvironmentCheckSuccess(): void
108153
{
109154
$pingController = new PingController();

tests/Integration/DependencyInjection/ConfigurationTest.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ final class ConfigurationTest extends TestCase
1313
public function testProcessConfigurationWithDefaultConfiguration(): void
1414
{
1515
$expectedBundleDefaultConfig = [
16+
'ping_error_response_code' => null,
17+
'health_error_response_code' => null,
1618
'health_checks' => [],
1719
'ping_checks' => [],
1820
];
@@ -27,8 +29,12 @@ public function testProcessConfigurationHealthChecks(): void
2729
['id' => 'symfony_health_check.doctrine_check'],
2830
],
2931
'ping_checks' => [],
32+
'ping_error_response_code' => null,
33+
'health_error_response_code' => null,
3034
];
31-
$new = ['health_checks' => [['id' => 'symfony_health_check.doctrine_check']], 'ping_checks' => []];
35+
$new = ['health_checks' => [
36+
['id' => 'symfony_health_check.doctrine_check']
37+
], 'ping_checks' => []];
3238

3339
self::assertSame(
3440
$expectedConfig,
@@ -43,8 +49,12 @@ public function testProcessConfigurationPing(): void
4349
'ping_checks' => [
4450
['id' => 'symfony_health_check.doctrine_check']
4551
],
52+
'ping_error_response_code' => null,
53+
'health_error_response_code' => null,
4654
];
47-
$new = ['health_checks' => [], 'ping_checks' => [['id' => 'symfony_health_check.doctrine_check']]];
55+
$new = ['health_checks' => [], 'ping_checks' => [
56+
['id' => 'symfony_health_check.doctrine_check']
57+
]];
4858

4959
self::assertSame(
5060
$expectedConfig,
@@ -61,6 +71,8 @@ public function testProcessConfigurationPingAndHealthChecks(): void
6171
'ping_checks' => [
6272
['id' => 'symfony_health_check.doctrine_check']
6373
],
74+
'ping_error_response_code' => null,
75+
'health_error_response_code' => null,
6476
];
6577
$new = [
6678
'health_checks' => [['id' => 'symfony_health_check.doctrine_check']],
@@ -73,6 +85,31 @@ public function testProcessConfigurationPingAndHealthChecks(): void
7385
);
7486
}
7587

88+
public function testProcessConfigurationCustomErrorCode(): void
89+
{
90+
$expectedConfig = [
91+
'health_checks' => [
92+
['id' => 'symfony_health_check.doctrine_check']
93+
],
94+
'ping_checks' => [
95+
['id' => 'symfony_health_check.doctrine_check']
96+
],
97+
'ping_error_response_code' => 404,
98+
'health_error_response_code' => 500,
99+
];
100+
$new = [
101+
'health_checks' => [['id' => 'symfony_health_check.doctrine_check']],
102+
'ping_checks' => [['id' => 'symfony_health_check.doctrine_check']],
103+
'ping_error_response_code' => 404,
104+
'health_error_response_code' => 500,
105+
];
106+
107+
self::assertSame(
108+
$expectedConfig,
109+
$this->processConfiguration($new)
110+
);
111+
}
112+
76113
private function processConfiguration(array $values): array
77114
{
78115
$processor = new Processor();

0 commit comments

Comments
 (0)