Fix inconsistencies when editing rules and saving a mix of new and old ones

This commit is contained in:
Alejandro Celaya 2024-03-03 09:09:19 +01:00
parent 3bfb29a51c
commit a843c59d77
4 changed files with 30 additions and 2 deletions

View File

@ -76,6 +76,7 @@ class ManageRedirectRulesCommand extends Command
$rulesToSave = $this->processRules($shortUrl, $io, $this->ruleService->rulesForShortUrl($shortUrl)); $rulesToSave = $this->processRules($shortUrl, $io, $this->ruleService->rulesForShortUrl($shortUrl));
if ($rulesToSave !== null) { if ($rulesToSave !== null) {
$this->ruleService->saveRulesForShortUrl($shortUrl, $rulesToSave); $this->ruleService->saveRulesForShortUrl($shortUrl, $rulesToSave);
$io->success('Rules properly saved');
} }
return ExitCode::EXIT_SUCCESS; return ExitCode::EXIT_SUCCESS;

View File

@ -25,6 +25,16 @@ class ShortUrlRedirectRule extends AbstractEntity implements JsonSerializable
) { ) {
} }
public function withPriority(int $newPriority): self
{
return new self(
$this->shortUrl,
$newPriority,
$this->longUrl,
$this->conditions,
);
}
/** /**
* Tells if this condition matches provided request * Tells if this condition matches provided request
*/ */

View File

@ -11,6 +11,7 @@ use Shlinkio\Shlink\Core\RedirectRule\Model\Validation\RedirectRulesInputFilter;
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
use function array_map; use function array_map;
use function Shlinkio\Shlink\Core\ArrayUtils\map;
readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServiceInterface readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServiceInterface
{ {
@ -49,7 +50,7 @@ readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServic
$rules[] = $rule; $rules[] = $rule;
} }
$this->saveRulesForShortUrl($shortUrl, $rules); $this->doSetRulesForShortUrl($shortUrl, $rules);
return $rules; return $rules;
} }
@ -57,6 +58,23 @@ readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServic
* @param ShortUrlRedirectRule[] $rules * @param ShortUrlRedirectRule[] $rules
*/ */
public function saveRulesForShortUrl(ShortUrl $shortUrl, array $rules): void public function saveRulesForShortUrl(ShortUrl $shortUrl, array $rules): void
{
$normalizedAndDetachedRules = map($rules, function (ShortUrlRedirectRule $rule, int|string|float $priority) {
// Make sure all rules and conditions are detached so that the EM considers them new.
$rule->mapConditions(fn (RedirectCondition $cond) => $this->em->detach($cond));
$this->em->detach($rule);
// Normalize priorities so that they are sequential
return $rule->withPriority(((int) $priority) + 1);
});
$this->doSetRulesForShortUrl($shortUrl, $normalizedAndDetachedRules);
}
/**
* @param ShortUrlRedirectRule[] $rules
*/
public function doSetRulesForShortUrl(ShortUrl $shortUrl, array $rules): void
{ {
$this->em->wrapInTransaction(function () use ($shortUrl, $rules): void { $this->em->wrapInTransaction(function () use ($shortUrl, $rules): void {
// First, delete existing rules for the short URL // First, delete existing rules for the short URL

View File

@ -6,7 +6,6 @@ use Doctrine\Common\Collections\ArrayCollection;
use Laminas\Diactoros\ServerRequestFactory; use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition; use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition;
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule; use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;