mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-20 01:09:56 +03:00
Created action which allows short URLs to be created on a single API request
This commit is contained in:
parent
28650aee2b
commit
e5ef8d7f8c
8 changed files with 84 additions and 23 deletions
|
@ -8,6 +8,7 @@ return [
|
||||||
'auth' => [
|
'auth' => [
|
||||||
'routes_whitelist' => [
|
'routes_whitelist' => [
|
||||||
Action\AuthenticateAction::class,
|
Action\AuthenticateAction::class,
|
||||||
|
Action\ShortCode\SingleStepCreateShortCodeAction::class,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ return [
|
||||||
|
|
||||||
Action\AuthenticateAction::class => ConfigAbstractFactory::class,
|
Action\AuthenticateAction::class => ConfigAbstractFactory::class,
|
||||||
Action\CreateShortCodeAction::class => ConfigAbstractFactory::class,
|
Action\CreateShortCodeAction::class => ConfigAbstractFactory::class,
|
||||||
|
Action\ShortCode\SingleStepCreateShortCodeAction::class => ConfigAbstractFactory::class,
|
||||||
Action\EditShortCodeAction::class => ConfigAbstractFactory::class,
|
Action\EditShortCodeAction::class => ConfigAbstractFactory::class,
|
||||||
Action\ResolveUrlAction::class => ConfigAbstractFactory::class,
|
Action\ResolveUrlAction::class => ConfigAbstractFactory::class,
|
||||||
Action\GetVisitsAction::class => ConfigAbstractFactory::class,
|
Action\GetVisitsAction::class => ConfigAbstractFactory::class,
|
||||||
|
@ -49,6 +50,13 @@ return [
|
||||||
'config.url_shortener.domain',
|
'config.url_shortener.domain',
|
||||||
'Logger_Shlink',
|
'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\EditShortCodeAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink',],
|
||||||
Action\ResolveUrlAction::class => [Service\UrlShortener::class, 'translator'],
|
Action\ResolveUrlAction::class => [Service\UrlShortener::class, 'translator'],
|
||||||
Action\GetVisitsAction::class => [Service\VisitsTracker::class, 'translator', 'Logger_Shlink'],
|
Action\GetVisitsAction::class => [Service\VisitsTracker::class, 'translator', 'Logger_Shlink'],
|
||||||
|
|
|
@ -10,12 +10,7 @@ return [
|
||||||
|
|
||||||
// Short codes
|
// Short codes
|
||||||
Action\CreateShortCodeAction::getRouteDef(),
|
Action\CreateShortCodeAction::getRouteDef(),
|
||||||
// [
|
Action\ShortCode\SingleStepCreateShortCodeAction::getRouteDef(),
|
||||||
// 'name' => Action\CreateShortCodeAction::class,
|
|
||||||
// 'path' => '/short-codes',
|
|
||||||
// 'middleware' => Action\CreateShortCodeAction::class,
|
|
||||||
// 'allowed_methods' => [RequestMethod::METHOD_GET],
|
|
||||||
// ],
|
|
||||||
Action\EditShortCodeAction::getRouteDef(),
|
Action\EditShortCodeAction::getRouteDef(),
|
||||||
Action\ResolveUrlAction::getRouteDef(),
|
Action\ResolveUrlAction::getRouteDef(),
|
||||||
Action\ListShortCodesAction::getRouteDef(),
|
Action\ListShortCodesAction::getRouteDef(),
|
||||||
|
|
|
@ -5,7 +5,6 @@ namespace Shlinkio\Shlink\Rest\Action;
|
||||||
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
|
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
|
||||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
|
||||||
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
|
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
|
||||||
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
|
||||||
use Shlinkio\Shlink\Rest\Action\ShortCode\AbstractCreateShortCodeAction;
|
use Shlinkio\Shlink\Rest\Action\ShortCode\AbstractCreateShortCodeAction;
|
||||||
|
@ -19,7 +18,6 @@ class CreateShortCodeAction extends AbstractCreateShortCodeAction
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return CreateShortCodeData
|
* @return CreateShortCodeData
|
||||||
* @throws ValidationException
|
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
|
@ -27,7 +25,7 @@ class CreateShortCodeAction extends AbstractCreateShortCodeAction
|
||||||
{
|
{
|
||||||
$postData = (array) $request->getParsedBody();
|
$postData = (array) $request->getParsedBody();
|
||||||
if (! isset($postData['longUrl'])) {
|
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(
|
return new CreateShortCodeData(
|
||||||
|
|
|
@ -9,7 +9,6 @@ use Psr\Log\LoggerInterface;
|
||||||
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
|
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
|
||||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||||
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
|
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
|
||||||
use Shlinkio\Shlink\Core\Exception\ValidationException;
|
|
||||||
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
|
use Shlinkio\Shlink\Core\Model\CreateShortCodeData;
|
||||||
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
|
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
|
||||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||||
|
@ -31,7 +30,7 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
|
||||||
/**
|
/**
|
||||||
* @var TranslatorInterface
|
* @var TranslatorInterface
|
||||||
*/
|
*/
|
||||||
private $translator;
|
protected $translator;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
UrlShortenerInterface $urlShortener,
|
UrlShortenerInterface $urlShortener,
|
||||||
|
@ -57,11 +56,11 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
|
||||||
$shortCodeMeta = $shortCodeData->getMeta();
|
$shortCodeMeta = $shortCodeData->getMeta();
|
||||||
$longUrl = $shortCodeData->getLongUrl();
|
$longUrl = $shortCodeData->getLongUrl();
|
||||||
$customSlug = $shortCodeMeta->getCustomSlug();
|
$customSlug = $shortCodeMeta->getCustomSlug();
|
||||||
} catch (ValidationException | InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
|
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
|
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
|
||||||
'message' => $this->translator->translate('Provided data is invalid'),
|
'message' => $e->getMessage(),
|
||||||
], self::STATUS_BAD_REQUEST);
|
], self::STATUS_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +113,6 @@ abstract class AbstractCreateShortCodeAction extends AbstractRestAction
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return CreateShortCodeData
|
* @return CreateShortCodeData
|
||||||
* @throws ValidationException
|
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
abstract protected function buildUrlToShortCodeData(Request $request): CreateShortCodeData;
|
abstract protected function buildUrlToShortCodeData(Request $request): CreateShortCodeData;
|
||||||
|
|
|
@ -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']));
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||||
public function create(\DateTime $expirationDate = null)
|
public function create(\DateTime $expirationDate = null)
|
||||||
{
|
{
|
||||||
$key = new ApiKey();
|
$key = new ApiKey();
|
||||||
if (isset($expirationDate)) {
|
if ($expirationDate !== null) {
|
||||||
$key->setExpirationDate($expirationDate);
|
$key->setExpirationDate($expirationDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function check($key)
|
public function check(string $key)
|
||||||
{
|
{
|
||||||
/** @var ApiKey|null $apiKey */
|
/** @var ApiKey|null $apiKey */
|
||||||
$apiKey = $this->getByKey($key);
|
$apiKey = $this->getByKey($key);
|
||||||
|
@ -58,7 +58,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||||
* @return ApiKey
|
* @return ApiKey
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function disable($key)
|
public function disable(string $key)
|
||||||
{
|
{
|
||||||
/** @var ApiKey|null $apiKey */
|
/** @var ApiKey|null $apiKey */
|
||||||
$apiKey = $this->getByKey($key);
|
$apiKey = $this->getByKey($key);
|
||||||
|
@ -77,7 +77,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||||
* @param bool $enabledOnly Tells if only enabled keys should be returned
|
* @param bool $enabledOnly Tells if only enabled keys should be returned
|
||||||
* @return ApiKey[]
|
* @return ApiKey[]
|
||||||
*/
|
*/
|
||||||
public function listKeys($enabledOnly = false)
|
public function listKeys(bool $enabledOnly = false)
|
||||||
{
|
{
|
||||||
$conditions = $enabledOnly ? ['enabled' => true] : [];
|
$conditions = $enabledOnly ? ['enabled' => true] : [];
|
||||||
return $this->em->getRepository(ApiKey::class)->findBy($conditions);
|
return $this->em->getRepository(ApiKey::class)->findBy($conditions);
|
||||||
|
@ -89,7 +89,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return ApiKey|null
|
* @return ApiKey|null
|
||||||
*/
|
*/
|
||||||
public function getByKey($key)
|
public function getByKey(string $key)
|
||||||
{
|
{
|
||||||
/** @var ApiKey|null $apiKey */
|
/** @var ApiKey|null $apiKey */
|
||||||
$apiKey = $this->em->getRepository(ApiKey::class)->findOneBy([
|
$apiKey = $this->em->getRepository(ApiKey::class)->findOneBy([
|
||||||
|
|
|
@ -22,7 +22,7 @@ interface ApiKeyServiceInterface
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function check($key);
|
public function check(string $key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables provided api key
|
* Disables provided api key
|
||||||
|
@ -31,7 +31,7 @@ interface ApiKeyServiceInterface
|
||||||
* @return ApiKey
|
* @return ApiKey
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function disable($key);
|
public function disable(string $key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all existing api keys
|
* Lists all existing api keys
|
||||||
|
@ -39,7 +39,7 @@ interface ApiKeyServiceInterface
|
||||||
* @param bool $enabledOnly Tells if only enabled keys should be returned
|
* @param bool $enabledOnly Tells if only enabled keys should be returned
|
||||||
* @return ApiKey[]
|
* @return ApiKey[]
|
||||||
*/
|
*/
|
||||||
public function listKeys($enabledOnly = false);
|
public function listKeys(bool $enabledOnly = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to find one API key by its key string
|
* Tries to find one API key by its key string
|
||||||
|
@ -47,5 +47,5 @@ interface ApiKeyServiceInterface
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return ApiKey|null
|
* @return ApiKey|null
|
||||||
*/
|
*/
|
||||||
public function getByKey($key);
|
public function getByKey(string $key);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue