Applied API role specs to single short URL resolution

This commit is contained in:
Alejandro Celaya 2021-01-03 13:33:07 +01:00
parent 940383646b
commit dc08286a72
7 changed files with 34 additions and 17 deletions

View File

@ -90,10 +90,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
}
private function createListQueryBuilder(
?string $searchTerm = null,
array $tags = [],
?DateRange $dateRange = null,
?Specification $spec = null
?string $searchTerm,
array $tags,
?DateRange $dateRange,
?Specification $spec
): QueryBuilder {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(ShortUrl::class, 's')
@ -171,9 +171,9 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
return $query->getOneOrNullResult();
}
public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl
public function findOne(string $shortCode, ?string $domain = null, ?Specification $spec = null): ?ShortUrl
{
$qb = $this->createFindOneQueryBuilder($shortCode, $domain);
$qb = $this->createFindOneQueryBuilder($shortCode, $domain, $spec);
$qb->select('s');
return $qb->getQuery()->getOneOrNullResult();
@ -181,13 +181,13 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
public function shortCodeIsInUse(string $slug, ?string $domain = null): bool
{
$qb = $this->createFindOneQueryBuilder($slug, $domain);
$qb = $this->createFindOneQueryBuilder($slug, $domain, null);
$qb->select('COUNT(DISTINCT s.id)');
return ((int) $qb->getQuery()->getSingleScalarResult()) > 0;
}
private function createFindOneQueryBuilder(string $slug, ?string $domain = null): QueryBuilder
private function createFindOneQueryBuilder(string $slug, ?string $domain, ?Specification $spec): QueryBuilder
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(ShortUrl::class, 's')
@ -198,6 +198,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
$this->whereDomainIs($qb, $domain);
if ($spec !== null) {
$this->applySpecification($qb, $spec, 's');
}
return $qb;
}

View File

@ -34,7 +34,7 @@ interface ShortUrlRepositoryInterface extends ObjectRepository, EntitySpecificat
public function findOneWithDomainFallback(string $shortCode, ?string $domain = null): ?ShortUrl;
public function findOne(string $shortCode, ?string $domain = null): ?ShortUrl;
public function findOne(string $shortCode, ?string $domain = null, ?Specification $spec = null): ?ShortUrl;
public function shortCodeIsInUse(string $slug, ?string $domain): bool;

View File

@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ShortUrlResolver implements ShortUrlResolverInterface
{
@ -22,11 +23,15 @@ class ShortUrlResolver implements ShortUrlResolverInterface
/**
* @throws ShortUrlNotFoundException
*/
public function resolveShortUrl(ShortUrlIdentifier $identifier): ShortUrl
public function resolveShortUrl(ShortUrlIdentifier $identifier, ?ApiKey $apiKey = null): ShortUrl
{
/** @var ShortUrlRepository $shortUrlRepo */
$shortUrlRepo = $this->em->getRepository(ShortUrl::class);
$shortUrl = $shortUrlRepo->findOne($identifier->shortCode(), $identifier->domain());
$shortUrl = $shortUrlRepo->findOne(
$identifier->shortCode(),
$identifier->domain(),
$apiKey !== null ? $apiKey->spec() : null,
);
if ($shortUrl === null) {
throw ShortUrlNotFoundException::fromNotFound($identifier);
}

View File

@ -7,13 +7,14 @@ namespace Shlinkio\Shlink\Core\Service\ShortUrl;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
interface ShortUrlResolverInterface
{
/**
* @throws ShortUrlNotFoundException
*/
public function resolveShortUrl(ShortUrlIdentifier $identifier): ShortUrl;
public function resolveShortUrl(ShortUrlIdentifier $identifier, ?ApiKey $apiKey = null): ShortUrl;
/**
* @throws ShortUrlNotFoundException

View File

@ -42,7 +42,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = $shortUrl->getShortCode();
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOne = $repo->findOne($shortCode, null)->willReturn($shortUrl);
$findOne = $repo->findOne($shortCode, null, null)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$result = $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode));
@ -58,7 +58,7 @@ class ShortUrlResolverTest extends TestCase
$shortCode = 'abc123';
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$findOne = $repo->findOne($shortCode, null)->willReturn(null);
$findOne = $repo->findOne($shortCode, null, null)->willReturn(null);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$this->expectException(ShortUrlNotFoundException::class);

View File

@ -11,6 +11,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
class ResolveShortUrlAction extends AbstractRestAction
{
@ -29,7 +30,10 @@ class ResolveShortUrlAction extends AbstractRestAction
public function handle(Request $request): Response
{
$transformer = new ShortUrlDataTransformer($this->domainConfig);
$url = $this->urlResolver->resolveShortUrl(ShortUrlIdentifier::fromApiRequest($request));
$url = $this->urlResolver->resolveShortUrl(
ShortUrlIdentifier::fromApiRequest($request),
AuthenticationMiddleware::apiKeyFromRequest($request),
);
return new JsonResponse($transformer->transform($url));
}

View File

@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use Shlinkio\Shlink\Rest\Action\ShortUrl\ResolveShortUrlAction;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use function strpos;
@ -32,12 +33,14 @@ class ResolveShortUrlActionTest extends TestCase
public function correctShortCodeReturnsSuccess(): void
{
$shortCode = 'abc123';
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn(
$apiKey = new ApiKey();
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn(
new ShortUrl('http://domain.com/foo/bar'),
)->shouldBeCalledOnce();
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode);
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey);
$response = $this->action->handle($request);
self::assertEquals(200, $response->getStatusCode());
self::assertTrue(strpos($response->getBody()->getContents(), 'http://domain.com/foo/bar') > 0);
}