mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Allow short URLs list to be filtered by domain authority
This commit is contained in:
parent
525a306ec6
commit
bb270396b6
@ -125,6 +125,15 @@
|
||||
"false"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"in": "query",
|
||||
"description": "Get short URLs for this particular domain only. Use **DEFAULT** keyword for default domain.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
|
@ -74,7 +74,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
}
|
||||
|
||||
$this->shortUrlService->expects($this->once())->method('listShortUrls')->with(
|
||||
ShortUrlsParams::emptyInstance(),
|
||||
ShortUrlsParams::empty(),
|
||||
)->willReturn(new Paginator(new ArrayAdapter($data)));
|
||||
|
||||
$this->commandTester->setInputs(['n']);
|
||||
@ -110,7 +110,7 @@ class ListShortUrlsCommandTest extends TestCase
|
||||
ApiKey $apiKey,
|
||||
): void {
|
||||
$this->shortUrlService->expects($this->once())->method('listShortUrls')->with(
|
||||
ShortUrlsParams::emptyInstance(),
|
||||
ShortUrlsParams::empty(),
|
||||
)->willReturn(new Paginator(new ArrayAdapter([
|
||||
ShortUrlWithVisitsSummary::fromShortUrl(
|
||||
ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
|
@ -19,17 +19,18 @@ final class ShortUrlsParams
|
||||
private function __construct(
|
||||
public readonly int $page,
|
||||
public readonly int $itemsPerPage,
|
||||
public readonly ?string $searchTerm,
|
||||
public readonly string|null $searchTerm,
|
||||
public readonly array $tags,
|
||||
public readonly Ordering $orderBy,
|
||||
public readonly ?DateRange $dateRange,
|
||||
public readonly bool $excludeMaxVisitsReached,
|
||||
public readonly bool $excludePastValidUntil,
|
||||
public readonly TagsMode $tagsMode = TagsMode::ANY,
|
||||
public readonly string|null $domain = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function emptyInstance(): self
|
||||
public static function empty(): self
|
||||
{
|
||||
return self::fromRawData([]);
|
||||
}
|
||||
@ -59,6 +60,7 @@ final class ShortUrlsParams
|
||||
excludeMaxVisitsReached: $inputFilter->getValue(ShortUrlsParamsInputFilter::EXCLUDE_MAX_VISITS_REACHED),
|
||||
excludePastValidUntil: $inputFilter->getValue(ShortUrlsParamsInputFilter::EXCLUDE_PAST_VALID_UNTIL),
|
||||
tagsMode: self::resolveTagsMode($inputFilter->getValue(ShortUrlsParamsInputFilter::TAGS_MODE)),
|
||||
domain: $inputFilter->getValue(ShortUrlsParamsInputFilter::DOMAIN),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ class ShortUrlsParamsInputFilter extends InputFilter
|
||||
public const ORDER_BY = 'orderBy';
|
||||
public const EXCLUDE_MAX_VISITS_REACHED = 'excludeMaxVisitsReached';
|
||||
public const EXCLUDE_PAST_VALID_UNTIL = 'excludePastValidUntil';
|
||||
public const DOMAIN = 'domain';
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
@ -56,5 +57,7 @@ class ShortUrlsParamsInputFilter extends InputFilter
|
||||
|
||||
$this->add(InputFactory::boolean(self::EXCLUDE_MAX_VISITS_REACHED));
|
||||
$this->add(InputFactory::boolean(self::EXCLUDE_PAST_VALID_UNTIL));
|
||||
|
||||
$this->add(InputFactory::basic(self::DOMAIN));
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class ShortUrlsCountFiltering
|
||||
$params->excludePastValidUntil,
|
||||
$apiKey,
|
||||
$defaultDomain,
|
||||
$params->domain,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ class ShortUrlsListFiltering extends ShortUrlsCountFiltering
|
||||
bool $excludeMaxVisitsReached = false,
|
||||
bool $excludePastValidUntil = false,
|
||||
?ApiKey $apiKey = null,
|
||||
// Used only to determine if search term includes default domain
|
||||
?string $defaultDomain = null,
|
||||
?string $domain = null,
|
||||
) {
|
||||
parent::__construct(
|
||||
$searchTerm,
|
||||
@ -34,6 +36,7 @@ class ShortUrlsListFiltering extends ShortUrlsCountFiltering
|
||||
$excludePastValidUntil,
|
||||
$apiKey,
|
||||
$defaultDomain,
|
||||
$domain,
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,6 +59,7 @@ class ShortUrlsListFiltering extends ShortUrlsCountFiltering
|
||||
$params->excludePastValidUntil,
|
||||
$apiKey,
|
||||
$defaultDomain,
|
||||
$params->domain,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Common\Doctrine\Type\ChronosDateTimeType;
|
||||
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\OrderableField;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlWithVisitsSummary;
|
||||
@ -118,8 +119,8 @@ class ShortUrlListRepository extends EntitySpecificationRepository implements Sh
|
||||
$qb->expr()->like('d.authority', ':searchPattern'),
|
||||
];
|
||||
|
||||
// Include default domain in search if provided
|
||||
if ($filtering->searchIncludesDefaultDomain) {
|
||||
// Include default domain in search if included, and a domain was not explicitly provided
|
||||
if ($filtering->searchIncludesDefaultDomain && $filtering->domain === null) {
|
||||
$conditions[] = $qb->expr()->isNull('s.domain');
|
||||
}
|
||||
|
||||
@ -142,6 +143,12 @@ class ShortUrlListRepository extends EntitySpecificationRepository implements Sh
|
||||
}
|
||||
|
||||
if ($filtering->domain !== null) {
|
||||
if ($filtering->domain === Domain::DEFAULT_AUTHORITY) {
|
||||
$qb->andWhere($qb->expr()->isNull('s.domain'));
|
||||
} else {
|
||||
$qb->andWhere($qb->expr()->eq('d.authority', ':domain'))
|
||||
->setParameter('domain', $filtering->domain);
|
||||
}
|
||||
}
|
||||
|
||||
if ($filtering->excludeMaxVisitsReached) {
|
||||
|
@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use ReflectionObject;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
|
||||
use Shlinkio\Shlink\Core\Model\Ordering;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\OrderableField;
|
||||
@ -261,16 +262,23 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$buildFiltering = static fn (string $searchTerm) => new ShortUrlsListFiltering(
|
||||
$buildFiltering = static fn (string $searchTerm = '', string|null $domain = null) => new ShortUrlsListFiltering(
|
||||
searchTerm: $searchTerm,
|
||||
defaultDomain: 'deFaulT-domain.com',
|
||||
domain: $domain,
|
||||
);
|
||||
|
||||
self::assertCount(2, $this->repo->findList($buildFiltering('default-dom')));
|
||||
self::assertCount(2, $this->repo->findList($buildFiltering('DOM')));
|
||||
self::assertCount(1, $this->repo->findList($buildFiltering('another')));
|
||||
self::assertCount(3, $this->repo->findList($buildFiltering('foo')));
|
||||
self::assertCount(0, $this->repo->findList($buildFiltering('no results')));
|
||||
self::assertCount(2, $this->repo->findList($buildFiltering(searchTerm: 'default-dom')));
|
||||
self::assertCount(2, $this->repo->findList($buildFiltering(searchTerm: 'DOM')));
|
||||
self::assertCount(1, $this->repo->findList($buildFiltering(searchTerm: 'another')));
|
||||
self::assertCount(3, $this->repo->findList($buildFiltering(searchTerm: 'foo')));
|
||||
self::assertCount(0, $this->repo->findList($buildFiltering(searchTerm: 'no results')));
|
||||
self::assertCount(1, $this->repo->findList($buildFiltering(domain: 'another.com')));
|
||||
self::assertCount(0, $this->repo->findList($buildFiltering(
|
||||
searchTerm: 'default-domain.com',
|
||||
domain: 'another.com',
|
||||
)));
|
||||
self::assertCount(2, $this->repo->findList($buildFiltering(domain: Domain::DEFAULT_AUTHORITY)));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@ -303,18 +311,42 @@ class ShortUrlListRepositoryTest extends DatabaseTestCase
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$filtering = static fn (bool $excludeMaxVisitsReached, bool $excludePastValidUntil) =>
|
||||
new ShortUrlsListFiltering(
|
||||
excludeMaxVisitsReached: $excludeMaxVisitsReached,
|
||||
excludePastValidUntil: $excludePastValidUntil,
|
||||
);
|
||||
new ShortUrlsListFiltering(
|
||||
excludeMaxVisitsReached: $excludeMaxVisitsReached,
|
||||
excludePastValidUntil: $excludePastValidUntil,
|
||||
);
|
||||
|
||||
self::assertCount(4, $this->repo->findList($filtering(false, false)));
|
||||
self::assertEquals(4, $this->repo->countList($filtering(false, false)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(true, false)));
|
||||
self::assertEquals(3, $this->repo->countList($filtering(true, false)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(false, true)));
|
||||
self::assertEquals(3, $this->repo->countList($filtering(false, true)));
|
||||
self::assertCount(2, $this->repo->findList($filtering(true, true)));
|
||||
self::assertEquals(2, $this->repo->countList($filtering(true, true)));
|
||||
self::assertCount(4, $this->repo->findList($filtering(
|
||||
excludeMaxVisitsReached: false,
|
||||
excludePastValidUntil: false,
|
||||
)));
|
||||
self::assertEquals(4, $this->repo->countList($filtering(
|
||||
excludeMaxVisitsReached: false,
|
||||
excludePastValidUntil: false,
|
||||
)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(
|
||||
excludeMaxVisitsReached: true,
|
||||
excludePastValidUntil: false,
|
||||
)));
|
||||
self::assertEquals(3, $this->repo->countList($filtering(
|
||||
excludeMaxVisitsReached: true,
|
||||
excludePastValidUntil: false,
|
||||
)));
|
||||
self::assertCount(3, $this->repo->findList($filtering(
|
||||
excludeMaxVisitsReached: false,
|
||||
excludePastValidUntil: true,
|
||||
)));
|
||||
self::assertEquals(3, $this->repo->countList($filtering(
|
||||
excludeMaxVisitsReached: false,
|
||||
excludePastValidUntil: true,
|
||||
)));
|
||||
self::assertCount(2, $this->repo->findList($filtering(
|
||||
excludeMaxVisitsReached: true,
|
||||
excludePastValidUntil: true,
|
||||
)));
|
||||
self::assertEquals(2, $this->repo->countList($filtering(
|
||||
excludeMaxVisitsReached: true,
|
||||
excludePastValidUntil: true,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class ShortUrlListServiceTest extends TestCase
|
||||
$this->repo->expects($this->once())->method('findList')->willReturn($list);
|
||||
$this->repo->expects($this->once())->method('countList')->willReturn(count($list));
|
||||
|
||||
$paginator = $this->service->listShortUrls(ShortUrlsParams::emptyInstance(), $apiKey);
|
||||
$paginator = $this->service->listShortUrls(ShortUrlsParams::empty(), $apiKey);
|
||||
|
||||
self::assertCount(4, $paginator);
|
||||
self::assertCount(4, $paginator->getCurrentPageResults());
|
||||
|
Loading…
Reference in New Issue
Block a user