Updated UrlShortener so that it does not match a short code which is out of the validity dat erange

This commit is contained in:
Alejandro Celaya 2017-10-21 11:58:20 +02:00
parent 68b4cfbae0
commit a3bbd06fe3
6 changed files with 116 additions and 42 deletions

View file

@ -15,14 +15,20 @@ interface PaginableRepositoryInterface
* @param string|array|null $orderBy
* @return array
*/
public function findList($limit = null, $offset = null, $searchTerm = null, array $tags = [], $orderBy = null);
public function findList(
int $limit = null,
int $offset = null,
string $searchTerm = null,
array $tags = [],
$orderBy = null
): array;
/**
* Counts the number of elements in a list using provided filtering data
*
* @param null $searchTerm
* @param string|null $searchTerm
* @param array $tags
* @return int
*/
public function countList($searchTerm = null, array $tags = []);
public function countList(string $searchTerm = null, array $tags = []): int;
}

View file

@ -54,22 +54,32 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* })
*/
protected $tags;
/**
* @var \DateTime
* @ORM\Column(name="valid_since", type="datetime", nullable=true)
*/
protected $validSince;
/**
* @var \DateTime
* @ORM\Column(name="valid_until", type="datetime", nullable=true)
*/
protected $validUntil;
/**
* ShortUrl constructor.
*/
public function __construct()
{
$this->setDateCreated(new \DateTime());
$this->setVisits(new ArrayCollection());
$this->setShortCode('');
$this->dateCreated = new \DateTime();
$this->visits = new ArrayCollection();
$this->shortCode = '';
$this->tags = new ArrayCollection();
}
/**
* @return string
*/
public function getOriginalUrl()
public function getOriginalUrl(): string
{
return $this->originalUrl;
}
@ -78,7 +88,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* @param string $originalUrl
* @return $this
*/
public function setOriginalUrl($originalUrl)
public function setOriginalUrl(string $originalUrl)
{
$this->originalUrl = (string) $originalUrl;
return $this;
@ -87,7 +97,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
/**
* @return string
*/
public function getShortCode()
public function getShortCode(): string
{
return $this->shortCode;
}
@ -96,7 +106,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* @param string $shortCode
* @return $this
*/
public function setShortCode($shortCode)
public function setShortCode(string $shortCode)
{
$this->shortCode = $shortCode;
return $this;
@ -105,7 +115,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
/**
* @return \DateTime
*/
public function getDateCreated()
public function getDateCreated(): \DateTime
{
return $this->dateCreated;
}
@ -114,34 +124,16 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* @param \DateTime $dateCreated
* @return $this
*/
public function setDateCreated($dateCreated)
public function setDateCreated(\DateTime $dateCreated)
{
$this->dateCreated = $dateCreated;
return $this;
}
/**
* @return Visit[]|Collection
*/
public function getVisits()
{
return $this->visits;
}
/**
* @param Visit[]|Collection $visits
* @return $this
*/
public function setVisits($visits)
{
$this->visits = $visits;
return $this;
}
/**
* @return Collection|Tag[]
*/
public function getTags()
public function getTags(): Collection
{
return $this->tags;
}
@ -150,7 +142,7 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
* @param Collection|Tag[] $tags
* @return $this
*/
public function setTags($tags)
public function setTags(Collection $tags)
{
$this->tags = $tags;
return $this;
@ -166,6 +158,42 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
return $this;
}
/**
* @return \DateTime|null
*/
public function getValidSince()
{
return $this->validSince;
}
/**
* @param \DateTime|null $validSince
* @return $this|self
*/
public function setValidSince($validSince): self
{
$this->validSince = $validSince;
return $this;
}
/**
* @return \DateTime|null
*/
public function getValidUntil()
{
return $this->validUntil;
}
/**
* @param \DateTime|null $validUntil
* @return $this|self
*/
public function setValidUntil($validUntil): self
{
$this->validUntil = $validUntil;
return $this;
}
/**
* Specify data which should be serialized to JSON
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php

View file

@ -17,8 +17,13 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
* @param string|array|null $orderBy
* @return \Shlinkio\Shlink\Core\Entity\ShortUrl[]
*/
public function findList($limit = null, $offset = null, $searchTerm = null, array $tags = [], $orderBy = null)
{
public function findList(
int $limit = null,
int $offset = null,
string $searchTerm = null,
array $tags = [],
$orderBy = null
): array {
$qb = $this->createListQueryBuilder($searchTerm, $tags);
$qb->select('s');
@ -74,7 +79,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
* @param array $tags
* @return int
*/
public function countList($searchTerm = null, array $tags = [])
public function countList(string $searchTerm = null, array $tags = []): int
{
$qb = $this->createListQueryBuilder($searchTerm, $tags);
$qb->select('COUNT(s)');
@ -87,7 +92,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
* @param array $tags
* @return QueryBuilder
*/
protected function createListQueryBuilder($searchTerm = null, array $tags = [])
protected function createListQueryBuilder(string $searchTerm = null, array $tags = []): QueryBuilder
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(ShortUrl::class, 's');
@ -117,4 +122,29 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
return $qb;
}
/**
* @param string $shortCode
* @return ShortUrl|null
*/
public function findOneByShortCode(string $shortCode)
{
$now = new \DateTimeImmutable();
$qb = $this->createQueryBuilder('s');
$qb->where($qb->expr()->eq('s.shortCode', ':shortCode'))
->setParameter('shortCode', $shortCode)
->andWhere($qb->expr()->orX(
$qb->expr()->lte('s.validSince', ':now'),
$qb->expr()->isNull('s.validSince')
))
->andWhere($qb->expr()->orX(
$qb->expr()->gte('s.validUntil', ':now'),
$qb->expr()->isNull('s.validUntil')
))
->setParameter('now', $now)
->setMaxResults(1);
return $qb->getQuery()->getOneOrNullResult();
}
}

View file

@ -5,7 +5,13 @@ namespace Shlinkio\Shlink\Core\Repository;
use Doctrine\Common\Persistence\ObjectRepository;
use Shlinkio\Shlink\Common\Repository\PaginableRepositoryInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
interface ShortUrlRepositoryInterface extends ObjectRepository, PaginableRepositoryInterface
{
/**
* @param string $shortCode
* @return ShortUrl|null
*/
public function findOneByShortCode(string $shortCode);
}

View file

@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
class UrlShortener implements UrlShortenerInterface
@ -160,11 +161,13 @@ class UrlShortener implements UrlShortenerInterface
throw InvalidShortCodeException::fromCharset($shortCode, $this->chars);
}
$criteria = ['shortCode' => $shortCode];
/** @var ShortUrl|null $shortUrl */
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy($criteria);
/** @var ShortUrlRepository $shortUrlRepo */
$shortUrlRepo = $this->em->getRepository(ShortUrl::class);
$shortUrl = $shortUrlRepo->findOneByShortCode($shortCode);
if ($shortUrl === null) {
throw EntityDoesNotExistException::createFromEntityAndConditions(ShortUrl::class, $criteria);
throw EntityDoesNotExistException::createFromEntityAndConditions(ShortUrl::class, [
'shortCode' => $shortCode,
]);
}
// Cache the shortcode

View file

@ -16,6 +16,7 @@ use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\UrlShortener;
use Zend\Diactoros\Uri;
@ -127,8 +128,8 @@ class UrlShortenerTest extends TestCase
$shortUrl->setShortCode($shortCode)
->setOriginalUrl('expected_url');
$repo = $this->prophesize(ObjectRepository::class);
$repo->findOneBy(['shortCode' => $shortCode])->willReturn($shortUrl);
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$repo->findOneByShortCode($shortCode)->willReturn($shortUrl);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->assertFalse($this->cache->contains($shortCode . '_longUrl'));