mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-22 08:56:42 -06:00
Create component to resolve the long URL to redirect to for a short URL
This commit is contained in:
parent
68b77e22c5
commit
09e81b00c5
@ -32,6 +32,8 @@ return [
|
||||
Options\QrCodeOptions::class => [ValinorConfigFactory::class, 'config.qr_codes'],
|
||||
Options\RabbitMqOptions::class => [ValinorConfigFactory::class, 'config.rabbitmq'],
|
||||
|
||||
RedirectRule\ShortUrlRedirectionResolver::class => ConfigAbstractFactory::class,
|
||||
|
||||
ShortUrl\UrlShortener::class => ConfigAbstractFactory::class,
|
||||
ShortUrl\ShortUrlService::class => ConfigAbstractFactory::class,
|
||||
ShortUrl\ShortUrlListService::class => ConfigAbstractFactory::class,
|
||||
@ -156,6 +158,7 @@ return [
|
||||
Util\RedirectResponseHelper::class => [Options\RedirectOptions::class],
|
||||
|
||||
Config\NotFoundRedirectResolver::class => [Util\RedirectResponseHelper::class, 'Logger_Shlink'],
|
||||
RedirectRule\ShortUrlRedirectionResolver::class => ['em'],
|
||||
|
||||
Action\RedirectAction::class => [
|
||||
ShortUrl\ShortUrlResolver::class,
|
||||
@ -179,7 +182,10 @@ return [
|
||||
],
|
||||
ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain', 'config.router.base_path'],
|
||||
ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => ['httpClient', Options\UrlShortenerOptions::class],
|
||||
ShortUrl\Helper\ShortUrlRedirectionBuilder::class => [Options\TrackingOptions::class],
|
||||
ShortUrl\Helper\ShortUrlRedirectionBuilder::class => [
|
||||
Options\TrackingOptions::class,
|
||||
RedirectRule\ShortUrlRedirectionResolver::class,
|
||||
],
|
||||
ShortUrl\Transformer\ShortUrlDataTransformer::class => [ShortUrl\Helper\ShortUrlStringifier::class],
|
||||
ShortUrl\Middleware\ExtraPathRedirectMiddleware::class => [
|
||||
ShortUrl\ShortUrlResolver::class,
|
||||
|
23
module/Core/src/RedirectRule/ShortUrlRedirectionResolver.php
Normal file
23
module/Core/src/RedirectRule/ShortUrlRedirectionResolver.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
readonly class ShortUrlRedirectionResolver implements ShortUrlRedirectionResolverInterface
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $em)
|
||||
{
|
||||
}
|
||||
|
||||
public function resolveLongUrl(ShortUrl $shortUrl, ServerRequestInterface $request): string
|
||||
{
|
||||
// TODO Resolve rules and check if any of them matches
|
||||
|
||||
$device = DeviceType::matchFromUserAgent($request->getHeaderLine('User-Agent'));
|
||||
return $shortUrl->longUrlForDevice($device);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
interface ShortUrlRedirectionResolverInterface
|
||||
{
|
||||
public function resolveLongUrl(ShortUrl $shortUrl, ServerRequestInterface $request): string;
|
||||
}
|
@ -8,16 +8,18 @@ use GuzzleHttp\Psr7\Query;
|
||||
use Laminas\Diactoros\Uri;
|
||||
use Laminas\Stdlib\ArrayUtils;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\Options\TrackingOptions;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface
|
||||
readonly class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface
|
||||
{
|
||||
public function __construct(private readonly TrackingOptions $trackingOptions)
|
||||
{
|
||||
public function __construct(
|
||||
private TrackingOptions $trackingOptions,
|
||||
private ShortUrlRedirectionResolverInterface $redirectionResolver,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildShortUrlRedirect(
|
||||
@ -25,9 +27,8 @@ class ShortUrlRedirectionBuilder implements ShortUrlRedirectionBuilderInterface
|
||||
ServerRequestInterface $request,
|
||||
?string $extraPath = null,
|
||||
): string {
|
||||
$uri = new Uri($this->redirectionResolver->resolveLongUrl($shortUrl, $request));
|
||||
$currentQuery = $request->getQueryParams();
|
||||
$device = DeviceType::matchFromUserAgent($request->getHeaderLine('User-Agent'));
|
||||
$uri = new Uri($shortUrl->longUrlForDevice($device));
|
||||
$shouldForwardQuery = $shortUrl->forwardQuery();
|
||||
|
||||
return $uri
|
||||
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace RedirectRule;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolver;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
|
||||
use const ShlinkioTest\Shlink\ANDROID_USER_AGENT;
|
||||
use const ShlinkioTest\Shlink\DESKTOP_USER_AGENT;
|
||||
use const ShlinkioTest\Shlink\IOS_USER_AGENT;
|
||||
|
||||
class ShortUrlRedirectionResolverTest extends TestCase
|
||||
{
|
||||
private ShortUrlRedirectionResolver $resolver;
|
||||
private EntityManagerInterface & MockObject $em;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->em = $this->createMock(EntityManagerInterface::class);
|
||||
$this->resolver = new ShortUrlRedirectionResolver($this->em);
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideData')]
|
||||
public function resolveLongUrlReturnsExpectedValue(ServerRequestInterface $request, string $expectedUrl): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'https://example.com/foo/bar',
|
||||
'deviceLongUrls' => [
|
||||
DeviceType::ANDROID->value => 'https://example.com/android',
|
||||
DeviceType::IOS->value => 'https://example.com/ios',
|
||||
],
|
||||
]));
|
||||
|
||||
$result = $this->resolver->resolveLongUrl($shortUrl, $request);
|
||||
|
||||
self::assertEquals($expectedUrl, $result);
|
||||
}
|
||||
|
||||
public static function provideData(): iterable
|
||||
{
|
||||
$request = static fn (string $userAgent = '') => ServerRequestFactory::fromGlobals()->withHeader(
|
||||
'User-Agent',
|
||||
$userAgent,
|
||||
);
|
||||
|
||||
yield 'unknown user agent' => [$request('Unknown'), 'https://example.com/foo/bar'];
|
||||
yield 'desktop user agent' => [$request(DESKTOP_USER_AGENT), 'https://example.com/foo/bar'];
|
||||
yield 'android user agent' => [$request(ANDROID_USER_AGENT), 'https://example.com/android'];
|
||||
yield 'ios user agent' => [$request(IOS_USER_AGENT), 'https://example.com/ios'];
|
||||
}
|
||||
}
|
@ -7,26 +7,26 @@ namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\Options\TrackingOptions;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilder;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
|
||||
use const ShlinkioTest\Shlink\ANDROID_USER_AGENT;
|
||||
use const ShlinkioTest\Shlink\DESKTOP_USER_AGENT;
|
||||
use const ShlinkioTest\Shlink\IOS_USER_AGENT;
|
||||
|
||||
class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
{
|
||||
private ShortUrlRedirectionBuilder $redirectionBuilder;
|
||||
private ShortUrlRedirectionResolverInterface & MockObject $redirectionResolver;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$trackingOptions = new TrackingOptions(disableTrackParam: 'foobar');
|
||||
$this->redirectionBuilder = new ShortUrlRedirectionBuilder($trackingOptions);
|
||||
$this->redirectionResolver = $this->createMock(ShortUrlRedirectionResolverInterface::class);
|
||||
|
||||
$this->redirectionBuilder = new ShortUrlRedirectionBuilder($trackingOptions, $this->redirectionResolver);
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideData')]
|
||||
@ -39,11 +39,12 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'https://domain.com/foo/bar?some=thing',
|
||||
'forwardQuery' => $forwardQuery,
|
||||
'deviceLongUrls' => [
|
||||
DeviceType::ANDROID->value => 'https://domain.com/android',
|
||||
DeviceType::IOS->value => 'https://domain.com/ios',
|
||||
],
|
||||
]));
|
||||
$this->redirectionResolver->expects($this->once())->method('resolveLongUrl')->with(
|
||||
$shortUrl,
|
||||
$request,
|
||||
)->willReturn($shortUrl->getLongUrl());
|
||||
|
||||
$result = $this->redirectionBuilder->buildShortUrlRedirect($shortUrl, $request, $extraPath);
|
||||
|
||||
self::assertEquals($expectedUrl, $result);
|
||||
@ -72,7 +73,7 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
];
|
||||
yield [
|
||||
'https://domain.com/foo/bar?some=overwritten',
|
||||
$request(['foobar' => 'notrack', 'some' => 'overwritten'])->withHeader('User-Agent', 'Unknown'),
|
||||
$request(['foobar' => 'notrack', 'some' => 'overwritten']),
|
||||
null,
|
||||
true,
|
||||
];
|
||||
@ -91,7 +92,7 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
yield ['https://domain.com/foo/bar/something/else-baz?some=thing', $request(), '/something/else-baz', true];
|
||||
yield [
|
||||
'https://domain.com/foo/bar/something/else-baz?some=thing&hello=world',
|
||||
$request(['hello' => 'world'])->withHeader('User-Agent', DESKTOP_USER_AGENT),
|
||||
$request(['hello' => 'world']),
|
||||
'/something/else-baz',
|
||||
true,
|
||||
];
|
||||
@ -107,17 +108,5 @@ class ShortUrlRedirectionBuilderTest extends TestCase
|
||||
'/something/else-baz',
|
||||
false,
|
||||
];
|
||||
yield [
|
||||
'https://domain.com/android/something',
|
||||
$request(['foo' => 'bar'])->withHeader('User-Agent', ANDROID_USER_AGENT),
|
||||
'/something',
|
||||
false,
|
||||
];
|
||||
yield [
|
||||
'https://domain.com/ios?foo=bar',
|
||||
$request(['foo' => 'bar'])->withHeader('User-Agent', IOS_USER_AGENT),
|
||||
null,
|
||||
null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user