Created new action to set redirects for a domain

This commit is contained in:
Alejandro Celaya 2021-07-30 18:53:57 +02:00 committed by Alejandro Celaya
parent 5a1a4f5594
commit 6a40bbdcb5
7 changed files with 151 additions and 1 deletions

View File

@ -66,6 +66,39 @@
}
}
},
"400": {
"description": "Provided data is invalid.",
"content": {
"application/problem+json": {
"schema": {
"type": "object",
"allOf": [
{
"$ref": "../definitions/Error.json"
},
{
"type": "object",
"required": ["invalidElements"],
"properties": {
"invalidElements": {
"type": "array",
"items": {
"type": "string",
"enum": [
"domain",
"baseUrlRedirect",
"regular404Redirect",
"invalidShortUrlRedirect"
]
}
}
}
}
]
}
}
}
},
"500": {
"description": "Unexpected error.",
"content": {

View File

@ -4,7 +4,9 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Config;
final class NotFoundRedirects
use JsonSerializable;
final class NotFoundRedirects implements JsonSerializable
{
public function __construct(
private ?string $baseUrlRedirect = null,
@ -27,4 +29,13 @@ final class NotFoundRedirects
{
return $this->invalidShortUrlRedirect;
}
public function jsonSerialize(): array
{
return [
'baseUrlRedirect' => $this->baseUrlRedirect,
'regular404Redirect' => $this->regular404Redirect,
'invalidShortUrlRedirect' => $this->invalidShortUrlRedirect,
];
}
}

View File

@ -40,6 +40,7 @@ return [
Action\Tag\CreateTagsAction::class => ConfigAbstractFactory::class,
Action\Tag\UpdateTagAction::class => ConfigAbstractFactory::class,
Action\Domain\ListDomainsAction::class => ConfigAbstractFactory::class,
Action\Domain\DomainRedirectsAction::class => ConfigAbstractFactory::class,
ImplicitOptionsMiddleware::class => Middleware\EmptyResponseImplicitOptionsMiddlewareFactory::class,
Middleware\BodyParserMiddleware::class => InvokableFactory::class,
@ -81,6 +82,7 @@ return [
Action\Tag\CreateTagsAction::class => [TagService::class],
Action\Tag\UpdateTagAction::class => [TagService::class],
Action\Domain\ListDomainsAction::class => [DomainService::class],
Action\Domain\DomainRedirectsAction::class => [DomainService::class],
Middleware\CrossDomainMiddleware::class => ['config.cors'],
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],

View File

@ -44,6 +44,7 @@ return [
// Domains
Action\Domain\ListDomainsAction::getRouteDef(),
Action\Domain\DomainRedirectsAction::getRouteDef(),
Action\MercureInfoAction::getRouteDef(),
],

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Domain;
use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Action\Domain\Request\DomainRedirectsRequest;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class DomainRedirectsAction extends AbstractRestAction
{
protected const ROUTE_PATH = '/domains/redirects';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_PATCH];
public function __construct(private DomainServiceInterface $domainService)
{
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
// TODO Do not allow to set redirects for default domain
/** @var array $body */
$body = $request->getParsedBody();
$requestData = DomainRedirectsRequest::fromRawData($body);
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$authority = $requestData->authority();
$domain = $this->domainService->getOrCreate($authority);
$notFoundRedirects = $requestData->toNotFoundRedirects($domain);
$this->domainService->configureNotFoundRedirects($authority, $notFoundRedirects, $apiKey);
return new JsonResponse($notFoundRedirects);
}
}

View File

@ -25,6 +25,8 @@ class ListDomainsAction extends AbstractRestAction
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
$domainItems = $this->domainService->listDomains($apiKey);
// TODO Support including not found redirects if requested via query param
return new JsonResponse([
'domains' => [
'data' => $domainItems,

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Action\Domain\Request;
use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface;
use Shlinkio\Shlink\Core\Config\NotFoundRedirects;
use function array_key_exists;
class DomainRedirectsRequest
{
private string $authority;
private ?string $baseUrlRedirect = null;
private bool $baseUrlRedirectWasProvided = false;
private ?string $regular404Redirect = null;
private bool $regular404RedirectWasProvided = false;
private ?string $invalidShortUrlRedirect = null;
private bool $invalidShortUrlRedirectWasProvided = false;
private function __construct()
{
}
public static function fromRawData(array $payload): self
{
$instance = new self();
$instance->validateAndInit($payload);
return $instance;
}
private function validateAndInit(array $payload): void
{
// TODO Validate data
$this->baseUrlRedirectWasProvided = array_key_exists('baseUrlRedirect', $payload);
$this->regular404RedirectWasProvided = array_key_exists('regular404Redirect', $payload);
$this->invalidShortUrlRedirectWasProvided = array_key_exists('invalidShortUrlRedirect', $payload);
$this->authority = $payload['domain'];
$this->baseUrlRedirect = $payload['baseUrlRedirect'] ?? null;
$this->regular404Redirect = $payload['regular404Redirect'] ?? null;
$this->invalidShortUrlRedirect = $payload['invalidShortUrlRedirect'] ?? null;
}
public function authority(): string
{
return $this->authority;
}
public function toNotFoundRedirects(?NotFoundRedirectConfigInterface $defaults = null): NotFoundRedirects
{
return new NotFoundRedirects(
$this->baseUrlRedirectWasProvided ? $this->baseUrlRedirect : $defaults?->baseUrlRedirect(),
$this->regular404RedirectWasProvided ? $this->regular404Redirect : $defaults?->regular404Redirect(),
$this->invalidShortUrlRedirectWasProvided
? $this->invalidShortUrlRedirect
: $defaults?->invalidShortUrlRedirect(),
);
}
}