mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
Added shortUrl field to serialized ShortUrl objects, both from CLI and REST
This commit is contained in:
parent
30297ac5ac
commit
2d6d35a398
12 changed files with 144 additions and 44 deletions
|
@ -13,6 +13,7 @@
|
|||
],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"ext-json": "*",
|
||||
"acelaya/ze-content-based-error-handler": "^2.2",
|
||||
"cocur/slugify": "^3.0",
|
||||
"doctrine/cache": "^1.6",
|
||||
|
|
|
@ -42,7 +42,11 @@ return [
|
|||
'config.url_shortener.domain',
|
||||
],
|
||||
Command\Shortcode\ResolveUrlCommand::class => [Service\UrlShortener::class, 'translator'],
|
||||
Command\Shortcode\ListShortcodesCommand::class => [Service\ShortUrlService::class, 'translator'],
|
||||
Command\Shortcode\ListShortcodesCommand::class => [
|
||||
Service\ShortUrlService::class,
|
||||
'translator',
|
||||
'config.url_shortener.domain',
|
||||
],
|
||||
Command\Shortcode\GetVisitsCommand::class => [Service\VisitsTracker::class, 'translator'],
|
||||
Command\Shortcode\GeneratePreviewCommand::class => [
|
||||
Service\ShortUrlService::class,
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\CLI\Command\Shortcode;
|
|||
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
|
||||
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -27,15 +28,23 @@ class ListShortcodesCommand extends Command
|
|||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $domainConfig;
|
||||
|
||||
public function __construct(ShortUrlServiceInterface $shortUrlService, TranslatorInterface $translator)
|
||||
{
|
||||
public function __construct(
|
||||
ShortUrlServiceInterface $shortUrlService,
|
||||
TranslatorInterface $translator,
|
||||
array $domainConfig
|
||||
) {
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
$this->translator = $translator;
|
||||
parent::__construct();
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
||||
public function configure()
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setName(self::NAME)
|
||||
->setDescription($this->translator->translate('List all short URLs'))
|
||||
|
@ -79,7 +88,7 @@ class ListShortcodesCommand extends Command
|
|||
);
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$page = (int) $input->getOption('page');
|
||||
|
@ -87,6 +96,7 @@ class ListShortcodesCommand extends Command
|
|||
$tags = $input->getOption('tags');
|
||||
$tags = ! empty($tags) ? \explode(',', $tags) : [];
|
||||
$showTags = $input->getOption('showTags');
|
||||
$transformer = new ShortUrlDataTransformer($this->domainConfig);
|
||||
|
||||
do {
|
||||
$result = $this->shortUrlService->listShortUrls($page, $searchTerm, $tags, $this->processOrderBy($input));
|
||||
|
@ -94,7 +104,8 @@ class ListShortcodesCommand extends Command
|
|||
|
||||
$headers = [
|
||||
$this->translator->translate('Short code'),
|
||||
$this->translator->translate('Original URL'),
|
||||
$this->translator->translate('Short URL'),
|
||||
$this->translator->translate('Long URL'),
|
||||
$this->translator->translate('Date created'),
|
||||
$this->translator->translate('Visits count'),
|
||||
];
|
||||
|
@ -104,17 +115,14 @@ class ListShortcodesCommand extends Command
|
|||
|
||||
$rows = [];
|
||||
foreach ($result as $row) {
|
||||
$shortUrl = $row->jsonSerialize();
|
||||
$shortUrl = $transformer->transform($row);
|
||||
if ($showTags) {
|
||||
$shortUrl['tags'] = [];
|
||||
foreach ($row->getTags() as $tag) {
|
||||
$shortUrl['tags'][] = $tag->getName();
|
||||
}
|
||||
$shortUrl['tags'] = implode(', ', $shortUrl['tags']);
|
||||
} else {
|
||||
unset($shortUrl['tags']);
|
||||
}
|
||||
|
||||
unset($shortUrl['originalUrl']);
|
||||
$rows[] = \array_values($shortUrl);
|
||||
}
|
||||
$io->table($headers, $rows);
|
||||
|
|
|
@ -30,7 +30,7 @@ class ListShortcodesCommandTest extends TestCase
|
|||
{
|
||||
$this->shortUrlService = $this->prophesize(ShortUrlServiceInterface::class);
|
||||
$app = new Application();
|
||||
$command = new ListShortcodesCommand($this->shortUrlService->reveal(), Translator::factory([]));
|
||||
$command = new ListShortcodesCommand($this->shortUrlService->reveal(), Translator::factory([]), []);
|
||||
$app->add($command);
|
||||
$this->commandTester = new CommandTester($command);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class ListShortcodesCommandTest extends TestCase
|
|||
// The paginator will return more than one page for the first 3 times
|
||||
$data = [];
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
$data[] = new ShortUrl();
|
||||
$data[] = (new ShortUrl())->setLongUrl('url_' . $i);
|
||||
}
|
||||
|
||||
$this->shortUrlService->listShortUrls(Argument::cetera())->will(function () use (&$data) {
|
||||
|
@ -74,7 +74,7 @@ class ListShortcodesCommandTest extends TestCase
|
|||
// The paginator will return more than one page
|
||||
$data = [];
|
||||
for ($i = 0; $i < 30; $i++) {
|
||||
$data[] = new ShortUrl();
|
||||
$data[] = (new ShortUrl())->setLongUrl('url_' . $i);
|
||||
}
|
||||
|
||||
$this->shortUrlService->listShortUrls(Argument::cetera())->willReturn(new Paginator(new ArrayAdapter($data)))
|
||||
|
|
|
@ -3,15 +3,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Common\Paginator\Util;
|
||||
|
||||
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
|
||||
use Zend\Paginator\Paginator;
|
||||
use Zend\Stdlib\ArrayUtils;
|
||||
|
||||
trait PaginatorUtilsTrait
|
||||
{
|
||||
protected function serializePaginator(Paginator $paginator): array
|
||||
private function serializePaginator(Paginator $paginator, ?DataTransformerInterface $transformer = null): array
|
||||
{
|
||||
return [
|
||||
'data' => ArrayUtils::iteratorToArray($paginator->getCurrentItems()),
|
||||
'data' => $this->serializeItems(ArrayUtils::iteratorToArray($paginator->getCurrentItems()), $transformer),
|
||||
'pagination' => [
|
||||
'currentPage' => $paginator->getCurrentPageNumber(),
|
||||
'pagesCount' => $paginator->count(),
|
||||
|
@ -22,13 +23,18 @@ trait PaginatorUtilsTrait
|
|||
];
|
||||
}
|
||||
|
||||
private function serializeItems(array $items, ?DataTransformerInterface $transformer = null): array
|
||||
{
|
||||
return $transformer === null ? $items : \array_map([$transformer, 'transform'], $items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if provided paginator is in last page
|
||||
*
|
||||
* @param Paginator $paginator
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLastPage(Paginator $paginator): bool
|
||||
private function isLastPage(Paginator $paginator): bool
|
||||
{
|
||||
return $paginator->getCurrentPageNumber() >= $paginator->count();
|
||||
}
|
||||
|
|
9
module/Common/src/Rest/DataTransformerInterface.php
Normal file
9
module/Common/src/Rest/DataTransformerInterface.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Common\Rest;
|
||||
|
||||
interface DataTransformerInterface
|
||||
{
|
||||
public function transform($value): array;
|
||||
}
|
|
@ -16,7 +16,7 @@ use Shlinkio\Shlink\Common\Entity\AbstractEntity;
|
|||
* @ORM\Entity(repositoryClass="Shlinkio\Shlink\Core\Repository\ShortUrlRepository")
|
||||
* @ORM\Table(name="short_urls")
|
||||
*/
|
||||
class ShortUrl extends AbstractEntity implements \JsonSerializable
|
||||
class ShortUrl extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
|
@ -84,21 +84,40 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalUrl(): string
|
||||
public function getLongUrl(): string
|
||||
{
|
||||
return $this->originalUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $originalUrl
|
||||
* @param string $longUrl
|
||||
* @return $this
|
||||
*/
|
||||
public function setOriginalUrl(string $originalUrl)
|
||||
public function setLongUrl(string $longUrl): self
|
||||
{
|
||||
$this->originalUrl = $originalUrl;
|
||||
$this->originalUrl = $longUrl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated Use getLongUrl() instead
|
||||
*/
|
||||
public function getOriginalUrl(): string
|
||||
{
|
||||
return $this->getLongUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $originalUrl
|
||||
* @return $this
|
||||
* @deprecated Use setLongUrl() instead
|
||||
*/
|
||||
public function setOriginalUrl(string $originalUrl): self
|
||||
{
|
||||
return $this->setLongUrl($originalUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@ -237,22 +256,4 @@ class ShortUrl extends AbstractEntity implements \JsonSerializable
|
|||
{
|
||||
return $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
|
||||
* @return mixed data which can be serialized by <b>json_encode</b>,
|
||||
* which is a value of any type other than a resource.
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'shortCode' => $this->shortCode,
|
||||
'originalUrl' => $this->originalUrl,
|
||||
'dateCreated' => $this->dateCreated !== null ? $this->dateCreated->format(\DateTime::ATOM) : null,
|
||||
'visitsCount' => $this->getVisitsCount(),
|
||||
'tags' => $this->tags->toArray(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class UrlShortener implements UrlShortenerInterface
|
|||
{
|
||||
use TagManagerTrait;
|
||||
|
||||
const DEFAULT_CHARS = '123456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';
|
||||
public const DEFAULT_CHARS = '123456789bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
|
|
54
module/Core/src/Transformer/ShortUrlDataTransformer.php
Normal file
54
module/Core/src/Transformer/ShortUrlDataTransformer.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\Transformer;
|
||||
|
||||
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
|
||||
class ShortUrlDataTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $domainConfig;
|
||||
|
||||
public function __construct(array $domainConfig)
|
||||
{
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShortUrl $value
|
||||
* @return array
|
||||
*/
|
||||
public function transform($value): array
|
||||
{
|
||||
$dateCreated = $value->getDateCreated();
|
||||
$longUrl = $value->getLongUrl();
|
||||
$shortCode = $value->getShortCode();
|
||||
|
||||
return [
|
||||
'shortCode' => $shortCode,
|
||||
'shortUrl' => \sprintf(
|
||||
'%s://%s/%s',
|
||||
$this->domainConfig['schema'] ?? 'http',
|
||||
$this->domainConfig['hostname'] ?? '',
|
||||
$shortCode
|
||||
),
|
||||
'longUrl' => $longUrl,
|
||||
'dateCreated' => $dateCreated !== null ? $dateCreated->format(\DateTime::ATOM) : null,
|
||||
'visitsCount' => $value->getVisitsCount(),
|
||||
'tags' => \array_map([$this, 'serializeTag'], $value->getTags()->toArray()),
|
||||
|
||||
// Deprecated
|
||||
'originalUrl' => $longUrl,
|
||||
];
|
||||
}
|
||||
|
||||
private function serializeTag(Tag $tag): string
|
||||
{
|
||||
return $tag->getName();
|
||||
}
|
||||
}
|
|
@ -61,7 +61,12 @@ return [
|
|||
Action\ShortCode\EditShortCodeAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink',],
|
||||
Action\ShortCode\ResolveUrlAction::class => [Service\UrlShortener::class, 'translator'],
|
||||
Action\Visit\GetVisitsAction::class => [Service\VisitsTracker::class, 'translator', 'Logger_Shlink'],
|
||||
Action\ShortCode\ListShortCodesAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink'],
|
||||
Action\ShortCode\ListShortCodesAction::class => [
|
||||
Service\ShortUrlService::class,
|
||||
'translator',
|
||||
'config.url_shortener.domain',
|
||||
'Logger_Shlink',
|
||||
],
|
||||
Action\ShortCode\EditShortCodeTagsAction::class => [
|
||||
Service\ShortUrlService::class,
|
||||
'translator',
|
||||
|
|
|
@ -8,6 +8,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
|
||||
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
@ -28,15 +29,21 @@ class ListShortCodesAction extends AbstractRestAction
|
|||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $domainConfig;
|
||||
|
||||
public function __construct(
|
||||
ShortUrlServiceInterface $shortUrlService,
|
||||
TranslatorInterface $translator,
|
||||
array $domainConfig,
|
||||
LoggerInterface $logger = null
|
||||
) {
|
||||
parent::__construct($logger);
|
||||
$this->shortUrlService = $shortUrlService;
|
||||
$this->translator = $translator;
|
||||
$this->domainConfig = $domainConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +56,9 @@ class ListShortCodesAction extends AbstractRestAction
|
|||
try {
|
||||
$params = $this->queryToListParams($request->getQueryParams());
|
||||
$shortUrls = $this->shortUrlService->listShortUrls(...$params);
|
||||
return new JsonResponse(['shortUrls' => $this->serializePaginator($shortUrls)]);
|
||||
return new JsonResponse(['shortUrls' => $this->serializePaginator($shortUrls, new ShortUrlDataTransformer(
|
||||
$this->domainConfig
|
||||
))]);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Unexpected error while listing short URLs.' . PHP_EOL . $e);
|
||||
return new JsonResponse([
|
||||
|
|
|
@ -26,7 +26,10 @@ class ListShortCodesActionTest extends TestCase
|
|||
public function setUp()
|
||||
{
|
||||
$this->service = $this->prophesize(ShortUrlService::class);
|
||||
$this->action = new ListShortCodesAction($this->service->reveal(), Translator::factory([]));
|
||||
$this->action = new ListShortCodesAction($this->service->reveal(), Translator::factory([]), [
|
||||
'hostname' => 'doma.in',
|
||||
'schema' => 'https',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue