Created action to set the togas for a short url

This commit is contained in:
Alejandro Celaya 2016-08-21 16:52:26 +02:00
parent 372488cbb4
commit 1da285a63a
7 changed files with 130 additions and 2 deletions

View file

@ -5,7 +5,7 @@ use Shlinkio\Shlink\Common\Exception\RuntimeException;
class InvalidShortCodeException extends RuntimeException class InvalidShortCodeException extends RuntimeException
{ {
public static function fromShortCode($shortCode, $charSet, \Exception $previous = null) public static function fromCharset($shortCode, $charSet, \Exception $previous = null)
{ {
$code = isset($previous) ? $previous->getCode() : -1; $code = isset($previous) ? $previous->getCode() : -1;
return new static( return new static(
@ -14,4 +14,9 @@ class InvalidShortCodeException extends RuntimeException
$previous $previous
); );
} }
public static function fromNotFoundShortCode($shortCode)
{
return new static(sprintf('Provided short code "%s" does not belong to a short URL', $shortCode));
}
} }

View file

@ -5,11 +5,15 @@ use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter; use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository; use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Zend\Paginator\Paginator; use Zend\Paginator\Paginator;
class ShortUrlService implements ShortUrlServiceInterface class ShortUrlService implements ShortUrlServiceInterface
{ {
use TagManagerTrait;
/** /**
* @var EntityManagerInterface * @var EntityManagerInterface
*/ */
@ -40,4 +44,26 @@ class ShortUrlService implements ShortUrlServiceInterface
return $paginator; return $paginator;
} }
/**
* @param string $shortCode
* @param string[] $tags
* @return ShortUrl
* @throws InvalidShortCodeException
*/
public function setTagsByShortCode($shortCode, array $tags = [])
{
/** @var ShortUrl $shortUrl */
$shortUrl = $this->em->getRepository(ShortUrl::class)->findOneBy([
'shortCode' => $shortCode,
]);
if (! isset($shortUrl)) {
throw InvalidShortCodeException::fromNotFoundShortCode($shortCode);
}
$shortUrl->setTags($this->tagNamesToEntities($this->em, $tags));
$this->em->flush();
return $shortUrl;
}
} }

View file

@ -2,6 +2,7 @@
namespace Shlinkio\Shlink\Core\Service; namespace Shlinkio\Shlink\Core\Service;
use Shlinkio\Shlink\Core\Entity\ShortUrl; use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Zend\Paginator\Paginator; use Zend\Paginator\Paginator;
interface ShortUrlServiceInterface interface ShortUrlServiceInterface
@ -11,4 +12,12 @@ interface ShortUrlServiceInterface
* @return ShortUrl[]|Paginator * @return ShortUrl[]|Paginator
*/ */
public function listShortUrls($page = 1); public function listShortUrls($page = 1);
/**
* @param string $shortCode
* @param string[] $tags
* @return ShortUrl
* @throws InvalidShortCodeException
*/
public function setTagsByShortCode($shortCode, array $tags = []);
} }

View file

@ -161,7 +161,7 @@ class UrlShortener implements UrlShortenerInterface
// Validate short code format // Validate short code format
if (! preg_match('|[' . $this->chars . "]+|", $shortCode)) { if (! preg_match('|[' . $this->chars . "]+|", $shortCode)) {
throw InvalidShortCodeException::fromShortCode($shortCode, $this->chars); throw InvalidShortCodeException::fromCharset($shortCode, $this->chars);
} }
/** @var ShortUrl $shortUrl */ /** @var ShortUrl $shortUrl */

View file

@ -16,6 +16,7 @@ trait TagManagerTrait
{ {
$entities = []; $entities = [];
foreach ($tags as $tagName) { foreach ($tags as $tagName) {
$tagName = $this->normalizeTagName($tagName);
$tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?: (new Tag())->setName($tagName); $tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?: (new Tag())->setName($tagName);
$em->persist($tag); $em->persist($tag);
$entities[] = $tag; $entities[] = $tag;
@ -23,4 +24,15 @@ trait TagManagerTrait
return new Collections\ArrayCollection($entities); return new Collections\ArrayCollection($entities);
} }
/**
* Tag names are trimmed, lowercased and spaces are replaced by dashes
*
* @param string $tagName
* @return string
*/
protected function normalizeTagName($tagName)
{
return str_replace(' ', '-', strtolower(trim($tagName)));
}
} }

View file

@ -34,6 +34,12 @@ return [
'middleware' => Action\GetVisitsAction::class, 'middleware' => Action\GetVisitsAction::class,
'allowed_methods' => ['GET', 'OPTIONS'], 'allowed_methods' => ['GET', 'OPTIONS'],
], ],
[
'name' => 'rest-edit-tags',
'path' => '/rest/short-codes/{shortCode}/tags',
'middleware' => Action\EditTagsAction::class,
'allowed_methods' => ['PUT', 'OPTIONS'],
],
], ],
]; ];

View file

@ -0,0 +1,70 @@
<?php
namespace Shlinkio\Shlink\Rest\Action;
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Service\ShortUrlService;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
use Zend\I18n\Translator\TranslatorInterface;
class EditTagsAction extends AbstractRestAction
{
/**
* @var ShortUrlServiceInterface
*/
private $shortUrlService;
/**
* @var TranslatorInterface
*/
private $translator;
/**
* EditTagsAction constructor.
* @param ShortUrlServiceInterface $shortUrlService
* @param TranslatorInterface $translator
* @param LoggerInterface|null $logger
*
* @Inject({ShortUrlService::class, "translator", "Logger_Shlink"})
*/
public function __construct(
ShortUrlServiceInterface $shortUrlService,
TranslatorInterface $translator,
LoggerInterface $logger = null
) {
parent::__construct($logger);
$this->shortUrlService = $shortUrlService;
$this->translator = $translator;
}
/**
* @param Request $request
* @param Response $response
* @param callable|null $out
* @return null|Response
*/
protected function dispatch(Request $request, Response $response, callable $out = null)
{
$shortCode = $request->getAttribute('shortCode');
$bodyParams = $request->getParsedBody();
if (! isset($bodyParams['tags'])) {
return new JsonResponse([
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
'message' => $this->translator->translate('A list of tags was not provided'),
], 400);
}
$tags = $bodyParams['tags'];
try {
$shortUrl = $this->shortUrlService->setTagsByShortCode($shortCode, $tags);
return new JsonResponse(['tags' => $shortUrl->getTags()->toArray()]);
} catch (InvalidShortCodeException $e) {
return $out($request, $response->withStatus(404), 'Not found');
}
}
}