Skip to content

Commit 12a8a98

Browse files
committed
feat(metadata): headers configuration
1 parent 5687986 commit 12a8a98

File tree

15 files changed

+106
-14
lines changed

15 files changed

+106
-14
lines changed

features/main/headers.feature

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ Feature: Headers addition
66
When I send a "GET" request to "/dummy_cars"
77
Then the response status code should be 200
88
And the header "Sunset" should be equal to "Sat, 01 Jan 2050 00:00:00 +0000"
9+
10+
Scenario: Declare headers from resource
11+
When I send a "GET" request to "/redirect_to_foobar"
12+
Then the response status code should be 301
13+
And the header "Location" should be equal to "/foobar"
14+
And the header "Hello" should be equal to "World"

src/Metadata/ApiResource.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class ApiResource extends Metadata
3838
/**
3939
* @param array<int, HttpOperation>|array<string, HttpOperation>|Operations|null $operations Operations is a list of HttpOperation
4040
* @param array<string, Link>|array<string, mixed[]>|string[]|string|null $uriVariables
41+
* @param array<string, string> $headers
4142
* @param string|callable|null $provider
4243
* @param string|callable|null $processor
4344
* @param mixed|null $mercure
@@ -314,6 +315,7 @@ public function __construct(
314315
* - With GraphQL, the [`isDeprecated` and `deprecationReason` properties](https://facebook.github.io/graphql/June2018/#sec-Deprecation) will be added to the schema
315316
*/
316317
protected ?string $deprecationReason = null,
318+
protected ?array $headers = null,
317319
protected ?array $cacheHeaders = null,
318320
protected ?array $normalizationContext = null,
319321
protected ?array $denormalizationContext = null,
@@ -1280,6 +1282,19 @@ public function withController(string $controller): self
12801282
return $self;
12811283
}
12821284

