Created new repository method which will look for short URLs without doing domain fallback

This commit is contained in:
Alejandro Celaya 2020-02-02 12:44:35 +01:00
parent e87d4d61bc
commit 10f79ec01d
5 changed files with 67 additions and 23 deletions

View file

@ -90,8 +90,8 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
?DateRange $dateRange = null ?DateRange $dateRange = null
): QueryBuilder { ): QueryBuilder {
$qb = $this->getEntityManager()->createQueryBuilder(); $qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(ShortUrl::class, 's'); $qb->from(ShortUrl::class, 's')
$qb->where('1=1'); ->where('1=1');
if ($dateRange !== null && $dateRange->getStartDate() !== null) { if ($dateRange !== null && $dateRange->getStartDate() !== null) {
$qb->andWhere($qb->expr()->gte('s.dateCreated', ':startDate')); $qb->andWhere($qb->expr()->gte('s.dateCreated', ':startDate'));
@ -127,7 +127,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
return $qb; return $qb;
} }
public function findOneByShortCode(string $shortCode, ?string $domain = null): ?ShortUrl public function findOneWithDomainFallback(string $shortCode, ?string $domain = null): ?ShortUrl
{ {
// When ordering DESC, Postgres puts nulls at the beginning while the rest of supported DB engines put them at // When ordering DESC, Postgres puts nulls at the beginning while the rest of supported DB engines put them at
// the bottom // the bottom
@ -159,14 +159,30 @@ DQL;
return $query->getOneOrNullResult(); return $query->getOneOrNullResult();
} }
public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl
{
$qb = $this->createFindOneQueryBuilder($shortCode, $domain);
$qb->select('s');
return $qb->getQuery()->getOneOrNullResult();
}
public function shortCodeIsInUse(string $slug, ?string $domain = null): bool public function shortCodeIsInUse(string $slug, ?string $domain = null): bool
{
$qb = $this->createFindOneQueryBuilder($slug, $domain);
$qb->select('COUNT(DISTINCT s.id)');
return ((int) $qb->getQuery()->getSingleScalarResult()) > 0;
}
private function createFindOneQueryBuilder(string $slug, ?string $domain = null): QueryBuilder
{ {
$qb = $this->getEntityManager()->createQueryBuilder(); $qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('COUNT(DISTINCT s.id)') $qb->from(ShortUrl::class, 's')
->from(ShortUrl::class, 's')
->where($qb->expr()->isNotNull('s.shortCode')) ->where($qb->expr()->isNotNull('s.shortCode'))
->andWhere($qb->expr()->eq('s.shortCode', ':slug')) ->andWhere($qb->expr()->eq('s.shortCode', ':slug'))
->setParameter('slug', $slug); ->setParameter('slug', $slug)
->setMaxResults(1);
if ($domain !== null) { if ($domain !== null) {
$qb->join('s.domain', 'd') $qb->join('s.domain', 'd')
@ -176,7 +192,6 @@ DQL;
$qb->andWhere($qb->expr()->isNull('s.domain')); $qb->andWhere($qb->expr()->isNull('s.domain'));
} }
$result = (int) $qb->getQuery()->getSingleScalarResult(); return $qb;
return $result > 0;
} }
} }

View file

@ -22,7 +22,9 @@ interface ShortUrlRepositoryInterface extends ObjectRepository
public function countList(?string $searchTerm = null, array $tags = [], ?DateRange $dateRange = null): int; public function countList(?string $searchTerm = null, array $tags = [], ?DateRange $dateRange = null): int;
public function findOneByShortCode(string $shortCode, ?string $domain = null): ?ShortUrl; public function findOneWithDomainFallback(string $shortCode, ?string $domain = null): ?ShortUrl;
public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl;
public function shortCodeIsInUse(string $slug, ?string $domain): bool; public function shortCodeIsInUse(string $slug, ?string $domain): bool;
} }

View file

