mirror of
				https://github.com/shlinkio/shlink.git
				synced 2025-02-25 18:45:27 -06:00 
			
		
		
		
	Enhanced list tags endpoint so that it can also return stats foir every tag
This commit is contained in:
		@@ -78,10 +78,10 @@ return [
 | 
				
			|||||||
        Command\Api\DisableKeyCommand::class => [ApiKeyService::class],
 | 
					        Command\Api\DisableKeyCommand::class => [ApiKeyService::class],
 | 
				
			||||||
        Command\Api\ListKeysCommand::class => [ApiKeyService::class],
 | 
					        Command\Api\ListKeysCommand::class => [ApiKeyService::class],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Command\Tag\ListTagsCommand::class => [Service\Tag\TagService::class],
 | 
					        Command\Tag\ListTagsCommand::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Command\Tag\CreateTagCommand::class => [Service\Tag\TagService::class],
 | 
					        Command\Tag\CreateTagCommand::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Command\Tag\RenameTagCommand::class => [Service\Tag\TagService::class],
 | 
					        Command\Tag\RenameTagCommand::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Command\Tag\DeleteTagsCommand::class => [Service\Tag\TagService::class],
 | 
					        Command\Tag\DeleteTagsCommand::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Command\Db\CreateDatabaseCommand::class => [
 | 
					        Command\Db\CreateDatabaseCommand::class => [
 | 
				
			||||||
            LockFactory::class,
 | 
					            LockFactory::class,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
					namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
					use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Command\Command;
 | 
					use Symfony\Component\Console\Command\Command;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputInterface;
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputOption;
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
					namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
					use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Command\Command;
 | 
					use Symfony\Component\Console\Command\Command;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputInterface;
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputOption;
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
@@ -16,9 +16,9 @@ class DeleteTagsCommand extends Command
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public const NAME = 'tag:delete';
 | 
					    public const NAME = 'tag:delete';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private TagServiceInterface $tagService;
 | 
					    private \Shlinkio\Shlink\Core\Tag\TagServiceInterface $tagService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(TagServiceInterface $tagService)
 | 
					    public function __construct(\Shlinkio\Shlink\Core\Tag\TagServiceInterface $tagService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        parent::__construct();
 | 
					        parent::__construct();
 | 
				
			||||||
        $this->tagService = $tagService;
 | 
					        $this->tagService = $tagService;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
				
			|||||||
use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
					use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Util\ShlinkTable;
 | 
					use Shlinkio\Shlink\CLI\Util\ShlinkTable;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Command\Command;
 | 
					use Symfony\Component\Console\Command\Command;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputInterface;
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink\CLI\Command\Tag;
 | 
				
			|||||||
use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
					use Shlinkio\Shlink\CLI\Util\ExitCodes;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Command\Command;
 | 
					use Symfony\Component\Console\Command\Command;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputArgument;
 | 
					use Symfony\Component\Console\Input\InputArgument;
 | 
				
			||||||
use Symfony\Component\Console\Input\InputInterface;
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
@@ -20,7 +20,7 @@ class RenameTagCommand extends Command
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private TagServiceInterface $tagService;
 | 
					    private TagServiceInterface $tagService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(TagServiceInterface $tagService)
 | 
					    public function __construct(\Shlinkio\Shlink\Core\Tag\TagServiceInterface $tagService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        parent::__construct();
 | 
					        parent::__construct();
 | 
				
			||||||
        $this->tagService = $tagService;
 | 
					        $this->tagService = $tagService;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 | 
				
			|||||||
use PHPUnit\Framework\TestCase;
 | 
					use PHPUnit\Framework\TestCase;
 | 
				
			||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Command\Tag\CreateTagCommand;
 | 
					use Shlinkio\Shlink\CLI\Command\Tag\CreateTagCommand;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Application;
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
use Symfony\Component\Console\Tester\CommandTester;
 | 
					use Symfony\Component\Console\Tester\CommandTester;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace ShlinkioTest\Shlink\CLI\Command\Tag;
 | 
				
			|||||||
use PHPUnit\Framework\TestCase;
 | 
					use PHPUnit\Framework\TestCase;
 | 
				
			||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Command\Tag\DeleteTagsCommand;
 | 
					use Shlinkio\Shlink\CLI\Command\Tag\DeleteTagsCommand;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Application;
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
use Symfony\Component\Console\Tester\CommandTester;
 | 
					use Symfony\Component\Console\Tester\CommandTester;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,7 +18,7 @@ class DeleteTagsCommandTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function setUp(): void
 | 
					    public function setUp(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->tagService = $this->prophesize(TagServiceInterface::class);
 | 
					        $this->tagService = $this->prophesize(\Shlinkio\Shlink\Core\Tag\TagServiceInterface::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $command = new DeleteTagsCommand($this->tagService->reveal());
 | 
					        $command = new DeleteTagsCommand($this->tagService->reveal());
 | 
				
			||||||
        $app = new Application();
 | 
					        $app = new Application();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase;
 | 
				
			|||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\CLI\Command\Tag\ListTagsCommand;
 | 
					use Shlinkio\Shlink\CLI\Command\Tag\ListTagsCommand;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Application;
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
use Symfony\Component\Console\Tester\CommandTester;
 | 
					use Symfony\Component\Console\Tester\CommandTester;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,7 +19,7 @@ class ListTagsCommandTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function setUp(): void
 | 
					    public function setUp(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->tagService = $this->prophesize(TagServiceInterface::class);
 | 
					        $this->tagService = $this->prophesize(\Shlinkio\Shlink\Core\Tag\TagServiceInterface::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $command = new ListTagsCommand($this->tagService->reveal());
 | 
					        $command = new ListTagsCommand($this->tagService->reveal());
 | 
				
			||||||
        $app = new Application();
 | 
					        $app = new Application();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			|||||||
use Shlinkio\Shlink\CLI\Command\Tag\RenameTagCommand;
 | 
					use Shlinkio\Shlink\CLI\Command\Tag\RenameTagCommand;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Symfony\Component\Console\Application;
 | 
					use Symfony\Component\Console\Application;
 | 
				
			||||||
use Symfony\Component\Console\Tester\CommandTester;
 | 
					use Symfony\Component\Console\Tester\CommandTester;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ return [
 | 
				
			|||||||
            Service\ShortUrlService::class => ConfigAbstractFactory::class,
 | 
					            Service\ShortUrlService::class => ConfigAbstractFactory::class,
 | 
				
			||||||
            Visit\VisitLocator::class => ConfigAbstractFactory::class,
 | 
					            Visit\VisitLocator::class => ConfigAbstractFactory::class,
 | 
				
			||||||
            Visit\VisitsStatsHelper::class => ConfigAbstractFactory::class,
 | 
					            Visit\VisitsStatsHelper::class => ConfigAbstractFactory::class,
 | 
				
			||||||
            Service\Tag\TagService::class => ConfigAbstractFactory::class,
 | 
					            Tag\TagService::class => ConfigAbstractFactory::class,
 | 
				
			||||||
            Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
 | 
					            Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
 | 
				
			||||||
            Service\ShortUrl\ShortUrlResolver::class => ConfigAbstractFactory::class,
 | 
					            Service\ShortUrl\ShortUrlResolver::class => ConfigAbstractFactory::class,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,7 +58,7 @@ return [
 | 
				
			|||||||
        Service\ShortUrlService::class => ['em', Service\ShortUrl\ShortUrlResolver::class, Util\UrlValidator::class],
 | 
					        Service\ShortUrlService::class => ['em', Service\ShortUrl\ShortUrlResolver::class, Util\UrlValidator::class],
 | 
				
			||||||
        Visit\VisitLocator::class => ['em'],
 | 
					        Visit\VisitLocator::class => ['em'],
 | 
				
			||||||
        Visit\VisitsStatsHelper::class => ['em'],
 | 
					        Visit\VisitsStatsHelper::class => ['em'],
 | 
				
			||||||
        Service\Tag\TagService::class => ['em'],
 | 
					        Tag\TagService::class => ['em'],
 | 
				
			||||||
        Service\ShortUrl\DeleteShortUrlService::class => [
 | 
					        Service\ShortUrl\DeleteShortUrlService::class => [
 | 
				
			||||||
            'em',
 | 
					            'em',
 | 
				
			||||||
            Options\DeleteShortUrlsOptions::class,
 | 
					            Options\DeleteShortUrlsOptions::class,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,4 +24,10 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
 | 
				
			|||||||
    $builder->createField('name', Types::STRING)
 | 
					    $builder->createField('name', Types::STRING)
 | 
				
			||||||
            ->unique()
 | 
					            ->unique()
 | 
				
			||||||
            ->build();
 | 
					            ->build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $builder->createManyToMany('shortUrls', Entity\ShortUrl::class)
 | 
				
			||||||
 | 
					            ->setJoinTable(determineTableName('short_urls_in_tags', $emConfig))
 | 
				
			||||||
 | 
					            ->addInverseJoinColumn('short_url_id', 'id', true, false, 'CASCADE')
 | 
				
			||||||
 | 
					            ->addJoinColumn('tag_id', 'id', true, false, 'CASCADE')
 | 
				
			||||||
 | 
					            ->build();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,16 +4,19 @@ declare(strict_types=1);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Shlinkio\Shlink\Core\Entity;
 | 
					namespace Shlinkio\Shlink\Core\Entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Doctrine\Common\Collections;
 | 
				
			||||||
use JsonSerializable;
 | 
					use JsonSerializable;
 | 
				
			||||||
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
 | 
					use Shlinkio\Shlink\Common\Entity\AbstractEntity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Tag extends AbstractEntity implements JsonSerializable
 | 
					class Tag extends AbstractEntity implements JsonSerializable
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private string $name;
 | 
					    private string $name;
 | 
				
			||||||
 | 
					    private Collections\Collection $shortUrls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(string $name)
 | 
					    public function __construct(string $name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->name = $name;
 | 
					        $this->name = $name;
 | 
				
			||||||
 | 
					        $this->shortUrls = new Collections\ArrayCollection();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function rename(string $name): void
 | 
					    public function rename(string $name): void
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ namespace Shlinkio\Shlink\Core\Repository;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use Doctrine\ORM\EntityRepository;
 | 
					use Doctrine\ORM\EntityRepository;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
 | 
				
			||||||
 | 
					use function Functional\map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagRepository extends EntityRepository implements TagRepositoryInterface
 | 
					class TagRepository extends EntityRepository implements TagRepositoryInterface
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -21,4 +23,25 @@ class TagRepository extends EntityRepository implements TagRepositoryInterface
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return $qb->getQuery()->execute();
 | 
					        return $qb->getQuery()->execute();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @return TagInfo[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function findTagsWithInfo(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $dql = <<<DQL
 | 
				
			||||||
 | 
					            SELECT t AS tag, COUNT(DISTINCT s.id) AS shortUrlsCount, COUNT(DISTINCT v.id) AS visitsCount
 | 
				
			||||||
 | 
					            FROM Shlinkio\Shlink\Core\Entity\Tag t
 | 
				
			||||||
 | 
					            LEFT JOIN t.shortUrls s
 | 
				
			||||||
 | 
					            LEFT JOIN s.visits v
 | 
				
			||||||
 | 
					            GROUP BY tag
 | 
				
			||||||
 | 
					            ORDER BY t.name ASC
 | 
				
			||||||
 | 
					        DQL;
 | 
				
			||||||
 | 
					        $query = $this->getEntityManager()->createQuery($dql);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return map(
 | 
				
			||||||
 | 
					            $query->getResult(),
 | 
				
			||||||
 | 
					            fn (array $row) => new TagInfo($row['tag'], (int) $row['shortUrlsCount'], (int) $row['visitsCount']),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,14 @@ declare(strict_types=1);
 | 
				
			|||||||
namespace Shlinkio\Shlink\Core\Repository;
 | 
					namespace Shlinkio\Shlink\Core\Repository;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Doctrine\Persistence\ObjectRepository;
 | 
					use Doctrine\Persistence\ObjectRepository;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TagRepositoryInterface extends ObjectRepository
 | 
					interface TagRepositoryInterface extends ObjectRepository
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public function deleteByName(array $names): int;
 | 
					    public function deleteByName(array $names): int;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @return TagInfo[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function findTagsWithInfo(): array;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								module/Core/src/Tag/Model/TagInfo.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								module/Core/src/Tag/Model/TagInfo.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Shlinkio\Shlink\Core\Tag\Model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use JsonSerializable;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class TagInfo implements JsonSerializable
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    private Tag $tag;
 | 
				
			||||||
 | 
					    private int $shortUrlsCount;
 | 
				
			||||||
 | 
					    private int $visitsCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(Tag $tag, int $shortUrlsCount, int $visitsCount)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->tag = $tag;
 | 
				
			||||||
 | 
					        $this->shortUrlsCount = $shortUrlsCount;
 | 
				
			||||||
 | 
					        $this->visitsCount = $visitsCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function tag(): Tag
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->tag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function shortUrlsCount(): int
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->shortUrlsCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function visitsCount(): int
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->visitsCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function jsonSerialize(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            'tag' => $this->tag,
 | 
				
			||||||
 | 
					            'shortUrlsCount' => $this->shortUrlsCount,
 | 
				
			||||||
 | 
					            'visitsCount' => $this->visitsCount,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Shlinkio\Shlink\Core\Service\Tag;
 | 
					namespace Shlinkio\Shlink\Core\Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Doctrine\Common\Collections\Collection;
 | 
					use Doctrine\Common\Collections\Collection;
 | 
				
			||||||
use Doctrine\ORM;
 | 
					use Doctrine\ORM;
 | 
				
			||||||
@@ -10,6 +10,8 @@ use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			|||||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Repository\TagRepository;
 | 
					use Shlinkio\Shlink\Core\Repository\TagRepository;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Repository\TagRepositoryInterface;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
 | 
					use Shlinkio\Shlink\Core\Util\TagManagerTrait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagService implements TagServiceInterface
 | 
					class TagService implements TagServiceInterface
 | 
				
			||||||
@@ -25,7 +27,6 @@ class TagService implements TagServiceInterface
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @return Tag[]
 | 
					     * @return Tag[]
 | 
				
			||||||
     * @throws \UnexpectedValueException
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function listTags(): array
 | 
					    public function listTags(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -34,6 +35,16 @@ class TagService implements TagServiceInterface
 | 
				
			|||||||
        return $tags;
 | 
					        return $tags;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @return TagInfo[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function tagsInfo(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /** @var TagRepositoryInterface $repo */
 | 
				
			||||||
 | 
					        $repo = $this->em->getRepository(Tag::class);
 | 
				
			||||||
 | 
					        return $repo->findTagsWithInfo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param string[] $tagNames
 | 
					     * @param string[] $tagNames
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
declare(strict_types=1);
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Shlinkio\Shlink\Core\Service\Tag;
 | 
					namespace Shlinkio\Shlink\Core\Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Doctrine\Common\Collections\Collection;
 | 
					use Doctrine\Common\Collections\Collection;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TagServiceInterface
 | 
					interface TagServiceInterface
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -16,6 +17,11 @@ interface TagServiceInterface
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function listTags(): array;
 | 
					    public function listTags(): array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @return TagInfo[]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function tagsInfo(): array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param string[] $tagNames
 | 
					     * @param string[] $tagNames
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			|||||||
use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagConflictException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
					use Shlinkio\Shlink\Core\Exception\TagNotFoundException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Repository\TagRepository;
 | 
					use Shlinkio\Shlink\Core\Repository\TagRepository;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagService;
 | 
					use Shlinkio\Shlink\Core\Tag\TagService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagServiceTest extends TestCase
 | 
					class TagServiceTest extends TestCase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,10 +65,10 @@ return [
 | 
				
			|||||||
        Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class],
 | 
					        Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class],
 | 
				
			||||||
        Action\ShortUrl\ListShortUrlsAction::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'],
 | 
					        Action\ShortUrl\ListShortUrlsAction::class => [Service\ShortUrlService::class, 'config.url_shortener.domain'],
 | 
				
			||||||
        Action\ShortUrl\EditShortUrlTagsAction::class => [Service\ShortUrlService::class],
 | 
					        Action\ShortUrl\EditShortUrlTagsAction::class => [Service\ShortUrlService::class],
 | 
				
			||||||
        Action\Tag\ListTagsAction::class => [Service\Tag\TagService::class],
 | 
					        Action\Tag\ListTagsAction::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Action\Tag\DeleteTagsAction::class => [Service\Tag\TagService::class],
 | 
					        Action\Tag\DeleteTagsAction::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Action\Tag\CreateTagsAction::class => [Service\Tag\TagService::class],
 | 
					        Action\Tag\CreateTagsAction::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
        Action\Tag\UpdateTagAction::class => [Service\Tag\TagService::class],
 | 
					        Action\Tag\UpdateTagAction::class => [\Shlinkio\Shlink\Core\Tag\TagService::class],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],
 | 
					        Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],
 | 
				
			||||||
        Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [
 | 
					        Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
 | 
				
			|||||||
use Laminas\Diactoros\Response\JsonResponse;
 | 
					use Laminas\Diactoros\Response\JsonResponse;
 | 
				
			||||||
use Psr\Http\Message\ResponseInterface;
 | 
					use Psr\Http\Message\ResponseInterface;
 | 
				
			||||||
use Psr\Http\Message\ServerRequestInterface;
 | 
					use Psr\Http\Message\ServerRequestInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
					use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateTagsAction extends AbstractRestAction
 | 
					class CreateTagsAction extends AbstractRestAction
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
 | 
				
			|||||||
use Laminas\Diactoros\Response\EmptyResponse;
 | 
					use Laminas\Diactoros\Response\EmptyResponse;
 | 
				
			||||||
use Psr\Http\Message\ResponseInterface;
 | 
					use Psr\Http\Message\ResponseInterface;
 | 
				
			||||||
use Psr\Http\Message\ServerRequestInterface;
 | 
					use Psr\Http\Message\ServerRequestInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
					use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeleteTagsAction extends AbstractRestAction
 | 
					class DeleteTagsAction extends AbstractRestAction
 | 
				
			||||||
@@ -17,7 +17,7 @@ class DeleteTagsAction extends AbstractRestAction
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private TagServiceInterface $tagService;
 | 
					    private TagServiceInterface $tagService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(TagServiceInterface $tagService)
 | 
					    public function __construct(\Shlinkio\Shlink\Core\Tag\TagServiceInterface $tagService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->tagService = $tagService;
 | 
					        $this->tagService = $tagService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,12 @@ namespace Shlinkio\Shlink\Rest\Action\Tag;
 | 
				
			|||||||
use Laminas\Diactoros\Response\JsonResponse;
 | 
					use Laminas\Diactoros\Response\JsonResponse;
 | 
				
			||||||
use Psr\Http\Message\ResponseInterface;
 | 
					use Psr\Http\Message\ResponseInterface;
 | 
				
			||||||
use Psr\Http\Message\ServerRequestInterface;
 | 
					use Psr\Http\Message\ServerRequestInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
 | 
				
			||||||
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
					use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use function Functional\map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ListTagsAction extends AbstractRestAction
 | 
					class ListTagsAction extends AbstractRestAction
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected const ROUTE_PATH = '/tags';
 | 
					    protected const ROUTE_PATH = '/tags';
 | 
				
			||||||
@@ -22,19 +25,27 @@ class ListTagsAction extends AbstractRestAction
 | 
				
			|||||||
        $this->tagService = $tagService;
 | 
					        $this->tagService = $tagService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Process an incoming server request and return a response, optionally delegating
 | 
					 | 
				
			||||||
     * to the next middleware component to create the response.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @throws \InvalidArgumentException
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function handle(ServerRequestInterface $request): ResponseInterface
 | 
					    public function handle(ServerRequestInterface $request): ResponseInterface
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $query = $request->getQueryParams();
 | 
				
			||||||
 | 
					        $withStats = ($query['withStats'] ?? null) === 'true';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (! $withStats) {
 | 
				
			||||||
            return new JsonResponse([
 | 
					            return new JsonResponse([
 | 
				
			||||||
                'tags' => [
 | 
					                'tags' => [
 | 
				
			||||||
                    'data' => $this->tagService->listTags(),
 | 
					                    'data' => $this->tagService->listTags(),
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $tagsInfo = $this->tagService->tagsInfo();
 | 
				
			||||||
 | 
					        $data = map($tagsInfo, fn (TagInfo $info) => (string) $info->tag());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new JsonResponse([
 | 
				
			||||||
 | 
					            'tags' => [
 | 
				
			||||||
 | 
					                'data' => $data,
 | 
				
			||||||
 | 
					                'stats' => $tagsInfo,
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ use Laminas\Diactoros\Response\EmptyResponse;
 | 
				
			|||||||
use Psr\Http\Message\ResponseInterface;
 | 
					use Psr\Http\Message\ResponseInterface;
 | 
				
			||||||
use Psr\Http\Message\ServerRequestInterface;
 | 
					use Psr\Http\Message\ServerRequestInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
 | 
					use Shlinkio\Shlink\Core\Exception\ValidationException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
					use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UpdateTagAction extends AbstractRestAction
 | 
					class UpdateTagAction extends AbstractRestAction
 | 
				
			||||||
@@ -16,7 +16,7 @@ class UpdateTagAction extends AbstractRestAction
 | 
				
			|||||||
    protected const ROUTE_PATH = '/tags';
 | 
					    protected const ROUTE_PATH = '/tags';
 | 
				
			||||||
    protected const ROUTE_ALLOWED_METHODS = [self::METHOD_PUT];
 | 
					    protected const ROUTE_ALLOWED_METHODS = [self::METHOD_PUT];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private TagServiceInterface $tagService;
 | 
					    private \Shlinkio\Shlink\Core\Tag\TagServiceInterface $tagService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(TagServiceInterface $tagService)
 | 
					    public function __construct(TagServiceInterface $tagService)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 | 
				
			|||||||
use Laminas\Diactoros\ServerRequest;
 | 
					use Laminas\Diactoros\ServerRequest;
 | 
				
			||||||
use PHPUnit\Framework\TestCase;
 | 
					use PHPUnit\Framework\TestCase;
 | 
				
			||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\Tag\CreateTagsAction;
 | 
					use Shlinkio\Shlink\Rest\Action\Tag\CreateTagsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateTagsActionTest extends TestCase
 | 
					class CreateTagsActionTest extends TestCase
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace ShlinkioTest\Shlink\Rest\Action\Tag;
 | 
				
			|||||||
use Laminas\Diactoros\ServerRequest;
 | 
					use Laminas\Diactoros\ServerRequest;
 | 
				
			||||||
use PHPUnit\Framework\TestCase;
 | 
					use PHPUnit\Framework\TestCase;
 | 
				
			||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\Tag\DeleteTagsAction;
 | 
					use Shlinkio\Shlink\Rest\Action\Tag\DeleteTagsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeleteTagsActionTest extends TestCase
 | 
					class DeleteTagsActionTest extends TestCase
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ use Laminas\Diactoros\ServerRequest;
 | 
				
			|||||||
use PHPUnit\Framework\TestCase;
 | 
					use PHPUnit\Framework\TestCase;
 | 
				
			||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\Tag\ListTagsAction;
 | 
					use Shlinkio\Shlink\Rest\Action\Tag\ListTagsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use function Shlinkio\Shlink\Common\json_decode;
 | 
					use function Shlinkio\Shlink\Common\json_decode;
 | 
				
			||||||
@@ -20,7 +20,7 @@ class ListTagsActionTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function setUp(): void
 | 
					    public function setUp(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->tagService = $this->prophesize(TagServiceInterface::class);
 | 
					        $this->tagService = $this->prophesize(\Shlinkio\Shlink\Core\Tag\TagServiceInterface::class);
 | 
				
			||||||
        $this->action = new ListTagsAction($this->tagService->reveal());
 | 
					        $this->action = new ListTagsAction($this->tagService->reveal());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
 | 
				
			|||||||
use Prophecy\Prophecy\ObjectProphecy;
 | 
					use Prophecy\Prophecy\ObjectProphecy;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Entity\Tag;
 | 
					use Shlinkio\Shlink\Core\Entity\Tag;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
 | 
					use Shlinkio\Shlink\Core\Exception\ValidationException;
 | 
				
			||||||
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
 | 
					use Shlinkio\Shlink\Core\Tag\TagServiceInterface;
 | 
				
			||||||
use Shlinkio\Shlink\Rest\Action\Tag\UpdateTagAction;
 | 
					use Shlinkio\Shlink\Rest\Action\Tag\UpdateTagAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UpdateTagActionTest extends TestCase
 | 
					class UpdateTagActionTest extends TestCase
 | 
				
			||||||
@@ -19,7 +19,7 @@ class UpdateTagActionTest extends TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function setUp(): void
 | 
					    public function setUp(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->tagService = $this->prophesize(TagServiceInterface::class);
 | 
					        $this->tagService = $this->prophesize(\Shlinkio\Shlink\Core\Tag\TagServiceInterface::class);
 | 
				
			||||||
        $this->action = new UpdateTagAction($this->tagService->reveal());
 | 
					        $this->action = new UpdateTagAction($this->tagService->reveal());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user