mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'feature/58' into develop
This commit is contained in:
commit
850ce152cd
@ -74,10 +74,22 @@
|
|||||||
{
|
{
|
||||||
"name": "searchTerm",
|
"name": "searchTerm",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields. (From Shlink 1.3.0)",
|
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields. (Since v1.3.0)",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"in": "query",
|
||||||
|
"description": "A list of tags used to filter the resultset. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
||||||
|
"required": false,
|
||||||
|
"type": "array",
|
||||||
|
"schema": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/Authorization"
|
"$ref": "#/parameters/Authorization"
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -1,8 +1,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Shlink 1.0\n"
|
"Project-Id-Version: Shlink 1.0\n"
|
||||||
"POT-Creation-Date: 2016-08-21 18:16+0200\n"
|
"POT-Creation-Date: 2016-10-22 13:14+0200\n"
|
||||||
"PO-Revision-Date: 2016-08-21 18:16+0200\n"
|
"PO-Revision-Date: 2016-10-22 13:15+0200\n"
|
||||||
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
"Last-Translator: Alejandro Celaya <alejandro@alejandrocelaya.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: es_ES\n"
|
"Language: es_ES\n"
|
||||||
@ -162,6 +162,16 @@ msgstr "Listar todas las URLs cortas"
|
|||||||
msgid "The first page to list (%s items per page)"
|
msgid "The first page to list (%s items per page)"
|
||||||
msgstr "La primera página a listar (%s elementos por página)"
|
msgstr "La primera página a listar (%s elementos por página)"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"A query used to filter results by searching for it on the longUrl and "
|
||||||
|
"shortCode fields"
|
||||||
|
msgstr ""
|
||||||
|
"Una consulta usada para filtrar el resultado buscándola en los campos "
|
||||||
|
"longUrl y shortCode"
|
||||||
|
|
||||||
|
msgid "A comma-separated list of tags to filter results"
|
||||||
|
msgstr "Una lista de etiquetas separadas por coma para filtrar el resultado"
|
||||||
|
|
||||||
msgid "Whether to display the tags or not"
|
msgid "Whether to display the tags or not"
|
||||||
msgstr "Si se desea mostrar las etiquetas o no"
|
msgstr "Si se desea mostrar las etiquetas o no"
|
||||||
|
|
||||||
|
@ -67,6 +67,12 @@ class ListShortcodesCommand extends Command
|
|||||||
->addOption(
|
->addOption(
|
||||||
'tags',
|
'tags',
|
||||||
't',
|
't',
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
$this->translator->translate('A comma-separated list of tags to filter results')
|
||||||
|
)
|
||||||
|
->addOption(
|
||||||
|
'showTags',
|
||||||
|
null,
|
||||||
InputOption::VALUE_NONE,
|
InputOption::VALUE_NONE,
|
||||||
$this->translator->translate('Whether to display the tags or not')
|
$this->translator->translate('Whether to display the tags or not')
|
||||||
);
|
);
|
||||||
@ -76,13 +82,15 @@ class ListShortcodesCommand extends Command
|
|||||||
{
|
{
|
||||||
$page = intval($input->getOption('page'));
|
$page = intval($input->getOption('page'));
|
||||||
$searchTerm = $input->getOption('searchTerm');
|
$searchTerm = $input->getOption('searchTerm');
|
||||||
$showTags = $input->getOption('tags');
|
$tags = $input->getOption('tags');
|
||||||
|
$tags = ! empty($tags) ? explode(',', $tags) : [];
|
||||||
|
$showTags = $input->getOption('showTags');
|
||||||
|
|
||||||
/** @var QuestionHelper $helper */
|
/** @var QuestionHelper $helper */
|
||||||
$helper = $this->getHelper('question');
|
$helper = $this->getHelper('question');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$result = $this->shortUrlService->listShortUrls($page, $searchTerm);
|
$result = $this->shortUrlService->listShortUrls($page, $searchTerm, $tags);
|
||||||
$page++;
|
$page++;
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class ListShortcodesCommandTest extends TestCase
|
|||||||
public function noInputCallsListJustOnce()
|
public function noInputCallsListJustOnce()
|
||||||
{
|
{
|
||||||
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
||||||
$this->shortUrlService->listShortUrls(1, null)->willReturn(new Paginator(new ArrayAdapter()))
|
$this->shortUrlService->listShortUrls(1, null, [])->willReturn(new Paginator(new ArrayAdapter()))
|
||||||
->shouldBeCalledTimes(1);
|
->shouldBeCalledTimes(1);
|
||||||
|
|
||||||
$this->commandTester->execute(['command' => 'shortcode:list']);
|
$this->commandTester->execute(['command' => 'shortcode:list']);
|
||||||
@ -103,7 +103,7 @@ class ListShortcodesCommandTest extends TestCase
|
|||||||
{
|
{
|
||||||
$page = 5;
|
$page = 5;
|
||||||
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
||||||
$this->shortUrlService->listShortUrls($page, null)->willReturn(new Paginator(new ArrayAdapter()))
|
$this->shortUrlService->listShortUrls($page, null, [])->willReturn(new Paginator(new ArrayAdapter()))
|
||||||
->shouldBeCalledTimes(1);
|
->shouldBeCalledTimes(1);
|
||||||
|
|
||||||
$this->commandTester->execute([
|
$this->commandTester->execute([
|
||||||
@ -118,12 +118,12 @@ class ListShortcodesCommandTest extends TestCase
|
|||||||
public function ifTagsFlagIsProvidedTagsColumnIsIncluded()
|
public function ifTagsFlagIsProvidedTagsColumnIsIncluded()
|
||||||
{
|
{
|
||||||
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
$this->questionHelper->setInputStream($this->getInputStream('\n'));
|
||||||
$this->shortUrlService->listShortUrls(1, null)->willReturn(new Paginator(new ArrayAdapter()))
|
$this->shortUrlService->listShortUrls(1, null, [])->willReturn(new Paginator(new ArrayAdapter()))
|
||||||
->shouldBeCalledTimes(1);
|
->shouldBeCalledTimes(1);
|
||||||
|
|
||||||
$this->commandTester->execute([
|
$this->commandTester->execute([
|
||||||
'command' => 'shortcode:list',
|
'command' => 'shortcode:list',
|
||||||
'--tags' => true,
|
'--showTags' => true,
|
||||||
]);
|
]);
|
||||||
$output = $this->commandTester->getDisplay();
|
$output = $this->commandTester->getDisplay();
|
||||||
$this->assertTrue(strpos($output, 'Tags') > 0);
|
$this->assertTrue(strpos($output, 'Tags') > 0);
|
||||||
|
@ -20,12 +20,21 @@ class PaginableRepositoryAdapter implements AdapterInterface
|
|||||||
* @var null|array|string
|
* @var null|array|string
|
||||||
*/
|
*/
|
||||||
private $orderBy;
|
private $orderBy;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $tags;
|
||||||
|
|
||||||
public function __construct(PaginableRepositoryInterface $paginableRepository, $searchQuery = null, $orderBy = null)
|
public function __construct(
|
||||||
{
|
PaginableRepositoryInterface $paginableRepository,
|
||||||
|
$searchTerm = null,
|
||||||
|
array $tags = [],
|
||||||
|
$orderBy = null
|
||||||
|
) {
|
||||||
$this->paginableRepository = $paginableRepository;
|
$this->paginableRepository = $paginableRepository;
|
||||||
$this->searchTerm = trim(strip_tags($searchQuery));
|
$this->searchTerm = trim(strip_tags($searchTerm));
|
||||||
$this->orderBy = $orderBy;
|
$this->orderBy = $orderBy;
|
||||||
|
$this->tags = $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +46,13 @@ class PaginableRepositoryAdapter implements AdapterInterface
|
|||||||
*/
|
*/
|
||||||
public function getItems($offset, $itemCountPerPage)
|
public function getItems($offset, $itemCountPerPage)
|
||||||
{
|
{
|
||||||
return $this->paginableRepository->findList($itemCountPerPage, $offset, $this->searchTerm, $this->orderBy);
|
return $this->paginableRepository->findList(
|
||||||
|
$itemCountPerPage,
|
||||||
|
$offset,
|
||||||
|
$this->searchTerm,
|
||||||
|
$this->tags,
|
||||||
|
$this->orderBy
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +66,6 @@ class PaginableRepositoryAdapter implements AdapterInterface
|
|||||||
*/
|
*/
|
||||||
public function count()
|
public function count()
|
||||||
{
|
{
|
||||||
return $this->paginableRepository->countList($this->searchTerm);
|
return $this->paginableRepository->countList($this->searchTerm, $this->tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,18 @@ interface PaginableRepositoryInterface
|
|||||||
* @param int|null $limit
|
* @param int|null $limit
|
||||||
* @param int|null $offset
|
* @param int|null $offset
|
||||||
* @param string|null $searchTerm
|
* @param string|null $searchTerm
|
||||||
|
* @param array $tags
|
||||||
* @param string|array|null $orderBy
|
* @param string|array|null $orderBy
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function findList($limit = null, $offset = null, $searchTerm = null, $orderBy = null);
|
public function findList($limit = null, $offset = null, $searchTerm = null, array $tags = [], $orderBy = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the number of elements in a list using provided filtering data
|
* Counts the number of elements in a list using provided filtering data
|
||||||
*
|
*
|
||||||
* @param null $searchTerm
|
* @param null $searchTerm
|
||||||
|
* @param array $tags
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function countList($searchTerm = null);
|
public function countList($searchTerm = null, array $tags = []);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class PaginableRepositoryAdapterTest extends TestCase
|
|||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->repo = $this->prophesize(PaginableRepositoryInterface::class);
|
$this->repo = $this->prophesize(PaginableRepositoryInterface::class);
|
||||||
$this->adapter = new PaginableRepositoryAdapter($this->repo->reveal(), 'search', 'order');
|
$this->adapter = new PaginableRepositoryAdapter($this->repo->reveal(), 'search', ['foo', 'bar'], 'order');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +28,7 @@ class PaginableRepositoryAdapterTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function getItemsFallbacksToFindList()
|
public function getItemsFallbacksToFindList()
|
||||||
{
|
{
|
||||||
$this->repo->findList(10, 5, 'search', 'order')->shouldBeCalledTimes(1);
|
$this->repo->findList(10, 5, 'search', ['foo', 'bar'], 'order')->shouldBeCalledTimes(1);
|
||||||
$this->adapter->getItems(5, 10);
|
$this->adapter->getItems(5, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class PaginableRepositoryAdapterTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function countFallbacksToCountList()
|
public function countFallbacksToCountList()
|
||||||
{
|
{
|
||||||
$this->repo->countList('search')->shouldBeCalledTimes(1);
|
$this->repo->countList('search', ['foo', 'bar'])->shouldBeCalledTimes(1);
|
||||||
$this->adapter->count();
|
$this->adapter->count();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,13 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
|||||||
* @param int|null $limit
|
* @param int|null $limit
|
||||||
* @param int|null $offset
|
* @param int|null $offset
|
||||||
* @param string|null $searchTerm
|
* @param string|null $searchTerm
|
||||||
|
* @param array $tags
|
||||||
* @param string|array|null $orderBy
|
* @param string|array|null $orderBy
|
||||||
* @return ShortUrl[]
|
* @return \Shlinkio\Shlink\Core\Entity\ShortUrl[]
|
||||||
*/
|
*/
|
||||||
public function findList($limit = null, $offset = null, $searchTerm = null, $orderBy = null)
|
public function findList($limit = null, $offset = null, $searchTerm = null, array $tags = [], $orderBy = null)
|
||||||
{
|
{
|
||||||
$qb = $this->createListQueryBuilder($searchTerm);
|
$qb = $this->createListQueryBuilder($searchTerm, $tags);
|
||||||
$qb->select('s');
|
$qb->select('s');
|
||||||
|
|
||||||
if (isset($limit)) {
|
if (isset($limit)) {
|
||||||
@ -43,11 +44,12 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
|||||||
* Counts the number of elements in a list using provided filtering data
|
* Counts the number of elements in a list using provided filtering data
|
||||||
*
|
*
|
||||||
* @param null|string $searchTerm
|
* @param null|string $searchTerm
|
||||||
|
* @param array $tags
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function countList($searchTerm = null)
|
public function countList($searchTerm = null, array $tags = [])
|
||||||
{
|
{
|
||||||
$qb = $this->createListQueryBuilder($searchTerm);
|
$qb = $this->createListQueryBuilder($searchTerm, $tags);
|
||||||
$qb->select('COUNT(s)');
|
$qb->select('COUNT(s)');
|
||||||
|
|
||||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||||
@ -55,12 +57,14 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param null|string $searchTerm
|
* @param null|string $searchTerm
|
||||||
|
* @param array $tags
|
||||||
* @return QueryBuilder
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
protected function createListQueryBuilder($searchTerm = null)
|
protected function createListQueryBuilder($searchTerm = null, array $tags = [])
|
||||||
{
|
{
|
||||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||||
$qb->from(ShortUrl::class, 's');
|
$qb->from(ShortUrl::class, 's');
|
||||||
|
$qb->where('1=1');
|
||||||
|
|
||||||
// Apply search term to every searchable field if not empty
|
// Apply search term to every searchable field if not empty
|
||||||
if (! empty($searchTerm)) {
|
if (! empty($searchTerm)) {
|
||||||
@ -70,11 +74,17 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Unpack and apply search conditions
|
// Unpack and apply search conditions
|
||||||
$qb->where($qb->expr()->orX(...$conditions));
|
$qb->andWhere($qb->expr()->orX(...$conditions));
|
||||||
$searchTerm = '%' . $searchTerm . '%';
|
$searchTerm = '%' . $searchTerm . '%';
|
||||||
$qb->setParameter('searchPattern', $searchTerm);
|
$qb->setParameter('searchPattern', $searchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter by tags if provided
|
||||||
|
if (! empty($tags)) {
|
||||||
|
$qb->join('s.tags', 't')
|
||||||
|
->andWhere($qb->expr()->in('t.name', $tags));
|
||||||
|
}
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,14 @@ class ShortUrlService implements ShortUrlServiceInterface
|
|||||||
/**
|
/**
|
||||||
* @param int $page
|
* @param int $page
|
||||||
* @param string $searchQuery
|
* @param string $searchQuery
|
||||||
|
* @param array $tags
|
||||||
* @return ShortUrl[]|Paginator
|
* @return ShortUrl[]|Paginator
|
||||||
*/
|
*/
|
||||||
public function listShortUrls($page = 1, $searchQuery = null)
|
public function listShortUrls($page = 1, $searchQuery = null, array $tags = [])
|
||||||
{
|
{
|
||||||
/** @var ShortUrlRepository $repo */
|
/** @var ShortUrlRepository $repo */
|
||||||
$repo = $this->em->getRepository(ShortUrl::class);
|
$repo = $this->em->getRepository(ShortUrl::class);
|
||||||
$paginator = new Paginator(new PaginableRepositoryAdapter($repo, $searchQuery));
|
$paginator = new Paginator(new PaginableRepositoryAdapter($repo, $searchQuery, $tags));
|
||||||
$paginator->setItemCountPerPage(PaginableRepositoryAdapter::ITEMS_PER_PAGE)
|
$paginator->setItemCountPerPage(PaginableRepositoryAdapter::ITEMS_PER_PAGE)
|
||||||
->setCurrentPageNumber($page);
|
->setCurrentPageNumber($page);
|
||||||
|
|
||||||
|
@ -10,9 +10,10 @@ interface ShortUrlServiceInterface
|
|||||||
/**
|
/**
|
||||||
* @param int $page
|
* @param int $page
|
||||||
* @param string $searchQuery
|
* @param string $searchQuery
|
||||||
|
* @param array $tags
|
||||||
* @return ShortUrl[]|Paginator
|
* @return ShortUrl[]|Paginator
|
||||||
*/
|
*/
|
||||||
public function listShortUrls($page = 1, $searchQuery = null);
|
public function listShortUrls($page = 1, $searchQuery = null, array $tags = []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $shortCode
|
* @param string $shortCode
|
||||||
|
@ -74,6 +74,7 @@ class ListShortcodesAction extends AbstractRestAction
|
|||||||
return [
|
return [
|
||||||
isset($query['page']) ? $query['page'] : 1,
|
isset($query['page']) ? $query['page'] : 1,
|
||||||
isset($query['searchTerm']) ? $query['searchTerm'] : null,
|
isset($query['searchTerm']) ? $query['searchTerm'] : null,
|
||||||
|
isset($query['tags']) ? $query['tags'] : [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class ListShortcodesActionTest extends TestCase
|
|||||||
public function properListReturnsSuccessResponse()
|
public function properListReturnsSuccessResponse()
|
||||||
{
|
{
|
||||||
$page = 3;
|
$page = 3;
|
||||||
$this->service->listShortUrls($page, null)->willReturn(new Paginator(new ArrayAdapter()))
|
$this->service->listShortUrls($page, null, [])->willReturn(new Paginator(new ArrayAdapter()))
|
||||||
->shouldBeCalledTimes(1);
|
->shouldBeCalledTimes(1);
|
||||||
|
|
||||||
$response = $this->action->__invoke(
|
$response = $this->action->__invoke(
|
||||||
@ -52,7 +52,7 @@ class ListShortcodesActionTest extends TestCase
|
|||||||
public function anExceptionsReturnsErrorResponse()
|
public function anExceptionsReturnsErrorResponse()
|
||||||
{
|
{
|
||||||
$page = 3;
|
$page = 3;
|
||||||
$this->service->listShortUrls($page, null)->willThrow(\Exception::class)
|
$this->service->listShortUrls($page, null, [])->willThrow(\Exception::class)
|
||||||
->shouldBeCalledTimes(1);
|
->shouldBeCalledTimes(1);
|
||||||
|
|
||||||
$response = $this->action->__invoke(
|
$response = $this->action->__invoke(
|
||||||
|
Loading…
Reference in New Issue
Block a user