Made visits not to be tracked if query param has been provided

This commit is contained in:
Alejandro Celaya 2018-01-14 09:24:33 +01:00
parent 5fd34e03fc
commit 1e79969c3b
3 changed files with 60 additions and 7 deletions

View File

@ -55,7 +55,11 @@ return [
Service\Tag\TagService::class => ['em'], Service\Tag\TagService::class => ['em'],
// Middleware // Middleware
Action\RedirectAction::class => [Service\UrlShortener::class, Service\VisitsTracker::class], Action\RedirectAction::class => [
Service\UrlShortener::class,
Service\VisitsTracker::class,
Options\AppOptions::class,
],
Action\QrCodeAction::class => [RouterInterface::class, Service\UrlShortener::class, 'Logger_Shlink'], Action\QrCodeAction::class => [RouterInterface::class, Service\UrlShortener::class, 'Logger_Shlink'],
Action\PreviewAction::class => [PreviewGenerator::class, Service\UrlShortener::class], Action\PreviewAction::class => [PreviewGenerator::class, Service\UrlShortener::class],
Middleware\QrCodeCacheMiddleware::class => [Cache::class], Middleware\QrCodeCacheMiddleware::class => [Cache::class],

View File

@ -10,6 +10,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Core\Action\Util\ErrorResponseBuilderTrait; use Shlinkio\Shlink\Core\Action\Util\ErrorResponseBuilderTrait;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Options\AppOptions;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Zend\Diactoros\Response\RedirectResponse; use Zend\Diactoros\Response\RedirectResponse;
@ -26,11 +27,19 @@ class RedirectAction implements MiddlewareInterface
* @var VisitsTrackerInterface * @var VisitsTrackerInterface
*/ */
private $visitTracker; private $visitTracker;
/**
* @var AppOptions
*/
private $appOptions;
public function __construct(UrlShortenerInterface $urlShortener, VisitsTrackerInterface $visitTracker) public function __construct(
{ UrlShortenerInterface $urlShortener,
VisitsTrackerInterface $visitTracker,
AppOptions $appOptions
) {
$this->urlShortener = $urlShortener; $this->urlShortener = $urlShortener;
$this->visitTracker = $visitTracker; $this->visitTracker = $visitTracker;
$this->appOptions = $appOptions;
} }
/** /**
@ -45,12 +54,16 @@ class RedirectAction implements MiddlewareInterface
public function process(Request $request, DelegateInterface $delegate) public function process(Request $request, DelegateInterface $delegate)
{ {
$shortCode = $request->getAttribute('shortCode', ''); $shortCode = $request->getAttribute('shortCode', '');
$query = $request->getQueryParams();
$disableTrackParam = $this->appOptions->getDisableTrackParam();
try { try {
$longUrl = $this->urlShortener->shortCodeToUrl($shortCode); $longUrl = $this->urlShortener->shortCodeToUrl($shortCode);
// Track visit to this short code // Track visit to this short code
$this->visitTracker->track($shortCode, $request); if ($disableTrackParam === null || ! \array_key_exists($disableTrackParam, $query)) {
$this->visitTracker->track($shortCode, $request);
}
// Return a redirect response to the long URL. // Return a redirect response to the long URL.
// Use a temporary redirect to make sure browsers always hit the server for analytics purposes // Use a temporary redirect to make sure browsers always hit the server for analytics purposes

View File

@ -10,6 +10,7 @@ use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Action\RedirectAction;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Options\AppOptions;
use Shlinkio\Shlink\Core\Service\UrlShortener; use Shlinkio\Shlink\Core\Service\UrlShortener;
use Shlinkio\Shlink\Core\Service\VisitsTracker; use Shlinkio\Shlink\Core\Service\VisitsTracker;
use ShlinkioTest\Shlink\Common\Util\TestUtils; use ShlinkioTest\Shlink\Common\Util\TestUtils;
@ -26,13 +27,21 @@ class RedirectActionTest extends TestCase
* @var ObjectProphecy * @var ObjectProphecy
*/ */
protected $urlShortener; protected $urlShortener;
/**
* @var ObjectProphecy
*/
protected $visitTracker;
public function setUp() public function setUp()
{ {
$this->urlShortener = $this->prophesize(UrlShortener::class); $this->urlShortener = $this->prophesize(UrlShortener::class);
$visitTracker = $this->prophesize(VisitsTracker::class); $this->visitTracker = $this->prophesize(VisitsTracker::class);
$visitTracker->track(Argument::any());
$this->action = new RedirectAction($this->urlShortener->reveal(), $visitTracker->reveal()); $this->action = new RedirectAction(
$this->urlShortener->reveal(),
$this->visitTracker->reveal(),
new AppOptions(['disableTrackParam' => 'foobar'])
);
} }
/** /**
@ -44,6 +53,8 @@ class RedirectActionTest extends TestCase
$expectedUrl = 'http://domain.com/foo/bar'; $expectedUrl = 'http://domain.com/foo/bar';
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($expectedUrl) $this->urlShortener->shortCodeToUrl($shortCode)->willReturn($expectedUrl)
->shouldBeCalledTimes(1); ->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->willReturn(null)
->shouldBeCalledTimes(1);
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode); $request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);
$response = $this->action->process($request, TestUtils::createDelegateMock()->reveal()); $response = $this->action->process($request, TestUtils::createDelegateMock()->reveal());
@ -62,6 +73,9 @@ class RedirectActionTest extends TestCase
$shortCode = 'abc123'; $shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willThrow(EntityDoesNotExistException::class) $this->urlShortener->shortCodeToUrl($shortCode)->willThrow(EntityDoesNotExistException::class)
->shouldBeCalledTimes(1); ->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->willReturn(null)
->shouldNotBeCalled();
$delegate = $this->prophesize(DelegateInterface::class); $delegate = $this->prophesize(DelegateInterface::class);
/** @var MethodProphecy $process */ /** @var MethodProphecy $process */
$process = $delegate->process(Argument::any())->willReturn(new Response()); $process = $delegate->process(Argument::any())->willReturn(new Response());
@ -71,4 +85,26 @@ class RedirectActionTest extends TestCase
$process->shouldHaveBeenCalledTimes(1); $process->shouldHaveBeenCalledTimes(1);
} }
/**
* @test
*/
public function visitIsNotTrackedIfDisableParamIsProvided()
{
$shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar';
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($expectedUrl)
->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->willReturn(null)
->shouldNotBeCalled();
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode)
->withQueryParams(['foobar' => true]);
$response = $this->action->process($request, TestUtils::createDelegateMock()->reveal());
$this->assertInstanceOf(Response\RedirectResponse::class, $response);
$this->assertEquals(302, $response->getStatusCode());
$this->assertTrue($response->hasHeader('Location'));
$this->assertEquals($expectedUrl, $response->getHeaderLine('Location'));
}
} }