Merge pull request #248 from acelaya/feature/fix-anemic-model

Feature/fix anemic model
This commit is contained in:
Alejandro Celaya 2018-10-28 16:27:14 +01:00 committed by GitHub
commit 6986d03c53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 241 additions and 403 deletions

View file

@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* [#241](https://github.com/shlinkio/shlink/issues/241) Fixed columns in `visit_locations` table, to be snake_case instead of camelCase.
* [#228](https://github.com/shlinkio/shlink/issues/228) Updated how exceptions are serialized into logs, by using monlog's `PsrLogMessageProcessor`.
* [#225](https://github.com/shlinkio/shlink/issues/225) Performance and maintainability slightly improved by enforcing via code sniffer that all global namespace classes, functions and constants are explicitly imported.
* [#196](https://github.com/shlinkio/shlink/issues/196) Reduced anemic model in entities, defining more expressive public APIs instead.
#### Deprecated

View file

@ -12,6 +12,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Zend\I18n\Translator\TranslatorInterface;
use function array_filter;
use function array_map;
use function sprintf;
class ListKeysCommand extends Command
@ -54,24 +55,20 @@ class ListKeysCommand extends Command
{
$io = new SymfonyStyle($input, $output);
$enabledOnly = $input->getOption('enabledOnly');
$list = $this->apiKeyService->listKeys($enabledOnly);
$rows = [];
/** @var ApiKey $row */
foreach ($list as $row) {
$key = $row->getKey();
$expiration = $row->getExpirationDate();
$messagePattern = $this->determineMessagePattern($row);
$rows = array_map(function (ApiKey $apiKey) use ($enabledOnly) {
$key = (string) $apiKey;
$expiration = $apiKey->getExpirationDate();
$messagePattern = $this->determineMessagePattern($apiKey);
// Set columns for this row
$rowData = [sprintf($messagePattern, $key)];
if (! $enabledOnly) {
$rowData[] = sprintf($messagePattern, $this->getEnabledSymbol($row));
$rowData[] = sprintf($messagePattern, $this->getEnabledSymbol($apiKey));
}
$rowData[] = $expiration !== null ? $expiration->toAtomString() : '-';
$rows[] = $rowData;
}
return $rowData;
}, $this->apiKeyService->listKeys($enabledOnly));
$io->table(array_filter([
$this->translator->translate('Key'),

View file

@ -62,7 +62,7 @@ class GeneratePreviewCommand extends Command
$page += 1;
foreach ($shortUrls as $shortUrl) {
$this->processUrl($shortUrl->getOriginalUrl(), $output);
$this->processUrl($shortUrl->getLongUrl(), $output);
}
} while ($page <= $shortUrls->count());

View file

@ -5,6 +5,7 @@ namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Cake\Chronos\Chronos;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@ -13,7 +14,8 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Zend\I18n\Translator\TranslatorInterface;
use function array_values;
use function array_map;
use function Shlinkio\Shlink\Common\pick;
class GetVisitsCommand extends Command
{
@ -87,17 +89,11 @@ class GetVisitsCommand extends Command
$endDate = $this->getDateOption($input, 'endDate');
$visits = $this->visitsTracker->info($shortCode, new DateRange($startDate, $endDate));
$rows = [];
foreach ($visits as $row) {
$rowData = $row->jsonSerialize();
// Unset location info and remote addr
unset($rowData['visitLocation'], $rowData['remoteAddr']);
$rowData['country'] = $row->getVisitLocation()->getCountryName();
$rows[] = array_values($rowData);
}
$rows = array_map(function (Visit $visit) {
$rowData = $visit->jsonSerialize();
$rowData['country'] = $visit->getVisitLocation()->getCountryName();
return pick($rowData, ['referer', 'date', 'userAgent', 'country']);
}, $visits);
$io->table([
$this->translator->translate('Referer'),
$this->translator->translate('Date'),

View file

@ -53,7 +53,7 @@ class ListTagsCommand extends Command
}
return array_map(function (Tag $tag) {
return [$tag->getName()];
return [(string) $tag];
}, $tags);
}
}

View file

@ -80,8 +80,7 @@ class ProcessVisitsCommand extends Command
try {
$result = $this->ipLocationResolver->resolveIpLocation($ipAddr);
$location = new VisitLocation();
$location->exchangeArray($result);
$location = new VisitLocation($result);
$visit->setVisitLocation($location);
$this->visitService->saveVisit($visit);

View file

@ -56,9 +56,9 @@ class GeneratePreviewCommandTest extends TestCase
public function previewsForEveryUrlAreGenerated()
{
$paginator = $this->createPaginator([
(new ShortUrl())->setOriginalUrl('http://foo.com'),
(new ShortUrl())->setOriginalUrl('https://bar.com'),
(new ShortUrl())->setOriginalUrl('http://baz.com/something'),
new ShortUrl('http://foo.com'),
new ShortUrl('https://bar.com'),
new ShortUrl('http://baz.com/something'),
]);
$this->shortUrlService->listShortUrls(1)->willReturn($paginator)->shouldBeCalledTimes(1);
@ -77,9 +77,9 @@ class GeneratePreviewCommandTest extends TestCase
public function exceptionWillOutputError()
{
$items = [
(new ShortUrl())->setOriginalUrl('http://foo.com'),
(new ShortUrl())->setOriginalUrl('https://bar.com'),
(new ShortUrl())->setOriginalUrl('http://baz.com/something'),
new ShortUrl('http://foo.com'),
new ShortUrl('https://bar.com'),
new ShortUrl('http://baz.com/something'),
];
$paginator = $this->createPaginator($items);
$this->shortUrlService->listShortUrls(1)->willReturn($paginator)->shouldBeCalledTimes(1);

View file

@ -45,8 +45,7 @@ class GenerateShortcodeCommandTest extends TestCase
{
$this->urlShortener->urlToShortCode(Argument::cetera())
->willReturn(
(new ShortUrl())->setShortCode('abc123')
->setLongUrl('')
(new ShortUrl(''))->setShortCode('abc123')
)
->shouldBeCalledTimes(1);

View file

@ -9,8 +9,10 @@ use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\ShortUrl\GetVisitsCommand;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
@ -79,9 +81,9 @@ class GetVisitsCommandTest extends TestCase
{
$shortCode = 'abc123';
$this->visitsTracker->info($shortCode, Argument::any())->willReturn([
(new Visit())->setReferer('foo')
->setVisitLocation((new VisitLocation())->setCountryName('Spain'))
->setUserAgent('bar'),
(new Visit(new ShortUrl(''), new Visitor('bar', 'foo', '')))->setVisitLocation(
new VisitLocation(['country_name' => 'Spain'])
),
])->shouldBeCalledTimes(1);
$this->commandTester->execute([

View file

@ -55,7 +55,7 @@ class ListShortcodesCommandTest extends TestCase
// The paginator will return more than one page for the first 3 times
$data = [];
for ($i = 0; $i < 50; $i++) {
$data[] = (new ShortUrl())->setLongUrl('url_' . $i);
$data[] = new ShortUrl('url_' . $i);
}
$this->shortUrlService->listShortUrls(Argument::cetera())->will(function () use (&$data) {
@ -74,7 +74,7 @@ class ListShortcodesCommandTest extends TestCase
// The paginator will return more than one page
$data = [];
for ($i = 0; $i < 30; $i++) {
$data[] = (new ShortUrl())->setLongUrl('url_' . $i);
$data[] = new ShortUrl('url_' . $i);
}
$this->shortUrlService->listShortUrls(Argument::cetera())->willReturn(new Paginator(new ArrayAdapter($data)))

View file

@ -43,7 +43,7 @@ class ResolveUrlCommandTest extends TestCase
{
$shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar';
$shortUrl = (new ShortUrl())->setLongUrl($expectedUrl);
$shortUrl = new ShortUrl($expectedUrl);
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($shortUrl)
->shouldBeCalledTimes(1);

View file

@ -61,8 +61,8 @@ class ListTagsCommandTest extends TestCase
{
/** @var MethodProphecy $listTags */
$listTags = $this->tagService->listTags()->willReturn([
(new Tag())->setName('foo'),
(new Tag())->setName('bar'),
new Tag('foo'),
new Tag('bar'),
]);
$this->commandTester->execute([]);

View file

@ -68,7 +68,7 @@ class RenameTagCommandTest extends TestCase
$oldName = 'foo';
$newName = 'bar';
/** @var MethodProphecy $renameTag */
$renameTag = $this->tagService->renameTag($oldName, $newName)->willReturn(new Tag());
$renameTag = $this->tagService->renameTag($oldName, $newName)->willReturn(new Tag($newName));
$this->commandTester->execute([
'oldName' => $oldName,

View file

@ -8,7 +8,9 @@ use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
use Shlinkio\Shlink\Common\Service\IpApiLocationResolver;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Service\VisitService;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Output\OutputInterface;
@ -54,10 +56,12 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function allReturnedVisitsIpsAreProcessed()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
];
$this->visitService->getUnlocatedVisits()->willReturn($visits)
->shouldBeCalledTimes(1);
@ -80,14 +84,16 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function localhostAndEmptyAddressIsIgnored()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('127.0.0.1'),
(new Visit())->setRemoteAddr('127.0.0.1'),
(new Visit())->setRemoteAddr(''),
(new Visit())->setRemoteAddr(null),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '127.0.0.1')),
new Visit($shortUrl, new Visitor('', '', '127.0.0.1')),
new Visit($shortUrl, new Visitor('', '', '')),
new Visit($shortUrl, new Visitor('', '', null)),
];
$this->visitService->getUnlocatedVisits()->willReturn($visits)
->shouldBeCalledTimes(1);
@ -109,17 +115,19 @@ class ProcessVisitsCommandTest extends TestCase
*/
public function sleepsEveryTimeTheApiLimitIsReached()
{
$shortUrl = new ShortUrl('');
$visits = [
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('1.2.3.4'),
(new Visit())->setRemoteAddr('4.3.2.1'),
(new Visit())->setRemoteAddr('12.34.56.78'),
(new Visit())->setRemoteAddr('4.3.2.1'),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '1.2.3.4')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
new Visit($shortUrl, new Visitor('', '', '12.34.56.78')),
new Visit($shortUrl, new Visitor('', '', '4.3.2.1')),
];
$apiLimit = 3;

View file

@ -3,7 +3,9 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Common;
use const ARRAY_FILTER_USE_KEY;
use const JSON_ERROR_NONE;
use function array_filter;
use function getenv;
use function in_array;
use function json_decode as spl_json_decode;
@ -52,6 +54,19 @@ function contains($needle, array $haystack): bool
return in_array($needle, $haystack, true);
}
/**
* Returns only the keys in keysToPick from provided array
*
* @param array $array
* @param array $keysToPick
*/
function pick(array $array, array $keysToPick): array
{
return array_filter($array, function (string $key) use ($keysToPick) {
return contains($key, $keysToPick);
}, ARRAY_FILTER_USE_KEY);
}
/**
* @throws Exception\InvalidArgumentException
*/

View file

@ -8,6 +8,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use function count;
@ -25,7 +26,7 @@ class ShortUrl extends AbstractEntity
* @var string
* @ORM\Column(name="original_url", type="string", nullable=false, length=1024)
*/
private $originalUrl;
private $longUrl;
/**
* @var string
* @ORM\Column(
@ -73,39 +74,23 @@ class ShortUrl extends AbstractEntity
*/
private $maxVisits;
public function __construct()
public function __construct(string $longUrl, ShortUrlMeta $meta = null)
{
$this->shortCode = '';
$meta = $meta ?? ShortUrlMeta::createEmpty();
$this->longUrl = $longUrl;
$this->dateCreated = Chronos::now();
$this->visits = new ArrayCollection();
$this->tags = new ArrayCollection();
$this->validSince = $meta->getValidSince();
$this->validUntil = $meta->getValidUntil();
$this->maxVisits = $meta->getMaxVisits();
$this->shortCode = $meta->getCustomSlug() ?? ''; // TODO logic to calculate short code should be passed somehow
}
public function getLongUrl(): string
{
return $this->originalUrl;
}
public function setLongUrl(string $longUrl): self
{
$this->originalUrl = $longUrl;
return $this;
}
/**
* @deprecated Use getLongUrl() instead
*/
public function getOriginalUrl(): string
{
return $this->getLongUrl();
}
/**
* @deprecated Use setLongUrl() instead
*/
public function setOriginalUrl(string $originalUrl): self
{
return $this->setLongUrl($originalUrl);
return $this->longUrl;
}
public function getShortCode(): string
@ -113,6 +98,7 @@ class ShortUrl extends AbstractEntity
return $this->shortCode;
}
// TODO Short code is currently calculated based on the ID, so a setter is needed
public function setShortCode(string $shortCode): self
{
$this->shortCode = $shortCode;
@ -124,12 +110,6 @@ class ShortUrl extends AbstractEntity
return $this->dateCreated;
}
public function setDateCreated(Chronos $dateCreated): self
{
$this->dateCreated = $dateCreated;
return $this;
}
/**
* @return Collection|Tag[]
*/
@ -147,10 +127,17 @@ class ShortUrl extends AbstractEntity
return $this;
}
public function addTag(Tag $tag): self
public function updateMeta(ShortUrlMeta $shortCodeMeta): void
{
$this->tags->add($tag);
return $this;
if ($shortCodeMeta->hasValidSince()) {
$this->validSince = $shortCodeMeta->getValidSince();
}
if ($shortCodeMeta->hasValidUntil()) {
$this->validUntil = $shortCodeMeta->getValidUntil();
}
if ($shortCodeMeta->hasMaxVisits()) {
$this->maxVisits = $shortCodeMeta->getMaxVisits();
}
}
public function getValidSince(): ?Chronos
@ -158,23 +145,11 @@ class ShortUrl extends AbstractEntity
return $this->validSince;
}
public function setValidSince(?Chronos $validSince): self
{
$this->validSince = $validSince;
return $this;
}
public function getValidUntil(): ?Chronos
{
return $this->validUntil;
}
public function setValidUntil(?Chronos $validUntil): self
{
$this->validUntil = $validUntil;
return $this;
}
public function getVisitsCount(): int
{
return count($this->visits);
@ -196,12 +171,6 @@ class ShortUrl extends AbstractEntity
return $this->maxVisits;
}
public function setMaxVisits(?int $maxVisits): self
{
$this->maxVisits = $maxVisits;
return $this;
}
public function maxVisitsReached(): bool
{
return $this->maxVisits !== null && $this->getVisitsCount() >= $this->maxVisits;

View file

@ -24,24 +24,23 @@ class Tag extends AbstractEntity implements JsonSerializable
*/
private $name;
public function __construct($name = null)
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name)
public function rename(string $name): void
{
$this->name = $name;
return $this;
}
public function jsonSerialize(): string
{
return $this->name;
}
public function __toString(): string
{
return $this->name;
}
}

View file

@ -9,6 +9,7 @@ use JsonSerializable;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
/**
@ -54,58 +55,13 @@ class Visit extends AbstractEntity implements JsonSerializable
*/
private $visitLocation;
public function __construct()
{
$this->date = Chronos::now();
}
public function getReferer(): string
{
return $this->referer;
}
public function setReferer(string $referer): self
{
$this->referer = $referer;
return $this;
}
public function getDate(): Chronos
{
return $this->date;
}
public function setDate(Chronos $date): self
{
$this->date = $date;
return $this;
}
public function getShortUrl(): ShortUrl
{
return $this->shortUrl;
}
public function setShortUrl(ShortUrl $shortUrl): self
public function __construct(ShortUrl $shortUrl, Visitor $visitor, ?Chronos $date = null)
{
$this->shortUrl = $shortUrl;
return $this;
}
public function getRemoteAddr(): ?string
{
return $this->remoteAddr;
}
public function setRemoteAddr(?string $remoteAddr): self
{
$this->remoteAddr = $this->obfuscateAddress($remoteAddr);
return $this;
}
public function hasRemoteAddr(): bool
{
return ! empty($this->remoteAddr);
$this->date = $date ?? Chronos::now();
$this->userAgent = $visitor->getUserAgent();
$this->referer = $visitor->getReferer();
$this->remoteAddr = $this->obfuscateAddress($visitor->getRemoteAddress());
}
private function obfuscateAddress(?string $address): ?string
@ -122,15 +78,14 @@ class Visit extends AbstractEntity implements JsonSerializable
}
}
public function getUserAgent(): string
public function getRemoteAddr(): ?string
{
return $this->userAgent;
return $this->remoteAddr;
}
public function setUserAgent(string $userAgent): self
public function hasRemoteAddr(): bool
{
$this->userAgent = $userAgent;
return $this;
return ! empty($this->remoteAddr);
}
public function getVisitLocation(): VisitLocation

View file

@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Core\Entity;
use Doctrine\ORM\Mapping as ORM;
use JsonSerializable;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Zend\Stdlib\ArraySerializableInterface;
use function array_key_exists;
/**
@ -17,7 +16,7 @@ use function array_key_exists;
* @ORM\Entity()
* @ORM\Table(name="visit_locations")
*/
class VisitLocation extends AbstractEntity implements ArraySerializableInterface, JsonSerializable
class VisitLocation extends AbstractEntity implements JsonSerializable
{
/**
* @var string
@ -55,15 +54,9 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
*/
private $timezone;
public function getCountryCode(): string
public function __construct(array $locationInfo)
{
return $this->countryCode ?? '';
}
public function setCountryCode(string $countryCode)
{
$this->countryCode = $countryCode;
return $this;
$this->exchangeArray($locationInfo);
}
public function getCountryName(): string
@ -71,99 +64,50 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
return $this->countryName ?? '';
}
public function setCountryName(string $countryName): self
{
$this->countryName = $countryName;
return $this;
}
public function getRegionName(): string
{
return $this->regionName ?? '';
}
public function setRegionName(string $regionName): self
{
$this->regionName = $regionName;
return $this;
}
public function getCityName(): string
{
return $this->cityName ?? '';
}
public function setCityName(string $cityName): self
{
$this->cityName = $cityName;
return $this;
}
public function getLatitude(): string
{
return $this->latitude ?? '';
}
public function setLatitude(string $latitude): self
{
$this->latitude = $latitude;
return $this;
}
public function getLongitude(): string
{
return $this->longitude ?? '';
}
public function setLongitude(string $longitude): self
public function getCityName(): string
{
$this->longitude = $longitude;
return $this;
}
public function getTimezone(): string
{
return $this->timezone ?? '';
}
public function setTimezone(string $timezone): self
{
$this->timezone = $timezone;
return $this;
return $this->cityName ?? '';
}
/**
* Exchange internal values from provided array
*/
public function exchangeArray(array $array): void
private function exchangeArray(array $array): void
{
if (array_key_exists('country_code', $array)) {
$this->setCountryCode((string) $array['country_code']);
$this->countryCode = (string) $array['country_code'];
}
if (array_key_exists('country_name', $array)) {
$this->setCountryName((string) $array['country_name']);
$this->countryName = (string) $array['country_name'];
}
if (array_key_exists('region_name', $array)) {
$this->setRegionName((string) $array['region_name']);
$this->regionName = (string) $array['region_name'];
}
if (array_key_exists('city', $array)) {
$this->setCityName((string) $array['city']);
$this->cityName = (string) $array['city'];
}
if (array_key_exists('latitude', $array)) {
$this->setLatitude((string) $array['latitude']);
$this->latitude = (string) $array['latitude'];
}
if (array_key_exists('longitude', $array)) {
$this->setLongitude((string) $array['longitude']);
$this->longitude = (string) $array['longitude'];
}
if (array_key_exists('time_zone', $array)) {
$this->setTimezone((string) $array['time_zone']);
$this->timezone = (string) $array['time_zone'];
}
}
/**
* Return an array representation of the object
*/
public function getArrayCopy(): array
public function jsonSerialize(): array
{
return [
'countryCode' => $this->countryCode,
@ -175,9 +119,4 @@ class VisitLocation extends AbstractEntity implements ArraySerializableInterface
'timezone' => $this->timezone,
];
}
public function jsonSerialize(): array
{
return $this->getArrayCopy();
}
}

View file

@ -32,9 +32,13 @@ final class ShortUrlMeta
{
}
public static function createEmpty(): self
{
return new self();
}
/**
* @param array $data
* @return ShortUrlMeta
* @throws ValidationException
*/
public static function createFromRawData(array $data): self
@ -49,7 +53,6 @@ final class ShortUrlMeta
* @param string|Chronos|null $validUntil
* @param string|null $customSlug
* @param int|null $maxVisits
* @return ShortUrlMeta
* @throws ValidationException
*/
public static function createFromParams(
@ -124,10 +127,7 @@ final class ShortUrlMeta
return $this->validUntil !== null;
}
/**
* @return null|string
*/
public function getCustomSlug()
public function getCustomSlug(): ?string
{
return $this->customSlug;
}
@ -137,10 +137,7 @@ final class ShortUrlMeta
return $this->customSlug !== null;
}
/**
* @return int|null
*/
public function getMaxVisits()
public function getMaxVisits(): ?int
{
return $this->maxVisits;
}

View file

@ -55,8 +55,8 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
{
// Map public field names to column names
$fieldNameMap = [
'originalUrl' => 'originalUrl',
'longUrl' => 'originalUrl',
'originalUrl' => 'longUrl',
'longUrl' => 'longUrl',
'shortCode' => 'shortCode',
'dateCreated' => 'dateCreated',
];
@ -112,7 +112,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
}
$conditions = [
$qb->expr()->like('s.originalUrl', ':searchPattern'),
$qb->expr()->like('s.longUrl', ':searchPattern'),
$qb->expr()->like('s.shortCode', ':searchPattern'),
$qb->expr()->like('t.name', ':searchPattern'),
];

View file

@ -60,18 +60,10 @@ class ShortUrlService implements ShortUrlServiceInterface
/**
* @throws InvalidShortCodeException
*/
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortCodeMeta): ShortUrl
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortUrlMeta): ShortUrl
{
$shortUrl = $this->findByShortCode($this->em, $shortCode);
if ($shortCodeMeta->hasValidSince()) {
$shortUrl->setValidSince($shortCodeMeta->getValidSince());
}
if ($shortCodeMeta->hasValidUntil()) {
$shortUrl->setValidUntil($shortCodeMeta->getValidUntil());
}
if ($shortCodeMeta->hasMaxVisits()) {
$shortUrl->setMaxVisits($shortCodeMeta->getMaxVisits());
}
$shortUrl->updateMeta($shortUrlMeta);
/** @var ORM\EntityManager $em */
$em = $this->em;

View file

@ -26,5 +26,5 @@ interface ShortUrlServiceInterface
/**
* @throws InvalidShortCodeException
*/
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortCodeMeta): ShortUrl;
public function updateMetadataByShortCode(string $shortCode, ShortUrlMeta $shortUrlMeta): ShortUrl;
}

View file

@ -76,7 +76,7 @@ class TagService implements TagServiceInterface
throw EntityDoesNotExistException::createFromEntityAndConditions(Tag::class, $criteria);
}
$tag->setName($newName);
$tag->rename($newName);
/** @var ORM\EntityManager $em */
$em = $this->em;

View file

@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Exception\RuntimeException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Throwable;
@ -90,15 +91,15 @@ class UrlShortener implements UrlShortenerInterface
$this->em->beginTransaction();
// First, create the short URL with an empty short code
$shortUrl = new ShortUrl();
$shortUrl->setOriginalUrl((string) $url)
->setValidSince($validSince)
->setValidUntil($validUntil)
->setMaxVisits($maxVisits);
$shortUrl = new ShortUrl(
(string) $url,
ShortUrlMeta::createFromParams($validSince, $validUntil, null, $maxVisits)
);
$this->em->persist($shortUrl);
$this->em->flush();
// Generate the short code and persist it
// TODO Somehow provide the logic to calculate the shortCode to avoid the need of a setter
$shortCode = $customSlug ?? $this->convertAutoincrementIdToShortCode((float) $shortUrl->getId());
$shortUrl->setShortCode($shortCode)
->setTags($this->tagNamesToEntities($this->em, $tags));

View file

@ -34,11 +34,7 @@ class VisitsTracker implements VisitsTrackerInterface
'shortCode' => $shortCode,
]);
$visit = new Visit();
$visit->setShortUrl($shortUrl)
->setUserAgent($visitor->getUserAgent())
->setReferer($visitor->getReferer())
->setRemoteAddr($visitor->getRemoteAddress());
$visit = new Visit($shortUrl, $visitor);
/** @var ORM\EntityManager $em */
$em = $this->em;

View file

@ -47,6 +47,6 @@ class ShortUrlDataTransformer implements DataTransformerInterface
private function serializeTag(Tag $tag): string
{
return $tag->getName();
return (string) $tag;
}
}