1285+
public function getHeaders(): ?array
1286+
{
1287+
return $this->headers;
1288+
}
1289+
1290+
public function withHeaders(array $headers): self
1291+
{
1292+
$self = clone $this;
1293+
$self->headers = $headers;
1294+
1295+
return $self;
1296+
}
1297+
12831298
public function getCacheHeaders(): ?array
12841299
{
12851300
return $this->cacheHeaders;

src/Metadata/Delete.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -115,6 +116,7 @@ public function __construct(
115116
schemes: $schemes,
116117
condition: $condition,
117118
controller: $controller,
119+
headers: $headers,
118120
cacheHeaders: $cacheHeaders,
119121
paginationViaCursor: $paginationViaCursor,
120122
hydraContext: $hydraContext,

src/Metadata/Error.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -114,6 +115,7 @@ public function __construct(
114115
schemes: $schemes,
115116
condition: $condition,
116117
controller: $controller,
118+
headers: $headers,
117119
cacheHeaders: $cacheHeaders,
118120
paginationViaCursor: $paginationViaCursor,
119121
hydraContext: $hydraContext,

src/Metadata/Get.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -114,6 +115,7 @@ public function __construct(
114115
schemes: $schemes,
115116
condition: $condition,
116117
controller: $controller,
118+
headers: $headers,
117119
cacheHeaders: $cacheHeaders,
118120
paginationViaCursor: $paginationViaCursor,
119121
hydraContext: $hydraContext,

src/Metadata/GetCollection.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -115,6 +116,7 @@ public function __construct(
115116
schemes: $schemes,
116117
condition: $condition,
117118
controller: $controller,
119+
headers: $headers,
118120
cacheHeaders: $cacheHeaders,
119121
paginationViaCursor: $paginationViaCursor,
120122
hydraContext: $hydraContext,

src/Metadata/HttpOperation.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class HttpOperation extends Operation
5555
* stale_while_revalidate?: int,
5656
* stale-if-error?: int,
5757
* }|null $cacheHeaders {@see https://api-platform.com/docs/core/performance/#setting-custom-http-cache-headers}
58+
* @param array<string, string>|null $headers
5859
* @param array{
5960
* field: string,
6061
* direction: string,
@@ -144,6 +145,7 @@ public function __construct(
144145
protected ?array $schemes = null,
145146
protected ?string $condition = null,
146147
protected ?string $controller = null,
148+
protected ?array $headers = null,
147149
protected ?array $cacheHeaders = null,
148150
protected ?array $paginationViaCursor = null,
149151
protected ?array $hydraContext = null,
@@ -511,6 +513,19 @@ public function withController(string $controller): self
511513
return $self;
512514
}
513515

516+
public function getHeaders(): ?array
517+
{
518+
return $this->headers;
519+
}
520+
521+
public function withHeaders(array $headers): self
522+
{
523+
$self = clone $this;
524+
$self->headers = $headers;
525+
526+
return $self;
527+
}
528+
514529
public function getCacheHeaders(): ?array
515530
{
516531
return $this->cacheHeaders;

src/Metadata/Metadata.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@
2121
abstract class Metadata
2222
{
2323
/**
24-
* @param string|null $deprecationReason https://api-platform.com/docs/core/deprecations/#deprecating-resource-classes-operations-and-properties
25-
* @param string|null $security https://api-platform.com/docs/core/security
26-
* @param string|null $securityPostDenormalize https://api-platform.com/docs/core/security/#executing-access-control-rules-after-denormalization
27-
* @param mixed|null $mercure
28-
* @param mixed|null $messenger
29-
* @param mixed|null $input
30-
* @param mixed|null $output
31-
* @param mixed|null $provider
32-
* @param mixed|null $processor
24+
* @param string|null $deprecationReason https://api-platform.com/docs/core/deprecations/#deprecating-resource-classes-operations-and-properties
25+
* @param string|null $security https://api-platform.com/docs/core/security
26+
* @param string|null $securityPostDenormalize https://api-platform.com/docs/core/security/#executing-access-control-rules-after-denormalization
27+
* @param array<string, string> $headers
28+
* @param mixed|null $mercure
29+
* @param mixed|null $messenger
30+
* @param mixed|null $input
31+
* @param mixed|null $output
32+
* @param mixed|null $provider
33+
* @param mixed|null $processor
3334
*/
3435
public function __construct(
3536
protected ?string $shortName = null,

src/Metadata/NotExposed.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function __construct(
5050
array $schemes = null,
5151
string $condition = null,
5252
?string $controller = 'api_platform.action.not_exposed',
53+
array $headers = null,
5354
array $cacheHeaders = null,
5455
array $paginationViaCursor = null,
5556

@@ -128,6 +129,7 @@ public function __construct(
128129
schemes: $schemes,
129130
condition: $condition,
130131
controller: $controller,
132+
headers: $headers,
131133
cacheHeaders: $cacheHeaders,
132134
paginationViaCursor: $paginationViaCursor,
133135
hydraContext: $hydraContext,

src/Metadata/Patch.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -115,6 +116,7 @@ public function __construct(
115116
schemes: $schemes,
116117
condition: $condition,
117118
controller: $controller,
119+
headers: $headers,
118120
cacheHeaders: $cacheHeaders,
119121
paginationViaCursor: $paginationViaCursor,
120122
hydraContext: $hydraContext,

src/Metadata/Post.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -116,6 +117,7 @@ public function __construct(
116117
schemes: $schemes,
117118
condition: $condition,
118119
controller: $controller,
120+
headers: $headers,
119121
cacheHeaders: $cacheHeaders,
120122
paginationViaCursor: $paginationViaCursor,
121123
hydraContext: $hydraContext,

src/Metadata/Put.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
array $schemes = null,
4040
string $condition = null,
4141
string $controller = null,
42+
array $headers = null,
4243
array $cacheHeaders = null,
4344
array $paginationViaCursor = null,
4445
array $hydraContext = null,
@@ -116,6 +117,7 @@ public function __construct(
116117
schemes: $schemes,
117118
condition: $condition,
118119
controller: $controller,
120+
headers: $headers,
119121
cacheHeaders: $cacheHeaders,
120122
paginationViaCursor: $paginationViaCursor,
121123
hydraContext: $hydraContext,

src/State/Processor/RespondProcessor.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
7171
$headers = array_merge($headers, $exceptionHeaders);
7272
}
7373

74+
if ($operationHeaders = $operation->getHeaders()) {
75+
$headers = array_merge($headers, $operationHeaders);
76+
}
77+
7478
$status = $operation->getStatus();
7579

7680
if ($sunset = $operation->getSunset()) {
@@ -86,7 +90,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
8690

8791
if ($hasData = ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData))) && $this->iriConverter) {
8892
if (
89-
300 <= $status && $status < 400
93+
!isset($headers['Location'])
94+
&& 300 <= $status && $status < 400
9095
&& (($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false) || ($operation->getExtraProperties()['canonical_uri_template'] ?? null))
9196
) {
9297
$canonicalOperation = $operation;
@@ -102,11 +107,11 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
102107

103108
$status ??= self::METHOD_TO_CODE[$method] ?? 200;
104109

105-
if ($hasData && $this->iriConverter) {
110+
if ($hasData && $this->iriConverter && !isset($headers['Content-Location'])) {
106111
$iri = $this->iriConverter->getIriFromResource($originalData);
107112
$headers['Content-Location'] = $iri;
108113

109-
if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method) {
114+
if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) {
110115
$headers['Location'] = $iri;
111116
}
112117
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
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 ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
19+
#[ApiResource(headers: ['Location' => '/foobar', 'Hello' => 'World'], status: 301, output: false, operations: [new Get(uriTemplate: 'redirect_to_foobar', read: false)])]
20+
class Headers
21+
{
22+
}

tests/State/RespondProcessorTest.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ public function testRedirectToOperation(): void
101101

102102
public function testAddsExceptionHeaders(): void
103103
{
104-
$operation = new Get();
105-
106104
/** @var ProcessorInterface<Response> $respondProcessor */
107105
$respondProcessor = new RespondProcessor();
108106
$req = new Request();
@@ -113,4 +111,18 @@ public function testAddsExceptionHeaders(): void
113111

114112
$this->assertSame('32', $response->headers->get('retry-after'));
115113
}
114+
115+
public function testAddsHeaders(): void
116+
{
117+
$operation = new Get(headers: ['foo' => 'bar']);
118+
119+
/** @var ProcessorInterface<Response> $respondProcessor */
120+
$respondProcessor = new RespondProcessor();
121+
$req = new Request();
122+
$response = $respondProcessor->process('content', $operation, context: [
123+
'request' => $req,
124+
]);
125+
126+
$this->assertSame('bar', $response->headers->get('foo'));
127+
}
116128
}

0 commit comments

Comments
 (0)