mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-23 07:33:58 -06:00
Create endpoint to list redirect rules for a specific short URL
This commit is contained in:
parent
721e3d9ef9
commit
33729289c7
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2023 Alejandro Celaya
|
||||
Copyright (c) 2016-2024 Alejandro Celaya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -17,7 +17,6 @@ use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
|
||||
use function sprintf;
|
||||
|
||||
return (static function (): array {
|
||||
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
|
||||
@ -32,9 +31,10 @@ return (static function (): array {
|
||||
...ConfigProvider::applyRoutesPrefix([
|
||||
Action\HealthAction::getRouteDef(),
|
||||
|
||||
// Visits and rules routes must go first, as they have a more specific path, otherwise, when
|
||||
// multi-segment slugs are enabled, routes with a less-specific path might match first
|
||||
|
||||
// Visits.
|
||||
// These routes must go first, as they have a more specific path, otherwise, when multi-segment slugs
|
||||
// are enabled, routes with a less-specific path might match first
|
||||
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\DeleteShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\TagVisitsAction::getRouteDef(),
|
||||
@ -44,15 +44,17 @@ return (static function (): array {
|
||||
Action\Visit\DeleteOrphanVisitsAction::getRouteDef(),
|
||||
Action\Visit\NonOrphanVisitsAction::getRouteDef(),
|
||||
|
||||
//Redirect rules
|
||||
Action\RedirectRule\ListRedirectRulesAction::getRouteDef([$dropDomainMiddleware]),
|
||||
|
||||
// Short URLs
|
||||
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$dropDomainMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
||||
]),
|
||||
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class,
|
||||
$overrideDomainMiddleware,
|
||||
]),
|
||||
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
|
39
docs/swagger/definitions/ShortUrlRedirectRule.json
Normal file
39
docs/swagger/definitions/ShortUrlRedirectRule.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["priority", "longUrl", "conditions"],
|
||||
"properties": {
|
||||
"longUrl": {
|
||||
"description": "Long URL to redirect to when this condition matches",
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"description": "Order in which attempting to match the rule. Lower goes first",
|
||||
"type": "number"
|
||||
},
|
||||
"conditions": {
|
||||
"description": "List of conditions that need to match in order to consider this rule matches",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "type", "matchKey", "matchValue"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Unique condition name"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["device", "language", "query"],
|
||||
"description": "The type of the condition, which will condition the logic used to match it"
|
||||
},
|
||||
"matchKey": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"matchValue": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
144
docs/swagger/paths/v3_short-urls_{shortCode}_redirect-rules.json
Normal file
144
docs/swagger/paths/v3_short-urls_{shortCode}_redirect-rules.json
Normal file
@ -0,0 +1,144 @@
|
||||
{
|
||||
"get": {
|
||||
"operationId": "listShortUrlRedirectRules",
|
||||
"tags": [
|
||||
"Redirect rules"
|
||||
],
|
||||
"summary": "List short URL redirect rules",
|
||||
"description": "Returns the list of redirect rules for a short URL.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "../parameters/version.json"
|
||||
},
|
||||
{
|
||||
"$ref": "../parameters/shortCode.json"
|
||||
},
|
||||
{
|
||||
"$ref": "../parameters/domain.json"
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"ApiKey": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The list of rules",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"redirectRules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "../definitions/ShortUrlRedirectRule.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"redirectRules": [
|
||||
{
|
||||
"longUrl": "https://example.com/android-en-us",
|
||||
"priority": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"name": "device-android",
|
||||
"type": "device",
|
||||
"matchValue": "android",
|
||||
"matchKey": null
|
||||
},
|
||||
{
|
||||
"name": "language-en-US",
|
||||
"type": "language",
|
||||
"matchValue": "en-US",
|
||||
"matchKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"longUrl": "https://example.com/fr",
|
||||
"priority": 2,
|
||||
"conditions": [
|
||||
{
|
||||
"name": "language-fr",
|
||||
"type": "language",
|
||||
"matchValue": "fr",
|
||||
"matchKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"longUrl": "https://example.com/query-foo-bar-hello-world",
|
||||
"priority": 3,
|
||||
"conditions": [
|
||||
{
|
||||
"name": "query-foo-bar",
|
||||
"type": "query",
|
||||
"matchKey": "foo",
|
||||
"matchValue": "bar"
|
||||
},
|
||||
{
|
||||
"name": "query-hello-world",
|
||||
"type": "query",
|
||||
"matchKey": "hello",
|
||||
"matchValue": "world"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "No URL was found for provided short code.",
|
||||
"content": {
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "../definitions/Error.json"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["shortCode"],
|
||||
"properties": {
|
||||
"shortCode": {
|
||||
"type": "string",
|
||||
"description": "The short code with which we tried to find the short URL"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "The domain with which we tried to find the short URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"examples": {
|
||||
"API v3 and newer": {
|
||||
"$ref": "../examples/short-url-not-found-v3.json"
|
||||
},
|
||||
"Previous to API v3": {
|
||||
"$ref": "../examples/short-url-not-found-v2.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Unexpected error.",
|
||||
"content": {
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"$ref": "../definitions/Error.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -42,6 +42,10 @@
|
||||
"name": "Short URLs",
|
||||
"description": "Operations that can be performed on short URLs"
|
||||
},
|
||||
{
|
||||
"name": "Redirect rules",
|
||||
"description": "Handle dynamic rule-based redirects"
|
||||
},
|
||||
{
|
||||
"name": "Tags",
|
||||
"description": "Let you handle the list of available tags"
|
||||
@ -79,6 +83,10 @@
|
||||
"$ref": "paths/v1_short-urls_{shortCode}.json"
|
||||
},
|
||||
|
||||
"/rest/v{version}/short-urls/{shortCode}/redirect-rules": {
|
||||
"$ref": "paths/v3_short-urls_{shortCode}_redirect-rules.json"
|
||||
},
|
||||
|
||||
"/rest/v{version}/tags": {
|
||||
"$ref": "paths/v1_tags.json"
|
||||
},
|
||||
|
@ -32,6 +32,7 @@ return [
|
||||
Options\QrCodeOptions::class => [ValinorConfigFactory::class, 'config.qr_codes'],
|
||||
Options\RabbitMqOptions::class => [ValinorConfigFactory::class, 'config.rabbitmq'],
|
||||
|
||||
RedirectRule\ShortUrlRedirectRuleService::class => ConfigAbstractFactory::class,
|
||||
RedirectRule\ShortUrlRedirectionResolver::class => ConfigAbstractFactory::class,
|
||||
|
||||
ShortUrl\UrlShortener::class => ConfigAbstractFactory::class,
|
||||
@ -158,7 +159,9 @@ return [
|
||||
Util\RedirectResponseHelper::class => [Options\RedirectOptions::class],
|
||||
|
||||
Config\NotFoundRedirectResolver::class => [Util\RedirectResponseHelper::class, 'Logger_Shlink'],
|
||||
RedirectRule\ShortUrlRedirectionResolver::class => ['em'],
|
||||
|
||||
RedirectRule\ShortUrlRedirectRuleService::class => ['em'],
|
||||
RedirectRule\ShortUrlRedirectionResolver::class => [RedirectRule\ShortUrlRedirectRuleService::class],
|
||||
|
||||
Action\RedirectAction::class => [
|
||||
ShortUrl\ShortUrlResolver::class,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule\Entity;
|
||||
|
||||
use JsonSerializable;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
@ -15,7 +16,7 @@ use function sprintf;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
class RedirectCondition extends AbstractEntity
|
||||
class RedirectCondition extends AbstractEntity implements JsonSerializable
|
||||
{
|
||||
private function __construct(
|
||||
public readonly string $name,
|
||||
@ -98,4 +99,14 @@ class RedirectCondition extends AbstractEntity
|
||||
$device = DeviceType::matchFromUserAgent($request->getHeaderLine('User-Agent'));
|
||||
return $device !== null && $device->value === strtolower($this->matchValue);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->name,
|
||||
'type' => $this->type->value,
|
||||
'matchKey' => $this->matchKey,
|
||||
'matchValue' => $this->matchValue,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,15 @@ namespace Shlinkio\Shlink\Core\RedirectRule\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use JsonSerializable;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
use function array_values;
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\every;
|
||||
|
||||
class ShortUrlRedirectRule extends AbstractEntity
|
||||
class ShortUrlRedirectRule extends AbstractEntity implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @param Collection<RedirectCondition> $conditions
|
||||
@ -33,4 +35,13 @@ class ShortUrlRedirectRule extends AbstractEntity
|
||||
static fn (RedirectCondition $condition) => $condition->matchesRequest($request),
|
||||
);
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'longUrl' => $this->longUrl,
|
||||
'priority' => $this->priority,
|
||||
'conditions' => array_values($this->conditions->toArray()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
25
module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php
Normal file
25
module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServiceInterface
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $em)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShortUrlRedirectRule[]
|
||||
*/
|
||||
public function rulesForShortUrl(ShortUrl $shortUrl): array
|
||||
{
|
||||
return $this->em->getRepository(ShortUrlRedirectRule::class)->findBy(
|
||||
criteria: ['shortUrl' => $shortUrl],
|
||||
orderBy: ['priority' => 'ASC'],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule;
|
||||
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
interface ShortUrlRedirectRuleServiceInterface
|
||||
{
|
||||
/**
|
||||
* @return ShortUrlRedirectRule[]
|
||||
*/
|
||||
public function rulesForShortUrl(ShortUrl $shortUrl): array;
|
||||
}
|
@ -2,23 +2,18 @@
|
||||
|
||||
namespace Shlinkio\Shlink\Core\RedirectRule;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
readonly class ShortUrlRedirectionResolver implements ShortUrlRedirectionResolverInterface
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $em)
|
||||
public function __construct(private ShortUrlRedirectRuleServiceInterface $ruleService)
|
||||
{
|
||||
}
|
||||
|
||||
public function resolveLongUrl(ShortUrl $shortUrl, ServerRequestInterface $request): string
|
||||
{
|
||||
$rules = $this->em->getRepository(ShortUrlRedirectRule::class)->findBy(
|
||||
criteria: ['shortUrl' => $shortUrl],
|
||||
orderBy: ['priority' => 'ASC'],
|
||||
);
|
||||
$rules = $this->ruleService->rulesForShortUrl($shortUrl);
|
||||
foreach ($rules as $rule) {
|
||||
// Return the long URL for the first rule found that matches
|
||||
if ($rule->matchesRequest($request)) {
|
||||
|
@ -48,6 +48,9 @@ readonly class ShortUrlResolver implements ShortUrlResolverInterface
|
||||
return $shortUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ShortUrlNotFoundException
|
||||
*/
|
||||
public function resolvePublicShortUrl(ShortUrlIdentifier $identifier): ShortUrl
|
||||
{
|
||||
/** @var ShortUrlRepository $shortUrlRepo */
|
||||
|
@ -13,13 +13,13 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
||||
class ShortUrlService implements ShortUrlServiceInterface
|
||||
readonly class ShortUrlService implements ShortUrlServiceInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ORM\EntityManagerInterface $em,
|
||||
private readonly ShortUrlResolverInterface $urlResolver,
|
||||
private readonly ShortUrlTitleResolutionHelperInterface $titleResolutionHelper,
|
||||
private readonly ShortUrlRelationResolverInterface $relationResolver,
|
||||
private ORM\EntityManagerInterface $em,
|
||||
private ShortUrlResolverInterface $urlResolver,
|
||||
private ShortUrlTitleResolutionHelperInterface $titleResolutionHelper,
|
||||
private ShortUrlRelationResolverInterface $relationResolver,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace ShlinkioTest\Shlink\Core\RedirectRule;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleService;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
|
||||
class ShortUrlRedirectRuleServiceTest extends TestCase
|
||||
{
|
||||
private EntityManagerInterface & MockObject $em;
|
||||
private ShortUrlRedirectRuleService $ruleService;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->em = $this->createMock(EntityManagerInterface::class);
|
||||
$this->ruleService = new ShortUrlRedirectRuleService($this->em);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function delegatesToRepository(): void
|
||||
{
|
||||
$shortUrl = ShortUrl::withLongUrl('https://shlink.io');
|
||||
$rules = [
|
||||
new ShortUrlRedirectRule($shortUrl, 1, 'https://example.com/from-rule', new ArrayCollection([
|
||||
RedirectCondition::forLanguage('en-US'),
|
||||
])),
|
||||
new ShortUrlRedirectRule($shortUrl, 2, 'https://example.com/from-rule-2', new ArrayCollection([
|
||||
RedirectCondition::forQueryParam('foo', 'bar'),
|
||||
RedirectCondition::forDevice(DeviceType::ANDROID),
|
||||
])),
|
||||
];
|
||||
|
||||
$repo = $this->createMock(EntityRepository::class);
|
||||
$repo->expects($this->once())->method('findBy')->with(
|
||||
['shortUrl' => $shortUrl],
|
||||
['priority' => 'ASC'],
|
||||
)->willReturn($rules);
|
||||
$this->em->expects($this->once())->method('getRepository')->with(ShortUrlRedirectRule::class)->willReturn(
|
||||
$repo,
|
||||
);
|
||||
|
||||
$result = $this->ruleService->rulesForShortUrl($shortUrl);
|
||||
|
||||
self::assertSame($rules, $result);
|
||||
}
|
||||
}
|
@ -3,8 +3,6 @@
|
||||
namespace ShlinkioTest\Shlink\Core\RedirectRule;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
@ -15,6 +13,7 @@ use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolver;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleServiceInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation;
|
||||
|
||||
@ -25,12 +24,12 @@ use const ShlinkioTest\Shlink\IOS_USER_AGENT;
|
||||
class ShortUrlRedirectionResolverTest extends TestCase
|
||||
{
|
||||
private ShortUrlRedirectionResolver $resolver;
|
||||
private EntityManagerInterface & MockObject $em;
|
||||
private ShortUrlRedirectRuleServiceInterface & MockObject $ruleService;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->em = $this->createMock(EntityManagerInterface::class);
|
||||
$this->resolver = new ShortUrlRedirectionResolver($this->em);
|
||||
$this->ruleService = $this->createMock(ShortUrlRedirectRuleServiceInterface::class);
|
||||
$this->resolver = new ShortUrlRedirectionResolver($this->ruleService);
|
||||
}
|
||||
|
||||
#[Test, DataProvider('provideData')]
|
||||
@ -43,14 +42,12 @@ class ShortUrlRedirectionResolverTest extends TestCase
|
||||
'longUrl' => 'https://example.com/foo/bar',
|
||||
]));
|
||||
|
||||
$repo = $this->createMock(EntityRepository::class);
|
||||
$repo->expects($this->once())->method('findBy')->willReturn($condition !== null ? [
|
||||
new ShortUrlRedirectRule($shortUrl, 1, 'https://example.com/from-rule', new ArrayCollection([
|
||||
$condition,
|
||||
])),
|
||||
] : []);
|
||||
$this->em->expects($this->once())->method('getRepository')->with(ShortUrlRedirectRule::class)->willReturn(
|
||||
$repo,
|
||||
$this->ruleService->expects($this->once())->method('rulesForShortUrl')->with($shortUrl)->willReturn(
|
||||
$condition !== null ? [
|
||||
new ShortUrlRedirectRule($shortUrl, 1, 'https://example.com/from-rule', new ArrayCollection([
|
||||
$condition,
|
||||
])),
|
||||
] : [],
|
||||
);
|
||||
|
||||
$result = $this->resolver->resolveLongUrl($shortUrl, $request);
|
||||
|
@ -12,6 +12,7 @@ use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
|
||||
use Shlinkio\Shlink\Core\Domain\DomainService;
|
||||
use Shlinkio\Shlink\Core\Options;
|
||||
use Shlinkio\Shlink\Core\RedirectRule;
|
||||
use Shlinkio\Shlink\Core\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer;
|
||||
use Shlinkio\Shlink\Core\Tag\TagService;
|
||||
@ -46,6 +47,7 @@ return [
|
||||
Action\Tag\UpdateTagAction::class => ConfigAbstractFactory::class,
|
||||
Action\Domain\ListDomainsAction::class => ConfigAbstractFactory::class,
|
||||
Action\Domain\DomainRedirectsAction::class => ConfigAbstractFactory::class,
|
||||
Action\RedirectRule\ListRedirectRulesAction::class => ConfigAbstractFactory::class,
|
||||
|
||||
ImplicitOptionsMiddleware::class => Middleware\EmptyResponseImplicitOptionsMiddlewareFactory::class,
|
||||
Middleware\BodyParserMiddleware::class => InvokableFactory::class,
|
||||
@ -103,6 +105,10 @@ return [
|
||||
Action\Tag\UpdateTagAction::class => [TagService::class],
|
||||
Action\Domain\ListDomainsAction::class => [DomainService::class, Options\NotFoundRedirectOptions::class],
|
||||
Action\Domain\DomainRedirectsAction::class => [DomainService::class],
|
||||
Action\RedirectRule\ListRedirectRulesAction::class => [
|
||||
ShortUrl\ShortUrlResolver::class,
|
||||
RedirectRule\ShortUrlRedirectRuleService::class,
|
||||
],
|
||||
|
||||
Middleware\CrossDomainMiddleware::class => ['config.cors'],
|
||||
Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'],
|
||||
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Shlinkio\Shlink\Rest\Action\RedirectRule;
|
||||
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleService;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface;
|
||||
use Shlinkio\Shlink\Rest\Action\AbstractRestAction;
|
||||
use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware;
|
||||
|
||||
class ListRedirectRulesAction extends AbstractRestAction
|
||||
{
|
||||
protected const ROUTE_PATH = '/short-urls/{shortCode}/redirect-rules';
|
||||
protected const ROUTE_ALLOWED_METHODS = [self::METHOD_GET];
|
||||
|
||||
public function __construct(
|
||||
private readonly ShortUrlResolverInterface $urlResolver,
|
||||
private readonly ShortUrlRedirectRuleService $ruleService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$shortUrl = $this->urlResolver->resolveShortUrl(
|
||||
ShortUrlIdentifier::fromApiRequest($request),
|
||||
AuthenticationMiddleware::apiKeyFromRequest($request),
|
||||
);
|
||||
$rules = $this->ruleService->rulesForShortUrl($shortUrl);
|
||||
|
||||
return new JsonResponse(['redirectRules' => $rules]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user