Skip to content

Commit b791f39

Browse files
committed
Allow extensions to activate on GET requests
1 parent ff4eea5 commit b791f39

2 files changed

Lines changed: 71 additions & 3 deletions

File tree

src/JsonApi.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,20 @@ public function handle(ServerRequestInterface $request): ResponseInterface
175175

176176
private function runExtensions(Context $context): ?ResponseInterface
177177
{
178-
$contentTypeExtensionUris = $this->getContentTypeExtensionUris($context->request);
179178
$acceptExtensionUris = $context->requestedExtensions();
180179

180+
$requestedExtensionUris = !$context->request->hasHeader('Content-Type')
181+
? $acceptExtensionUris
182+
: array_values(
183+
array_intersect(
184+
$acceptExtensionUris,
185+
$this->getContentTypeExtensionUris($context->request),
186+
),
187+
);
188+
181189
$activeExtensions = array_intersect_key(
182190
$this->extensions,
183-
array_flip($contentTypeExtensionUris),
184-
array_flip($acceptExtensionUris),
191+
array_flip($requestedExtensionUris),
185192
);
186193

187194
foreach ($activeExtensions as $extension) {

tests/specification/ContentNegotiationTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Tobyz\Tests\JsonApiServer\specification;
44

5+
use Psr\Http\Message\ResponseInterface;
56
use Tobyz\JsonApiServer\Endpoint\Show;
7+
use Tobyz\JsonApiServer\Extension\Extension;
68
use Tobyz\JsonApiServer\Exception\NotAcceptableException;
9+
use Tobyz\JsonApiServer\Exception\ResourceNotFoundException;
710
use Tobyz\JsonApiServer\Exception\UnsupportedMediaTypeException;
811
use Tobyz\JsonApiServer\JsonApi;
912
use Tobyz\Tests\JsonApiServer\AbstractTestCase;
@@ -196,4 +199,62 @@ public function test_profiles_from_lines_with_unsupported_extensions_are_ignored
196199
// Should only get the profile from the second (valid) Accept line
197200
$this->assertEquals(['https://example.com/valid-profile'], $capturedProfiles);
198201
}
202+
203+
public function test_extensions_can_activate_on_get_requests_without_content_type()
204+
{
205+
$this->api = new JsonApi();
206+
$this->api->extension($this->extensionDemo());
207+
208+
$response = $this->api->handle(
209+
$this->buildRequest('GET', '/extension-demo')->withHeader(
210+
'Accept',
211+
'application/vnd.api+json; ext="https://example.com/extensions/demo"',
212+
),
213+
);
214+
215+
$this->assertEquals(200, $response->getStatusCode());
216+
$this->assertEquals(
217+
'application/vnd.api+json; ext=https://example.com/extensions/demo',
218+
$response->getHeaderLine('Content-Type'),
219+
);
220+
$this->assertJsonApiDocumentSubset(['meta' => ['activated' => true]], (string) $response->getBody());
221+
}
222+
223+
public function test_extensions_with_content_type_must_be_requested_in_both_headers()
224+
{
225+
$this->api = new JsonApi();
226+
$this->api->extension($this->extensionDemo());
227+
228+
$this->expectException(ResourceNotFoundException::class);
229+
230+
$this->api->handle(
231+
$this->buildRequest('POST', '/extension-demo')
232+
->withHeader(
233+
'Accept',
234+
'application/vnd.api+json; ext="https://example.com/extensions/demo"',
235+
)
236+
->withHeader('Content-Type', 'application/vnd.api+json')
237+
->withParsedBody(['data' => ['type' => 'users']]),
238+
);
239+
}
240+
241+
private function extensionDemo(): Extension
242+
{
243+
return new class extends Extension
244+
{
245+
public function uri(): string
246+
{
247+
return 'https://example.com/extensions/demo';
248+
}
249+
250+
public function handle(\Tobyz\JsonApiServer\Context $context): ?ResponseInterface
251+
{
252+
if ($context->path() !== 'extension-demo') {
253+
return null;
254+
}
255+
256+
return $context->createResponse(['meta' => ['activated' => true]]);
257+
}
258+
};
259+
}
199260
}

0 commit comments

Comments
 (0)