mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-24 05:38:06 +03:00
Created new repository method which will look for short URLs without doing domain fallback
This commit is contained in:
parent
e87d4d61bc
commit
10f79ec01d
5 changed files with 67 additions and 23 deletions
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue