From 5714a8f884fed1f04d1989ffda5fe9ce68c78510 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 15 Sep 2018 12:56:17 +0200 Subject: [PATCH] Created action to delete short URLs --- module/Core/config/dependencies.config.php | 2 + module/Core/src/Action/QrCodeAction.php | 12 ++-- module/Rest/config/dependencies.config.php | 8 ++- module/Rest/config/routes.config.php | 1 + .../ShortCode/DeleteShortCodeAction.php | 71 ++++++++++++++++++ module/Rest/src/Util/RestUtils.php | 3 + .../ShortCode/DeleteShortCodeActionTest.php | 72 +++++++++++++++++++ 7 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 module/Rest/src/Action/ShortCode/DeleteShortCodeAction.php create mode 100644 module/Rest/test/Action/ShortCode/DeleteShortCodeActionTest.php diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 8eb6e9b6..31fd1297 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -26,6 +26,7 @@ return [ Service\ShortUrlService::class => ConfigAbstractFactory::class, Service\VisitService::class => ConfigAbstractFactory::class, Service\Tag\TagService::class => ConfigAbstractFactory::class, + Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class, // Middleware Action\RedirectAction::class => ConfigAbstractFactory::class, @@ -50,6 +51,7 @@ return [ Service\ShortUrlService::class => ['em'], Service\VisitService::class => ['em'], Service\Tag\TagService::class => ['em'], + Service\ShortUrl\DeleteShortUrlService::class => ['em', Options\DeleteShortUrlsOptions::class], // Middleware Action\RedirectAction::class => [ diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index 81b1d326..c410802f 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -22,6 +22,10 @@ class QrCodeAction implements MiddlewareInterface { use ErrorResponseBuilderTrait; + private const DEFAULT_SIZE = 300; + private const MIN_SIZE = 50; + private const MAX_SIZE = 1000; + /** * @var RouterInterface */ @@ -82,11 +86,11 @@ class QrCodeAction implements MiddlewareInterface */ private function getSizeParam(Request $request): int { - $size = (int) $request->getAttribute('size', 300); - if ($size < 50) { - return 50; + $size = (int) $request->getAttribute('size', self::DEFAULT_SIZE); + if ($size < self::MIN_SIZE) { + return self::MIN_SIZE; } - return $size > 1000 ? 1000 : $size; + return $size > self::MAX_SIZE ? self::MAX_SIZE : $size; } } diff --git a/module/Rest/config/dependencies.config.php b/module/Rest/config/dependencies.config.php index 92c991e1..ae500a22 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -23,6 +23,7 @@ return [ Action\ShortCode\CreateShortCodeAction::class => ConfigAbstractFactory::class, Action\ShortCode\SingleStepCreateShortCodeAction::class => ConfigAbstractFactory::class, Action\ShortCode\EditShortCodeAction::class => ConfigAbstractFactory::class, + Action\ShortCode\DeleteShortCodeAction::class => ConfigAbstractFactory::class, Action\ShortCode\ResolveUrlAction::class => ConfigAbstractFactory::class, Action\Visit\GetVisitsAction::class => ConfigAbstractFactory::class, Action\ShortCode\ListShortCodesAction::class => ConfigAbstractFactory::class, @@ -58,7 +59,12 @@ return [ 'config.url_shortener.domain', 'Logger_Shlink', ], - Action\ShortCode\EditShortCodeAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink',], + Action\ShortCode\EditShortCodeAction::class => [Service\ShortUrlService::class, 'translator', 'Logger_Shlink'], + Action\ShortCode\DeleteShortCodeAction::class => [ + Service\ShortUrl\DeleteShortUrlService::class, + 'translator', + 'Logger_Shlink', + ], Action\ShortCode\ResolveUrlAction::class => [ Service\UrlShortener::class, 'translator', diff --git a/module/Rest/config/routes.config.php b/module/Rest/config/routes.config.php index 0f453e26..7058ad59 100644 --- a/module/Rest/config/routes.config.php +++ b/module/Rest/config/routes.config.php @@ -18,6 +18,7 @@ return [ Middleware\ShortCode\CreateShortCodeContentNegotiationMiddleware::class, ]), Action\ShortCode\EditShortCodeAction::getRouteDef(), + Action\ShortCode\DeleteShortCodeAction::getRouteDef(), Action\ShortCode\ResolveUrlAction::getRouteDef(), Action\ShortCode\ListShortCodesAction::getRouteDef(), Action\ShortCode\EditShortCodeTagsAction::getRouteDef(), diff --git a/module/Rest/src/Action/ShortCode/DeleteShortCodeAction.php b/module/Rest/src/Action/ShortCode/DeleteShortCodeAction.php new file mode 100644 index 00000000..8fa9ca5b --- /dev/null +++ b/module/Rest/src/Action/ShortCode/DeleteShortCodeAction.php @@ -0,0 +1,71 @@ +deleteShortUrlService = $deleteShortUrlService; + $this->translator = $translator; + } + + /** + * Handle the request and return a response. + */ + public function handle(ServerRequestInterface $request): ResponseInterface + { + $shortCode = $request->getAttribute('shortCode', ''); + + try { + $this->deleteShortUrlService->deleteByShortCode($shortCode); + return new EmptyResponse(); + } catch (Exception\InvalidShortCodeException $e) { + $this->logger->warning( + \sprintf('Provided short code %s does not belong to any URL.', $shortCode) . PHP_EOL . $e + ); + return new JsonResponse([ + 'error' => RestUtils::getRestErrorCodeFromException($e), + 'message' => \sprintf($this->translator->translate('No URL found for short code "%s"'), $shortCode), + ], self::STATUS_NOT_FOUND); + } catch (Exception\DeleteShortUrlException $e) { + $this->logger->warning('Provided data is invalid.' . PHP_EOL . $e); + $messagePlaceholder = $this->translator->translate( + 'It is not possible to delete URL with short code "%s" because it has reached more than "%s" visits.' + ); + + return new JsonResponse([ + 'error' => RestUtils::getRestErrorCodeFromException($e), + 'message' => \sprintf($messagePlaceholder, $shortCode, $e->getVisitsThreshold()), + ], self::STATUS_BAD_REQUEST); + } + } +} diff --git a/module/Rest/src/Util/RestUtils.php b/module/Rest/src/Util/RestUtils.php index 666469ed..c783f6cb 100644 --- a/module/Rest/src/Util/RestUtils.php +++ b/module/Rest/src/Util/RestUtils.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Rest\Exception as Rest; class RestUtils { public const INVALID_SHORTCODE_ERROR = 'INVALID_SHORTCODE'; + public const INVALID_SHORTCODE_DELETION_ERROR = 'INVALID_SHORTCODE_DELETION'; public const INVALID_URL_ERROR = 'INVALID_URL'; public const INVALID_ARGUMENT_ERROR = 'INVALID_ARGUMENT'; public const INVALID_SLUG_ERROR = 'INVALID_SLUG'; @@ -35,6 +36,8 @@ class RestUtils return self::INVALID_ARGUMENT_ERROR; case $e instanceof Rest\AuthenticationException: return self::INVALID_CREDENTIALS_ERROR; + case $e instanceof Core\DeleteShortUrlException: + return self::INVALID_SHORTCODE_DELETION_ERROR; default: return self::UNKNOWN_ERROR; } diff --git a/module/Rest/test/Action/ShortCode/DeleteShortCodeActionTest.php b/module/Rest/test/Action/ShortCode/DeleteShortCodeActionTest.php new file mode 100644 index 00000000..3c5a5c87 --- /dev/null +++ b/module/Rest/test/Action/ShortCode/DeleteShortCodeActionTest.php @@ -0,0 +1,72 @@ +service = $this->prophesize(DeleteShortUrlServiceInterface::class); + $this->action = new DeleteShortCodeAction($this->service->reveal(), Translator::factory([])); + } + + /** + * @test + */ + public function emptyResponseIsReturnedIfProperlyDeleted() + { + $deleteByShortCode = $this->service->deleteByShortCode(Argument::any())->will(function () { + }); + + $resp = $this->action->handle(ServerRequestFactory::fromGlobals()); + + $this->assertEquals(204, $resp->getStatusCode()); + $deleteByShortCode->shouldHaveBeenCalledTimes(1); + } + + /** + * @test + * @dataProvider provideExceptions + */ + public function returnsErrorResponseInCaseOfException(\Throwable $e, string $error, int $statusCode) + { + $deleteByShortCode = $this->service->deleteByShortCode(Argument::any())->willThrow($e); + + /** @var JsonResponse $resp */ + $resp = $this->action->handle(ServerRequestFactory::fromGlobals()); + $payload = $resp->getPayload(); + + $this->assertEquals($statusCode, $resp->getStatusCode()); + $this->assertEquals($error, $payload['error']); + $deleteByShortCode->shouldHaveBeenCalledTimes(1); + } + + public function provideExceptions(): array + { + return [ + [new Exception\InvalidShortCodeException(), RestUtils::INVALID_SHORTCODE_ERROR, 404], + [new Exception\DeleteShortUrlException(5), RestUtils::INVALID_SHORTCODE_DELETION_ERROR, 400], + ]; + } +}