Created rest endpoint to list existing domains

This commit is contained in:
Alejandro Celaya 2020-09-27 09:53:12 +02:00
parent 8109ceb7eb
commit 76d6d9a7a9
11 changed files with 239 additions and 2 deletions

View File

@ -0,0 +1,86 @@
{
"get": {
"operationId": "listDomains",
"tags": [
"Domains"
],
"summary": "List existing domains",
"description": "Returns the list of all domains ever used, with a flag that tells if they are the default domain",
"security": [
{
"ApiKey": []
}
],
"parameters": [
{
"$ref": "../parameters/version.json"
}
],
"responses": {
"200": {
"description": "The list of tags",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["domains"],
"properties": {
"domains": {
"type": "object",
"required": ["data"],
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"required": ["domain", "isDefault"],
"properties": {
"domain": {
"type": "string"
},
"isDefault": {
"type": "boolean"
}
}
}
}
}
}
}
}
}
},
"examples": {
"application/json": {
"domains": {
"data": [
{
"domain": "example.com",
"isDefault": true
},
{
"domain": "aaa.com",
"isDefault": false
},
{
"domain": "bbb.com",
"isDefault": false
}
]
}
}
}
},
"500": {
"description": "Unexpected error.",
"content": {
"application/problem+json": {
"schema": {
"$ref": "../definitions/Error.json"
}
}
}
}
}
}
}

View File

@ -88,6 +88,10 @@
"$ref": "paths/v2_tags_{tag}_visits.json"
},
"/rest/v{version}/domains": {
"$ref": "paths/v2_domains.json"
},
"/rest/v{version}/mercure-info": {
"$ref": "paths/v2_mercure-info.json"
},

View File

@ -31,6 +31,7 @@ return [
Tag\TagService::class => ConfigAbstractFactory::class,
Service\ShortUrl\DeleteShortUrlService::class => ConfigAbstractFactory::class,
Service\ShortUrl\ShortUrlResolver::class => ConfigAbstractFactory::class,
Domain\DomainService::class => ConfigAbstractFactory::class,
Util\UrlValidator::class => ConfigAbstractFactory::class,
@ -69,6 +70,7 @@ return [
Service\ShortUrl\ShortUrlResolver::class,
],
Service\ShortUrl\ShortUrlResolver::class => ['em'],
Domain\DomainService::class => ['em'],
Util\UrlValidator::class => ['httpClient', Options\UrlShortenerOptions::class],

View File

@ -11,7 +11,8 @@ use Doctrine\ORM\Mapping\ClassMetadata;
return static function (ClassMetadata $metadata, array $emConfig): void {
$builder = new ClassMetadataBuilder($metadata);
$builder->setTable(determineTableName('domains', $emConfig));
$builder->setTable(determineTableName('domains', $emConfig))
->setCustomRepositoryClass(Domain\Repository\DomainRepository::class);
$builder->createField('id', Types::BIGINT)
->columnName('id')

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Domain;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
use Shlinkio\Shlink\Core\Entity\Domain;
class DomainService implements DomainServiceInterface
{
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* @return Domain[]
*/
public function listDomainsWithout(?string $excludeDomain = null): array
{
/** @var DomainRepositoryInterface $repo */
$repo = $this->em->getRepository(Domain::class);
return $repo->findDomainsWithout($excludeDomain);
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Domain;
use Shlinkio\Shlink\Core\Entity\Domain;
interface DomainServiceInterface
{
/**
* @return Domain[]
*/
public function listDomainsWithout(?string $excludeDomain = null): array;
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Domain\Repository;
use Doctrine\ORM\EntityRepository;
use Shlinkio\Shlink\Core\Entity\Domain;
class DomainRepository extends EntityRepository implements DomainRepositoryInterface
{
/**
* @return Domain[]
*/
public function findDomainsWithout(?string $excludedAuthority = null): array
{
$qb = $this->createQueryBuilder('d')->orderBy('d.authority', 'ASC');
if ($excludedAuthority !== null) {
$qb->where($qb->expr()->neq('d.authority', ':excludedAuthority'))
->setParameter('excludedAuthority', $excludedAuthority);
}
return $qb->getQuery()->getResult();
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Domain\Repository;
use Doctrine\Persistence\ObjectRepository;
use Shlinkio\Shlink\Core\Entity\Domain;
interface DomainRepositoryInterface extends ObjectRepository
{
/**
* @return Domain[]
*/
public function findDomainsWithout(?string $excludedAuthority = null): array;
}

View File

@ -8,6 +8,7 @@ use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Mezzio\Router\Middleware\ImplicitOptionsMiddleware;
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
use Shlinkio\Shlink\Core\Domain\DomainService;
use Shlinkio\Shlink\Core\Options\AppOptions;
use Shlinkio\Shlink\Core\Service;
use Shlinkio\Shlink\Core\Tag\TagService;
@ -36,6 +37,7 @@ return [
Action\Tag\DeleteTagsAction::class => ConfigAbstractFactory::class,
Action\Tag\CreateTagsAction::class => ConfigAbstractFactory::class,
Action\Tag\UpdateTagAction::class => ConfigAbstractFactory::class,
Action\Domain\ListDomainsAction::class => ConfigAbstractFactory::class,
ImplicitOptionsMiddleware::class => Middleware\EmptyResponseImplicitOptionsMiddlewareFactory::class,
Middleware\BodyParserMiddleware::class => InvokableFactory::class,
@ -72,6 +74,7 @@ return [
Action\Tag\DeleteTagsAction::class => [TagService::class],
Action\Tag\CreateTagsAction::class => [TagService::class],
Action\Tag\UpdateTagAction::class => [TagService::class],
Action\Domain\ListDomainsAction::class => [DomainService::class, 'config.url_shortener.domain.hostname'],
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [

View File

@ -12,7 +12,7 @@ return [
'routes' => [
Action\HealthAction::getRouteDef(),
// Short codes
// Short URLs
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
$contentNegotiationMiddleware,
$dropDomainMiddleware,
@ -36,6 +36,9 @@ return [
Action\Tag\CreateTagsAction::getRouteDef(),
Action\Tag\UpdateTagAction::getRouteDef(),
// Domains
Action\Domain\ListDomainsAction::getRouteDef(),
Action\MercureInfoAction::getRouteDef(),
],

View File

@ -0,0 +1,52 @@
<?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\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use function Functional\compose;
use function Functional\map;
class ListDomainsAction extends AbstractRestAction
{
protected const ROUTE_PATH = '/domains';
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
private DomainServiceInterface $domainService;
private string $defaultDomain;
public function __construct(DomainServiceInterface $domainService, string $defaultDomain)
{
$this->domainService = $domainService;
$this->defaultDomain = $defaultDomain;
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$regularDomains = $this->domainService->listDomainsWithout($this->defaultDomain);
return new JsonResponse([
'domains' => [
'data' => [
$this->mapDomain($this->defaultDomain, true),
...map($regularDomains, fn (Domain $domain) => $this->mapDomain($domain->getAuthority())),
],
],
]);
}
private function mapDomain(string $domain, bool $isDefault = false): array
{
return [
'domain' => $domain,
'isDefault' => $isDefault,
];
}
}