Skip to content

Commit 6626549

Browse files
authored
feat: correctly map problem-detail fields when using ProblemExceptionInterface (#7776)
* feat: use values from ProblemExceptionInterface * Add test case to JsonProblemTest * Apply change to State/ApiResource/Error.php as well * Fix StrictParametersTest in line with detail of ParameterNotSupportedException
1 parent 23840f9 commit 6626549

7 files changed

Lines changed: 110 additions & 1 deletion

File tree

src/Laravel/ApiResource/Error.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public static function createFromException(\Exception|\Throwable $exception, int
136136
{
137137
$headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : [];
138138

139+
if ($exception instanceof ProblemExceptionInterface) {
140+
return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers);
141+
}
142+
139143
return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: '/errors/'.$status, headers: $headers);
140144
}
141145

src/Laravel/Tests/JsonProblemTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ public function testRetrieveErrorHtml(): void
8585
</html>', $response->getContent());
8686
}
8787

88+
public function testProblemExceptionInterface(): void
89+
{
90+
$response = $this->get('/api/teapot', headers: ['accept' => 'application/json']);
91+
$response->assertStatus(418);
92+
$response->assertHeader('content-type', 'application/problem+json; charset=utf-8');
93+
$response->assertJsonFragment([
94+
'type' => '/problem/teapot',
95+
'title' => 'I\'m a teapot',
96+
'status' => 418,
97+
'detail' => 'No coffee here',
98+
'instance' => '/teapot',
99+
]);
100+
}
101+
88102
/**
89103
* @return list<array{0: string, 1: string, 2: array<string, mixed>}>
90104
*/

src/Laravel/workbench/app/ApiResource/ServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
use ApiPlatform\Metadata\Get;
1717
use Workbench\App\State\CustomProvider;
1818
use Workbench\App\State\CustomProviderWithDependency;
19+
use Workbench\App\State\TeapotProvider;
1920

21+
#[Get(uriTemplate: 'teapot', name: 'teapot', provider: TeapotProvider::class)]
2022
#[Get(uriTemplate: 'custom_service_provider', provider: CustomProvider::class)]
2123
#[Get(uriTemplate: 'custom_service_provider_with_dependency', provider: CustomProviderWithDependency::class)]
2224
class ServiceProvider
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Workbench\App\Exceptions;
15+
16+
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
17+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
18+
19+
class TeapotException extends \Exception implements ProblemExceptionInterface, HttpExceptionInterface
20+
{
21+
public function getStatusCode(): int
22+
{
23+
return 418;
24+
}
25+
26+
public function getHeaders(): array
27+
{
28+
return [];
29+
}
30+
31+
public function getType(): string
32+
{
33+
return '/problem/teapot';
34+
}
35+
36+
public function getTitle(): ?string
37+
{
38+
return 'I\'m a teapot';
39+
}
40+
41+
public function getStatus(): ?int
42+
{
43+
return 418;
44+
}
45+
46+
public function getDetail(): ?string
47+
{
48+
return 'No coffee here';
49+
}
50+
51+
public function getInstance(): ?string
52+
{
53+
return '/teapot';
54+
}
55+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Workbench\App\State;
15+
16+
use ApiPlatform\Metadata\Operation;
17+
use ApiPlatform\State\ProviderInterface;
18+
use Workbench\App\ApiResource\ServiceProvider;
19+
use Workbench\App\Exceptions\TeapotException;
20+
21+
/**
22+
* @implements ProviderInterface<ServiceProvider>
23+
*/
24+
class TeapotProvider implements ProviderInterface
25+
{
26+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
27+
{
28+
throw new TeapotException('I\'m boiling');
29+
}
30+
}

src/State/ApiResource/Error.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ public static function createFromException(\Exception|\Throwable $exception, int
199199
{
200200
$headers = ($exception instanceof SymfonyHttpExceptionInterface || $exception instanceof HttpExceptionInterface) ? $exception->getHeaders() : [];
201201

202+
if ($exception instanceof ProblemExceptionInterface) {
203+
return new self($exception->getTitle(), $exception->getDetail(), $exception->getStatus(), $exception->getTrace(), $exception->getInstance(), $exception->getType(), $headers);
204+
}
205+
202206
return new self('An error occurred', $exception->getMessage(), $status, $exception->getTrace(), type: "/errors/$status", headers: $headers, previous: $exception->getPrevious());
203207
}
204208

tests/Functional/Parameters/StrictParametersTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static function getResources(): array
3434
public function testErrorAsParameterIsNotAllowed(): void
3535
{
3636
self::createClient()->request('GET', 'strict_query_parameters?bar=test');
37-
$this->assertJsonContains(['detail' => 'Parameter not supported']);
37+
$this->assertJsonContains(['detail' => 'Parameter "bar" not supported']);
3838
$this->assertResponseStatusCodeSame(400);
3939
}
4040

0 commit comments

Comments
 (0)