Removed everything that was deprecated

This commit is contained in:
Alejandro Celaya
2021-12-14 22:21:53 +01:00
parent 351e36b273
commit 1ff241411b
54 changed files with 108 additions and 1507 deletions

View File

@@ -43,16 +43,6 @@ return [
],
'allowed_methods' => [RequestMethod::METHOD_GET],
],
// Deprecated
[
'name' => 'old_' . Action\QrCodeAction::class,
'path' => '/{shortCode}/qr-code/{size:[0-9]+}',
'middleware' => [
Action\QrCodeAction::class,
],
'allowed_methods' => [RequestMethod::METHOD_GET],
],
],
];

View File

@@ -16,7 +16,6 @@ use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Writer\SvgWriter;
use Endroid\QrCode\Writer\WriterInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Core\Options\QrCodeOptions;
use function Functional\contains;
@@ -43,7 +42,7 @@ final class QrCodeParams
$query = $request->getQueryParams();
return new self(
self::resolveSize($request, $query, $defaults),
self::resolveSize($query, $defaults),
self::resolveMargin($query, $defaults),
self::resolveWriter($query, $defaults),
self::resolveErrorCorrection($query, $defaults),
@@ -51,10 +50,9 @@ final class QrCodeParams
);
}
private static function resolveSize(Request $request, array $query, QrCodeOptions $defaults): int
private static function resolveSize(array $query, QrCodeOptions $defaults): int
{
// FIXME Size attribute is deprecated. After v3.0.0, always use the query param instead
$size = (int) $request->getAttribute('size', $query['size'] ?? $defaults->size());
$size = (int) ($query['size'] ?? $defaults->size());
if ($size < self::MIN_SIZE) {
return self::MIN_SIZE;
}

View File

@@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Config;
use function Functional\compose;
/** @deprecated */
class DeprecatedConfigParser
{
public function __invoke(array $config): array
{
return compose([$this, 'parseNotFoundRedirect'], [$this, 'removeSecretKey'])($config);
}
public function parseNotFoundRedirect(array $config): array
{
// If the new config value is already set, keep it
if (isset($config['not_found_redirects']['invalid_short_url'])) {
return $config;
}
$oldRedirectEnabled = $config['url_shortener']['not_found_short_url']['enable_redirection'] ?? false;
if (! $oldRedirectEnabled) {
return $config;
}
$oldRedirectValue = $config['url_shortener']['not_found_short_url']['redirect_to'] ?? null;
$config['not_found_redirects']['invalid_short_url'] = $oldRedirectValue;
return $config;
}
public function removeSecretKey(array $config): array
{
// Removing secret_key from any generated config will prevent the AppOptions object from crashing
unset($config['app_options']['secret_key']);
return $config;
}
}

View File

@@ -1,94 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Config;
use Laminas\Stdlib\ArrayUtils;
use Shlinkio\Shlink\Config\Collection\PathCollection;
use function array_flip;
use function array_intersect_key;
use function array_key_exists;
use function array_keys;
use function Functional\contains;
use function Functional\reduce_left;
use function uksort;
/** @deprecated */
class SimplifiedConfigParser
{
private const SIMPLIFIED_CONFIG_MAPPING = [
'disable_track_param' => ['tracking', 'disable_track_param'],
'short_domain_schema' => ['url_shortener', 'domain', 'schema'],
'short_domain_host' => ['url_shortener', 'domain', 'hostname'],
'validate_url' => ['url_shortener', 'validate_url'],
'invalid_short_url_redirect_to' => ['not_found_redirects', 'invalid_short_url'],
'regular_404_redirect_to' => ['not_found_redirects', 'regular_404'],
'base_url_redirect_to' => ['not_found_redirects', 'base_url'],
'db_config' => ['entity_manager', 'connection'],
'delete_short_url_threshold' => ['delete_short_urls', 'visits_threshold'],
'redis_servers' => ['cache', 'redis', 'servers'],
'base_path' => ['router', 'base_path'],
'web_worker_num' => ['mezzio-swoole', 'swoole-http-server', 'options', 'worker_num'],
'task_worker_num' => ['mezzio-swoole', 'swoole-http-server', 'options', 'task_worker_num'],
'visits_webhooks' => ['url_shortener', 'visits_webhooks'],
'default_short_codes_length' => ['url_shortener', 'default_short_codes_length'],
'geolite_license_key' => ['geolite2', 'license_key'],
'mercure_public_hub_url' => ['mercure', 'public_hub_url'],
'mercure_internal_hub_url' => ['mercure', 'internal_hub_url'],
'mercure_jwt_secret' => ['mercure', 'jwt_secret'],
'anonymize_remote_addr' => ['tracking', 'anonymize_remote_addr'],
'redirect_status_code' => ['url_shortener', 'redirect_status_code'],
'redirect_cache_lifetime' => ['url_shortener', 'redirect_cache_lifetime'],
'port' => ['mezzio-swoole', 'swoole-http-server', 'port'],
];
private const SIMPLIFIED_CONFIG_SIDE_EFFECTS = [
'delete_short_url_threshold' => [
'path' => ['delete_short_urls', 'check_visits_threshold'],
'value' => true,
],
'redis_servers' => [
'path' => ['dependencies', 'aliases', 'lock_store'],
'value' => 'redis_lock_store',
],
];
private const SIMPLIFIED_MERGEABLE_CONFIG = ['db_config'];
public function __invoke(array $config): array
{
$configForExistingKeys = $this->getConfigForKeysInMappingOrderedByMapping($config);
return reduce_left($configForExistingKeys, function ($value, string $key, $c, PathCollection $collection) {
$path = self::SIMPLIFIED_CONFIG_MAPPING[$key];
if (contains(self::SIMPLIFIED_MERGEABLE_CONFIG, $key)) {
$value = ArrayUtils::merge($collection->getValueInPath($path), $value);
}
$collection->setValueInPath($value, $path);
if (array_key_exists($key, self::SIMPLIFIED_CONFIG_SIDE_EFFECTS)) {
['path' => $sideEffectPath, 'value' => $sideEffectValue] = self::SIMPLIFIED_CONFIG_SIDE_EFFECTS[$key];
$collection->setValueInPath($sideEffectValue, $sideEffectPath);
}
return $collection;
}, new PathCollection($config))->toArray();
}
private function getConfigForKeysInMappingOrderedByMapping(array $config): array
{
// Ignore any config which is not defined in the mapping
$configForExistingKeys = array_intersect_key($config, self::SIMPLIFIED_CONFIG_MAPPING);
// Order the config by their key, based on the order it was defined in the mapping.
// This mainly allows deprecating keys and defining new ones that will replace the older and always take
// preference, while the old one keeps working for backwards compatibility if the new one is not provided.
$simplifiedConfigOrder = array_flip(array_keys(self::SIMPLIFIED_CONFIG_MAPPING));
uksort(
$configForExistingKeys,
fn (string $a, string $b): int => $simplifiedConfigOrder[$a] - $simplifiedConfigOrder[$b],
);
return $configForExistingKeys;
}
}

View File

@@ -16,7 +16,7 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE
use CommonProblemDetailsExceptionTrait;
private const TITLE = 'Cannot delete short URL';
private const TYPE = 'INVALID_SHORTCODE_DELETION'; // FIXME Deprecated: Should be INVALID_SHORT_URL_DELETION
private const TYPE = 'INVALID_SHORT_URL_DELETION';
public static function fromVisitsThreshold(int $threshold, ShortUrlIdentifier $identifier): self
{

View File

@@ -8,9 +8,6 @@ use Shlinkio\Shlink\Core\Exception\ValidationException;
use function array_pad;
use function explode;
use function is_array;
use function is_string;
use function key;
final class ShortUrlsOrdering
{
@@ -41,22 +38,9 @@ final class ShortUrlsOrdering
return;
}
// FIXME Providing the ordering as array is considered deprecated. To be removed in v3.0.0
$isArray = is_array($orderBy);
if (! $isArray && ! is_string($orderBy)) {
throw ValidationException::fromArray([
'orderBy' => '"Order by" must be an array, string or null',
]);
}
if (! $isArray) {
[$field, $dir] = array_pad(explode('-', $orderBy), 2, null);
$this->orderField = $field;
$this->orderDirection = $dir ?? self::DEFAULT_ORDER_DIRECTION;
} else {
$this->orderField = key($orderBy);
$this->orderDirection = $orderBy[$this->orderField];
}
[$field, $dir] = array_pad(explode('-', $orderBy), 2, null);
$this->orderField = $field;
$this->orderDirection = $dir ?? self::DEFAULT_ORDER_DIRECTION;
}
public function orderField(): ?string

View File

@@ -10,8 +10,8 @@ use function sprintf;
class AppOptions extends AbstractOptions
{
private string $name = '';
private string $version = '1.0';
private string $name = 'Shlink';
private string $version = '3.0.0';
public function getName(): string
{
@@ -35,13 +35,6 @@ class AppOptions extends AbstractOptions
return $this;
}
/** @deprecated */
protected function setDisableTrackParam(?string $disableTrackParam): self
{
// Keep just for backwards compatibility during hydration
return $this;
}
public function __toString(): string
{
return sprintf('%s:v%s', $this->name, $this->version);

View File

@@ -77,16 +77,4 @@ class UrlShortenerOptions extends AbstractOptions
{
$this->appendExtraPath = $appendExtraPath;
}
/** @deprecated */
protected function setAnonymizeRemoteAddr(bool $anonymizeRemoteAddr): void
{
// Keep just for backwards compatibility during hydration
}
/** @deprecated */
protected function setTrackOrphanVisits(bool $trackOrphanVisits): void
{
// Keep just for backwards compatibility during hydration
}
}

View File

@@ -56,8 +56,7 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
$fieldName = $orderBy->orderField();
$order = $orderBy->orderDirection();
// visitsCount and visitCount are deprecated. Only visits should work
if (contains(['visits', 'visitsCount', 'visitCount'], $fieldName)) {
if ($fieldName === 'visits') {
// FIXME This query is inefficient. Debug it.
$qb->addSelect('COUNT(DISTINCT v) AS totalVisits')
->leftJoin('s.visits', 'v')
@@ -67,17 +66,9 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
return array_column($qb->getQuery()->getResult(), 0);
}
// Map public field names to column names
$fieldNameMap = [
'originalUrl' => 'longUrl', // Deprecated
'longUrl' => 'longUrl',
'shortCode' => 'shortCode',
'dateCreated' => 'dateCreated',
'title' => 'title',
];
$resolvedFieldName = $fieldNameMap[$fieldName] ?? null;
if ($resolvedFieldName !== null) {
$qb->orderBy('s.' . $resolvedFieldName, $order);
$orderableFields = ['longUrl', 'shortCode', 'dateCreated', 'title'];
if (contains($orderableFields, $fieldName)) {
$qb->orderBy('s.' . $fieldName, $order);
}
return $qb->getQuery()->getResult();

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM;
use Happyr\DoctrineSpecification\Spec;
use Shlinkio\Shlink\Core\Entity\Tag;
@@ -15,14 +14,11 @@ use Shlinkio\Shlink\Core\Repository\TagRepository;
use Shlinkio\Shlink\Core\Repository\TagRepositoryInterface;
use Shlinkio\Shlink\Core\Tag\Model\TagInfo;
use Shlinkio\Shlink\Core\Tag\Model\TagRenaming;
use Shlinkio\Shlink\Core\Util\TagManagerTrait;
use Shlinkio\Shlink\Rest\ApiKey\Spec\WithApiKeySpecsEnsuringJoin;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class TagService implements TagServiceInterface
{
use TagManagerTrait;
public function __construct(private ORM\EntityManagerInterface $em)
{
}
@@ -34,12 +30,10 @@ class TagService implements TagServiceInterface
{
/** @var TagRepository $repo */
$repo = $this->em->getRepository(Tag::class);
/** @var Tag[] $tags */
$tags = $repo->match(Spec::andX(
return $repo->match(Spec::andX(
Spec::orderBy('name'),
new WithApiKeySpecsEnsuringJoin($apiKey),
));
return $tags;
}
/**
@@ -67,21 +61,6 @@ class TagService implements TagServiceInterface
$repo->deleteByName($tagNames);
}
/**
* Provided a list of tag names, creates all that do not exist yet
*
* @deprecated
* @param string[] $tagNames
* @return Collection|Tag[]
*/
public function createTags(array $tagNames): Collection
{
$tags = $this->tagNamesToEntities($this->em, $tagNames);
$this->em->flush();
return $tags;
}
/**
* @throws TagNotFoundException
* @throws TagConflictException

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Tag;
use Doctrine\Common\Collections\Collection;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Exception\ForbiddenTagOperationException;
use Shlinkio\Shlink\Core\Exception\TagConflictException;
@@ -31,13 +30,6 @@ interface TagServiceInterface
*/
public function deleteTags(array $tagNames, ?ApiKey $apiKey = null): void;
/**
* @deprecated
* @param string[] $tagNames
* @return Collection|Tag[]
*/
public function createTags(array $tagNames): Collection;
/**
* @throws TagNotFoundException
* @throws TagConflictException

View File

@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Util;
use Doctrine\Common\Collections;
use Doctrine\ORM\EntityManagerInterface;
use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Validation\ShortUrlInputFilter;
use function Functional\map;
/** @deprecated */
trait TagManagerTrait
{
/**
* @param string[] $tags
* @deprecated
* @return Collections\Collection|Tag[]
*/
private function tagNamesToEntities(EntityManagerInterface $em, array $tags): Collections\Collection
{
$normalizedTags = ShortUrlInputFilter::withNonRequiredLongUrl([
ShortUrlInputFilter::TAGS => $tags,
])->getValue(ShortUrlInputFilter::TAGS);
$entities = map($normalizedTags, function (string $tagName) use ($em) {
$tag = $em->getRepository(Tag::class)->findOneBy(['name' => $tagName]) ?? new Tag($tagName);
$em->persist($tag);
return $tag;
});
return new Collections\ArrayCollection($entities);
}
}

View File

@@ -154,18 +154,12 @@ class QrCodeActionTest extends TestCase
];
yield 'no size' => [[], ServerRequestFactory::fromGlobals(), 300];
yield 'no size, different default' => [['size' => 500], ServerRequestFactory::fromGlobals(), 500];
yield 'size in attr' => [[], ServerRequestFactory::fromGlobals()->withAttribute('size', '400'), 400];
yield 'size in query' => [[], ServerRequestFactory::fromGlobals()->withQueryParams(['size' => '123']), 123];
yield 'size in query, default margin' => [
['margin' => 25],
ServerRequestFactory::fromGlobals()->withQueryParams(['size' => '123']),
173,
];
yield 'size in query and attr' => [
[],
ServerRequestFactory::fromGlobals()->withAttribute('size', '350')->withQueryParams(['size' => '123']),
350,
];
yield 'margin' => [[], ServerRequestFactory::fromGlobals()->withQueryParams(['margin' => '35']), 370];
yield 'margin and different default' => [
['size' => 400],

View File

@@ -1,111 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Config;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Config\DeprecatedConfigParser;
use function array_merge;
class DeprecatedConfigParserTest extends TestCase
{
private DeprecatedConfigParser $postProcessor;
public function setUp(): void
{
$this->postProcessor = new DeprecatedConfigParser();
}
/** @test */
public function returnsConfigAsIsIfNewValueIsDefined(): void
{
$config = [
'not_found_redirects' => [
'invalid_short_url' => 'somewhere',
],
];
$result = ($this->postProcessor)($config);
self::assertEquals($config, $result);
}
/** @test */
public function doesNotProvideNewConfigIfOldOneIsDefinedButDisabled(): void
{
$config = [
'url_shortener' => [
'not_found_short_url' => [
'enable_redirection' => false,
'redirect_to' => 'somewhere',
],
],
];
$result = ($this->postProcessor)($config);
self::assertEquals($config, $result);
}
/** @test */
public function mapsOldConfigToNewOneWhenOldOneIsEnabled(): void
{
$config = [
'url_shortener' => [
'not_found_short_url' => [
'enable_redirection' => true,
'redirect_to' => 'somewhere',
],
],
];
$expected = array_merge($config, [
'not_found_redirects' => [
'invalid_short_url' => 'somewhere',
],
]);
$result = ($this->postProcessor)($config);
self::assertEquals($expected, $result);
}
/** @test */
public function definesNewConfigAsNullIfOldOneIsEnabledWithNoRedirectValue(): void
{
$config = [
'url_shortener' => [
'not_found_short_url' => [
'enable_redirection' => true,
],
],
];
$expected = array_merge($config, [
'not_found_redirects' => [
'invalid_short_url' => null,
],
]);
$result = ($this->postProcessor)($config);
self::assertEquals($expected, $result);
}
/** @test */
public function removesTheOldSecretKey(): void
{
$config = [
'app_options' => [
'secret_key' => 'foobar',
],
];
$expected = [
'app_options' => [],
];
$result = ($this->postProcessor)($config);
self::assertEquals($expected, $result);
}
}

View File

@@ -1,158 +0,0 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\Config;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Config\SimplifiedConfigParser;
use function array_merge;
class SimplifiedConfigParserTest extends TestCase
{
private SimplifiedConfigParser $postProcessor;
public function setUp(): void
{
$this->postProcessor = new SimplifiedConfigParser();
}
/** @test */
public function properlyMapsSimplifiedConfig(): void
{
$config = [
'tracking' => [
'disable_track_param' => 'foo',
],
'entity_manager' => [
'connection' => [
'driver' => 'mysql',
'host' => 'shlink_db_mysql',
'port' => '3306',
],
],
];
$simplified = [
'disable_track_param' => 'bar',
'short_domain_schema' => 'https',
'short_domain_host' => 'doma.in',
'validate_url' => true,
'delete_short_url_threshold' => 50,
'invalid_short_url_redirect_to' => 'foobar.com',
'regular_404_redirect_to' => 'bar.com',
'base_url_redirect_to' => 'foo.com',
'redis_servers' => [
'tcp://1.1.1.1:1111',
'tcp://1.2.2.2:2222',
],
'db_config' => [
'dbname' => 'shlink',
'user' => 'foo',
'password' => 'bar',
'port' => '1234',
],
'base_path' => '/foo/bar',
'task_worker_num' => 50,
'visits_webhooks' => [
'http://my-api.com/api/v2.3/notify',
'https://third-party.io/foo',
],
'default_short_codes_length' => 8,
'geolite_license_key' => 'kjh23ljkbndskj345',
'mercure_public_hub_url' => 'public_url',
'mercure_internal_hub_url' => 'internal_url',
'mercure_jwt_secret' => 'super_secret_value',
'anonymize_remote_addr' => false,
'redirect_status_code' => 301,
'redirect_cache_lifetime' => 90,
'port' => 8888,
];
$expected = [
'tracking' => [
'disable_track_param' => 'bar',
'anonymize_remote_addr' => false,
],
'entity_manager' => [
'connection' => [
'driver' => 'mysql',
'host' => 'shlink_db_mysql',
'dbname' => 'shlink',
'user' => 'foo',
'password' => 'bar',
'port' => '1234',
],
],
'url_shortener' => [
'domain' => [
'schema' => 'https',
'hostname' => 'doma.in',
],
'validate_url' => true,
'visits_webhooks' => [
'http://my-api.com/api/v2.3/notify',
'https://third-party.io/foo',
],
'default_short_codes_length' => 8,
'redirect_status_code' => 301,
'redirect_cache_lifetime' => 90,
],
'delete_short_urls' => [
'visits_threshold' => 50,
'check_visits_threshold' => true,
],
'dependencies' => [
'aliases' => [
'lock_store' => 'redis_lock_store',
],
],
'cache' => [
'redis' => [
'servers' => [
'tcp://1.1.1.1:1111',
'tcp://1.2.2.2:2222',
],
],
],
'router' => [
'base_path' => '/foo/bar',
],
'not_found_redirects' => [
'invalid_short_url' => 'foobar.com',
'regular_404' => 'bar.com',
'base_url' => 'foo.com',
],
'mezzio-swoole' => [
'swoole-http-server' => [
'port' => 8888,
'options' => [
'task_worker_num' => 50,
],
],
],
'geolite2' => [
'license_key' => 'kjh23ljkbndskj345',
],
'mercure' => [
'public_hub_url' => 'public_url',
'internal_hub_url' => 'internal_url',
'jwt_secret' => 'super_secret_value',
],
];
$result = ($this->postProcessor)(array_merge($config, $simplified));
self::assertEquals(array_merge($expected, $simplified), $result);
}
}

View File

@@ -37,7 +37,7 @@ class DeleteShortUrlExceptionTest extends TestCase
'threshold' => $threshold,
], $e->getAdditionalData());
self::assertEquals('Cannot delete short URL', $e->getTitle());
self::assertEquals('INVALID_SHORTCODE_DELETION', $e->getType());
self::assertEquals('INVALID_SHORT_URL_DELETION', $e->getType());
self::assertEquals(422, $e->getStatus());
}

View File

@@ -97,21 +97,6 @@ class TagServiceTest extends TestCase
);
}
/** @test */
public function createTagsPersistsEntities(): void
{
$find = $this->repo->findOneBy(Argument::cetera())->willReturn(new Tag('foo'));
$persist = $this->em->persist(Argument::type(Tag::class))->willReturn(null);
$flush = $this->em->flush()->willReturn(null);
$result = $this->service->createTags(['foo', 'bar']);
self::assertCount(2, $result);
$find->shouldHaveBeenCalled();
$persist->shouldHaveBeenCalledTimes(2);
$flush->shouldHaveBeenCalled();
}
/**
* @test
* @dataProvider provideAdminApiKeys