Skip to content

Commit a35ca04

Browse files
committed
Merge branch 'develop' into 3.x
2 parents bcfc762 + 626594a commit a35ca04

File tree

4 files changed

+121
-16
lines changed

4 files changed

+121
-16
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
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+
## [2.0.1] - 2023-07-29
7+
8+
### Fixed
9+
10+
- [#3](https://github.com/laravel-json-api/exceptions/issues/3) Ensure HTTP status is correctly set when the status on a
11+
validation exception is not `422`.
12+
613
## [2.0.0] - 2023-02-14
714

815
### Changed

src/Pipes/Concerns/SetsHttpTitle.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@
2424

2525
trait SetsHttpTitle
2626
{
27-
2827
/**
29-
* @var Translator
28+
* @var Translator|null
3029
*/
31-
private Translator $translator;
30+
private ?Translator $translator = null;
3231

3332
/**
3433
* @param int|null $status
@@ -37,7 +36,8 @@ trait SetsHttpTitle
3736
private function getTitle(?int $status): ?string
3837
{
3938
if ($status && isset(Response::$statusTexts[$status])) {
40-
return $this->translator->get(Response::$statusTexts[$status]);
39+
$title = Response::$statusTexts[$status];
40+
return $this->translator?->get($title) ?? $title;
4141
}
4242

4343
return null;

src/Pipes/ValidationExceptionHandler.php

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,33 @@
2020
namespace LaravelJsonApi\Exceptions\Pipes;
2121

2222
use Closure;
23+
use Illuminate\Contracts\Translation\Translator;
2324
use Illuminate\Validation\ValidationException;
2425
use LaravelJsonApi\Contracts\ErrorProvider;
26+
use LaravelJsonApi\Core\Document\Error;
27+
use LaravelJsonApi\Core\Document\ErrorList;
2528
use LaravelJsonApi\Core\Responses\ErrorResponse;
29+
use LaravelJsonApi\Exceptions\Pipes\Concerns\SetsHttpTitle;
2630
use LaravelJsonApi\Validation\Factory;
31+
use Symfony\Component\HttpFoundation\Response;
2732
use Throwable;
2833

2934
class ValidationExceptionHandler
3035
{
31-
32-
/**
33-
* @var Factory
34-
*/
35-
private Factory $factory;
36+
use SetsHttpTitle;
3637

3738
/**
3839
* ValidationExceptionHandler constructor.
3940
*
4041
* @param Factory $factory
42+
* @param Translator|null $translator
43+
* @TODO next major version, make translator compulsory.
4144
*/
42-
public function __construct(Factory $factory)
43-
{
44-
$this->factory = $factory;
45+
public function __construct(
46+
private readonly Factory $factory,
47+
Translator $translator = null,
48+
) {
49+
$this->translator = $translator;
4550
}
4651

4752
/**
@@ -55,7 +60,7 @@ public function handle(Throwable $ex, Closure $next): ErrorResponse
5560
{
5661
if ($ex instanceof ValidationException) {
5762
return new ErrorResponse(
58-
$this->toErrors($ex)
63+
$this->toErrors($ex),
5964
);
6065
}
6166

@@ -64,12 +69,43 @@ public function handle(Throwable $ex, Closure $next): ErrorResponse
6469

6570
/**
6671
* @param ValidationException $ex
67-
* @return ErrorProvider
72+
* @return ErrorProvider|ErrorList
6873
*/
69-
private function toErrors(ValidationException $ex): ErrorProvider
74+
private function toErrors(ValidationException $ex): ErrorProvider|ErrorList
7075
{
71-
return $this->factory->createErrors(
76+
$errors = $this->factory->createErrors(
7277
$ex->validator
7378
);
79+
80+
if (Response::HTTP_UNPROCESSABLE_ENTITY !== $ex->status) {
81+
$errors = $errors->toErrors();
82+
$this->withStatus($ex->status, $errors);
83+
}
84+
85+
return $errors;
86+
}
87+
88+
/**
89+
* Override the status and title of the provided error list.
90+
*
91+
* As the validation exception can have a custom HTTP status, we sometimes need to override
92+
* the HTTP status and title on each JSON:API error.
93+
*
94+
* This could be improved by allowing the status and title to be overridden on the
95+
* `ValidatorErrorIterator` class (in the validation package).
96+
*
97+
* @param int $status
98+
* @param ErrorList $errors
99+
* @return void
100+
*/
101+
private function withStatus(int $status, ErrorList $errors): void
102+
{
103+
$title = $this->getTitle($status);
104+
105+
/** @var Error $error */
106+
foreach ($errors as $error) {
107+
/** This works as error is mutable; note a future version might make error immutable. */
108+
$error->setStatus($status)->setTitle($title);
109+
}
74110
}
75111
}

tests/Integration/ExceptionsTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,68 @@ public function testValidationException(): void
455455
->assertExactJson($expected);
456456
}
457457

458+
/**
459+
* If the validator has a status set, we ensure the response and JSON:API errors have that status.
460+
*
461+
* @see https://github.com/laravel-json-api/exceptions/issues/3
462+
*/
463+
public function testValidationExceptionWithStatus(): void
464+
{
465+
$this->ex = ValidationException::withMessages([
466+
'data.email' => 'Hello teapot@example.com',
467+
])->status(418);
468+
469+
$expected = [
470+
'errors' => [
471+
[
472+
'detail' => 'Hello teapot@example.com',
473+
'source' => ['pointer' => '/data/email'],
474+
'status' => '418',
475+
'title' => "I'm a teapot",
476+
],
477+
],
478+
'jsonapi' => [
479+
'version' => '1.0',
480+
],
481+
];
482+
483+
$this
484+
->get('/test', ['Accept' => 'application/vnd.api+json'])
485+
->assertStatus(418)
486+
->assertHeader('Content-Type', 'application/vnd.api+json')
487+
->assertExactJson($expected);
488+
}
489+
490+
/**
491+
* If the validator has a status set, we ensure the response and JSON:API errors have that status.
492+
*
493+
* @see https://github.com/laravel-json-api/exceptions/issues/3
494+
*/
495+
public function testValidationExceptionWithStatusThatDoesNotHaveTitle(): void
496+
{
497+
$this->ex = ValidationException::withMessages([
498+
'data.email' => 'Too many attempts',
499+
])->status(419);
500+
501+
$expected = [
502+
'errors' => [
503+
[
504+
'detail' => 'Too many attempts',
505+
'source' => ['pointer' => '/data/email'],
506+
'status' => '419',
507+
],
508+
],
509+
'jsonapi' => [
510+
'version' => '1.0',
511+
],
512+
];
513+
514+
$this->get('/test', ['Accept' => 'application/vnd.api+json'])
515+
->assertStatus(419)
516+
->assertHeader('Content-Type', 'application/vnd.api+json')
517+
->assertExactJson($expected);
518+
}
519+
458520
public function testDefaultExceptionWithoutDebug(): void
459521
{
460522
config()->set('app.debug', false);

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