diff --git a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderRedirect.php b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderRedirect.php index cad408b5e..3a32edbc9 100644 --- a/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderRedirect.php +++ b/modules/thunder_gqls/src/Plugin/GraphQL/DataProducer/ThunderRedirect.php @@ -3,9 +3,13 @@ namespace Drupal\thunder_gqls\Plugin\GraphQL\DataProducer; use Drupal\Core\Cache\RefinableCacheableDependencyInterface; +use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Path\PathValidatorInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Render\RenderContext; +use Drupal\Core\Render\RendererInterface; +use Drupal\Core\Url; use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase; use Drupal\redirect\Entity\Redirect; use Drupal\redirect\RedirectRepository; @@ -32,27 +36,6 @@ */ class ThunderRedirect extends DataProducerPluginBase implements ContainerFactoryPluginInterface { - /** - * Optional redirect module repository. - * - * @var \Drupal\redirect\RedirectRepository|null - */ - protected $redirectRepository; - - /** - * The language manager. - * - * @var \Drupal\Core\Language\LanguageManagerInterface - */ - protected $languageManager; - - /** - * The path validator. - * - * @var \Drupal\Core\Path\PathValidatorInterface - */ - protected $pathValidator; - /** * {@inheritdoc} * @@ -65,7 +48,9 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('language_manager'), $container->get('path.validator'), - $container->get('redirect.repository', ContainerInterface::NULL_ON_INVALID_REFERENCE) + $container->get('renderer'), + $container->get('config.factory'), + $container->get('redirect.repository', ContainerInterface::NULL_ON_INVALID_REFERENCE), ); } @@ -82,6 +67,10 @@ public static function create(ContainerInterface $container, array $configuratio * The language manager. * @param \Drupal\Core\Path\PathValidatorInterface $pathValidator * The path validator. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer service. + * @param \Drupal\Core\Config\ConfigFactory $config + * The config. * @param \Drupal\redirect\RedirectRepository|null $redirectRepository * The redirect repository. * @@ -91,14 +80,13 @@ public function __construct( array $configuration, $pluginId, $pluginDefinition, - LanguageManagerInterface $languageManager, - PathValidatorInterface $pathValidator, - ?RedirectRepository $redirectRepository = NULL, + protected LanguageManagerInterface $languageManager, + protected PathValidatorInterface $pathValidator, + protected RendererInterface $renderer, + protected ConfigFactory $config, + protected ?RedirectRepository $redirectRepository = NULL, ) { parent::__construct($configuration, $pluginId, $pluginDefinition); - $this->languageManager = $languageManager; - $this->pathValidator = $pathValidator; - $this->redirectRepository = $redirectRepository; } /** @@ -116,6 +104,9 @@ public function resolve(string $path, RefinableCacheableDependencyInterface $met $metadata->addCacheTags(['redirect_list']); if ($this->redirectRepository) { + $redirectConfig = $this->config->get('redirect.settings'); + $metadata->addCacheTags($redirectConfig->getCacheTags()); + $queryString = parse_url($path, PHP_URL_QUERY) ?: ''; $pathWithoutQuery = parse_url($path, PHP_URL_PATH) ?: $path; @@ -141,6 +132,30 @@ public function resolve(string $path, RefinableCacheableDependencyInterface $met 'status' => $redirect->getStatusCode(), ]; } + + if ($redirectConfig->get('route_normalizer_enabled')) { + // Ensure the path starts with a slash, fromUserInput fails otherwise. + if (!str_starts_with($path, '/')) { + $aliasPath = '/' . $path; + } + else { + $aliasPath = $path; + } + $context = new RenderContext(); + $alias = $this->renderer->executeInRenderContext($context, function () use ($aliasPath): string { + return Url::fromUserInput($aliasPath)->toString(); + }); + if (!$context->isEmpty()) { + $metadata->addCacheableDependency($context->pop()); + } + + if ($alias !== $path) { + return [ + 'url' => $alias, + 'status' => $redirectConfig->get('default_status_code'), + ]; + } + } } if (($url = $this->pathValidator->getUrlIfValidWithoutAccessCheck($path)) && $url->isRouted()) { diff --git a/modules/thunder_gqls/tests/src/Functional/RedirectSchemaTest.php b/modules/thunder_gqls/tests/src/Functional/RedirectSchemaTest.php index d26c16a5a..39239358e 100644 --- a/modules/thunder_gqls/tests/src/Functional/RedirectSchemaTest.php +++ b/modules/thunder_gqls/tests/src/Functional/RedirectSchemaTest.php @@ -18,6 +18,13 @@ class RedirectSchemaTest extends ThunderGqlsTestBase { */ protected $unpublishedEntity; + /** + * The redirect query. + * + * @var string + */ + protected $query; + /** * {@inheritdoc} */ @@ -26,6 +33,48 @@ protected function setUp(): void { $this->unpublishedEntity = $this->loadNodeByUuid('94ad928b-3ec8-4bcb-b617-ab1607bf69cb'); $this->unpublishedEntity->set('moderation_state', 'unpublished')->save(); + $this->query = $this->getQueryFromFile('redirect'); + } + + /** + * Test redirect to alias, depending on redirect settings. + */ + public function testAlias(): void { + $path = '/node/' . $this->loadNodeByUuid('36b2e2b2-3df0-43eb-a282-d792b0999c07')->id(); + $variables = Json::encode(['path' => $path]); + + $this->config('redirect.settings') + ->set('route_normalizer_enabled', TRUE) + ->save(); + + $response = $this->query($this->query, $variables); + $this->assertEquals(200, $response->getStatusCode(), 'Response not 200'); + + $redirectResponseData = Json::decode($response->getBody())['data']['redirect']; + $expectedResponse = [ + 'url' => '/come-drupalcon-new-orleans', + 'status' => 301, + ]; + + $this->assertEqualsCanonicalizing($expectedResponse, $redirectResponseData, 'Not redirected to alias'); + + $this->config('redirect.settings') + ->set('route_normalizer_enabled', FALSE) + ->save(); + + // Rebuild caches. + $this->container->get('cache.graphql.results')->deleteAll(); + + $response = $this->query($this->query, $variables); + $this->assertEquals(200, $response->getStatusCode(), 'Response not 200'); + + $redirectResponseData = Json::decode($response->getBody())['data']['redirect']; + $expectedResponse = [ + 'url' => $path, + 'status' => 200, + ]; + + $this->assertEqualsCanonicalizing($expectedResponse, $redirectResponseData, 'False redirect to alias'); } /** @@ -39,9 +88,7 @@ public function testRedirect(): void { foreach ($testCases as $description => $testCase) { [$variables, $expectedResponse] = $testCase; - $query = $this->getQueryFromFile('redirect'); - - $response = $this->query($query, Json::encode($variables)); + $response = $this->query($this->query, Json::encode($variables)); $this->assertEquals(200, $response->getStatusCode(), 'Response not 200'); $redirectResponseData = Json::decode($response->getBody())['data']['redirect']; diff --git a/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php b/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php index 422909e1e..920302497 100644 --- a/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php +++ b/modules/thunder_gqls/tests/src/Kernel/DataProducer/ThunderRedirectTest.php @@ -50,6 +50,7 @@ public function setUp(): void { $this->node = Node::create([ 'title' => 'Title', 'type' => 'article', + 'path' => ['alias' => '/article'], ]); $this->node->save(); @@ -60,27 +61,28 @@ public function setUp(): void { * Test simple redirect and redirect with query string. */ public function testRedirect(): void { - $redirectPath = 'redirect-test-path'; + $redirectSource = 'redirect-test-source'; + $redirectDestination = '/redirect-test-destination'; /** @var \Drupal\redirect\Entity\Redirect $redirect */ $redirect = $this->storage->create(); - $redirect->setSource($redirectPath); - $redirect->setRedirect('node/' . $this->node->id()); + $redirect->setSource($redirectSource); + $redirect->setRedirect($redirectDestination); $redirect->setStatusCode(301); $redirect->save(); $result = $this->executeDataProducer('thunder_redirect', [ - 'path' => $redirectPath, + 'path' => $redirectSource, ]); - $this->assertEquals('/node/1', $result['url']); + $this->assertEquals($redirectDestination, $result['url']); $this->assertEquals('301', $result['status']); $result = $this->executeDataProducer('thunder_redirect', [ - 'path' => $redirectPath . '?test=1', + 'path' => $redirectSource . '?test=1', ]); - $this->assertEquals('/node/1?test=1', $result['url']); + $this->assertEquals($redirectDestination . '?test=1', $result['url']); $this->assertEquals('301', $result['status']); }