Merge pull request #1948 from acelaya-forks/feature/fix-postgres-import

Fix error when importing short URLs while using Postgres
This commit is contained in:
Alejandro Celaya 2023-12-16 20:38:46 +01:00 committed by GitHub
commit 4d28adf4a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 11 deletions

View file

@ -18,7 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
* *Nothing*
### Fixed
* *Nothing*
* [#1947](https://github.com/shlinkio/shlink/issues/1947) Fix error when importing short URLs while using Postgres.
## [3.7.0] - 2023-11-25

View file

@ -12,9 +12,9 @@ use Shlinkio\Shlink\Importer\Model\ImportedShlinkVisit;
use function Shlinkio\Shlink\Core\normalizeDate;
use function sprintf;
final class ShortUrlImporting
final readonly class ShortUrlImporting
{
private function __construct(private readonly ShortUrl $shortUrl, private readonly bool $isNew)
private function __construct(private ShortUrl $shortUrl, private bool $isNew)
{
}
@ -57,11 +57,18 @@ final class ShortUrlImporting
private function resolveShortUrl(EntityManagerInterface $em): ShortUrl
{
// If wrapped ShortUrl has no ID, avoid trying to query the EM, as it would fail in Postgres.
// See https://github.com/shlinkio/shlink/issues/1947
$id = $this->shortUrl->getId();
if (!$id) {
return $this->shortUrl;
}
// Instead of directly accessing wrapped ShortUrl entity, try to get it from the EM.
// With this, we will get the same entity from memory if it is known by the EM, but if it was cleared, the EM
// will fetch it again from the database, preventing errors at runtime.
// However, if the EM was not flushed yet, the entity will not be found by ID, but it is known by the EM.
// In that case, we fall back to wrapped ShortUrl entity directly.
return $em->find(ShortUrl::class, $this->shortUrl->getId()) ?? $this->shortUrl;
return $em->find(ShortUrl::class, $id) ?? $this->shortUrl;
}
}

View file

@ -232,15 +232,20 @@ class ImportedLinksProcessorTest extends TestCase
}
#[Test, DataProvider('provideFoundShortUrls')]
public function visitsArePersistedWithProperShortUrl(?ShortUrl $foundShortUrl): void
public function visitsArePersistedWithProperShortUrl(ShortUrl $originalShortUrl, ?ShortUrl $foundShortUrl): void
{
$originalShortUrl = ShortUrl::withLongUrl('https://foo');
$this->em->method('getRepository')->with(ShortUrl::class)->willReturn($this->repo);
$this->repo->expects($this->once())->method('findOneByImportedUrl')->willReturn($originalShortUrl);
$this->em->expects($this->exactly(2))->method('find')->willReturn($foundShortUrl);
if (!$originalShortUrl->getId()) {
$this->em->expects($this->never())->method('find');
} else {
$this->em->expects($this->exactly(2))->method('find')->willReturn($foundShortUrl);
}
$this->em->expects($this->once())->method('persist')->willReturnCallback(
static fn (Visit $visit) => Assert::assertSame($foundShortUrl ?? $originalShortUrl, $visit->getShortUrl()),
static fn (Visit $visit) => Assert::assertSame(
$foundShortUrl ?? $originalShortUrl,
$visit->getShortUrl(),
),
);
$now = Chronos::now();
@ -253,8 +258,12 @@ class ImportedLinksProcessorTest extends TestCase
public static function provideFoundShortUrls(): iterable
{
yield [null];
yield [ShortUrl::withLongUrl('https://bar')];
yield 'not found new URL' => [ShortUrl::withLongUrl('https://foo')->setId('123'), null];
yield 'found new URL' => [
ShortUrl::withLongUrl('https://foo')->setId('123'),
ShortUrl::withLongUrl('https://bar'),
];
yield 'old URL without ID' => [$originalShortUrl = ShortUrl::withLongUrl('https://foo'), $originalShortUrl];
}
/**