From a03f2312a0dffbf48ac76362489b0cca444713f2 Mon Sep 17 00:00:00 2001 From: Jonathan Minder Date: Wed, 9 Nov 2022 15:35:33 +0100 Subject: [PATCH 1/5] Add missing SubRequestBuffer Class. --- .../SubrequestExtractionController.php | 18 ++- src/GraphQL/Buffers/SubRequestBuffer.php | 150 ++++++++++++++++++ 2 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/GraphQL/Buffers/SubRequestBuffer.php diff --git a/src/Controller/SubrequestExtractionController.php b/src/Controller/SubrequestExtractionController.php index 68f223938..8f7560e64 100644 --- a/src/Controller/SubrequestExtractionController.php +++ b/src/Controller/SubrequestExtractionController.php @@ -2,9 +2,9 @@ namespace Drupal\graphql\Controller; -use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\RendererInterface; use Drupal\graphql\SubRequestResponse; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -70,9 +70,19 @@ public function extract() { $request = $this->requestStack->getCurrentRequest(); $callback = $request->attributes->get('_graphql_subrequest'); - $metadata = new CacheableMetadata(); - $response = new SubRequestResponse($callback($metadata)); - $response->addCacheableDependency($metadata); + // TODO: Remove this once https://www.drupal.org/project/drupal/issues/2940036#comment-12479912 is resolved. + $this->languageManager->reset(); + + // Collect any potentially leaked cache metadata released by the callback. + $context = new RenderContext(); + $result = $this->renderer->executeInRenderContext($context, function () use ($callback) { + return $callback(); + }); + + $response = new SubRequestResponse($result); + if (!$context->isEmpty()) { + $response->addCacheableDependency($context->pop()); + } return $response; } diff --git a/src/GraphQL/Buffers/SubRequestBuffer.php b/src/GraphQL/Buffers/SubRequestBuffer.php new file mode 100644 index 000000000..9c1033217 --- /dev/null +++ b/src/GraphQL/Buffers/SubRequestBuffer.php @@ -0,0 +1,150 @@ +httpKernel = $httpKernel; + $this->requestStack = $requestStack; + } + + /** + * Add an item to the buffer. + * + * @param \Drupal\Core\Url $url + * The url object to run the subrequest on. + * @param callable $extract + * The callback to run within the sub-request. + * + * @return \Closure + * The callback to invoke to load the result for this buffer item. + */ + public function add(Url $url, callable $extract) { + $item = new \ArrayObject([ + 'url' => $url, + 'extract' => $extract, + ]); + + return $this->createBufferResolver($item); + } + + /** + * {@inheritdoc} + */ + protected function getBufferId($item) { + /** @var \Drupal\Core\GeneratedUrl $url */ + $url = $item['url']->toString(TRUE); + + return hash('sha256', json_encode([ + 'url' => $url->getGeneratedUrl(), + 'tags' => $url->getCacheTags(), + 'contexts' => $url->getCacheContexts(), + 'age' => $url->getCacheMaxAge(), + ])); + } + + /** + * Create a sub-request for the given url. + * + * @param \Symfony\Component\HttpFoundation\Request $current + * The current main request. + * @param string $url + * The url to run the subrequest on. + * @param array $buffer + * The buffer. + * + * @return \Symfony\Component\HttpFoundation\Request + * The request object. + */ + protected function createRequest(Request $current, array $buffer, $url) { + $request = Request::create( + $url, + 'GET', + $current->query->all(), + $current->cookies->all(), + $current->files->all(), + $current->server->all() + ); + + $request->attributes->set('_graphql_subrequest', function () use ($buffer) { + return array_map(function ($item) { + return $item['extract']($item['url']); + }, $buffer); + }); + + if ($session = $current->getSession()) { + $request->setSession($session); + } + + return $request; + } + + /** + * {@inheritdoc} + */ + public function resolveBufferArray(array $buffer) { + /** @var \Drupal\Core\GeneratedUrl $url */ + $url = reset($buffer)['url']->toString(TRUE); + + $current = $this->requestStack->getCurrentRequest(); + $target = $url->getGeneratedUrl(); + $request = $this->createRequest($current, $buffer, $target); + + /** @var \Drupal\graphql\SubRequestResponse $response */ + $response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST); + while ($response instanceof LocalRedirectResponse) { + $target = $response->getTargetUrl(); + $request = $this->createRequest($current, $buffer, $target); + $response = $this->httpKernel->handle($request, HttpKernelInterface::SUB_REQUEST); + } + + if (!($response instanceof SubRequestResponse)) { + return array_fill_keys(array_keys($buffer), NULL); + } + + // TODO: + // Remove the request stack manipulation once the core issue described at + // https://www.drupal.org/node/2613044 is resolved. + while ($this->requestStack->getCurrentRequest() !== $current) { + $this->requestStack->pop(); + } + + if ($url instanceof CacheableDependencyInterface) { + $response->addCacheableDependency($url); + } + + return $response->getResult(); + } + +} From 3c15b620d0a19bbf45075bfb987f381fe61bb225 Mon Sep 17 00:00:00 2001 From: Jonathan Minder Date: Wed, 9 Nov 2022 15:44:11 +0100 Subject: [PATCH 2/5] Code style --- src/Controller/SubrequestExtractionController.php | 2 +- src/GraphQL/Buffers/SubRequestBuffer.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Controller/SubrequestExtractionController.php b/src/Controller/SubrequestExtractionController.php index 8f7560e64..ab70fe2e9 100644 --- a/src/Controller/SubrequestExtractionController.php +++ b/src/Controller/SubrequestExtractionController.php @@ -70,7 +70,7 @@ public function extract() { $request = $this->requestStack->getCurrentRequest(); $callback = $request->attributes->get('_graphql_subrequest'); - // TODO: Remove this once https://www.drupal.org/project/drupal/issues/2940036#comment-12479912 is resolved. + // @todo Remove this once https://www.drupal.org/project/drupal/issues/2940036#comment-12479912 is resolved. $this->languageManager->reset(); // Collect any potentially leaked cache metadata released by the callback. diff --git a/src/GraphQL/Buffers/SubRequestBuffer.php b/src/GraphQL/Buffers/SubRequestBuffer.php index 9c1033217..6aef79ce9 100644 --- a/src/GraphQL/Buffers/SubRequestBuffer.php +++ b/src/GraphQL/Buffers/SubRequestBuffer.php @@ -10,6 +10,9 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\HttpKernelInterface; +/** + * SubRequestBuffer class + */ class SubRequestBuffer extends BufferBase { /** @@ -133,7 +136,7 @@ public function resolveBufferArray(array $buffer) { return array_fill_keys(array_keys($buffer), NULL); } - // TODO: + // @todo // Remove the request stack manipulation once the core issue described at // https://www.drupal.org/node/2613044 is resolved. while ($this->requestStack->getCurrentRequest() !== $current) { From 850313baf1aad5316b74c8f1552a555cac5eb81c Mon Sep 17 00:00:00 2001 From: Jonathan Minder Date: Wed, 9 Nov 2022 16:05:08 +0100 Subject: [PATCH 3/5] More PHPCS fixes. --- src/GraphQL/Buffers/SubRequestBuffer.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/GraphQL/Buffers/SubRequestBuffer.php b/src/GraphQL/Buffers/SubRequestBuffer.php index 6aef79ce9..c29c1b43e 100644 --- a/src/GraphQL/Buffers/SubRequestBuffer.php +++ b/src/GraphQL/Buffers/SubRequestBuffer.php @@ -11,7 +11,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; /** - * SubRequestBuffer class + * SubRequestBuffer class provided a buffer for sub-requests. */ class SubRequestBuffer extends BufferBase { @@ -82,15 +82,15 @@ protected function getBufferId($item) { * * @param \Symfony\Component\HttpFoundation\Request $current * The current main request. - * @param string $url - * The url to run the subrequest on. * @param array $buffer * The buffer. + * @param string $url + * The url to run the subrequest on. * * @return \Symfony\Component\HttpFoundation\Request * The request object. */ - protected function createRequest(Request $current, array $buffer, $url) { + protected function createRequest(Request $current, array $buffer, string $url) { $request = Request::create( $url, 'GET', @@ -136,8 +136,7 @@ public function resolveBufferArray(array $buffer) { return array_fill_keys(array_keys($buffer), NULL); } - // @todo - // Remove the request stack manipulation once the core issue described at + // @todo Remove the request stack manipulation once the core issue described at // https://www.drupal.org/node/2613044 is resolved. while ($this->requestStack->getCurrentRequest() !== $current) { $this->requestStack->pop(); From fd9a040b78e71ae155afad79e4985a77bb7c8d83 Mon Sep 17 00:00:00 2001 From: Pieter Frenssen Date: Mon, 11 Sep 2023 11:54:18 +0300 Subject: [PATCH 4/5] Use an arrow function. --- src/Controller/SubrequestExtractionController.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Controller/SubrequestExtractionController.php b/src/Controller/SubrequestExtractionController.php index ab70fe2e9..e24fab911 100644 --- a/src/Controller/SubrequestExtractionController.php +++ b/src/Controller/SubrequestExtractionController.php @@ -73,13 +73,11 @@ public function extract() { // @todo Remove this once https://www.drupal.org/project/drupal/issues/2940036#comment-12479912 is resolved. $this->languageManager->reset(); - // Collect any potentially leaked cache metadata released by the callback. + // Collect the cache metadata generated by the callback. $context = new RenderContext(); - $result = $this->renderer->executeInRenderContext($context, function () use ($callback) { - return $callback(); - }); + $sub_request = $this->renderer->executeInRenderContext($context, fn() => $callback()); - $response = new SubRequestResponse($result); + $response = new SubRequestResponse($sub_request); if (!$context->isEmpty()) { $response->addCacheableDependency($context->pop()); } From db692ecb0da8a34bda1aeaa0850fb283eacea478 Mon Sep 17 00:00:00 2001 From: Pieter Frenssen Date: Mon, 11 Sep 2023 12:25:01 +0300 Subject: [PATCH 5/5] Fix PHPStan warning. --- src/GraphQL/Buffers/SubRequestBuffer.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/GraphQL/Buffers/SubRequestBuffer.php b/src/GraphQL/Buffers/SubRequestBuffer.php index c29c1b43e..468d68b52 100644 --- a/src/GraphQL/Buffers/SubRequestBuffer.php +++ b/src/GraphQL/Buffers/SubRequestBuffer.php @@ -6,6 +6,7 @@ use Drupal\Core\Url; use Drupal\Core\Routing\LocalRedirectResponse; use Drupal\graphql\SubRequestResponse; +use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -106,8 +107,11 @@ protected function createRequest(Request $current, array $buffer, string $url) { }, $buffer); }); - if ($session = $current->getSession()) { - $request->setSession($session); + // Pass the current session to the sub request if it exists. + try { + $request->setSession($current->getSession()); + } + catch (SessionNotFoundException) { } return $request;