mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 12:13:13 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6991138812 | ||
|
|
5eb1808217 | ||
|
|
5eb14c5315 | ||
|
|
a18360a4d6 | ||
|
|
104b1e7d04 | ||
|
|
af2d67695b | ||
|
|
449a588796 | ||
|
|
7bbc938743 | ||
|
|
766758ff9b |
37
CHANGELOG.md
37
CHANGELOG.md
@@ -4,6 +4,43 @@ 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).
|
||||
|
||||
## [4.4.5] - 2025-03-01
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2373](https://github.com/shlinkio/shlink/issues/2373) Ensure deprecation warnings do not end up escalated to `ErrorException`s by `ProblemDetailsMiddleware`.
|
||||
|
||||
In order to do this, Shlink will entirely ignore deprecation warnings when running in production, as those do not mean something is not working, but only that something will break in future versions.
|
||||
|
||||
|
||||
## [4.4.4] - 2025-02-19
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2366](https://github.com/shlinkio/shlink/issues/2366) Fix error "Cannot use 'SCRIPT' with redis-cluster" thrown when creating a lock while using a redis cluster.
|
||||
* [#2368](https://github.com/shlinkio/shlink/issues/2368) Fix error when listing non-orphan visits using API key with `AUTHORED_SHORT_URLS` role.
|
||||
|
||||
|
||||
## [4.4.3] - 2025-02-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"spiral/roadrunner-jobs": "^4.6",
|
||||
"symfony/console": "^7.2",
|
||||
"symfony/filesystem": "^7.2",
|
||||
"symfony/lock": "7.2.0",
|
||||
"symfony/lock": "7.1.6",
|
||||
"symfony/process": "^7.2",
|
||||
"symfony/string": "^7.2"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
// Set current directory to the project's root directory
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
@@ -21,7 +22,11 @@ loadEnvVarsFromConfig(
|
||||
enumValues(EnvVars::class),
|
||||
);
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone and memory limit here
|
||||
// This is one of the first files loaded. Set global configuration here
|
||||
error_reporting(
|
||||
// Set a less strict error reporting for prod, where deprecation warnings should be ignored
|
||||
EnvVars::isProdEnv() ? E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED : E_ALL,
|
||||
);
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv());
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv());
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
display_errors=On
|
||||
error_reporting=-1
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
|
||||
@@ -21,6 +21,7 @@ use Shlinkio\Shlink\Core\Visit\Persistence\VisitsListFiltering;
|
||||
use Shlinkio\Shlink\Core\Visit\Spec\CountOfNonOrphanVisits;
|
||||
use Shlinkio\Shlink\Core\Visit\Spec\CountOfOrphanVisits;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
@@ -177,7 +178,12 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
|
||||
$qb = $this->createAllVisitsQueryBuilder($filtering);
|
||||
$qb->andWhere($qb->expr()->isNotNull('v.shortUrl'));
|
||||
|
||||
$this->applySpecification($qb, $filtering->apiKey?->inlinedSpec());
|
||||
$apiKey = $filtering->apiKey;
|
||||
if (ApiKey::isShortUrlRestricted($apiKey)) {
|
||||
$qb->join('v.shortUrl', 's');
|
||||
}
|
||||
|
||||
$this->applySpecification($qb, $apiKey?->inlinedSpec(), 'v');
|
||||
|
||||
return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
|
||||
}
|
||||
|
||||
@@ -470,22 +470,18 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
#[Test]
|
||||
public function findNonOrphanVisitsReturnsExpectedResult(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://1']));
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, 7);
|
||||
$authoredApiKey = ApiKey::fromMeta(ApiKeyMeta::withRoles(RoleDefinition::forAuthoredShortUrls()));
|
||||
$this->getEntityManager()->persist($authoredApiKey);
|
||||
|
||||
$shortUrl2 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://2']));
|
||||
$this->getEntityManager()->persist($shortUrl2);
|
||||
$this->createVisitsForShortUrl($shortUrl2, 4);
|
||||
|
||||
$shortUrl3 = ShortUrl::create(ShortUrlCreation::fromRawData(['longUrl' => 'https://3']));
|
||||
$this->getEntityManager()->persist($shortUrl3);
|
||||
$this->createVisitsForShortUrl($shortUrl3, 10);
|
||||
$this->createShortUrlsAndVisits(withDomain: false, visitsAmount: 7);
|
||||
$this->createShortUrlsAndVisits(withDomain: false, apiKey: $authoredApiKey, visitsAmount: 4);
|
||||
$this->createShortUrlsAndVisits(withDomain: false, visitsAmount: 10);
|
||||
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
self::assertCount(21, $this->repo->findNonOrphanVisits(new VisitsListFiltering()));
|
||||
self::assertCount(21, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::allTime())));
|
||||
self::assertCount(4, $this->repo->findNonOrphanVisits(new VisitsListFiltering(apiKey: $authoredApiKey)));
|
||||
self::assertCount(7, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::since(
|
||||
Chronos::parse('2016-01-05')->endOfDay(),
|
||||
))));
|
||||
@@ -503,11 +499,11 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
self::assertCount(3, $this->repo->findNonOrphanVisits(new VisitsListFiltering(DateRange::between(
|
||||
Chronos::parse('2016-01-03')->startOfDay(),
|
||||
Chronos::parse('2016-01-08')->endOfDay(),
|
||||
), false, null, 10, 10)));
|
||||
self::assertCount(15, $this->repo->findNonOrphanVisits(new VisitsListFiltering(null, true)));
|
||||
self::assertCount(10, $this->repo->findNonOrphanVisits(new VisitsListFiltering(null, false, null, 10)));
|
||||
self::assertCount(1, $this->repo->findNonOrphanVisits(new VisitsListFiltering(null, false, null, 10, 20)));
|
||||
self::assertCount(5, $this->repo->findNonOrphanVisits(new VisitsListFiltering(null, false, null, 5, 5)));
|
||||
), limit: 10, offset: 10)));
|
||||
self::assertCount(15, $this->repo->findNonOrphanVisits(new VisitsListFiltering(excludeBots: true)));
|
||||
self::assertCount(10, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 10)));
|
||||
self::assertCount(1, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 10, offset: 20)));
|
||||
self::assertCount(5, $this->repo->findNonOrphanVisits(new VisitsListFiltering(limit: 5, offset: 5)));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -535,6 +531,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
bool|string $withDomain = true,
|
||||
array $tags = [],
|
||||
ApiKey|null $apiKey = null,
|
||||
int $visitsAmount = 6,
|
||||
): array {
|
||||
$shortUrl = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
ShortUrlInputFilter::LONG_URL => 'https://longUrl',
|
||||
@@ -545,7 +542,7 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
$shortCode = $shortUrl->getShortCode();
|
||||
$this->getEntityManager()->persist($shortUrl);
|
||||
|
||||
$this->createVisitsForShortUrl($shortUrl);
|
||||
$this->createVisitsForShortUrl($shortUrl, $visitsAmount);
|
||||
|
||||
if ($withDomain !== false) {
|
||||
$shortUrlWithDomain = ShortUrl::create(ShortUrlCreation::fromRawData([
|
||||
|
||||
Reference in New Issue
Block a user