Extracted ApiKey metadata to the ApiKeyMeta object

This commit is contained in:
Alejandro Celaya 2021-03-14 09:59:35 +01:00
parent 9b55389538
commit 0a5c265b12
36 changed files with 186 additions and 121 deletions

View File

@ -40,11 +40,7 @@ class GenerateKeyCommandTest extends TestCase
/** @test */
public function noExpirationDateIsDefinedIfNotProvided(): void
{
$this->apiKeyService->create(
null, // Expiration date
null, // Name
)->shouldBeCalledOnce()
->willReturn(new ApiKey());
$this->apiKeyService->create(null, null)->shouldBeCalledOnce()->willReturn(ApiKey::create());
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
@ -55,11 +51,9 @@ class GenerateKeyCommandTest extends TestCase
/** @test */
public function expirationDateIsDefinedIfProvided(): void
{
$this->apiKeyService->create(
Argument::type(Chronos::class), // Expiration date
null, // Name
)->shouldBeCalledOnce()
->willReturn(new ApiKey());
$this->apiKeyService->create(Argument::type(Chronos::class), null)->shouldBeCalledOnce()->willReturn(
ApiKey::create(),
);
$this->commandTester->execute([
'--expiration-date' => '2016-01-01',
@ -69,11 +63,9 @@ class GenerateKeyCommandTest extends TestCase
/** @test */
public function nameIsDefinedIfProvided(): void
{
$this->apiKeyService->create(
null, // Expiration date
Argument::type('string'), // Name
)->shouldBeCalledOnce()
->willReturn(new ApiKey());
$this->apiKeyService->create(null, Argument::type('string'))->shouldBeCalledOnce()->willReturn(
ApiKey::create(),
);
$this->commandTester->execute([
'--name' => 'Alice',

View File

@ -9,6 +9,7 @@ use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Api\ListKeysCommand;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
@ -49,66 +50,66 @@ class ListKeysCommandTest extends TestCase
public function provideKeysAndOutputs(): iterable
{
yield 'all keys' => [
[ApiKey::withKey('foo'), ApiKey::withKey('bar'), ApiKey::withKey('baz')],
[$apiKey1 = ApiKey::create(), $apiKey2 = ApiKey::create(), $apiKey3 = ApiKey::create()],
false,
<<<OUTPUT
+-----+------+------------+-----------------+-------+
| Key | Name | Is enabled | Expiration date | Roles |
+-----+------+------------+-----------------+-------+
| foo | - | +++ | - | Admin |
| bar | - | +++ | - | Admin |
| baz | - | +++ | - | Admin |
+-----+------+------------+-----------------+-------+
+--------------------------------------+------+------------+-----------------+-------+
| Key | Name | Is enabled | Expiration date | Roles |
+--------------------------------------+------+------------+-----------------+-------+
| {$apiKey1} | - | +++ | - | Admin |
| {$apiKey2} | - | +++ | - | Admin |
| {$apiKey3} | - | +++ | - | Admin |
+--------------------------------------+------+------------+-----------------+-------+
OUTPUT,
];
yield 'enabled keys' => [
[ApiKey::withKey('foo')->disable(), ApiKey::withKey('bar')],
[$apiKey1 = ApiKey::create()->disable(), $apiKey2 = ApiKey::create()],
true,
<<<OUTPUT
+-----+------+-----------------+-------+
| Key | Name | Expiration date | Roles |
+-----+------+-----------------+-------+
| foo | - | - | Admin |
| bar | - | - | Admin |
+-----+------+-----------------+-------+
+--------------------------------------+------+-----------------+-------+
| Key | Name | Expiration date | Roles |
+--------------------------------------+------+-----------------+-------+
| {$apiKey1} | - | - | Admin |
| {$apiKey2} | - | - | Admin |
+--------------------------------------+------+-----------------+-------+
OUTPUT,
];
yield 'with roles' => [
[
ApiKey::withKey('foo'),
$this->apiKeyWithRoles('bar', [RoleDefinition::forAuthoredShortUrls()]),
$this->apiKeyWithRoles('baz', [RoleDefinition::forDomain((new Domain('example.com'))->setId('1'))]),
ApiKey::withKey('foo2'),
$this->apiKeyWithRoles('baz2', [
$apiKey1 = ApiKey::create(),
$apiKey2 = $this->apiKeyWithRoles([RoleDefinition::forAuthoredShortUrls()]),
$apiKey3 = $this->apiKeyWithRoles([RoleDefinition::forDomain((new Domain('example.com'))->setId('1'))]),
$apiKey4 = ApiKey::create(),
$apiKey5 = $this->apiKeyWithRoles([
RoleDefinition::forAuthoredShortUrls(),
RoleDefinition::forDomain((new Domain('example.com'))->setId('1')),
]),
ApiKey::withKey('foo3'),
$apiKey6 = ApiKey::create(),
],
true,
<<<OUTPUT
+------+------+-----------------+--------------------------+
| Key | Name | Expiration date | Roles |
+------+------+-----------------+--------------------------+
| foo | - | - | Admin |
| bar | - | - | Author only |
| baz | - | - | Domain only: example.com |
| foo2 | - | - | Admin |
| baz2 | - | - | Author only |
| | | | Domain only: example.com |
| foo3 | - | - | Admin |
+------+------+-----------------+--------------------------+
+--------------------------------------+------+-----------------+--------------------------+
| Key | Name | Expiration date | Roles |
+--------------------------------------+------+-----------------+--------------------------+
| {$apiKey1} | - | - | Admin |
| {$apiKey2} | - | - | Author only |
| {$apiKey3} | - | - | Domain only: example.com |
| {$apiKey4} | - | - | Admin |
| {$apiKey5} | - | - | Author only |
| | | | Domain only: example.com |
| {$apiKey6} | - | - | Admin |
+--------------------------------------+------+-----------------+--------------------------+
OUTPUT,
];
yield 'with names' => [
[
$apiKey1 = ApiKey::withName('Alice'),
$apiKey2 = ApiKey::withName('Alice and Bob'),
$apiKey3 = ApiKey::withName(''),
$apiKey4 = new ApiKey(),
$apiKey1 = ApiKey::fromMeta(ApiKeyMeta::withName('Alice')),
$apiKey2 = ApiKey::fromMeta(ApiKeyMeta::withName('Alice and Bob')),
$apiKey3 = ApiKey::fromMeta(ApiKeyMeta::withName('')),
$apiKey4 = ApiKey::create(),
],
true,
<<<OUTPUT
@ -125,9 +126,9 @@ class ListKeysCommandTest extends TestCase
];
}
private function apiKeyWithRoles(string $key, array $roles): ApiKey
private function apiKeyWithRoles(array $roles): ApiKey
{
$apiKey = ApiKey::withKey($key);
$apiKey = ApiKey::create();
foreach ($roles as $role) {
$apiKey->registerRole($role);
}

View File

@ -50,6 +50,7 @@ function parseDateRangeFromQuery(array $query, string $startDateName, string $en
$startDate = parseDateFromQuery($query, $startDateName);
$endDate = parseDateFromQuery($query, $endDateName);
// TODO Use match expression when migrating to PHP8
if ($startDate === null && $endDate === null) {
return DateRange::emptyInstance();
}

View File

@ -11,6 +11,7 @@ use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
@ -53,9 +54,9 @@ class DomainRepositoryTest extends DatabaseTestCase
/** @test */
public function findDomainsReturnsJustThoseMatchingProvidedApiKey(): void
{
$authorApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$authorApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($authorApiKey);
$authorAndDomainApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$authorAndDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($authorAndDomainApiKey);
$fooDomain = new Domain('foo.com');
@ -74,10 +75,10 @@ class DomainRepositoryTest extends DatabaseTestCase
$authorAndDomainApiKey->registerRole(RoleDefinition::forDomain($fooDomain));
$fooDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($fooDomain));
$fooDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($fooDomain)));
$this->getEntityManager()->persist($fooDomainApiKey);
$barDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($barDomain));
$barDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($barDomain)));
$this->getEntityManager()->persist($fooDomainApiKey);
$this->getEntityManager()->flush();

View File

@ -17,6 +17,7 @@ use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
@ -335,13 +336,13 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->flush();
$apiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$apiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($apiKey);
$otherApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$otherApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($otherApiKey);
$wrongDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($wrongDomain));
$wrongDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($wrongDomain)));
$this->getEntityManager()->persist($wrongDomainApiKey);
$rightDomainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($rightDomain));
$rightDomainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($rightDomain)));
$this->getEntityManager()->persist($rightDomainApiKey);
$shortUrl = ShortUrl::fromMeta(ShortUrlMeta::fromRawData([

View File

@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\TagRepository;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
@ -100,9 +101,9 @@ class TagRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist($domain);
$this->getEntityManager()->flush();
$authorApiKey = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$authorApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($authorApiKey);
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain));
$domainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($domain)));
$this->getEntityManager()->persist($domainApiKey);
$names = ['foo', 'bar', 'baz', 'another'];

View File

@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
use Shlinkio\Shlink\IpGeolocation\Model\Location;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
@ -176,7 +177,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->flush();
$apiKey1 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$apiKey1 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($apiKey1);
$shortUrl = ShortUrl::fromMeta(
ShortUrlMeta::fromRawData(['apiKey' => $apiKey1, 'domain' => $domain->getAuthority(), 'longUrl' => '']),
@ -185,7 +186,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist($shortUrl);
$this->createVisitsForShortUrl($shortUrl, 4);
$apiKey2 = ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls());
$apiKey2 = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->getEntityManager()->persist($apiKey2);
$shortUrl2 = ShortUrl::fromMeta(ShortUrlMeta::fromRawData(['apiKey' => $apiKey2, 'longUrl' => '']));
$this->getEntityManager()->persist($shortUrl2);
@ -198,7 +199,7 @@ class VisitRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist($shortUrl3);
$this->createVisitsForShortUrl($shortUrl3, 7);
$domainApiKey = ApiKey::withRoles(RoleDefinition::forDomain($domain));
$domainApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forDomain($domain)));
$this->getEntityManager()->persist($domainApiKey);
// Visits not linked to any short URL

View File

@ -14,6 +14,7 @@ use Shlinkio\Shlink\Core\Domain\Model\DomainItem;
use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Core\Exception\DomainNotFoundException;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
@ -50,8 +51,10 @@ class DomainServiceTest extends TestCase
public function provideExcludedDomains(): iterable
{
$default = new DomainItem('default.com', true);
$adminApiKey = new ApiKey();
$domainSpecificApiKey = ApiKey::withRoles(RoleDefinition::forDomain((new Domain(''))->setId('123')));
$adminApiKey = ApiKey::create();
$domainSpecificApiKey = ApiKey::fromMeta(
ApiKeyMeta::withRoles(RoleDefinition::forDomain((new Domain(''))->setId('123'))),
);
yield 'empty list without API key' => [[], [$default], null];
yield 'one item without API key' => [

View File

@ -66,7 +66,7 @@ class ShortUrlRepositoryAdapterTest extends TestCase
'startDate' => $startDate,
'endDate' => $endDate,
]);
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$adapter = new ShortUrlRepositoryAdapter($this->repo->reveal(), $params, $apiKey);
$dateRange = $params->dateRange();

View File

@ -44,7 +44,7 @@ class VisitsForTagPaginatorAdapterTest extends TestCase
public function repoIsCalledOnlyOnceForCount(): void
{
$count = 3;
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$adapter = $this->createAdapter($apiKey);
$countVisits = $this->repo->countVisitsByTag('foo', new DateRange(), $apiKey->spec())->willReturn(3);

View File

@ -47,7 +47,7 @@ class VisitsPaginatorAdapterTest extends TestCase
public function repoIsCalledOnlyOnceForCount(): void
{
$count = 3;
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$adapter = $this->createAdapter($apiKey);
$countVisits = $this->repo->countVisitsByShortCode('', null, new DateRange(), $apiKey->spec())->willReturn(3);

View File

@ -124,7 +124,7 @@ class ShortUrlServiceTest extends TestCase
'maxVisits' => 10,
'longUrl' => 'modifiedLongUrl',
],
), new ApiKey()];
), ApiKey::create()];
yield 'long URL with validation' => [1, ShortUrlEdit::fromRawData(
[
'longUrl' => 'modifiedLongUrl',

View File

@ -17,6 +17,7 @@ use Shlinkio\Shlink\Core\Repository\TagRepository;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagRenaming;
use Shlinkio\Shlink\Core\Tag\TagService;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use ShlinkioTest\Shlink\Core\Util\ApiKeyHelpersTrait;
@ -90,7 +91,10 @@ class TagServiceTest extends TestCase
$this->expectExceptionMessage('You are not allowed to delete tags');
$delete->shouldNotBeCalled();
$this->service->deleteTags(['foo', 'bar'], ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls()));
$this->service->deleteTags(
['foo', 'bar'],
ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls())),
);
}
/** @test */
@ -178,7 +182,7 @@ class TagServiceTest extends TestCase
$this->service->renameTag(
TagRenaming::fromNames('foo', 'bar'),
ApiKey::withRoles(RoleDefinition::forAuthoredShortUrls()),
ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls())),
);
}
}

