From 76d6d9a7a9b6467ca3034641c2f4c1e90bcec025 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 27 Sep 2020 09:53:12 +0200 Subject: [PATCH] Created rest endpoint to list existing domains --- docs/swagger/paths/v2_domains.json | 86 +++++++++++++++++++ docs/swagger/swagger.json | 4 + module/Core/config/dependencies.config.php | 2 + .../Shlinkio.Shlink.Core.Entity.Domain.php | 3 +- module/Core/src/Domain/DomainService.php | 29 +++++++ .../src/Domain/DomainServiceInterface.php | 15 ++++ .../Domain/Repository/DomainRepository.php | 26 ++++++ .../Repository/DomainRepositoryInterface.php | 16 ++++ module/Rest/config/dependencies.config.php | 3 + module/Rest/config/routes.config.php | 5 +- .../src/Action/Domain/ListDomainsAction.php | 52 +++++++++++ 11 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 docs/swagger/paths/v2_domains.json create mode 100644 module/Core/src/Domain/DomainService.php create mode 100644 module/Core/src/Domain/DomainServiceInterface.php create mode 100644 module/Core/src/Domain/Repository/DomainRepository.php create mode 100644 module/Core/src/Domain/Repository/DomainRepositoryInterface.php create mode 100644 module/Rest/src/Action/Domain/ListDomainsAction.php diff --git a/docs/swagger/paths/v2_domains.json b/docs/swagger/paths/v2_domains.json new file mode 100644 index 00000000..d92ae995 --- /dev/null +++ b/docs/swagger/paths/v2_domains.json @@ -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" + } + } + } + } + } + } +} diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 8dc21997..5abe1946 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -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" }, diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 46bf1735..5dcef9a2 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -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], diff --git a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Domain.php b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Domain.php index c6349b74..e3d8c3cf 100644 --- a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Domain.php +++ b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Domain.php @@ -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') diff --git a/module/Core/src/Domain/DomainService.php b/module/Core/src/Domain/DomainService.php new file mode 100644 index 00000000..d7575361 --- /dev/null +++ b/module/Core/src/Domain/DomainService.php @@ -0,0 +1,29 @@ +em = $em; + } + + /** + * @return Domain[] + */ + public function listDomainsWithout(?string $excludeDomain = null): array + { + /** @var DomainRepositoryInterface $repo */ + $repo = $this->em->getRepository(Domain::class); + return $repo->findDomainsWithout($excludeDomain); + } +} diff --git a/module/Core/src/Domain/DomainServiceInterface.php b/module/Core/src/Domain/DomainServiceInterface.php new file mode 100644 index 00000000..3e56c69c --- /dev/null +++ b/module/Core/src/Domain/DomainServiceInterface.php @@ -0,0 +1,15 @@ +createQueryBuilder('d')->orderBy('d.authority', 'ASC'); + + if ($excludedAuthority !== null) { + $qb->where($qb->expr()->neq('d.authority', ':excludedAuthority')) + ->setParameter('excludedAuthority', $excludedAuthority); + } + + return $qb->getQuery()->getResult(); + } +} diff --git a/module/Core/src/Domain/Repository/DomainRepositoryInterface.php b/module/Core/src/Domain/Repository/DomainRepositoryInterface.php new file mode 100644 index 00000000..56a765ac --- /dev/null +++ b/module/Core/src/Domain/Repository/DomainRepositoryInterface.php @@ -0,0 +1,16 @@ + 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 => [ diff --git a/module/Rest/config/routes.config.php b/module/Rest/config/routes.config.php index 0bde3da0..64333254 100644 --- a/module/Rest/config/routes.config.php +++ b/module/Rest/config/routes.config.php @@ -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(), ], diff --git a/module/Rest/src/Action/Domain/ListDomainsAction.php b/module/Rest/src/Action/Domain/ListDomainsAction.php new file mode 100644 index 00000000..682286a1 --- /dev/null +++ b/module/Rest/src/Action/Domain/ListDomainsAction.php @@ -0,0 +1,52 @@ +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, + ]; + } +}