-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4cd8dd1
commit 184bea1
Showing
6 changed files
with
279 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?php | ||
namespace Pitch\AdrBundle\Responder\Handler; | ||
|
||
use Pitch\AdrBundle\Configuration\DefaultContentType; | ||
use Pitch\AdrBundle\Responder\ResponsePayloadEvent; | ||
use Symfony\Component\HttpFoundation\AcceptHeader; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
trait AcceptPriorityTrait | ||
{ | ||
public function getResponseHandlerPriority(ResponsePayloadEvent $event): ?float | ||
{ | ||
return $this->getAcceptPriority($event->request); | ||
} | ||
|
||
/** | ||
* @return string[] | ||
*/ | ||
abstract protected function getSupportedContentTypes(): array; | ||
|
||
protected function getAcceptPriority( | ||
Request $request | ||
): ?float { | ||
$accept = $this->getRequestAcceptHeader($request); | ||
|
||
if ($accept) { | ||
foreach ($accept->all() as $a) { | ||
$v = $a->getValue(); | ||
if ($v === '*/*') { | ||
return $this->supportsDefaultContentType($request) ? $a->getQuality() : 0; | ||
} elseif (\in_array($v, $this->getSupportedContentTypes())) { | ||
return $a->getQuality(); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
return $this->supportsDefaultContentType($request) ? 1 : null; | ||
} | ||
|
||
private function supportsDefaultContentType( | ||
Request $request | ||
): bool { | ||
$defaultType = $request->attributes->has('_' . DefaultContentType::class) | ||
? $request->attributes->get('_' . DefaultContentType::class) | ||
: null; | ||
|
||
return $defaultType instanceof DefaultContentType | ||
? \in_array($defaultType->value, $this->getSupportedContentTypes()) | ||
: true; | ||
} | ||
|
||
private function getRequestAcceptHeader( | ||
Request $request | ||
): ?AcceptHeader { | ||
if ($request->attributes->has(AcceptHeader::class)) { | ||
$accept = $request->attributes->get(AcceptHeader::class); | ||
} else { | ||
$accept = $request->headers->has('accept') | ||
? AcceptHeader::fromString($request->headers->get('accept')) | ||
: null; | ||
|
||
$request->attributes->set(AcceptHeader::class, $accept); | ||
} | ||
|
||
return $accept; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
namespace Pitch\AdrBundle\Responder\Handler; | ||
|
||
use JsonException; | ||
use Pitch\AdrBundle\Responder\PrioritisedResponseHandlerInterface; | ||
use Pitch\AdrBundle\Responder\ResponsePayloadEvent; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
class JsonResponder implements PrioritisedResponseHandlerInterface | ||
{ | ||
use AcceptPriorityTrait; | ||
|
||
public function getSupportedPayloadTypes(): array | ||
{ | ||
return [ | ||
'array', | ||
'object', | ||
]; | ||
} | ||
|
||
protected function getSupportedContentTypes(): array | ||
{ | ||
return ['application/json']; | ||
} | ||
|
||
public function handleResponsePayload(ResponsePayloadEvent $payloadEvent) | ||
{ | ||
if (!($payloadEvent->payload instanceof Response)) { | ||
try { | ||
$payloadEvent->payload = new JsonResponse( | ||
\json_encode($payloadEvent->payload, \JSON_THROW_ON_ERROR), | ||
$payloadEvent->httpStatus ?? 200, | ||
$payloadEvent->httpHeaders->all(), | ||
true, | ||
); | ||
$payloadEvent->stopPropagation = true; | ||
} catch (JsonException $e) { | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?php | ||
namespace Pitch\AdrBundle\Responder\Handler; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Pitch\AdrBundle\Configuration\DefaultContentType; | ||
use Pitch\AdrBundle\Responder\ResponsePayloadEvent; | ||
use Symfony\Component\HttpFoundation\AcceptHeader; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
class AcceptPriorityTraitTest extends TestCase | ||
{ | ||
public function provideRequestSettings() | ||
{ | ||
return [ | ||
'default to 1' => [ | ||
1, | ||
], | ||
'matching defaultContentType' => [ | ||
1, | ||
['foo/bar', 'foo/baz'], | ||
'foo/baz', | ||
], | ||
'not matching defaultContentType' => [ | ||
null, | ||
['foo/bar'], | ||
'foo/baz', | ||
], | ||
'matching accept' => [ | ||
0.8, | ||
['foo/bar'], | ||
null, | ||
'foo/baz;q=1,foo/bar;q=0.8', | ||
], | ||
'not matching accept' => [ | ||
null, | ||
['foo/bar'], | ||
'foo/bar', | ||
'foo/baz', | ||
], | ||
'accept any matching defaultContentType' => [ | ||
0.8, | ||
['foo/bar'], | ||
'foo/bar', | ||
'foo/baz;q=1,foo/bar;q=0.1,*/*;q=0.8', | ||
], | ||
'accept any not matching defaultContentType' => [ | ||
0, | ||
['foo/bar'], | ||
'foo/baz', | ||
'foo/baz,foo/bar;q=0.1,*/*;q=0.8', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider provideRequestSettings | ||
*/ | ||
public function testGetPriorityFromAcceptQuality( | ||
?float $expectedPriority, | ||
array $supportedContentTypes = [], | ||
?string $defaultContentType = null, | ||
?string $acceptHeader = null | ||
) { | ||
$handler = $this->getMockForTrait(AcceptPriorityTrait::class); | ||
$handler->method('getSupportedContentTypes')->willReturn($supportedContentTypes); | ||
/** @var AcceptPriorityTrait $handler */ | ||
|
||
$request = new Request(); | ||
if ($acceptHeader) { | ||
$request->headers->set('accept', $acceptHeader); | ||
} | ||
if ($defaultContentType) { | ||
$request->attributes->set( | ||
'_' . DefaultContentType::class, | ||
new DefaultContentType($defaultContentType) | ||
); | ||
} | ||
|
||
$event = new ResponsePayloadEvent(null, $request); | ||
|
||
$this->assertSame($expectedPriority, $handler->getResponseHandlerPriority($event)); | ||
} | ||
|
||
public function testStoreAccessHeaderOnRequestAttributes() | ||
{ | ||
$handler = $this->getMockForTrait(AcceptPriorityTrait::class); | ||
$handler->method('getSupportedContentTypes')->willReturn(['foo/baz']); | ||
/** @var AcceptPriorityTrait $handler */ | ||
|
||
$request = new Request(); | ||
$request->headers->set('accept', 'foo/bar,foo/baz;q=0.2'); | ||
$event = new ResponsePayloadEvent(null, $request); | ||
|
||
$this->assertEquals(0.2, $handler->getResponseHandlerPriority($event)); | ||
|
||
/** @var AcceptHeader */ | ||
$attr = $request->attributes->get(AcceptHeader::class); | ||
$this->assertInstanceOf(AcceptHeader::class, $attr); | ||
$this->assertEquals(0.2, $attr->get('foo/baz')->getQuality()); | ||
|
||
$request->attributes->set(AcceptHeader::class, AcceptHeader::fromString('foo/baz;q=0.5')); | ||
|
||
$this->assertEquals(0.5, $handler->getResponseHandlerPriority($event)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?php | ||
namespace Pitch\AdrBundle\Responder\Handler; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Pitch\AdrBundle\Responder\ResponsePayloadEvent; | ||
use stdClass; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
class JsonResponderTest extends TestCase | ||
{ | ||
public function testCreateJsonResponse() | ||
{ | ||
$event = new ResponsePayloadEvent(['a' => 'b'], new Request()); | ||
|
||
(new JsonResponder())->handleResponsePayload($event); | ||
|
||
$this->assertInstanceOf(JsonResponse::class, $event->payload); | ||
$this->assertEquals('{"a":"b"}', $event->payload->getContent()); | ||
} | ||
|
||
public function testCatchJsonExceptions() | ||
{ | ||
$circular = new stdClass(); | ||
$circular->foo = $circular; | ||
|
||
$event = new ResponsePayloadEvent($circular, new Request()); | ||
|
||
(new JsonResponder)->handleResponsePayload($event); | ||
|
||
$this->assertSame($circular, $event->payload); | ||
} | ||
} |