View File

@ -11,6 +11,6 @@ trait ApiKeyHelpersTrait
public function provideAdminApiKeys(): iterable
{
yield 'no API key' => [null];
yield 'admin API key' => [new ApiKey()];
yield 'admin API key' => [ApiKey::create()];
}
}

View File

@ -113,7 +113,7 @@ class VisitsStatsHelperTest extends TestCase
public function throwsExceptionWhenRequestingVisitsForInvalidTag(): void
{
$tag = 'foo';
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$repo = $this->prophesize(TagRepository::class);
$tagExists = $repo->tagExists($tag, $apiKey)->willReturn(false);
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());

View File

@ -4,15 +4,57 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\ApiKey\Model;
use Cake\Chronos\Chronos;
final class ApiKeyMeta
{
public static function withKey(string $key): self
{
private ?string $name = null;
private ?Chronos $expirationDate = null;
/** @var RoleDefinition[] */
private array $roleDefinitions;
private function __construct(?string $name, ?Chronos $expirationDate, array $roleDefinitions)
{
$this->name = $name;
$this->expirationDate = $expirationDate;
$this->roleDefinitions = $roleDefinitions;
}
public static function withName(string $key): self
public static function withName(string $name): self
{
return new self($name, null, []);
}
public static function withExpirationDate(Chronos $expirationDate): self
{
return new self(null, $expirationDate, []);
}
public static function withNameAndExpirationDate(string $name, Chronos $expirationDate): self
{
return new self($name, $expirationDate, []);
}
public static function withRoles(RoleDefinition ...$roleDefinitions): self
{
return new self(null, null, $roleDefinitions);
}
public function name(): ?string
{
return $this->name;
}
public function expirationDate(): ?Chronos
{
return $this->expirationDate;
}
/**
* @return RoleDefinition[]
*/
public function roleDefinitions(): array
{
return $this->roleDefinitions;
}
}

View File

@ -12,6 +12,7 @@ use Happyr\DoctrineSpecification\Spec;
use Happyr\DoctrineSpecification\Specification\Specification;
use Ramsey\Uuid\Uuid;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\ApiKey\Role;
@ -27,7 +28,7 @@ class ApiKey extends AbstractEntity
/**
* @throws Exception
*/
public function __construct(?Chronos $expirationDate = null, ?string $name = null)
private function __construct(?string $name = null, ?Chronos $expirationDate = null)
{
$this->key = Uuid::uuid4()->toString();
$this->expirationDate = $expirationDate;
@ -36,30 +37,21 @@ class ApiKey extends AbstractEntity
$this->roles = new ArrayCollection();
}
public static function withRoles(RoleDefinition ...$roleDefinitions): self
public static function create(): ApiKey
{
$apiKey = new self();
return new self();
}
foreach ($roleDefinitions as $roleDefinition) {
public static function fromMeta(ApiKeyMeta $meta): self
{
$apiKey = new self($meta->name(), $meta->expirationDate());
foreach ($meta->roleDefinitions() as $roleDefinition) {
$apiKey->registerRole($roleDefinition);
}
return $apiKey;
}
public static function withKey(string $key, ?Chronos $expirationDate = null, ?string $name = null): self
{
$apiKey = new self($expirationDate, $name);
$apiKey->key = $key;
return $apiKey;
}
public static function withName(string $name): self
{
return new self(null, $name);
}
public function getExpirationDate(): ?Chronos
{
return $this->expirationDate;

View File

@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Rest\Service;
use Cake\Chronos\Chronos;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
@ -26,7 +27,7 @@ class ApiKeyService implements ApiKeyServiceInterface
?string $name = null,
RoleDefinition ...$roleDefinitions
): ApiKey {
$key = new ApiKey($expirationDate, $name);
$key = $this->buildApiKeyWithParams($expirationDate, $name);
foreach ($roleDefinitions as $definition) {
$key->registerRole($definition);
}
@ -37,6 +38,24 @@ class ApiKeyService implements ApiKeyServiceInterface
return $key;
}
private function buildApiKeyWithParams(?Chronos $expirationDate, ?string $name): ApiKey
{
// TODO Use match expression when migrating to PHP8
if ($expirationDate === null && $name === null) {
return ApiKey::create();
}
if ($expirationDate !== null && $name !== null) {
return ApiKey::fromMeta(ApiKeyMeta::withNameAndExpirationDate($name, $expirationDate));
}
if ($name === null) {
return ApiKey::fromMeta(ApiKeyMeta::withExpirationDate($expirationDate));
}
return ApiKey::fromMeta(ApiKeyMeta::withName($name));
}
public function check(string $key): ApiKeyCheckResult
{
$apiKey = $this->getByKey($key);

View File

@ -8,7 +8,9 @@ use Cake\Chronos\Chronos;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use ReflectionObject;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
@ -41,7 +43,11 @@ class ApiKeyFixture extends AbstractFixture implements DependentFixtureInterface
private function buildApiKey(string $key, bool $enabled, ?Chronos $expiresAt = null): ApiKey
{
$apiKey = ApiKey::withKey($key, $expiresAt);
$apiKey = $expiresAt !== null ? ApiKey::fromMeta(ApiKeyMeta::withExpirationDate($expiresAt)) : ApiKey::create();
$ref = new ReflectionObject($apiKey);
$keyProp = $ref->getProperty('key');
$keyProp->setAccessible(true);
$keyProp->setValue($apiKey, $key);
if (! $enabled) {
$apiKey->disable();

View File

@ -30,7 +30,7 @@ class ListDomainsActionTest extends TestCase
/** @test */
public function domainsAreProperlyListed(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$domains = [
new DomainItem('bar.com', true),
new DomainItem('baz.com', false),

View File

@ -40,7 +40,7 @@ class CreateShortUrlActionTest extends TestCase
/** @test */
public function properShortcodeConversionReturnsData(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$shortUrl = ShortUrl::createEmpty();
$expectedMeta = $body = [
'longUrl' => 'http://www.domain.com/foo/bar',
@ -80,7 +80,7 @@ class CreateShortUrlActionTest extends TestCase
$request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar',
'domain' => $domain,
])->withAttribute(ApiKey::class, new ApiKey());
])->withAttribute(ApiKey::class, ApiKey::create());
$this->expectException(ValidationException::class);
$urlToShortCode->shouldNotBeCalled();

View File

@ -29,7 +29,7 @@ class DeleteShortUrlActionTest extends TestCase
/** @test */
public function emptyResponseIsReturnedIfProperlyDeleted(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$deleteByShortCode = $this->service->deleteByShortCode(Argument::any(), false, $apiKey)->will(
function (): void {
},

View File

@ -48,7 +48,7 @@ class EditShortUrlActionTest extends TestCase
public function correctShortCodeReturnsSuccess(): void
{
$request = (new ServerRequest())->withAttribute('shortCode', 'abc123')
->withAttribute(ApiKey::class, new ApiKey())
->withAttribute(ApiKey::class, ApiKey::create())
->withParsedBody([
'maxVisits' => 5,
]);

View File

@ -58,6 +58,6 @@ class EditShortUrlTagsActionTest extends TestCase
private function createRequestWithAPiKey(): ServerRequestInterface
{
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, new ApiKey());
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, ApiKey::create());
}
}

View File

@ -51,7 +51,7 @@ class ListShortUrlsActionTest extends TestCase
?string $startDate = null,
?string $endDate = null
): void {
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$request = (new ServerRequest())->withQueryParams($query)->withAttribute(ApiKey::class, $apiKey);
$listShortUrls = $this->service->listShortUrls(ShortUrlsParams::fromRawData([
'page' => $expectedPage,

View File

@ -37,7 +37,7 @@ class ResolveShortUrlActionTest extends TestCase
public function correctShortCodeReturnsSuccess(): void
{
$shortCode = 'abc123';
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn(
ShortUrl::withLongUrl('http://domain.com/foo/bar'),
)->shouldBeCalledOnce();

View File

@ -39,7 +39,7 @@ class SingleStepCreateShortUrlActionTest extends TestCase
/** @test */
public function properDataIsPassedWhenGeneratingShortCode(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$request = (new ServerRequest())->withQueryParams([
'longUrl' => 'http://foobar.com',

View File

@ -34,7 +34,7 @@ class DeleteTagsActionTest extends TestCase
{
$request = (new ServerRequest())
->withQueryParams(['tags' => $tags])
->withAttribute(ApiKey::class, new ApiKey());
->withAttribute(ApiKey::class, ApiKey::create());
$deleteTags = $this->tagService->deleteTags($tags ?: [], Argument::type(ApiKey::class));
$response = $this->action->handle($request);

View File

@ -83,6 +83,6 @@ class ListTagsActionTest extends TestCase
private function requestWithApiKey(): ServerRequestInterface
{
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, new ApiKey());
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, ApiKey::create());
}
}

View File

@ -70,6 +70,6 @@ class UpdateTagActionTest extends TestCase
private function requestWithApiKey(): ServerRequestInterface
{
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, new ApiKey());
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, ApiKey::create());
}
}

View File

@ -30,7 +30,7 @@ class GlobalVisitsActionTest extends TestCase
/** @test */
public function statsAreReturnedFromHelper(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$stats = new VisitsStats(5, 3);
$getStats = $this->helper->getVisitsStats($apiKey)->willReturn($stats);

View File

@ -73,6 +73,6 @@ class ShortUrlVisitsActionTest extends TestCase
private function requestWithApiKey(): ServerRequestInterface
{
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, new ApiKey());
return ServerRequestFactory::fromGlobals()->withAttribute(ApiKey::class, ApiKey::create());
}
}

View File

@ -33,7 +33,7 @@ class TagVisitsActionTest extends TestCase
public function providingCorrectShortCodeReturnsVisits(): void
{
$tag = 'foo';
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$getVisits = $this->visitsHelper->visitsForTag($tag, Argument::type(VisitsParams::class), $apiKey)->willReturn(
new Paginator(new ArrayAdapter([])),
);

View File

@ -28,7 +28,7 @@ class RoleTest extends TestCase
public function provideRoles(): iterable
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
yield 'inline invalid role' => [new ApiKeyRole('invalid', [], $apiKey), true, Spec::andX()];
yield 'not inline invalid role' => [new ApiKeyRole('invalid', [], $apiKey), false, Spec::andX()];

View File

@ -138,7 +138,7 @@ class AuthenticationMiddlewareTest extends TestCase
/** @test */
public function validApiKeyFallsBackToNextMiddleware(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$key = $apiKey->toString();
$request = ServerRequestFactory::fromGlobals()
->withAttribute(

View File

@ -13,6 +13,7 @@ use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
@ -82,14 +83,14 @@ class ApiKeyServiceTest extends TestCase
public function provideInvalidApiKeys(): iterable
{
yield 'non-existent api key' => [null];
yield 'disabled api key' => [(new ApiKey())->disable()];
yield 'expired api key' => [new ApiKey(Chronos::now()->subDay())];
yield 'disabled api key' => [ApiKey::create()->disable()];
yield 'expired api key' => [ApiKey::fromMeta(ApiKeyMeta::withExpirationDate(Chronos::now()->subDay()))];
}
/** @test */
public function checkReturnsTrueWhenConditionsAreFavorable(): void
{
$apiKey = new ApiKey();
$apiKey = ApiKey::create();
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($apiKey)
@ -118,7 +119,7 @@ class ApiKeyServiceTest extends TestCase
/** @test */
public function disableReturnsDisabledApiKeyWhenFound(): void
{
$key = new ApiKey();
$key = ApiKey::create();
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($key)
->shouldBeCalledOnce();
@ -135,7 +136,7 @@ class ApiKeyServiceTest extends TestCase
/** @test */
public function listFindsAllApiKeys(): void
{
$expectedApiKeys = [new ApiKey(), new ApiKey(), new ApiKey()];
$expectedApiKeys = [ApiKey::create(), ApiKey::create(), ApiKey::create()];
$repo = $this->prophesize(EntityRepository::class);
$repo->findBy([])->willReturn($expectedApiKeys)
@ -150,7 +151,7 @@ class ApiKeyServiceTest extends TestCase
/** @test */
public function listEnabledFindsOnlyEnabledApiKeys(): void
{
$expectedApiKeys = [new ApiKey(), new ApiKey(), new ApiKey()];
$expectedApiKeys = [ApiKey::create(), ApiKey::create(), ApiKey::create()];
$repo = $this->prophesize(EntityRepository::class);
$repo->findBy(['enabled' => true])->willReturn($expectedApiKeys)