mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-22 08:56:42 -06:00
Added db test for filtering of disabled short URLs
This commit is contained in:
parent
463dfe9729
commit
cdde59b543
@ -65,29 +65,29 @@ class ShortUrl extends AbstractEntity
|
||||
return self::fromMeta(ShortUrlCreation::fromRawData([ShortUrlInputFilter::LONG_URL => $longUrl]));
|
||||
}
|
||||
|
||||
public static function fromMeta(
|
||||
ShortUrlCreation $meta,
|
||||
public static function fromMeta( // TODO Rename to create(...)
|
||||
ShortUrlCreation $creation,
|
||||
?ShortUrlRelationResolverInterface $relationResolver = null,
|
||||
): self {
|
||||
$instance = new self();
|
||||
$relationResolver = $relationResolver ?? new SimpleShortUrlRelationResolver();
|
||||
|
||||
$instance->longUrl = $meta->getLongUrl();
|
||||
$instance->longUrl = $creation->getLongUrl();
|
||||
$instance->dateCreated = Chronos::now();
|
||||
$instance->visits = new ArrayCollection();
|
||||
$instance->tags = $relationResolver->resolveTags($meta->getTags());
|
||||
$instance->validSince = $meta->getValidSince();
|
||||
$instance->validUntil = $meta->getValidUntil();
|
||||
$instance->maxVisits = $meta->getMaxVisits();
|
||||
$instance->customSlugWasProvided = $meta->hasCustomSlug();
|
||||
$instance->shortCodeLength = $meta->getShortCodeLength();
|
||||
$instance->shortCode = $meta->getCustomSlug() ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->domain = $relationResolver->resolveDomain($meta->getDomain());
|
||||
$instance->authorApiKey = $meta->getApiKey();
|
||||
$instance->title = $meta->getTitle();
|
||||
$instance->titleWasAutoResolved = $meta->titleWasAutoResolved();
|
||||
$instance->crawlable = $meta->isCrawlable();
|
||||
$instance->forwardQuery = $meta->forwardQuery();
|
||||
$instance->tags = $relationResolver->resolveTags($creation->getTags());
|
||||
$instance->validSince = $creation->getValidSince();
|
||||
$instance->validUntil = $creation->getValidUntil();
|
||||
$instance->maxVisits = $creation->getMaxVisits();
|
||||
$instance->customSlugWasProvided = $creation->hasCustomSlug();
|
||||
$instance->shortCodeLength = $creation->getShortCodeLength();
|
||||
$instance->shortCode = $creation->getCustomSlug() ?? generateRandomShortCode($instance->shortCodeLength);
|
||||
$instance->domain = $relationResolver->resolveDomain($creation->getDomain());
|
||||
$instance->authorApiKey = $creation->getApiKey();
|
||||
$instance->title = $creation->getTitle();
|
||||
$instance->titleWasAutoResolved = $creation->titleWasAutoResolved();
|
||||
$instance->crawlable = $creation->isCrawlable();
|
||||
$instance->forwardQuery = $creation->forwardQuery();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
->setMaxResults($filtering->limit)
|
||||
->setFirstResult($filtering->offset);
|
||||
|
||||
// Some DB engines (postgres and ms) require aggregates in the select for ordering and filtering
|
||||
if ($filtering->excludeMaxVisitsReached || $filtering->orderBy->field === 'visits') {
|
||||
$qb->addSelect('COUNT(DISTINCT v)');
|
||||
}
|
||||
|
||||
// In case the ordering has been specified, the query could be more complex. Process it
|
||||
if ($filtering->orderBy->hasOrderField()) {
|
||||
$this->processOrderByForList($qb, $filtering);
|
||||
@ -59,10 +64,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
// Diagnostic: It might need to use a sub-query, as done with the tags list query.
|
||||
if (! $filtering->excludeMaxVisitsReached) {
|
||||
// Left join only if this was not true, otherwise this left join already happened
|
||||
$this->leftJoinShortUrlsWithVisitsCount($qb);
|
||||
$this->leftJoinShortUrlsWithVisits($qb);
|
||||
}
|
||||
|
||||
$qb->orderBy('totalVisits', $order);
|
||||
$qb->orderBy('COUNT(DISTINCT v)', $order);
|
||||
} elseif (contains(['longUrl', 'shortCode', 'dateCreated', 'title'], $fieldName)) {
|
||||
$qb->orderBy('s.' . $fieldName, $order);
|
||||
} else {
|
||||
@ -77,7 +82,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
$qb->select('COUNT(DISTINCT s)');
|
||||
$query = $qb->getQuery();
|
||||
|
||||
// dump($query->getSQL());
|
||||
// TODO Check if this is needed
|
||||
// if ($filtering->excludeMaxVisitsReached) {
|
||||
// $qb->addSelect('COUNT(DISTINCT v)');
|
||||
// }
|
||||
|
||||
// TODO This is crap...
|
||||
return $filtering->excludeMaxVisitsReached
|
||||
@ -85,10 +93,9 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
: (int) $query->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function leftJoinShortUrlsWithVisitsCount(QueryBuilder $qb): void
|
||||
private function leftJoinShortUrlsWithVisits(QueryBuilder $qb): void
|
||||
{
|
||||
$qb->addSelect('COUNT(DISTINCT v) AS totalVisits')
|
||||
->leftJoin('s.visits', 'v')
|
||||
$qb->leftJoin('s.visits', 'v')
|
||||
->groupBy('s');
|
||||
}
|
||||
|
||||
@ -150,7 +157,7 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||
}
|
||||
|
||||
if ($filtering->excludeMaxVisitsReached) {
|
||||
$this->leftJoinShortUrlsWithVisitsCount($qb);
|
||||
$this->leftJoinShortUrlsWithVisits($qb);
|
||||
$qb->having($qb->expr()->orX(
|
||||
$qb->expr()->isNull('s.maxVisits'),
|
||||
$qb->expr()->gt('s.maxVisits', 'COUNT(DISTINCT v)'),
|
||||
|
@ -339,6 +339,50 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
|
||||
self::assertCount(0, $this->repo->findList($buildFiltering('no results')));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function findListReturnsOnlyThoseWithoutExcludedUrls(): void
|
||||
{
|
||||
$shortUrl1 = ShortUrl::fromMeta(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo1',
|
||||
'validUntil' => Chronos::now()->addDays(1)->toAtomString(),
|
||||
'maxVisits' => 100,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl1);
|
||||
$shortUrl2 = ShortUrl::fromMeta(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo2',
|
||||
'validUntil' => Chronos::now()->subDays(1)->toAtomString(),
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$shortUrl3 = ShortUrl::fromMeta(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo3',
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
$shortUrl4 = ShortUrl::fromMeta(ShortUrlCreation::fromRawData([
|
||||
'longUrl' => 'foo4',
|
||||
'maxVisits' => 3,
|
||||
]), $this->relationResolver);
|
||||
$this->getEntityManager()->persist($shortUrl4);
|
||||
$this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance()));
|
||||
$this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance()));
|
||||
$this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl4, Visitor::emptyInstance()));
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$filtering = static fn (bool $excludeMaxVisitsReached, bool $excludePastValidUntil) =>
|
||||
new ShortUrlsListFiltering(
|
||||
null,
|
||||
null,
|
||||
Ordering::emptyInstance(),
|
||||
excludeMaxVisitsReached: $excludeMaxVisitsReached,
|
||||
excludePastValidUntil: $excludePastValidUntil,
|
||||
);
|
||||
|
||||
self::assertCount(4, $this->repo->findList($filtering(false, false)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(true, false)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(false, true)));
|
||||
self::assertCount(2, $this->repo->findList($filtering(true, true)));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function shortCodeIsInUseLooksForShortUrlInProperSetOfTables(): void
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user