diff --git a/CHANGELOG.md b/CHANGELOG.md index 375a8849..9ec8f4df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ 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). +## [2.4.2] - 2020-11-22 +### Added +* *Nothing* + +### Changed +* *Nothing* + +### Deprecated +* *Nothing* + +### Removed +* *Nothing* + +### Fixed +* [#904](https://github.com/shlinkio/shlink/issues/904) Explicitly added missing "Domains" and "Integrations" tags to swagger docs. +* [#901](https://github.com/shlinkio/shlink/issues/901) Ensured domains which are not in use on any short URL are not returned on the list of domains. +* [#899](https://github.com/shlinkio/shlink/issues/899) Avoided filesystem errors produced while downloading geolite DB files on several shlink instances that share the same filesystem. +* [#827](https://github.com/shlinkio/shlink/issues/827) Fixed swoole config getting loaded in config cache if a console command is run before any web execution, when swoole extension is enabled, making subsequent non-swoole web requests fail. + + ## [2.4.1] - 2020-11-10 ### Added * *Nothing* diff --git a/composer.json b/composer.json index 0fef97db..c085f69b 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "laminas/laminas-paginator": "^2.8", "laminas/laminas-servicemanager": "^3.4", "laminas/laminas-stdlib": "^3.2", - "lcobucci/jwt": "^4.0@alpha", + "lcobucci/jwt": "^4.0@alpha <4.0@beta", "league/uri": "^6.2", "lstrojny/functional-php": "^1.9", "mezzio/mezzio": "^3.2", diff --git a/config/autoload/common.global.php b/config/autoload/common.global.php index 4d8c3520..2bc4c2db 100644 --- a/config/autoload/common.global.php +++ b/config/autoload/common.global.php @@ -7,6 +7,9 @@ use Laminas\ConfigAggregator\ConfigAggregator; return [ 'debug' => false, - ConfigAggregator::ENABLE_CACHE => true, + + // Disabling config cache for cli, ensures it's never used for swoole and also that console commands don't generate + // a cache file that's then used by non-swoole web executions + ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli', ]; diff --git a/config/autoload/geolite2.global.php b/config/autoload/geolite2.global.php index 10f58042..83702ca3 100644 --- a/config/autoload/geolite2.global.php +++ b/config/autoload/geolite2.global.php @@ -6,8 +6,8 @@ return [ 'geolite2' => [ 'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb', - 'temp_dir' => sys_get_temp_dir(), - 'license_key' => 'G4Lm0C60yJsnkdPi', + 'temp_dir' => __DIR__ . '/../../data', + 'license_key' => 'G4Lm0C60yJsnkdPi', // Deprecated. Remove hardcoded license on v3 ], ]; diff --git a/docker/config/shlink_in_docker.local.php b/docker/config/shlink_in_docker.local.php index 8efee8a4..c4502b7c 100644 --- a/docker/config/shlink_in_docker.local.php +++ b/docker/config/shlink_in_docker.local.php @@ -99,8 +99,6 @@ $helper = new class { return [ - 'config_cache_enabled' => false, - 'app_options' => [ 'disable_track_param' => env('DISABLE_TRACK_PARAM'), ], diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 5abe1946..8dc04412 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -50,6 +50,14 @@ "name": "Visits", "description": "Operations to manage visits on short URLs" }, + { + "name": "Domains", + "description": "Operations to manage domains used on short URLs" + }, + { + "name": "Integrations", + "description": "Handle services with which shlink is integrated" + }, { "name": "Monitoring", "description": "Public endpoints designed to monitor the service" diff --git a/module/Core/src/Domain/Repository/DomainRepository.php b/module/Core/src/Domain/Repository/DomainRepository.php index 6f998c42..f02dd120 100644 --- a/module/Core/src/Domain/Repository/DomainRepository.php +++ b/module/Core/src/Domain/Repository/DomainRepository.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Domain\Repository; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query\Expr\Join; use Shlinkio\Shlink\Core\Entity\Domain; +use Shlinkio\Shlink\Core\Entity\ShortUrl; class DomainRepository extends EntityRepository implements DomainRepositoryInterface { @@ -14,7 +16,9 @@ class DomainRepository extends EntityRepository implements DomainRepositoryInter */ public function findDomainsWithout(?string $excludedAuthority = null): array { - $qb = $this->createQueryBuilder('d')->orderBy('d.authority', 'ASC'); + $qb = $this->createQueryBuilder('d'); + $qb->join(ShortUrl::class, 's', Join::WITH, 's.domain = d') + ->orderBy('d.authority', 'ASC'); if ($excludedAuthority !== null) { $qb->where($qb->expr()->neq('d.authority', ':excludedAuthority')) diff --git a/module/Core/test-db/Domain/Repository/DomainRepositoryTest.php b/module/Core/test-db/Domain/Repository/DomainRepositoryTest.php index b79f15f1..79f9caaf 100644 --- a/module/Core/test-db/Domain/Repository/DomainRepositoryTest.php +++ b/module/Core/test-db/Domain/Repository/DomainRepositoryTest.php @@ -6,11 +6,15 @@ namespace ShlinkioTest\Shlink\Core\Domain\Repository; use Shlinkio\Shlink\Core\Domain\Repository\DomainRepository; 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\Entity\ApiKey; use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase; class DomainRepositoryTest extends DatabaseTestCase { - protected const ENTITIES_TO_EMPTY = [Domain::class]; + protected const ENTITIES_TO_EMPTY = [ShortUrl::class, Domain::class]; private DomainRepository $repo; @@ -23,12 +27,23 @@ class DomainRepositoryTest extends DatabaseTestCase public function findDomainsReturnsExpectedResult(): void { $fooDomain = new Domain('foo.com'); - $barDomain = new Domain('bar.com'); - $bazDomain = new Domain('baz.com'); - $this->getEntityManager()->persist($fooDomain); + $fooShortUrl = $this->createShortUrl($fooDomain); + $this->getEntityManager()->persist($fooShortUrl); + + $barDomain = new Domain('bar.com'); $this->getEntityManager()->persist($barDomain); + $barShortUrl = $this->createShortUrl($barDomain); + $this->getEntityManager()->persist($barShortUrl); + + $bazDomain = new Domain('baz.com'); $this->getEntityManager()->persist($bazDomain); + $bazShortUrl = $this->createShortUrl($bazDomain); + $this->getEntityManager()->persist($bazShortUrl); + + $detachedDomain = new Domain('detached.com'); + $this->getEntityManager()->persist($detachedDomain); + $this->getEntityManager()->flush(); self::assertEquals([$barDomain, $bazDomain, $fooDomain], $this->repo->findDomainsWithout()); @@ -36,4 +51,30 @@ class DomainRepositoryTest extends DatabaseTestCase self::assertEquals([$bazDomain, $fooDomain], $this->repo->findDomainsWithout('bar.com')); self::assertEquals([$barDomain, $fooDomain], $this->repo->findDomainsWithout('baz.com')); } + + private function createShortUrl(Domain $domain): ShortUrl + { + return new ShortUrl( + 'foo', + ShortUrlMeta::fromRawData(['domain' => $domain->getAuthority()]), + new class ($domain) implements ShortUrlRelationResolverInterface { + private Domain $domain; + + public function __construct(Domain $domain) + { + $this->domain = $domain; + } + + public function resolveDomain(?string $domain): ?Domain + { + return $this->domain; + } + + public function resolveApiKey(?string $key): ?ApiKey + { + return null; + } + }, + ); + } } diff --git a/module/Rest/test-api/Fixtures/DomainFixture.php b/module/Rest/test-api/Fixtures/DomainFixture.php new file mode 100644 index 00000000..4c30b5b8 --- /dev/null +++ b/module/Rest/test-api/Fixtures/DomainFixture.php @@ -0,0 +1,19 @@ +persist($orphanDomain); + $manager->flush(); + } +}