View file

@ -22,7 +22,7 @@ trait TagManagerTrait
$entities = [];
foreach ($tags as $tagName) {
$tagName = $this->normalizeTagName($tagName);
$tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?: (new Tag())->setName($tagName);
$tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?: new Tag($tagName);
$em->persist($tag);
$entities[] = $tag;
}

View file

@ -8,6 +8,8 @@ use Doctrine\Common\Collections\ArrayCollection;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
use ShlinkioTest\Shlink\Common\DbUnit\DatabaseTestCase;
use function count;
@ -35,28 +37,23 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
*/
public function findOneByShortCodeReturnsProperData()
{
$foo = new ShortUrl();
$foo->setOriginalUrl('foo')
->setShortCode('foo');
$foo = new ShortUrl('foo');
$foo->setShortCode('foo');
$this->getEntityManager()->persist($foo);
$bar = new ShortUrl();
$bar->setOriginalUrl('bar')
->setShortCode('bar_very_long_text')
->setValidSince(Chronos::now()->addMonth());
$bar = new ShortUrl('bar', ShortUrlMeta::createFromParams(Chronos::now()->addMonth()));
$bar->setShortCode('bar_very_long_text');
$this->getEntityManager()->persist($bar);
$baz = new ShortUrl('baz', ShortUrlMeta::createFromRawData(['maxVisits' => 3]));
$visits = [];
for ($i = 0; $i < 3; $i++) {
$visit = new Visit();
$visit = new Visit($baz, Visitor::emptyInstance());
$this->getEntityManager()->persist($visit);
$visits[] = $visit;
}
$baz = new ShortUrl();
$baz->setOriginalUrl('baz')
->setShortCode('baz')
->setVisits(new ArrayCollection($visits))
->setMaxVisits(3);
$baz->setShortCode('baz')
->setVisits(new ArrayCollection($visits));
$this->getEntityManager()->persist($baz);
$this->getEntityManager()->flush();
@ -75,8 +72,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$count = 5;
for ($i = 0; $i < $count; $i++) {
$this->getEntityManager()->persist(
(new ShortUrl())->setOriginalUrl((string) $i)
->setShortCode((string) $i)
(new ShortUrl((string) $i))->setShortCode((string) $i)
);
}
$this->getEntityManager()->flush();
@ -92,20 +88,17 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$tag = new Tag('bar');
$this->getEntityManager()->persist($tag);
$foo = new ShortUrl();
$foo->setOriginalUrl('foo')
->setShortCode('foo')
$foo = new ShortUrl('foo');
$foo->setShortCode('foo')
->setTags(new ArrayCollection([$tag]));
$this->getEntityManager()->persist($foo);
$bar = new ShortUrl();
$bar->setOriginalUrl('bar')
->setShortCode('bar_very_long_text');
$bar = new ShortUrl('bar');
$bar->setShortCode('bar_very_long_text');
$this->getEntityManager()->persist($bar);
$foo2 = new ShortUrl();
$foo2->setOriginalUrl('foo_2')
->setShortCode('foo_2');
$foo2 = new ShortUrl('foo_2');
$foo2->setShortCode('foo_2');
$this->getEntityManager()->persist($foo2);
$this->getEntityManager()->flush();
@ -123,8 +116,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
$urls = ['a', 'z', 'c', 'b'];
foreach ($urls as $url) {
$this->getEntityManager()->persist(
(new ShortUrl())->setShortCode($url)
->setLongUrl($url)
(new ShortUrl($url))->setShortCode($url)
);
}

View file

@ -8,6 +8,7 @@ use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
use ShlinkioTest\Shlink\Common\DbUnit\DatabaseTestCase;
use function sprintf;
@ -35,11 +36,14 @@ class VisitRepositoryTest extends DatabaseTestCase
*/
public function findUnlocatedVisitsReturnsProperVisits()
{
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);
for ($i = 0; $i < 6; $i++) {
$visit = new Visit();
$visit = new Visit($shortUrl, Visitor::emptyInstance());
if ($i % 2 === 0) {
$location = new VisitLocation();
$location = new VisitLocation([]);
$this->getEntityManager()->persist($location);
$visit->setVisitLocation($location);
}
@ -56,15 +60,11 @@ class VisitRepositoryTest extends DatabaseTestCase
*/
public function findVisitsByShortUrlReturnsProperData()
{
$shortUrl = new ShortUrl();
$shortUrl->setOriginalUrl('');
$shortUrl = new ShortUrl('');
$this->getEntityManager()->persist($shortUrl);
for ($i = 0; $i < 6; $i++) {
$visit = new Visit();
$visit->setShortUrl($shortUrl)
->setDate(Chronos::parse(sprintf('2016-01-0%s', $i + 1)));
$visit = new Visit($shortUrl, Visitor::emptyInstance(), Chronos::parse(sprintf('2016-01-0%s', $i + 1)));
$this->getEntityManager()->persist($visit);
}
$this->getEntityManager()->flush();

View file

@ -50,7 +50,7 @@ class PixelActionTest extends TestCase
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(
(new ShortUrl())->setLongUrl('http://domain.com/foo/bar')
new ShortUrl('http://domain.com/foo/bar')
)->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->shouldBeCalledTimes(1);

View file

@ -68,7 +68,7 @@ class PreviewActionTest extends TestCase
{
$shortCode = 'abc123';
$url = 'foobar.com';
$shortUrl = (new ShortUrl())->setLongUrl($url);
$shortUrl = new ShortUrl($url);
$path = __FILE__;
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($shortUrl)->shouldBeCalledTimes(1);
$this->previewGenerator->generatePreview($url)->willReturn($path)->shouldBeCalledTimes(1);

View file

@ -84,7 +84,7 @@ class QrCodeActionTest extends TestCase
public function aCorrectRequestReturnsTheQrCodeResponse()
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn((new ShortUrl())->setLongUrl(''))
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(new ShortUrl(''))
->shouldBeCalledTimes(1);
$delegate = $this->prophesize(RequestHandlerInterface::class);

View file

@ -52,7 +52,7 @@ class RedirectActionTest extends TestCase
{
$shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar';
$shortUrl = (new ShortUrl())->setLongUrl($expectedUrl);
$shortUrl = new ShortUrl($expectedUrl);
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($shortUrl)
->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->shouldBeCalledTimes(1);
@ -93,7 +93,7 @@ class RedirectActionTest extends TestCase
{
$shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar';
$shortUrl = (new ShortUrl())->setLongUrl($expectedUrl);
$shortUrl = new ShortUrl($expectedUrl);
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn($shortUrl)
->shouldBeCalledTimes(1);
$this->visitTracker->track(Argument::cetera())->shouldNotBeCalled();

View file

@ -11,10 +11,9 @@ class TagTest extends TestCase
/**
* @test
*/
public function jsonSerializationOfTagsReturnsItsName()
public function jsonSerializationOfTagsReturnsItsStringRepresentation()
{
$tag = new Tag();
$tag->setName('This is my name');
$this->assertEquals($tag->getName(), $tag->jsonSerialize());
$tag = new Tag('This is my name');
$this->assertEquals((string) $tag, $tag->jsonSerialize());
}
}

View file

@ -18,8 +18,7 @@ class VisitLocationTest extends TestCase
'longitude' => -2000.4,
];
$location = new VisitLocation();
$location->exchangeArray($payload);
$location = new VisitLocation($payload);
$this->assertSame('1000.7', $location->getLatitude());
$this->assertSame('-2000.4', $location->getLongitude());

View file

@ -11,6 +11,7 @@ use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlService;
@ -30,10 +31,10 @@ class DeleteShortUrlServiceTest extends TestCase
public function setUp()
{
$shortUrl = (new ShortUrl())->setShortCode('abc123')
->setVisits(new ArrayCollection(array_map(function () {
return new Visit();
}, range(0, 10))));
$shortUrl = (new ShortUrl(''))->setShortCode('abc123')
->setVisits(new ArrayCollection(array_map(function () {
return new Visit(new ShortUrl(''), Visitor::emptyInstance());
}, range(0, 10))));
$this->em = $this->prophesize(EntityManagerInterface::class);

View file

@ -42,10 +42,10 @@ class ShortUrlServiceTest extends TestCase
public function listedUrlsAreReturnedFromEntityManager()
{
$list = [
new ShortUrl(),
new ShortUrl(),
new ShortUrl(),
new ShortUrl(),
new ShortUrl(''),
new ShortUrl(''),
new ShortUrl(''),
new ShortUrl(''),
];
$repo = $this->prophesize(ShortUrlRepository::class);
@ -86,7 +86,7 @@ class ShortUrlServiceTest extends TestCase
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
$tagRepo = $this->prophesize(EntityRepository::class);
$tagRepo->findOneBy(['name' => 'foo'])->willReturn(new Tag())->shouldbeCalledTimes(1);
$tagRepo->findOneBy(['name' => 'foo'])->willReturn(new Tag('foo'))->shouldbeCalledTimes(1);
$tagRepo->findOneBy(['name' => 'bar'])->willReturn(null)->shouldbeCalledTimes(1);
$this->em->getRepository(Tag::class)->willReturn($tagRepo->reveal());
@ -98,7 +98,7 @@ class ShortUrlServiceTest extends TestCase
*/
public function updateMetadataByShortCodeUpdatesProvidedData()
{
$shortUrl = new ShortUrl();
$shortUrl = new ShortUrl('');
$repo = $this->prophesize(ShortUrlRepository::class);
$findShortUrl = $repo->findOneBy(['shortCode' => 'abc123'])->willReturn($shortUrl);

View file

@ -36,7 +36,7 @@ class TagServiceTest extends TestCase
*/
public function listTagsDelegatesOnRepository()
{
$expected = [new Tag(), new Tag()];
$expected = [new Tag('foo'), new Tag('bar')];
$repo = $this->prophesize(EntityRepository::class);
/** @var MethodProphecy $find */
@ -75,7 +75,7 @@ class TagServiceTest extends TestCase
{
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $find */
$find = $repo->findOneBy(Argument::cetera())->willReturn(new Tag());
$find = $repo->findOneBy(Argument::cetera())->willReturn(new Tag('foo'));
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(Tag::class)->willReturn($repo->reveal());
/** @var MethodProphecy $persist */
@ -115,7 +115,7 @@ class TagServiceTest extends TestCase
*/
public function renameValidTagChangesItsName()
{
$expected = new Tag();
$expected = new Tag('foo');
$repo = $this->prophesize(TagRepository::class);
/** @var MethodProphecy $find */
@ -128,7 +128,7 @@ class TagServiceTest extends TestCase
$tag = $this->service->renameTag('foo', 'bar');
$this->assertSame($expected, $tag);
$this->assertEquals('bar', $tag->getName());
$this->assertEquals('bar', (string) $tag);
$find->shouldHaveBeenCalled();
$getRepo->shouldHaveBeenCalled();
$flush->shouldHaveBeenCalled();

View file

@ -148,7 +148,7 @@ class UrlShortenerTest extends TestCase
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
/** @var MethodProphecy $findBySlug */
$findBySlug = $repo->findOneBy(['shortCode' => 'custom-slug'])->willReturn(new ShortUrl());
$findBySlug = $repo->findOneBy(['shortCode' => 'custom-slug'])->willReturn(new ShortUrl(''));
$repo->findOneBy(Argument::cetera())->willReturn(null);
/** @var MethodProphecy $getRepo */
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
@ -174,9 +174,8 @@ class UrlShortenerTest extends TestCase
{
// 12C1c -> 10
$shortCode = '12C1c';
$shortUrl = new ShortUrl();
$shortUrl->setShortCode($shortCode)
->setOriginalUrl('expected_url');
$shortUrl = new ShortUrl('expected_url');
$shortUrl->setShortCode($shortCode);
$repo = $this->prophesize(ShortUrlRepositoryInterface::class);
$repo->findOneByShortCode($shortCode)->willReturn($shortUrl);

View file

@ -6,7 +6,9 @@ namespace ShlinkioTest\Shlink\Core\Service;
use Doctrine\ORM\EntityManager;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\VisitRepository;
use Shlinkio\Shlink\Core\Service\VisitService;
@ -32,7 +34,7 @@ class VisitServiceTest extends TestCase
*/
public function saveVisitsPersistsProvidedVisit()
{
$visit = new Visit();
$visit = new Visit(new ShortUrl(''), Visitor::emptyInstance());
$this->em->persist($visit)->shouldBeCalledTimes(1);
$this->em->flush()->shouldBeCalledTimes(1);
$this->visitService->saveVisit($visit);

View file

@ -38,7 +38,7 @@ class VisitsTrackerTest extends TestCase
{
$shortCode = '123ABC';
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(new ShortUrl());
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(new ShortUrl(''));
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
$this->em->persist(Argument::any())->shouldBeCalledTimes(1);
@ -55,7 +55,7 @@ class VisitsTrackerTest extends TestCase
$shortCode = '123ABC';
$test = $this;
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(new ShortUrl());
$repo->findOneBy(['shortCode' => $shortCode])->willReturn(new ShortUrl(''));
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
$this->em->persist(Argument::any())->will(function ($args) use ($test) {
@ -74,14 +74,14 @@ class VisitsTrackerTest extends TestCase
public function infoReturnsVisistForCertainShortCode()
{
$shortCode = '123ABC';
$shortUrl = (new ShortUrl())->setOriginalUrl('http://domain.com/foo/bar');
$shortUrl = new ShortUrl('http://domain.com/foo/bar');
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['shortCode' => $shortCode])->willReturn($shortUrl);
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal())->shouldBeCalledTimes(1);
$list = [
new Visit(),
new Visit(),
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
new Visit(new ShortUrl(''), Visitor::emptyInstance()),
];
$repo2 = $this->prophesize(VisitRepository::class);
$repo2->findVisitsByShortUrl($shortUrl, null)->willReturn($list);

View file

@ -36,21 +36,11 @@ class ApiKey extends AbstractEntity
*/
private $enabled;
public function __construct()
public function __construct(?Chronos $expirationDate = null)
{
$this->enabled = true;
$this->key = $this->generateV4Uuid();
}
public function getKey(): string
{
return $this->key;
}
public function setKey(string $key): self
{
$this->key = $key;
return $this;
$this->expirationDate = $expirationDate;
$this->enabled = true;
}
public function getExpirationDate(): ?Chronos
@ -58,12 +48,6 @@ class ApiKey extends AbstractEntity
return $this->expirationDate;
}
public function setExpirationDate(Chronos $expirationDate): self
{
$this->expirationDate = $expirationDate;
return $this;
}
public function isExpired(): bool
{
if ($this->expirationDate === null) {
@ -78,15 +62,10 @@ class ApiKey extends AbstractEntity
return $this->enabled;
}
public function setEnabled(bool $enabled): self
{
$this->enabled = $enabled;
return $this;
}
public function disable(): self
{
return $this->setEnabled(false);
$this->enabled = false;
return $this;
}
/**
@ -99,6 +78,6 @@ class ApiKey extends AbstractEntity
public function __toString(): string
{
return $this->getKey();
return $this->key;
}
}

View file

@ -23,11 +23,7 @@ class ApiKeyService implements ApiKeyServiceInterface
public function create(?Chronos $expirationDate = null): ApiKey
{
$key = new ApiKey();
if ($expirationDate !== null) {
$key->setExpirationDate($expirationDate);
}
$key = new ApiKey($expirationDate);
$this->em->persist($key);
$this->em->flush();
@ -57,6 +53,9 @@ class ApiKeyService implements ApiKeyServiceInterface
return $apiKey;
}
/**
* @return ApiKey[]
*/
public function listKeys(bool $enabledOnly = false): array
{
$conditions = $enabledOnly ? ['enabled' => true] : [];

View file

@ -18,6 +18,9 @@ interface ApiKeyServiceInterface
*/
public function disable(string $key): ApiKey;
/**
* @return ApiKey[]
*/
public function listKeys(bool $enabledOnly = false): array;
public function getByKey(string $key): ?ApiKey;

View file

@ -74,7 +74,7 @@ class AuthenticateActionTest extends TestCase
*/
public function invalidApiKeyReturnsErrorResponse()
{
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->setEnabled(false))
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->disable())
->shouldBeCalledTimes(1);
$request = ServerRequestFactory::fromGlobals()->withParsedBody([

View file

@ -54,8 +54,7 @@ class CreateShortUrlActionTest extends TestCase
{
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
->willReturn(
(new ShortUrl())->setShortCode('abc123')
->setLongUrl('')
(new ShortUrl(''))->setShortCode('abc123')
)
->shouldBeCalledTimes(1);

View file

@ -82,7 +82,9 @@ class EditShortUrlActionTest extends TestCase
->withParsedBody([
'maxVisits' => 5,
]);
$updateMeta = $this->shortUrlService->updateMetadataByShortCode(Argument::cetera())->willReturn(new ShortUrl());
$updateMeta = $this->shortUrlService->updateMetadataByShortCode(Argument::cetera())->willReturn(
new ShortUrl('')
);
$resp = $this->action->handle($request);

View file

@ -60,7 +60,7 @@ class EditShortUrlTagsActionTest extends TestCase
public function tagsListIsReturnedIfCorrectShortCodeIsProvided()
{
$shortCode = 'abc123';
$this->shortUrlService->setTagsByShortCode($shortCode, [])->willReturn(new ShortUrl())
$this->shortUrlService->setTagsByShortCode($shortCode, [])->willReturn(new ShortUrl(''))
->shouldBeCalledTimes(1);
$response = $this->action->handle(

View file

@ -55,7 +55,7 @@ class ResolveShortUrlActionTest extends TestCase
{
$shortCode = 'abc123';
$this->urlShortener->shortCodeToUrl($shortCode)->willReturn(
(new ShortUrl())->setLongUrl('http://domain.com/foo/bar')
new ShortUrl('http://domain.com/foo/bar')
)->shouldBeCalledTimes(1);
$request = ServerRequestFactory::fromGlobals()->withAttribute('shortCode', $shortCode);

View file

@ -103,7 +103,7 @@ class SingleStepCreateShortUrlActionTest extends TestCase
null,
null,
null
)->willReturn((new ShortUrl())->setLongUrl(''));
)->willReturn(new ShortUrl(''));
$resp = $this->action->handle($request);

View file

@ -80,7 +80,7 @@ class UpdateTagActionTest extends TestCase
'newName' => 'bar',
]);
/** @var MethodProphecy $rename */
$rename = $this->tagService->renameTag('foo', 'bar')->willReturn(new Tag());
$rename = $this->tagService->renameTag('foo', 'bar')->willReturn(new Tag('bar'));
$resp = $this->action->handle($request);

View file

@ -87,8 +87,7 @@ class ApiKeyServiceTest extends TestCase
*/
public function checkReturnsFalseWhenKeyIsExpired()
{
$key = new ApiKey();
$key->setExpirationDate(Chronos::now()->subDay());
$key = new ApiKey(Chronos::now()->subDay());
$repo = $this->prophesize(EntityRepository::class);
$repo->findOneBy(['key' => '12345'])->willReturn($key)
->shouldBeCalledTimes(1);