Created action which allows short URLs to be created on a single API request

This commit is contained in:
Alejandro Celaya 2018-05-03 13:21:43 +02:00
parent 28650aee2b
commit e5ef8d7f8c
8 changed files with 84 additions and 23 deletions

View File

@ -8,6 +8,7 @@ return [
'auth' => [
'routes_whitelist' => [
Action\AuthenticateAction::class,
Action\ShortCode\SingleStepCreateShortCodeAction::class,
],
],

View File

@ -21,6 +21,7 @@ return [
Action\AuthenticateAction::class => ConfigAbstractFactory::class,
Action\CreateShortCodeAction::class => ConfigAbstractFactory::class,
Action\ShortCode\SingleStepCreateShortCodeAction::class => ConfigAbstractFactory::class,
Action\EditShortCodeAction::class => ConfigAbstractFactory::class,
Action\ResolveUrlAction::class => ConfigAbstractFactory::class,
Action\GetVisitsAction::class => ConfigAbstractFactory::class,
@ -49,6 +50,13 @@ return [
'config.url_shortener.domain',
'Logger_Shlink',
],
Action\ShortCode\SingleStepCreateShortCodeAction::class => [
Service\UrlShortener::class,
'translator',
ApiKeyService::class,
'config.url_shortener.domain',
'Logger_Shlink',
],
Action\EditShortCodeAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink',],
Action\ResolveUrlAction::class => [Service\UrlShortener::class, 'translator'],
Action\GetVisitsAction::class => [Service\VisitsTracker::class, 'translator', 'Logger_Shlink'],

View File

@ -10,12 +10,7 @@ return [
// Short codes
Action\CreateShortCodeAction::getRouteDef(),
// [
// 'name' => Action\CreateShortCodeAction::class,
// 'path' => '/short-codes',
// 'middleware' => Action\CreateShortCodeAction::class,
// 'allowed_methods' => [RequestMethod::METHOD_GET],
// ],
Action\ShortCode\SingleStepCreateShortCodeAction::getRouteDef(),
Action\EditShortCodeAction::getRouteDef(),
Action\ResolveUrlAction::getRouteDef(),
Action\ListShortCodesAction::getRouteDef(),

View File

@ -5,7 +5,6 @@ namespace Shlinkio\Shlink\Rest\Action;
use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Rest\Action\ShortCode\AbstractCreateShortCodeAction;
@ -19,7 +18,6 @@ class CreateShortCodeAction extends AbstractCreateShortCodeAction
/**
* @param Request $request
* @return CreateShortCodeData
* @throws ValidationException
* @throws InvalidArgumentException
* @throws \InvalidArgumentException
*/
@ -27,7 +25,7 @@ class CreateShortCodeAction extends AbstractCreateShortCodeAction
{
$postData = (array) $request->getParsedBody();
if (! isset($postData['longUrl'])) {
throw new InvalidArgumentException('A URL was not provided');
throw new InvalidArgumentException($this->translator->translate('A URL was not provided'));
}
return new CreateShortCodeData(

View File

@ -9,7 +9,6 @@ use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
@ -31,7 +30,7 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
/**
* @var TranslatorInterface
*/
private $translator;
protected $translator;
public function __construct(
UrlShortenerInterface $urlShortener,
@ -57,11 +56,11 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
$shortCodeMeta = $shortCodeData->getMeta();
$longUrl = $shortCodeData->getLongUrl();
$customSlug = $shortCodeMeta->getCustomSlug();
} catch (ValidationException | InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
return new JsonResponse([
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
'message' => $this->translator->translate('Provided data is invalid'),
'message' => $e->getMessage(),
], self::STATUS_BAD_REQUEST);
}
@ -114,7 +113,6 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
/**
* @param Request $request
* @return CreateShortCodeData
* @throws ValidationException
* @throws InvalidArgumentException
*/
abstract protected function buildUrlToShortCodeData(Request $request): CreateShortCodeData;

View File

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\ShortCode;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use Zend\Diactoros\Uri;
use Zend\I18n\Translator\TranslatorInterface;
class SingleStepCreateShortCodeAction extends AbstractCreateShortCodeAction
{
protected const ROUTE_PATH = '/short-codes/shorten';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
/**
* @var ApiKeyServiceInterface
*/
private $apiKeyService;
public function __construct(
UrlShortenerInterface $urlShortener,
TranslatorInterface $translator,
ApiKeyServiceInterface $apiKeyService,
array $domainConfig,
LoggerInterface $logger = null
) {
parent::__construct($urlShortener, $translator, $domainConfig, $logger);
$this->apiKeyService = $apiKeyService;
}
/**
* @param Request $request
* @return CreateShortCodeData
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
protected function buildUrlToShortCodeData(Request $request): CreateShortCodeData
{
$query = $request->getQueryParams();
// Check provided API key
$apiKey = $this->apiKeyService->getByKey($query['apiKey'] ?? '');
if ($apiKey === null || ! $apiKey->isValid()) {
throw new InvalidArgumentException(
$this->translator->translate('No API key was provided or it is not valid')
);
}
if (! isset($query['longUrl'])) {
throw new InvalidArgumentException($this->translator->translate('A URL was not provided'));
}
return new CreateShortCodeData(new Uri($query['longUrl']));
}
}

View File

@ -28,7 +28,7 @@ class ApiKeyService implements ApiKeyServiceInterface
public function create(\DateTime $expirationDate = null)
{
$key = new ApiKey();
if (isset($expirationDate)) {
if ($expirationDate !== null) {
$key->setExpirationDate($expirationDate);
}
@ -44,7 +44,7 @@ class ApiKeyService implements ApiKeyServiceInterface
* @param string $key
* @return bool
*/
public function check($key)
public function check(string $key)
{
/** @var ApiKey|null $apiKey */
$apiKey = $this->getByKey($key);
@ -58,7 +58,7 @@ class ApiKeyService implements ApiKeyServiceInterface
* @return ApiKey
* @throws InvalidArgumentException
*/
public function disable($key)
public function disable(string $key)
{
/** @var ApiKey|null $apiKey */
$apiKey = $this->getByKey($key);
@ -77,7 +77,7 @@ class ApiKeyService implements ApiKeyServiceInterface
* @param bool $enabledOnly Tells if only enabled keys should be returned
* @return ApiKey[]
*/
public function listKeys($enabledOnly = false)
public function listKeys(bool $enabledOnly = false)
{
$conditions = $enabledOnly ? ['enabled' => true] : [];
return $this->em->getRepository(ApiKey::class)->findBy($conditions);
@ -89,7 +89,7 @@ class ApiKeyService implements ApiKeyServiceInterface
* @param string $key
* @return ApiKey|null
*/
public function getByKey($key)
public function getByKey(string $key)
{
/** @var ApiKey|null $apiKey */
$apiKey = $this->em->getRepository(ApiKey::class)->findOneBy([

View File

@ -22,7 +22,7 @@ interface ApiKeyServiceInterface
* @param string $key
* @return bool
*/
public function check($key);
public function check(string $key);
/**
* Disables provided api key
@ -31,7 +31,7 @@ interface ApiKeyServiceInterface
* @return ApiKey
* @throws InvalidArgumentException
*/
public function disable($key);
public function disable(string $key);
/**
* Lists all existing api keys
@ -39,7 +39,7 @@ interface ApiKeyServiceInterface
* @param bool $enabledOnly Tells if only enabled keys should be returned
* @return ApiKey[]
*/
public function listKeys($enabledOnly = false);
public function listKeys(bool $enabledOnly = false);
/**
* Tries to find one API key by its key string
@ -47,5 +47,5 @@ interface ApiKeyServiceInterface
* @param string $key
* @return ApiKey|null
*/
public function getByKey($key);
public function getByKey(string $key);
}