Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL: Fix alias redirects. #795

Open
wants to merge 25 commits into
base: 7.3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}
*
Expand All @@ -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),
);
}

Expand All @@ -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.
*
Expand All @@ -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;
}

/**
Expand All @@ -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;

Expand All @@ -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()) {
Expand Down
53 changes: 50 additions & 3 deletions modules/thunder_gqls/tests/src/Functional/RedirectSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class RedirectSchemaTest extends ThunderGqlsTestBase {
*/
protected $unpublishedEntity;

/**
* The redirect query.
*
* @var string
*/
protected $query;

/**
* {@inheritdoc}
*/
Expand All @@ -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');
}

/**
Expand All @@ -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'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function setUp(): void {
$this->node = Node::create([
'title' => 'Title',
'type' => 'article',
'path' => ['alias' => '/article'],
]);

$this->node->save();
Expand All @@ -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']);
}

Expand Down
Loading