mirror of
https://github.com/shlinkio/shlink.git
synced 2024-11-24 13:49:03 +03:00
Merge pull request #1038 from acelaya-forks/feature/happyr-spec-2
Feature/happyr spec 2
This commit is contained in:
commit
c931874bac
25 changed files with 62 additions and 50 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#1036](https://github.com/shlinkio/shlink/issues/1036) Updated to `happyr/doctrine-specification` 2.0.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [2.6.1] - 2021-02-22
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"endroid/qr-code": "dev-master#0f1613a as 3.10",
|
||||
"geoip2/geoip2": "^2.9",
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"happyr/doctrine-specification": "2.0.x-dev#cb116d3 as 2.0",
|
||||
"happyr/doctrine-specification": "^2.0",
|
||||
"laminas/laminas-config": "^3.3",
|
||||
"laminas/laminas-config-aggregator": "^1.1",
|
||||
"laminas/laminas-diactoros": "^2.1.3",
|
||||
|
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Common;
|
||||
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
|
||||
return [
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Domain\Repository;
|
||||
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Domain\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
use Shlinkio\Shlink\Core\Entity\Domain;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Shlinkio\Shlink\Core\Repository;
|
|||
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Doctrine\Type\ChronosDateTimeType;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
|
|
|
@ -4,9 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Core\Entity\Tag;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
use Shlinkio\Shlink\Core\Tag\Spec\CountTagsWithName;
|
||||
|
@ -33,7 +32,7 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
|
|||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function findTagsWithInfo(?Specification $spec = null): array
|
||||
public function findTagsWithInfo(?ApiKey $apiKey = null): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t');
|
||||
$qb->select('t AS tag', 'COUNT(DISTINCT s.id) AS shortUrlsCount', 'COUNT(DISTINCT v.id) AS visitsCount')
|
||||
|
@ -42,7 +41,9 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
|
|||
->groupBy('t')
|
||||
->orderBy('t.name', 'ASC');
|
||||
|
||||
$this->applySpecification($qb, $spec, 't');
|
||||
if ($apiKey !== null) {
|
||||
$this->applySpecification($qb, $apiKey->spec(false, 'shortUrls'), 't');
|
||||
}
|
||||
|
||||
$query = $qb->getQuery();
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
|
@ -17,7 +16,7 @@ interface TagRepositoryInterface extends ObjectRepository, EntitySpecificationRe
|
|||
/**
|
||||
* @return TagInfo[]
|
||||
*/
|
||||
public function findTagsWithInfo(?Specification $spec = null): array;
|
||||
public function findTagsWithInfo(?ApiKey $apiKey = null): array;
|
||||
|
||||
public function tagExists(string $tag, ?ApiKey $apiKey = null): bool;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Shlinkio\Shlink\Core\Repository;
|
|||
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Entity\ShortUrl;
|
||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\Repository;
|
||||
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use Happyr\DoctrineSpecification\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryInterface;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
|
|
|
@ -4,21 +4,21 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\ShortUrl\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Filter\Filter;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class BelongsToApiKey extends BaseSpecification
|
||||
{
|
||||
private ApiKey $apiKey;
|
||||
private string $dqlAlias;
|
||||
private ?string $dqlAlias;
|
||||
|
||||
public function __construct(ApiKey $apiKey, ?string $dqlAlias = null)
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
$this->dqlAlias = $dqlAlias ?? 's';
|
||||
parent::__construct($this->dqlAlias);
|
||||
$this->dqlAlias = $dqlAlias;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function getSpec(): Filter
|
||||
|
|
|
@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\ShortUrl\Spec;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Happyr\DoctrineSpecification\Filter\Filter;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class BelongsToApiKeyInlined implements Specification
|
||||
class BelongsToApiKeyInlined implements Filter
|
||||
{
|
||||
private ApiKey $apiKey;
|
||||
|
||||
|
@ -22,8 +22,4 @@ class BelongsToApiKeyInlined implements Specification
|
|||
// Parameters in this query need to be inlined, not bound, as we need to use it as sub-query later
|
||||
return (string) $qb->expr()->eq('s.authorApiKey', '\'' . $this->apiKey->getId() . '\'');
|
||||
}
|
||||
|
||||
public function modify(QueryBuilder $qb, string $dqlAlias): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,20 +4,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\ShortUrl\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Filter\Filter;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
|
||||
class BelongsToDomain extends BaseSpecification
|
||||
{
|
||||
private string $domainId;
|
||||
private string $dqlAlias;
|
||||
private ?string $dqlAlias;
|
||||
|
||||
public function __construct(string $domainId, ?string $dqlAlias = null)
|
||||
{
|
||||
$this->domainId = $domainId;
|
||||
$this->dqlAlias = $dqlAlias ?? 's';
|
||||
parent::__construct($this->dqlAlias);
|
||||
$this->dqlAlias = $dqlAlias;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function getSpec(): Filter
|
||||
|
|
|
@ -5,9 +5,9 @@ declare(strict_types=1);
|
|||
namespace Shlinkio\Shlink\Core\ShortUrl\Spec;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Happyr\DoctrineSpecification\Filter\Filter;
|
||||
|
||||
class BelongsToDomainInlined implements Specification
|
||||
class BelongsToDomainInlined implements Filter
|
||||
{
|
||||
private string $domainId;
|
||||
|
||||
|
@ -21,8 +21,4 @@ class BelongsToDomainInlined implements Specification
|
|||
// Parameters in this query need to be inlined, not bound, as we need to use it as sub-query later
|
||||
return (string) $qb->expr()->eq('s.domain', '\'' . $this->domainId . '\'');
|
||||
}
|
||||
|
||||
public function modify(QueryBuilder $qb, string $dqlAlias): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\Tag\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
|
||||
class CountTagsWithName extends BaseSpecification
|
||||
|
|
|
@ -52,7 +52,7 @@ class TagService implements TagServiceInterface
|
|||
{
|
||||
/** @var TagRepositoryInterface $repo */
|
||||
$repo = $this->em->getRepository(Tag::class);
|
||||
return $repo->findTagsWithInfo($apiKey !== null ? $apiKey->spec() : null);
|
||||
return $repo->findTagsWithInfo($apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\Visit\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Common\Util\DateRange;
|
||||
use Shlinkio\Shlink\Core\Spec\InDateRange;
|
||||
|
|
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Core\Visit\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
|
|
@ -60,7 +60,7 @@ class TagServiceTest extends TestCase
|
|||
{
|
||||
$expected = [new TagInfo(new Tag('foo'), 1, 1), new TagInfo(new Tag('bar'), 3, 10)];
|
||||
|
||||
$find = $this->repo->findTagsWithInfo($apiKey === null ? null : $apiKey->spec())->willReturn($expected);
|
||||
$find = $this->repo->findTagsWithInfo($apiKey)->willReturn($expected);
|
||||
|
||||
$result = $this->service->tagsInfo($apiKey);
|
||||
|
||||
|
|
|
@ -21,15 +21,18 @@ class Role
|
|||
self::DOMAIN_SPECIFIC => 'Domain only',
|
||||
];
|
||||
|
||||
public static function toSpec(ApiKeyRole $role, bool $inlined): Specification
|
||||
public static function toSpec(ApiKeyRole $role, bool $inlined, ?string $context = null): Specification
|
||||
{
|
||||
if ($role->name() === self::AUTHORED_SHORT_URLS) {
|
||||
return $inlined ? new BelongsToApiKeyInlined($role->apiKey()) : new BelongsToApiKey($role->apiKey());
|
||||
$apiKey = $role->apiKey();
|
||||
return $inlined ? Spec::andX(new BelongsToApiKeyInlined($apiKey)) : new BelongsToApiKey($apiKey, $context);
|
||||
}
|
||||
|
||||
if ($role->name() === self::DOMAIN_SPECIFIC) {
|
||||
$domainId = self::domainIdFromMeta($role->meta());
|
||||
return $inlined ? new BelongsToDomainInlined($domainId) : new BelongsToDomain($domainId);
|
||||
return $inlined
|
||||
? Spec::andX(new BelongsToDomainInlined($domainId))
|
||||
: new BelongsToDomain($domainId, $context);
|
||||
}
|
||||
|
||||
return Spec::andX();
|
||||
|
|
|
@ -4,8 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shlinkio\Shlink\Rest\ApiKey\Spec;
|
||||
|
||||
use Happyr\DoctrineSpecification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Spec;
|
||||
use Happyr\DoctrineSpecification\Specification\BaseSpecification;
|
||||
use Happyr\DoctrineSpecification\Specification\Specification;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
|
@ -25,7 +25,7 @@ class WithApiKeySpecsEnsuringJoin extends BaseSpecification
|
|||
{
|
||||
return $this->apiKey === null || $this->apiKey->isAdmin() ? Spec::andX() : Spec::andX(
|
||||
Spec::join($this->fieldToJoin, 's'),
|
||||
$this->apiKey->spec(),
|
||||
$this->apiKey->spec(false, $this->fieldToJoin),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,9 +92,9 @@ class ApiKey extends AbstractEntity
|
|||
return $this->key;
|
||||
}
|
||||
|
||||
public function spec(bool $inlined = false): Specification
|
||||
public function spec(bool $inlined = false, ?string $context = null): Specification
|
||||
{
|
||||
$specs = $this->roles->map(fn (ApiKeyRole $role) => Role::toSpec($role, $inlined))->getValues();
|
||||
$specs = $this->roles->map(fn (ApiKeyRole $role) => Role::toSpec($role, $inlined, $context))->getValues();
|
||||
return Spec::andX(...$specs);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class RoleTest extends TestCase
|
|||
yield 'inline author role' => [
|
||||
new ApiKeyRole(Role::AUTHORED_SHORT_URLS, [], $apiKey),
|
||||
true,
|
||||
new BelongsToApiKeyInlined($apiKey),
|
||||
Spec::andX(new BelongsToApiKeyInlined($apiKey)),
|
||||
];
|
||||
yield 'not inline author role' => [
|
||||
new ApiKeyRole(Role::AUTHORED_SHORT_URLS, [], $apiKey),
|
||||
|
@ -45,7 +45,7 @@ class RoleTest extends TestCase
|
|||
yield 'inline domain role' => [
|
||||
new ApiKeyRole(Role::DOMAIN_SPECIFIC, ['domain_id' => '123'], $apiKey),
|
||||
true,
|
||||
new BelongsToDomainInlined('123'),
|
||||
Spec::andX(new BelongsToDomainInlined('123')),
|
||||
];
|
||||
yield 'not inline domain role' => [
|
||||
new ApiKeyRole(Role::DOMAIN_SPECIFIC, ['domain_id' => '456'], $apiKey),
|
||||
|
|
Loading…
Reference in a new issue