Skip to content
This repository was archived by the owner on Jul 6, 2024. It is now read-only.

Commit 13c82cd

Browse files
author
Dominik Zogg
committed
Fix ApiExceptionMiddleware
1 parent 528cb45 commit 13c82cd

File tree

3 files changed

+96
-14
lines changed

3 files changed

+96
-14
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"@test:cs"
6565
],
6666
"test:cs": "mkdir -p build && PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --stop-on-violation --cache-file=build/phpcs.cache",
67-
"test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=63 --verbose --coverage=build/phpunit",
67+
"test:infection": "vendor/bin/infection --threads=$(nproc) --min-msi=100 --verbose --coverage=build/phpunit",
6868
"test:integration": "vendor/bin/phpunit --testsuite=Integration --cache-result-file=build/phpunit/result.cache",
6969
"test:lint": "mkdir -p build && find src tests -name '*.php' -print0 | xargs -0 -n1 -P$(nproc) php -l | tee build/phplint.log",
7070
"test:loc": "mkdir -p build && vendor/bin/phploc src | tee build/phploc.log",

src/Middleware/ApiExceptionMiddleware.php

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface;
88
use Chubbyphp\HttpException\HttpException;
9+
use Chubbyphp\HttpException\HttpExceptionInterface;
910
use Psr\Http\Message\ResponseInterface;
1011
use Psr\Http\Message\ServerRequestInterface;
1112
use Psr\Http\Server\MiddlewareInterface;
@@ -38,21 +39,16 @@ private function handleException(ServerRequestInterface $request, \Throwable $ex
3839
{
3940
$backtrace = $this->backtrace($exception);
4041

41-
$this->logger->error('Exception', ['backtrace' => $backtrace]);
42+
$httpException = $this->exceptionToHttpException($exception, $backtrace);
43+
44+
$logLevel = $httpException->getStatus() >= 500 ? 'error' : 'info';
45+
46+
$this->logger->{$logLevel}('Http Exception', ['backtrace' => $backtrace]);
4247

4348
if (null === $accept = $request->getAttribute('accept')) {
4449
throw $exception;
4550
}
4651

47-
if ($this->debug) {
48-
$httpException = HttpException::createInternalServerError([
49-
'detail' => $exception->getMessage(),
50-
'backtrace' => $backtrace,
51-
]);
52-
} else {
53-
$httpException = HttpException::createInternalServerError();
54-
}
55-
5652
return $this->responseManager->createFromHttpException($httpException, $accept);
5753
}
5854

@@ -75,4 +71,23 @@ private function backtrace(\Throwable $exception): array
7571

7672
return $exceptions;
7773
}
74+
75+
/**
76+
* @param array<int, array<string, mixed>> $backtrace
77+
*/
78+
private function exceptionToHttpException(\Throwable $exception, array $backtrace): HttpExceptionInterface
79+
{
80+
if ($exception instanceof HttpExceptionInterface) {
81+
return $exception;
82+
}
83+
84+
if (!$this->debug) {
85+
return HttpException::createInternalServerError();
86+
}
87+
88+
return HttpException::createInternalServerError([
89+
'detail' => $exception->getMessage(),
90+
'backtrace' => $backtrace,
91+
]);
92+
}
7893
}

tests/Unit/Middleware/ApiExceptionMiddlewareTest.php

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface;
88
use Chubbyphp\ApiHttp\Middleware\ApiExceptionMiddleware;
9+
use Chubbyphp\HttpException\HttpException;
910
use Chubbyphp\HttpException\HttpExceptionInterface;
1011
use Chubbyphp\Mock\Argument\ArgumentCallback;
1112
use Chubbyphp\Mock\Call;
@@ -84,7 +85,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
8485
$logger = $this->getMockByCalls(LoggerInterface::class, [
8586
Call::create('error')
8687
->with(
87-
'Exception',
88+
'Http Exception',
8889
new ArgumentCallback(static function (array $context): void {
8990
$backtrace = $context['backtrace'];
9091

@@ -116,6 +117,68 @@ public function handle(ServerRequestInterface $request): ResponseInterface
116117
self::assertSame($response, $middleware->process($request, $requestHandler));
117118
}
118119

120+
public function testWithHttpExceptionWithDebugWithLogger(): void
121+
{
122+
$httpException = HttpException::createNotFound();
123+
124+
/** @var MockObject|ServerRequestInterface $request */
125+
$request = $this->getMockByCalls(ServerRequestInterface::class, [
126+
Call::create('getAttribute')->with('accept', null)->willReturn('application/xml'),
127+
]);
128+
129+
/** @var MockObject|ResponseInterface $response */
130+
$response = $this->getMockByCalls(ResponseInterface::class);
131+
132+
$requestHandler = new class($httpException) implements RequestHandlerInterface {
133+
public function __construct(private HttpExceptionInterface $httpException)
134+
{
135+
}
136+
137+
public function handle(ServerRequestInterface $request): ResponseInterface
138+
{
139+
throw $this->httpException;
140+
}
141+
};
142+
143+
/** @var MockObject|ResponseManagerInterface $responseManager */
144+
$responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [
145+
Call::create('createFromHttpException')
146+
->with(
147+
new ArgumentCallback(static function (HttpExceptionInterface $givenhttpException) use ($httpException): void {
148+
self::assertSame($httpException, $givenhttpException);
149+
}),
150+
'application/xml',
151+
)
152+
->willReturn($response),
153+
]);
154+
155+
/** @var LoggerInterface|MockObject $logger */
156+
$logger = $this->getMockByCalls(LoggerInterface::class, [
157+
Call::create('info')
158+
->with(
159+
'Http Exception',
160+
new ArgumentCallback(static function (array $context): void {
161+
$backtrace = $context['backtrace'];
162+
163+
self::assertCount(1, $backtrace);
164+
165+
$exception = array_shift($backtrace);
166+
167+
self::assertSame(HttpException::class, $exception['class']);
168+
self::assertSame('Not Found', $exception['message']);
169+
self::assertSame(404, $exception['code']);
170+
self::assertArrayHasKey('file', $exception);
171+
self::assertArrayHasKey('line', $exception);
172+
self::assertArrayHasKey('trace', $exception);
173+
})
174+
),
175+
]);
176+
177+
$middleware = new ApiExceptionMiddleware($responseManager, true, $logger);
178+
179+
self::assertSame($response, $middleware->process($request, $requestHandler));
180+
}
181+
119182
public function testWithExceptionWithDebugWithLogger(): void
120183
{
121184
/** @var MockObject|ServerRequestInterface $request */
@@ -142,6 +205,10 @@ public function handle(ServerRequestInterface $request): ResponseInterface
142205

143206
$data = $httpException->jsonSerialize();
144207

208+
self::assertArrayHasKey('detail', $data);
209+
210+
self::assertSame('runtime exception', $data['detail']);
211+
145212
self::assertArrayHasKey('backtrace', $data);
146213

147214
$backtrace = $data['backtrace'];
@@ -175,7 +242,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
175242
$logger = $this->getMockByCalls(LoggerInterface::class, [
176243
Call::create('error')
177244
->with(
178-
'Exception',
245+
'Http Exception',
179246
new ArgumentCallback(static function (array $context): void {
180247
$backtrace = $context['backtrace'];
181248

@@ -246,7 +313,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
246313
$logger = $this->getMockByCalls(LoggerInterface::class, [
247314
Call::create('error')
248315
->with(
249-
'Exception',
316+
'Http Exception',
250317
new ArgumentCallback(static function (array $context): void {
251318
$backtrace = $context['backtrace'];
252319

0 commit comments

Comments
 (0)