@ -26,7 +26,7 @@ class ShortUrlResolver implements ShortUrlResolverInterface
{ {
/** @var ShortUrlRepository $shortUrlRepo */ /** @var ShortUrlRepository $shortUrlRepo */
$shortUrlRepo = $this->em->getRepository(ShortUrl::class); $shortUrlRepo = $this->em->getRepository(ShortUrl::class);
$shortUrl = $shortUrlRepo->findOneByShortCode($identifier->shortCode(), $identifier->domain()); $shortUrl = $shortUrlRepo->findOneWithDomainFallback($identifier->shortCode(), $identifier->domain());
if ($shortUrl === null) { if ($shortUrl === null) {
throw ShortUrlNotFoundException::fromNotFound($identifier); throw ShortUrlNotFoundException::fromNotFound($identifier);
} }

View file

@ -37,7 +37,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
} }
/** @test */ /** @test */
public function findOneByShortCodeReturnsProperData(): void public function findOneWithDomainFallbackReturnsProperData(): void
{ {
$regularOne = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'foo'])); $regularOne = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'foo']));
$this->getEntityManager()->persist($regularOne); $this->getEntityManager()->persist($regularOne);
@ -54,20 +54,25 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->flush(); $this->getEntityManager()->flush();
$this->assertSame($regularOne, $this->repo->findOneByShortCode($regularOne->getShortCode())); $this->assertSame($regularOne, $this->repo->findOneWithDomainFallback($regularOne->getShortCode()));
$this->assertSame($regularOne, $this->repo->findOneByShortCode($withDomainDuplicatingRegular->getShortCode())); $this->assertSame($regularOne, $this->repo->findOneWithDomainFallback(
$this->assertSame($withDomain, $this->repo->findOneByShortCode($withDomain->getShortCode(), 'example.com')); $withDomainDuplicatingRegular->getShortCode(),
));
$this->assertSame($withDomain, $this->repo->findOneWithDomainFallback(
$withDomain->getShortCode(),
'example.com',
));
$this->assertSame( $this->assertSame(
$withDomainDuplicatingRegular, $withDomainDuplicatingRegular,
$this->repo->findOneByShortCode($withDomainDuplicatingRegular->getShortCode(), 'doma.in'), $this->repo->findOneWithDomainFallback($withDomainDuplicatingRegular->getShortCode(), 'doma.in'),
); );
$this->assertSame( $this->assertSame(
$regularOne, $regularOne,
$this->repo->findOneByShortCode($withDomainDuplicatingRegular->getShortCode(), 'other-domain.com'), $this->repo->findOneWithDomainFallback($withDomainDuplicatingRegular->getShortCode(), 'other-domain.com'),
); );
$this->assertNull($this->repo->findOneByShortCode('invalid')); $this->assertNull($this->repo->findOneWithDomainFallback('invalid'));
$this->assertNull($this->repo->findOneByShortCode($withDomain->getShortCode())); $this->assertNull($this->repo->findOneWithDomainFallback($withDomain->getShortCode()));
$this->assertNull($this->repo->findOneByShortCode($withDomain->getShortCode(), 'other-domain.com')); $this->assertNull($this->repo->findOneWithDomainFallback($withDomain->getShortCode(), 'other-domain.com'));
} }
/** @test */ /** @test */
@ -183,4 +188,26 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->assertFalse($this->repo->shortCodeIsInUse('another-slug', 'example.com')); $this->assertFalse($this->repo->shortCodeIsInUse('another-slug', 'example.com'));
$this->assertTrue($this->repo->shortCodeIsInUse('another-slug', 'doma.in')); $this->assertTrue($this->repo->shortCodeIsInUse('another-slug', 'doma.in'));
} }
/** @test */
public function findOneLooksForShortUrlInProperSetOfTables(): void
{
$shortUrlWithoutDomain = new ShortUrl('foo', ShortUrlMeta::fromRawData(['customSlug' => 'my-cool-slug']));
$this->getEntityManager()->persist($shortUrlWithoutDomain);
$shortUrlWithDomain = new ShortUrl(
'foo',
ShortUrlMeta::fromRawData(['domain' => 'doma.in', 'customSlug' => 'another-slug']),
);
$this->getEntityManager()->persist($shortUrlWithDomain);
$this->getEntityManager()->flush();
$this->assertNotNull($this->repo->findOne('my-cool-slug'));
$this->assertNull($this->repo->findOne('my-cool-slug', 'doma.in'));
$this->assertNull($this->repo->findOne('slug-not-in-use'));
$this->assertNull($this->repo->findOne('another-slug'));
$this->assertNull($this->repo->findOne('another-slug', 'example.com'));
$this->assertNotNull($this->repo->findOne('another-slug', 'doma.in'));
}
} }

View file

@ -39,7 +39,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOneByShortCode = $repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl); $findOneByShortCode = $repo->findOneWithDomainFallback($shortCode, null)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$result = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode)); $result = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode));
@ -55,7 +55,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = 'abc123'; $shortCode = 'abc123';
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOneByShortCode = $repo->findOneByShortCode($shortCode, null)->willReturn(null); $findOneByShortCode = $repo->findOneWithDomainFallback($shortCode, null)->willReturn(null);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->expectException(ShortUrlNotFoundException::class); $this->expectException(ShortUrlNotFoundException::class);
@ -72,7 +72,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOneByShortCode = $repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl); $findOneByShortCode = $repo->findOneWithDomainFallback($shortCode, null)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$result = $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode)); $result = $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode));
@ -91,7 +91,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = $shortUrl->getShortCode(); $shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class); $repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOneByShortCode = $repo->findOneByShortCode($shortCode, null)->willReturn($shortUrl); $findOneByShortCode = $repo->findOneWithDomainFallback($shortCode, null)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); $getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->expectException(ShortUrlNotFoundException::class); $this->expectException(ShortUrlNotFoundException::class);