summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMateu Aguiló Bosch2017-09-16 04:52:28 +0200
committerMateu Aguiló Bosch2017-09-16 05:06:11 +0200
commit235bc9fbfd129b15b17fae1113742f8251297d70 (patch)
treeaab60563922dc4278e5092e310d2d80a34c1952e
parent9b599ae506d7fe26fe22f476fc9be78ba2563fe1 (diff)
feat(Merger): Allow JSON output
-rw-r--r--src/Blueprint/BlueprintManager.php22
-rw-r--r--src/Controller/FrontController.php9
-rw-r--r--src/Normalizer/MultiresponseJsonNormalizer.php62
-rw-r--r--src/Normalizer/MultiresponseNormalizer.php17
-rw-r--r--subrequests.services.yml4
-rw-r--r--tests/src/Unit/Blueprint/BlueprintManagerTest.php8
-rw-r--r--tests/src/Unit/Normalizer/JsonSubrequestDenormalizerTest.php3
-rw-r--r--tests/src/Unit/Normalizer/MultiresponseJsonNormalizerTest.php64
-rw-r--r--tests/src/Unit/Normalizer/MultiresponseNormalizerTest.php16
9 files changed, 176 insertions, 29 deletions
diff --git a/src/Blueprint/BlueprintManager.php b/src/Blueprint/BlueprintManager.php
index 77788d6..9b65a29 100644
--- a/src/Blueprint/BlueprintManager.php
+++ b/src/Blueprint/BlueprintManager.php
@@ -41,25 +41,19 @@ class BlueprintManager {
/**
* @param \Symfony\Component\HttpFoundation\Response[] $responses
* The responses to combine.
+ * @param string $format
+ * The format to combine the responses on. Default is multipart/related.
*
* @return \Symfony\Component\HttpFoundation\Response
* The combined response with a 207.
*/
- public function combineResponses(array $responses) {
- $delimiter = md5(microtime());
-
- // Prepare the root content type header.
- $content_type = sprintf(
- 'multipart/related; boundary="%s", type=%s',
- $delimiter,
- $this->negotiateSubContentType($responses)
- );
- $headers = ['Content-Type' => $content_type];
-
- $context = ['delimiter' => $delimiter];
+ public function combineResponses(array $responses, $format) {
+ $context = [
+ 'sub-content-type' => $this->negotiateSubContentType($responses),
+ ];
// Set the content.
- $content = $this->serializer->normalize($responses, 'multipart-related', $context);
- $response = CacheableResponse::create($content, 207, $headers);
+ $normalized = $this->serializer->normalize($responses, $format, $context);
+ $response = CacheableResponse::create($normalized['content'], 207, $normalized['headers']);
// Set the cacheability metadata.
$cacheable_responses = array_filter($responses, function ($response) {
return $response instanceof CacheableResponseInterface;
diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php
index 418d4cc..ae79339 100644
--- a/src/Controller/FrontController.php
+++ b/src/Controller/FrontController.php
@@ -57,7 +57,14 @@ class FrontController extends ControllerBase {
}
$tree = $this->blueprintManager->parse($data, $request);
$responses = $this->subrequestsManager->request($tree);
- return $this->blueprintManager->combineResponses($responses);
+ $master_request = $tree->getMasterRequest();
+ $output_format = $master_request->getRequestFormat();
+ if ($output_format === 'html') {
+ // Change the default format from html to multipart-related.
+ $output_format = 'multipart-related';
+ }
+ $master_request->getMimeType($output_format);
+ return $this->blueprintManager->combineResponses($responses, $output_format);
}
}
diff --git a/src/Normalizer/MultiresponseJsonNormalizer.php b/src/Normalizer/MultiresponseJsonNormalizer.php
new file mode 100644
index 0000000..8b667b2
--- /dev/null
+++ b/src/Normalizer/MultiresponseJsonNormalizer.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\subrequests\Normalizer;
+
+use Drupal\Component\Serialization\Json;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+
+/**
+ * Normalizes multiple response objects into a single string.
+ */
+class MultiresponseJsonNormalizer implements NormalizerInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function normalize($object, $format = NULL, array $context = []) {
+ // Prepare the root content type header.
+ $content_type = sprintf(
+ 'application/json; type=%s',
+ $context['sub-content-type']
+ );
+ $headers = ['Content-Type' => $content_type];
+
+ // Join the content responses as a JSON object with the separator.
+ $output = array_reduce((array) $object, function ($carry, Response $part_response) {
+ $part_response->headers->set('Status', $part_response->getStatusCode());
+ $content_id = $part_response->headers->get('Content-ID');
+ $content_id = substr($content_id, 1, strlen($content_id) - 2);
+ $carry[$content_id] = [
+ 'headers' => $part_response->headers->all(),
+ 'body' => $part_response->getContent(),
+ ];
+ return $carry;
+ }, []);
+ $content = Json::encode($output);
+ return [
+ 'content' => $content,
+ 'headers' => $headers,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsNormalization($data, $format = NULL) {
+ if ($format !== 'json') {
+ return FALSE;
+ }
+ if (!is_array($data)) {
+ return FALSE;
+ }
+ $responses = array_filter($data, function ($response) {
+ return $response instanceof Response;
+ });
+ if (count($responses) !== count($data)) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+}
diff --git a/src/Normalizer/MultiresponseNormalizer.php b/src/Normalizer/MultiresponseNormalizer.php
index 07d0993..7c02d7f 100644
--- a/src/Normalizer/MultiresponseNormalizer.php
+++ b/src/Normalizer/MultiresponseNormalizer.php
@@ -14,7 +14,16 @@ class MultiresponseNormalizer implements NormalizerInterface {
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
- $delimiter = $context['delimiter'];
+ $delimiter = md5(microtime());
+
+ // Prepare the root content type header.
+ $content_type = sprintf(
+ 'multipart/related; boundary="%s", type=%s',
+ $delimiter,
+ $context['sub-content-type']
+ );
+ $headers = ['Content-Type' => $content_type];
+
$separator = sprintf("\r\n--%s\r\n", $delimiter);
// Join the content responses with the separator.
$content_items = array_map(function (Response $part_response) {
@@ -25,7 +34,11 @@ class MultiresponseNormalizer implements NormalizerInterface {
$part_response->getContent()
);
}, (array) $object);
- return sprintf("--%s\r\n", $delimiter) . implode($separator, $content_items) . sprintf("\r\n--%s--", $delimiter);
+ $content = sprintf("--%s\r\n", $delimiter) . implode($separator, $content_items) . sprintf("\r\n--%s--", $delimiter);
+ return [
+ 'content' => $content,
+ 'headers' => $headers,
+ ];
}
/**
diff --git a/subrequests.services.yml b/subrequests.services.yml
index cf1a21d..1c6bd7d 100644
--- a/subrequests.services.yml
+++ b/subrequests.services.yml
@@ -23,3 +23,7 @@ services:
class: Drupal\subrequests\Normalizer\MultiresponseNormalizer
tags:
- { name: normalizer, priority: 0 }
+ subrequests.normalizer.multiresponse_json:
+ class: Drupal\subrequests\Normalizer\MultiresponseJsonNormalizer
+ tags:
+ - { name: normalizer, priority: 0 }
diff --git a/tests/src/Unit/Blueprint/BlueprintManagerTest.php b/tests/src/Unit/Blueprint/BlueprintManagerTest.php
index 6cd10a9..160a5b2 100644
--- a/tests/src/Unit/Blueprint/BlueprintManagerTest.php
+++ b/tests/src/Unit/Blueprint/BlueprintManagerTest.php
@@ -35,7 +35,7 @@ class BlueprintManagerTest extends UnitTestCase {
$denormalizer->setSerializer(Argument::any())->willReturn(NULL);
$normalizer = $this->prophesize(MultiresponseNormalizer::class);
$normalizer->normalize(Argument::type('array'), 'multipart-related', Argument::type('array'))
- ->willReturn('Booh!');
+ ->willReturn(['content' => 'Booh!', 'headers' => ['head' => 'Ha!']]);
$normalizer->supportsNormalization(Argument::type('array'), 'multipart-related')
->willReturn([])->willReturn(TRUE);
$serializer = new Serializer(
@@ -62,11 +62,9 @@ class BlueprintManagerTest extends UnitTestCase {
Response::create('foo', 200, ['lorem' => 'ipsum', 'Content-Type' => 'sparrow']),
Response::create('bar', 201, ['dolor' => 'sid', 'Content-Type' => 'sparrow']),
];
- $combined = $this->sut->combineResponses($responses);
+ $combined = $this->sut->combineResponses($responses, 'multipart-related');
$this->assertInstanceOf(CacheableResponse::class, $combined);
- $content_type = $combined->headers->get('Content-Type');
- $this->assertStringStartsWith('multipart/related; boundary="', $content_type);
- $this->assertStringEndsWith('", type=sparrow', $content_type);
+ $this->assertSame('Ha!', $combined->headers->get('head'));
$this->assertSame('Booh!', $combined->getContent());
}
diff --git a/tests/src/Unit/Normalizer/JsonSubrequestDenormalizerTest.php b/tests/src/Unit/Normalizer/JsonSubrequestDenormalizerTest.php
index 007fead..2dbdd1d 100644
--- a/tests/src/Unit/Normalizer/JsonSubrequestDenormalizerTest.php
+++ b/tests/src/Unit/Normalizer/JsonSubrequestDenormalizerTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\subrequests\Normalizer;
+use Drupal\Component\Serialization\Json;
use Drupal\subrequests\Normalizer\JsonSubrequestDenormalizer;
use Drupal\subrequests\Subrequest;
use Drupal\Tests\UnitTestCase;
@@ -42,7 +43,7 @@ class JsonSubrequestDenormalizerTest extends UnitTestCase {
$request->setSession(new Session());
$actual = $this->sut->denormalize($data, $class, NULL, ['master_request' => $request]);
$this->assertSame('POST', $actual->getMethod());
- $this->assertEquals(['bar' => 'foo'], $actual->getContent());
+ $this->assertEquals(['bar' => 'foo'], Json::decode($actual->getContent()));
$this->assertSame('<oof>', $actual->headers->get('Content-ID'));
$this->assertSame('lorem', $actual->headers->get('PHP_AUTH_USER'));
$this->assertSame('ipsum', $actual->headers->get('PHP_AUTH_PW'));
diff --git a/tests/src/Unit/Normalizer/MultiresponseJsonNormalizerTest.php b/tests/src/Unit/Normalizer/MultiresponseJsonNormalizerTest.php
new file mode 100644
index 0000000..80110c8
--- /dev/null
+++ b/tests/src/Unit/Normalizer/MultiresponseJsonNormalizerTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\Tests\subrequests\Normalizer;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\subrequests\Normalizer\MultiresponseJsonNormalizer;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * @coversDefaultClass \Drupal\subrequests\Normalizer\MultiresponseJsonNormalizer
+ * @group subrequests
+ */
+class MultiresponseJsonNormalizerTest extends UnitTestCase {
+
+ /**
+ * @var \Drupal\subrequests\Normalizer\MultiresponseJsonNormalizer
+ */
+ protected $sut;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->sut = new MultiresponseJsonNormalizer();
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsNormalization
+ * @covers ::supportsNormalization
+ */
+ public function testSupportsNormalization($data, $format, $is_supported) {
+ $actual = $this->sut->supportsNormalization($data, $format);
+ $this->assertSame($is_supported, $actual);
+ }
+
+ public function dataProviderSupportsNormalization() {
+ return [
+ [[Response::create('')], 'json', TRUE],
+ [[], 'json', TRUE],
+ [[Response::create('')], 'fail', FALSE],
+ [NULL, 'json', FALSE],
+ [[Response::create(''), NULL], 'json', FALSE],
+ ];
+ }
+
+ /**
+ * @covers ::normalize
+ */
+ public function testNormalize() {
+ $sub_content_type = $this->getRandomGenerator()->string();
+ $data = [
+ Response::create('Foo!', 200, ['Content-ID' => '<f>']),
+ Response::create('Bar', 200, ['Content-ID' => '<b>']),
+ ];
+ $actual = $this->sut->normalize($data, NULL, ['sub-content-type' => $sub_content_type]);
+ $this->assertSame(
+ 'application/json; type=' . $sub_content_type,
+ $actual['headers']['Content-Type']
+ );
+ $parsed = Json::decode($actual['content']);
+ $this->assertSame('Foo!', $parsed['f']['body']);
+ $this->assertSame('Bar', $parsed['b']['body']);
+ }
+
+}
diff --git a/tests/src/Unit/Normalizer/MultiresponseNormalizerTest.php b/tests/src/Unit/Normalizer/MultiresponseNormalizerTest.php
index 46c6e21..36e81c6 100644
--- a/tests/src/Unit/Normalizer/MultiresponseNormalizerTest.php
+++ b/tests/src/Unit/Normalizer/MultiresponseNormalizerTest.php
@@ -45,13 +45,17 @@ class MultiresponseNormalizerTest extends UnitTestCase {
* @covers ::normalize
*/
public function testNormalize() {
- $delimiter = $this->getRandomGenerator()->string();
+ $sub_content_type = $this->getRandomGenerator()->string();
$data = [Response::create('Foo!'), Response::create('Bar')];
- $actual = $this->sut->normalize($data, NULL, ['delimiter' => $delimiter]);
- $this->assertStringStartsWith('--' . $delimiter, $actual);
- $this->assertStringEndsWith('--' . $delimiter . '--', $actual);
- $this->assertRegExp("/\r\nFoo!\r\n/", $actual);
- $this->assertRegExp("/\r\nBar\r\n/", $actual);
+ $actual = $this->sut->normalize($data, NULL, ['sub-content-type' => $sub_content_type]);
+ $parts = explode(', ', $actual['headers']['Content-Type']);
+ $parts = explode('; ', $parts[0]);
+ parse_str($parts[1], $parts);
+ $delimiter = substr($parts['boundary'], 1, strlen($parts['boundary']) - 2);
+ $this->assertStringStartsWith('--' . $delimiter, $actual['content']);
+ $this->assertStringEndsWith('--' . $delimiter . '--', $actual['content']);
+ $this->assertRegExp("/\r\nFoo!\r\n/", $actual['content']);
+ $this->assertRegExp("/\r\nBar\r\n/", $actual['content']);
}
}