Support for redirects with a condition before date

This commit is contained in:
Andrei Vasilev
2025-05-07 23:04:05 +07:00
committed by Alejandro Celaya
parent 77898d1edc
commit dae52fedf4
7 changed files with 44 additions and 2 deletions

View File

@@ -122,6 +122,9 @@ class RedirectRuleHandler implements RedirectRuleHandlerInterface
),
RedirectConditionType::GEOLOCATION_CITY_NAME => RedirectCondition::forGeolocationCityName(
$this->askMandatory('City name to match?', $io),
),
RedirectConditionType::BEFORE_DATE => RedirectCondition::forBeforeDate(
$this->askMandatory('Date to match? (ISO 8601)', $io),
)
};

View File

@@ -122,6 +122,7 @@ class RedirectRuleHandlerTest extends TestCase
'IP address, CIDR block or wildcard-pattern (1.2.*.*)' => '1.2.3.4',
'Country code to match?' => 'FR',
'City name to match?' => 'Los angeles',
'Date to match? (ISO 8601)' => '2016-05-01T20:34:16+02:00',
default => '',
},
);
@@ -186,6 +187,10 @@ class RedirectRuleHandlerTest extends TestCase
RedirectConditionType::GEOLOCATION_CITY_NAME,
[RedirectCondition::forGeolocationCityName('Los angeles')],
];
yield 'Before date' => [
RedirectConditionType::BEFORE_DATE,
[RedirectCondition::forBeforeDate('2016-05-01T20:34:16+02:00')],
];
}
#[Test]

View File

@@ -2,6 +2,7 @@
namespace Shlinkio\Shlink\Core\RedirectRule\Entity;
use Cake\Chronos\Chronos;
use JsonSerializable;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
@@ -75,6 +76,11 @@ class RedirectCondition extends AbstractEntity implements JsonSerializable
return new self(RedirectConditionType::GEOLOCATION_CITY_NAME, $cityName);
}
public static function forBeforeDate(string $date): self
{
return new self(RedirectConditionType::BEFORE_DATE, $date);
}
public static function fromRawData(array $rawData): self
{
$type = RedirectConditionType::from($rawData[RedirectRulesInputFilter::CONDITION_TYPE]);
@@ -100,6 +106,7 @@ class RedirectCondition extends AbstractEntity implements JsonSerializable
RedirectConditionType::IP_ADDRESS => self::forIpAddress($cond->matchValue),
RedirectConditionType::GEOLOCATION_COUNTRY_CODE => self::forGeolocationCountryCode($cond->matchValue),
RedirectConditionType::GEOLOCATION_CITY_NAME => self::forGeolocationCityName($cond->matchValue),
RedirectConditionType::BEFORE_DATE => self::forBeforeDate($cond->matchValue),
};
}
@@ -117,6 +124,7 @@ class RedirectCondition extends AbstractEntity implements JsonSerializable
RedirectConditionType::IP_ADDRESS => $this->matchesRemoteIpAddress($request),
RedirectConditionType::GEOLOCATION_COUNTRY_CODE => $this->matchesGeolocationCountryCode($request),
RedirectConditionType::GEOLOCATION_CITY_NAME => $this->matchesGeolocationCityName($request),
RedirectConditionType::BEFORE_DATE => $this->matchesBeforeDate(),
};
}
@@ -200,6 +208,11 @@ class RedirectCondition extends AbstractEntity implements JsonSerializable
return strcasecmp($geolocation->city, $this->matchValue) === 0;
}
private function matchesBeforeDate(): bool
{
return Chronos::now()->lessThan(Chronos::parse($this->matchValue));
}
public function jsonSerialize(): array
{
return [
@@ -230,6 +243,7 @@ class RedirectCondition extends AbstractEntity implements JsonSerializable
RedirectConditionType::IP_ADDRESS => sprintf('IP address matches %s', $this->matchValue),
RedirectConditionType::GEOLOCATION_COUNTRY_CODE => sprintf('country code is %s', $this->matchValue),
RedirectConditionType::GEOLOCATION_CITY_NAME => sprintf('city name is %s', $this->matchValue),
RedirectConditionType::BEFORE_DATE => sprintf('date before %s', $this->matchValue),
};
}
}

View File

@@ -20,6 +20,7 @@ enum RedirectConditionType: string
case IP_ADDRESS = 'ip-address';
case GEOLOCATION_COUNTRY_CODE = 'geolocation-country-code';
case GEOLOCATION_CITY_NAME = 'geolocation-city-name';
case BEFORE_DATE = 'before-date';
/**
* Tells if a value is valid for the condition type

View File

@@ -2,6 +2,7 @@
namespace ShlinkioTest\Shlink\Core\RedirectRule\Entity;
use Cake\Chronos\Chronos;
use Laminas\Diactoros\ServerRequestFactory;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
@@ -195,4 +196,19 @@ class RedirectConditionTest extends TestCase
);
self::assertEquals($expectedType, $condition?->type);
}
#[Test, DataProvider('provideVisitsWithBeforeDateCondition')]
public function matchesBeforeDate(string $date, bool $expectedResult): void
{
$request = ServerRequestFactory::fromGlobals();
$result = RedirectCondition::forBeforeDate($date)->matchesRequest($request);
self::assertEquals($expectedResult, $result);
}
public static function provideVisitsWithBeforeDateCondition(): iterable
{
yield 'date later than current' => [Chronos::now()->addHours(1)->toIso8601String(), true];
yield 'date earlier than current' => [Chronos::now()->subHours(1)->toIso8601String(), false];
}
}