diff --git a/module/Core/src/Repository/VisitRepository.php b/module/Core/src/Repository/VisitRepository.php index 67b4256f..befd104d 100644 --- a/module/Core/src/Repository/VisitRepository.php +++ b/module/Core/src/Repository/VisitRepository.php @@ -142,7 +142,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo $qb->from(Visit::class, 'v') ->join('v.shortUrl', 's') ->join('s.tags', 't') - ->where($qb->expr()->eq('t.name', '\'' . $tag . '\'')); // This needs to be concatenated, not bound + ->where($qb->expr()->eq('t.name', $this->getEntityManager()->getConnection()->quote($tag))); if ($filtering->excludeBots()) { $qb->andWhere($qb->expr()->eq('v.potentialBot', 'false')); diff --git a/module/Core/src/ShortUrl/Spec/BelongsToApiKeyInlined.php b/module/Core/src/ShortUrl/Spec/BelongsToApiKeyInlined.php index 809d19b7..01440bb3 100644 --- a/module/Core/src/ShortUrl/Spec/BelongsToApiKeyInlined.php +++ b/module/Core/src/ShortUrl/Spec/BelongsToApiKeyInlined.php @@ -17,6 +17,7 @@ class BelongsToApiKeyInlined implements Filter public function getFilter(QueryBuilder $qb, string $dqlAlias): string { // Parameters in this query need to be inlined, not bound, as we need to use it as sub-query later - return $qb->expr()->eq('s.authorApiKey', '\'' . $this->apiKey->getId() . '\'')->__toString(); + $conn = $qb->getEntityManager()->getConnection(); + return $qb->expr()->eq('s.authorApiKey', $conn->quote($this->apiKey->getId()))->__toString(); } } diff --git a/module/Core/src/ShortUrl/Spec/BelongsToDomainInlined.php b/module/Core/src/ShortUrl/Spec/BelongsToDomainInlined.php index 46fba689..baaed1a6 100644 --- a/module/Core/src/ShortUrl/Spec/BelongsToDomainInlined.php +++ b/module/Core/src/ShortUrl/Spec/BelongsToDomainInlined.php @@ -16,6 +16,7 @@ class BelongsToDomainInlined implements Filter public function getFilter(QueryBuilder $qb, string $context): string { // Parameters in this query need to be inlined, not bound, as we need to use it as sub-query later - return $qb->expr()->eq('s.domain', '\'' . $this->domainId . '\'')->__toString(); + $conn = $qb->getEntityManager()->getConnection(); + return $qb->expr()->eq('s.domain', $conn->quote($this->domainId))->__toString(); } } diff --git a/module/Core/test-db/Repository/TagRepositoryTest.php b/module/Core/test-db/Repository/TagRepositoryTest.php index 5ac1f3ac..d030bd03 100644 --- a/module/Core/test-db/Repository/TagRepositoryTest.php +++ b/module/Core/test-db/Repository/TagRepositoryTest.php @@ -63,19 +63,27 @@ class TagRepositoryTest extends DatabaseTestCase foreach ($names as $name) { $this->getEntityManager()->persist(new Tag($name)); } + + $apiKey = $filtering?->apiKey(); + if ($apiKey !== null) { + $this->getEntityManager()->persist($apiKey); + } + $this->getEntityManager()->flush(); [$firstUrlTags] = array_chunk($names, 3); $secondUrlTags = [$names[0]]; - $metaWithTags = fn (array $tags) => ShortUrlMeta::fromRawData(['longUrl' => '', 'tags' => $tags]); + $metaWithTags = fn (array $tags, ?ApiKey $apiKey) => ShortUrlMeta::fromRawData( + ['longUrl' => '', 'tags' => $tags, 'apiKey' => $apiKey], + ); - $shortUrl = ShortUrl::fromMeta($metaWithTags($firstUrlTags), $this->relationResolver); + $shortUrl = ShortUrl::fromMeta($metaWithTags($firstUrlTags, $apiKey), $this->relationResolver); $this->getEntityManager()->persist($shortUrl); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl, Visitor::emptyInstance())); - $shortUrl2 = ShortUrl::fromMeta($metaWithTags($secondUrlTags), $this->relationResolver); + $shortUrl2 = ShortUrl::fromMeta($metaWithTags($secondUrlTags, null), $this->relationResolver); $this->getEntityManager()->persist($shortUrl2); $this->getEntityManager()->persist(Visit::forValidShortUrl($shortUrl2, Visitor::emptyInstance())); $this->getEntityManager()->flush(); @@ -181,6 +189,23 @@ class TagRepositoryTest extends DatabaseTestCase self::assertEquals($tagNames[0], $result[0]->tag()->__toString()); }, ]; + yield 'api key' => [new TagsListFiltering(null, null, null, null, ApiKey::fromMeta( + ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()), + )), static function (array $result, array $tagNames): void { + /** @var TagInfo[] $result */ + self::assertCount(3, $result); + self::assertEquals(1, $result[0]->shortUrlsCount()); + self::assertEquals(3, $result[0]->visitsCount()); + self::assertEquals($tagNames[1], $result[0]->tag()->__toString()); + + self::assertEquals(1, $result[1]->shortUrlsCount()); + self::assertEquals(3, $result[1]->visitsCount()); + self::assertEquals($tagNames[2], $result[1]->tag()->__toString()); + + self::assertEquals(1, $result[2]->shortUrlsCount()); + self::assertEquals(3, $result[2]->visitsCount()); + self::assertEquals($tagNames[0], $result[2]->tag()->__toString()); + }]; } /** @test */