Skip to content

Commit 18aceb3

Browse files
committed
feat: include previous exceptions in debug error information
1 parent 626594a commit 18aceb3

File tree

3 files changed

+112
-7
lines changed

3 files changed

+112
-7
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
All notable changes to this project will be documented in this file. This project adheres to
44
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
55

6+
## Unreleased
7+
8+
### Added
9+
10+
- Debug exception output now includes any previous exceptions.
11+
612
## [2.0.1] - 2023-07-29
713

814
### Fixed

src/ExceptionParser.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Illuminate\Support\Collection;
2929
use Illuminate\Support\Enumerable;
3030
use LaravelJsonApi\Core\Document\Error;
31+
use LaravelJsonApi\Core\Document\ErrorList;
3132
use LaravelJsonApi\Core\Exceptions\JsonApiException;
3233
use LaravelJsonApi\Core\Responses\ErrorResponse;
3334
use Throwable;
@@ -297,20 +298,38 @@ private function mustAccept(Throwable $ex, $request): bool
297298
* Get the default JSON API error.
298299
*
299300
* @param Throwable $ex
300-
* @return Error
301+
* @return Error|ErrorList
301302
*/
302-
private function getDefaultError(Throwable $ex): Error
303+
private function getDefaultError(Throwable $ex): Error|ErrorList
303304
{
304305
if ($this->default) {
305306
return $this->default;
306307
}
307308

309+
$errors = [];
310+
$debug = config('app.debug');
311+
312+
do {
313+
$errors[] = $this->convertExceptionToError($ex, $debug);
314+
} while ($ex = $ex->getPrevious());
315+
316+
return new ErrorList(...$errors);
317+
}
318+
319+
/**
320+
* @param Throwable $ex
321+
* @param bool $debug
322+
* @return Error
323+
*/
324+
private function convertExceptionToError(Throwable $ex, bool $debug): Error
325+
{
308326
$error = Error::make()
309327
->setStatus(500)
310328
->setTitle(__(Response::$statusTexts[500]));
311329

312-
if (config('app.debug')) {
313-
$error->setCode($ex->getCode())
330+
if ($debug) {
331+
$error
332+
->setCode($ex->getCode())
314333
->setDetail($ex->getMessage())
315334
->setMeta($this->convertExceptionToMeta($ex));
316335
}
@@ -325,13 +344,13 @@ private function getDefaultError(Throwable $ex): Error
325344
* puts into its JSON representation of an exception when in debug mode.
326345
*
327346
* @param Throwable $ex
328-
* @return array
347+
* @return array<string, mixed>
329348
* @see \Illuminate\Foundation\Exceptions\Handler::convertExceptionToArray()
330349
*/
331350
private function convertExceptionToMeta(Throwable $ex): array
332351
{
333352
return [
334-
'exception' => get_class($ex),
353+
'exception' => $ex::class,
335354
'file' => $ex->getFile(),
336355
'line' => $ex->getLine(),
337356
'trace' => Collection::make($ex->getTrace())

tests/Integration/ExceptionsTest.php

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ public function testDefaultExceptionWithoutDebug(): void
541541
->assertExactJson($expected);
542542
}
543543

544+
/**
545+
* @return void
546+
*/
544547
public function testDefaultExceptionWithDebug(): void
545548
{
546549
config()->set('app.debug', true);
@@ -569,10 +572,87 @@ public function testDefaultExceptionWithDebug(): void
569572
],
570573
];
571574

572-
$this->get('/test', ['Accept' => 'application/vnd.api+json'])
575+
$this
576+
->get('/test', ['Accept' => 'application/vnd.api+json'])
573577
->assertStatus(500)
574578
->assertHeader('Content-Type', 'application/vnd.api+json')
575579
->assertExactJson($expected);
576580
}
577581

582+
/**
583+
* @return void
584+
*/
585+
public function testDefaultExceptionWithPreviousExceptionAndWithDebug(): void
586+
{
587+
config()->set('app.debug', true);
588+
589+
$this->ex = $ex = new \LogicException(
590+
message: 'Boom.',
591+
code: 99,
592+
previous: $previous1 = new \RuntimeException(
593+
message: 'Blah!',
594+
code: 98,
595+
previous: $previous2 = new \Exception(
596+
message: 'Baz!',
597+
code: 97
598+
),
599+
),
600+
);
601+
602+
$expected = [
603+
'errors' => [
604+
[
605+
'code' => (string) $ex->getCode(),
606+
'detail' => $ex->getMessage(),
607+
'meta' => [
608+
'exception' => $ex::class,
609+
'file' => $ex->getFile(),
610+
'line' => $ex->getLine(),
611+
'trace' => Collection::make($ex->getTrace())
612+
->map(fn($trace) => Arr::except($trace, ['args']))
613+
->all(),
614+
],
615+
'status' => '500',
616+
'title' => 'Internal Server Error',
617+
],
618+
[
619+
'code' => (string) $previous1->getCode(),
620+
'detail' => $previous1->getMessage(),
621+
'meta' => [
622+
'exception' => $previous1::class,
623+
'file' => $previous1->getFile(),
624+
'line' => $previous1->getLine(),
625+
'trace' => Collection::make($previous1->getTrace())
626+
->map(fn($trace) => Arr::except($trace, ['args']))
627+
->all(),
628+
],
629+
'status' => '500',
630+
'title' => 'Internal Server Error',
631+
],
632+
[
633+
'code' => (string) $previous2->getCode(),
634+
'detail' => $previous2->getMessage(),
635+
'meta' => [
636+
'exception' => $previous2::class,
637+
'file' => $previous2->getFile(),
638+
'line' => $previous2->getLine(),
639+
'trace' => Collection::make($previous2->getTrace())
640+
->map(fn($trace) => Arr::except($trace, ['args']))
641+
->all(),
642+
],
643+
'status' => '500',
644+
'title' => 'Internal Server Error',
645+
],
646+
],
647+
'jsonapi' => [
648+
'version' => '1.0',
649+
],
650+
];
651+
652+
$this
653+
->get('/test', ['Accept' => 'application/vnd.api+json'])
654+
->assertStatus(500)
655+
->assertHeader('Content-Type', 'application/vnd.api+json')
656+
->assertExactJson($expected);
657+
}
578658
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy