diff --git a/module/Core/src/RedirectRule/Entity/RedirectCondition.php b/module/Core/src/RedirectRule/Entity/RedirectCondition.php index 608acd06..5f3de073 100644 --- a/module/Core/src/RedirectRule/Entity/RedirectCondition.php +++ b/module/Core/src/RedirectRule/Entity/RedirectCondition.php @@ -9,17 +9,34 @@ use Shlinkio\Shlink\Core\RedirectRule\Model\RedirectConditionType; use function explode; use function Shlinkio\Shlink\Core\ArrayUtils\some; use function Shlinkio\Shlink\Core\normalizeLocale; +use function sprintf; class RedirectCondition extends AbstractEntity { - public function __construct( + private function __construct( public readonly string $name, - public readonly RedirectConditionType $type, + private readonly RedirectConditionType $type, public readonly string $matchValue, public readonly ?string $matchKey = null, ) { } + public static function forQueryParam(string $param, string $value): self + { + $type = RedirectConditionType::QUERY_PARAM; + $name = sprintf('%s-%s-%s', $type->value, $param, $value); + + return new self($name, $type, $value, $param); + } + + public static function forLanguage(string $language): self + { + $type = RedirectConditionType::LANGUAGE; + $name = sprintf('%s-%s', $type->value, $language); + + return new self($name, $type, $language); + } + /** * Tells if this condition matches provided request */ @@ -28,23 +45,18 @@ class RedirectCondition extends AbstractEntity return match ($this->type) { RedirectConditionType::QUERY_PARAM => $this->matchesQueryParam($request), RedirectConditionType::LANGUAGE => $this->matchesLanguage($request), - default => false, }; } - public function matchesQueryParam(ServerRequestInterface $request): bool + private function matchesQueryParam(ServerRequestInterface $request): bool { - if ($this->matchKey !== null) { - return false; - } - $query = $request->getQueryParams(); $queryValue = $query[$this->matchKey] ?? null; return $queryValue === $this->matchValue; } - public function matchesLanguage(ServerRequestInterface $request): bool + private function matchesLanguage(ServerRequestInterface $request): bool { $acceptLanguage = $request->getHeaderLine('Accept-Language'); if ($acceptLanguage === '' || $acceptLanguage === '*') { diff --git a/module/Core/test/RedirectRule/Entity/RedirectConditionTest.php b/module/Core/test/RedirectRule/Entity/RedirectConditionTest.php new file mode 100644 index 00000000..c52988dc --- /dev/null +++ b/module/Core/test/RedirectRule/Entity/RedirectConditionTest.php @@ -0,0 +1,59 @@ +withQueryParams(['foo' => 'bar']); + $result = RedirectCondition::forQueryParam($param, $value)->matchesRequest($request); + + self::assertEquals($expectedResult, $result); + } + + #[Test] + #[TestWith([null, '', false])] // no accept language + #[TestWith(['', '', false])] // empty accept language + #[TestWith(['*', '', false])] // wildcard accept language + #[TestWith(['en', 'en', true])] // single language match + #[TestWith(['es, en,fr', 'en', true])] // multiple languages match + #[TestWith(['es_ES', 'es-ES', true])] // single locale match + #[TestWith(['en-UK', 'en-uk', true])] // different casing match + public function matchesLanguage(?string $acceptLanguage, string $value, bool $expected): void + { + $request = ServerRequestFactory::fromGlobals(); + if ($acceptLanguage !== null) { + $request = $request->withHeader('Accept-Language', $acceptLanguage); + } + + $result = RedirectCondition::forLanguage($value)->matchesRequest($request); + + self::assertEquals($expected, $result); + } + + #[Test, DataProvider('provideNames')] + public function generatesExpectedName(RedirectCondition $condition, string $expectedName): void + { + self::assertEquals($expectedName, $condition->name); + } + + public static function provideNames(): iterable + { + yield [RedirectCondition::forLanguage('es-ES'), 'language-es-ES']; + yield [RedirectCondition::forLanguage('en_UK'), 'language-en_UK']; + yield [RedirectCondition::forQueryParam('foo', 'bar'), 'query-foo-bar']; + yield [RedirectCondition::forQueryParam('baz', 'foo'), 'query-baz-foo']; + } +}