From 8212d3c540879e2458a50a6ee13b228a0341c2a6 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 2 Oct 2021 10:02:43 +0200 Subject: [PATCH] Allowed to set and update the forwardQuery param on short URLs --- docs/swagger/definitions/ShortUrlEdition.json | 48 +++++++++++ docs/swagger/paths/v1_short-urls.json | 82 +++++++------------ .../paths/v1_short-urls_{shortCode}.json | 43 +--------- module/Core/src/Entity/ShortUrl.php | 4 + module/Core/src/Model/ShortUrlEdit.php | 14 ++++ module/Core/src/Model/ShortUrlMeta.php | 7 ++ .../src/Validation/ShortUrlInputFilter.php | 6 +- 7 files changed, 106 insertions(+), 98 deletions(-) create mode 100644 docs/swagger/definitions/ShortUrlEdition.json diff --git a/docs/swagger/definitions/ShortUrlEdition.json b/docs/swagger/definitions/ShortUrlEdition.json new file mode 100644 index 00000000..94ef6135 --- /dev/null +++ b/docs/swagger/definitions/ShortUrlEdition.json @@ -0,0 +1,48 @@ +{ + "type": "object", + "properties": { + "longUrl": { + "description": "The long URL this short URL will redirect to", + "type": "string" + }, + "validSince": { + "description": "The date (in ISO-8601 format) from which this short code will be valid", + "type": "string", + "nullable": true + }, + "validUntil": { + "description": "The date (in ISO-8601 format) until which this short code will be valid", + "type": "string", + "nullable": true + }, + "maxVisits": { + "description": "The maximum number of allowed visits for this short code", + "type": "number", + "nullable": true + }, + "validateUrl": { + "description": "Tells if the long URL (if provided) should or should not be validated as a reachable URL. If not provided, it will fall back to app-level config", + "type": "boolean" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of tags to set to the short URL." + }, + "title": { + "type": "string", + "description": "A descriptive title of the short URL.", + "nullable": true + }, + "crawlable": { + "type": "boolean", + "description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt." + }, + "forwardQuery": { + "type": "boolean", + "description": "Tells if the query params should be forwarded from the short URL to the long one, as explained in [the docs](https://shlink.io/documentation/some-features/#query-params-forwarding)." + } + } +} diff --git a/docs/swagger/paths/v1_short-urls.json b/docs/swagger/paths/v1_short-urls.json index 8cf22045..a4643058 100644 --- a/docs/swagger/paths/v1_short-urls.json +++ b/docs/swagger/paths/v1_short-urls.json @@ -225,63 +225,37 @@ "content": { "application/json": { "schema": { - "type": "object", - "required": [ - "longUrl" - ], - "properties": { - "longUrl": { - "description": "The URL to parse", - "type": "string" + "allOf": [ + { + "$ref": "../definitions/ShortUrlEdition.json" }, - "tags": { - "description": "The URL to parse", - "type": "array", - "items": { - "type": "string" + { + "type": "object", + "required": ["longUrl"], + "properties": { + "customSlug": { + "description": "A unique custom slug to be used instead of the generated short code", + "type": "string" + }, + "findIfExists": { + "description": "Will force existing matching URL to be returned if found, instead of creating a new one", + "type": "boolean" + }, + "domain": { + "description": "The domain to which the short URL will be attached", + "type": "string" + }, + "shortCodeLength": { + "description": "The length for generated short code. It has to be at least 4 and defaults to 5. It will be ignored when customSlug is provided", + "type": "number" + }, + "validateUrl": { + "description": "Tells if the long URL should or should not be validated as a reachable URL. If not provided, it will fall back to app-level config", + "type": "boolean" + } } - }, - "validSince": { - "description": "The date (in ISO-8601 format) from which this short code will be valid", - "type": "string" - }, - "validUntil": { - "description": "The date (in ISO-8601 format) until which this short code will be valid", - "type": "string" - }, - "customSlug": { - "description": "A unique custom slug to be used instead of the generated short code", - "type": "string" - }, - "maxVisits": { - "description": "The maximum number of allowed visits for this short code", - "type": "number" - }, - "findIfExists": { - "description": "Will force existing matching URL to be returned if found, instead of creating a new one", - "type": "boolean" - }, - "domain": { - "description": "The domain to which the short URL will be attached", - "type": "string" - }, - "shortCodeLength": { - "description": "The length for generated short code. It has to be at least 4 and defaults to 5. It will be ignored when customSlug is provided", - "type": "number" - }, - "validateUrl": { - "description": "Tells if the long URL should or should not be validated as a reachable URL. If not provided, it will fall back to app-level config", - "type": "boolean" - }, - "title": { - "type": "string", - "description": "A descriptive title of the short URL." - }, - "crawlable": { - "type": "boolean", - "description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt." } - } + ] } } } diff --git a/docs/swagger/paths/v1_short-urls_{shortCode}.json b/docs/swagger/paths/v1_short-urls_{shortCode}.json index 8691c0b5..e37df965 100644 --- a/docs/swagger/paths/v1_short-urls_{shortCode}.json +++ b/docs/swagger/paths/v1_short-urls_{shortCode}.json @@ -112,48 +112,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "longUrl": { - "description": "The long URL this short URL will redirect to", - "type": "string" - }, - "validSince": { - "description": "The date (in ISO-8601 format) from which this short code will be valid", - "type": "string", - "nullable": true - }, - "validUntil": { - "description": "The date (in ISO-8601 format) until which this short code will be valid", - "type": "string", - "nullable": true - }, - "maxVisits": { - "description": "The maximum number of allowed visits for this short code", - "type": "number", - "nullable": true - }, - "validateUrl": { - "description": "Tells if the long URL (if provided) should or should not be validated as a reachable URL. If not provided, it will fall back to app-level config", - "type": "boolean" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The list of tags to set to the short URL." - }, - "title": { - "type": "string", - "description": "A descriptive title of the short URL.", - "nullable": true - }, - "crawlable": { - "type": "boolean", - "description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt." - } - } + "$ref": "../definitions/ShortUrlEdition.json" } } } diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php index 210310f5..9fff1509 100644 --- a/module/Core/src/Entity/ShortUrl.php +++ b/module/Core/src/Entity/ShortUrl.php @@ -81,6 +81,7 @@ class ShortUrl extends AbstractEntity $instance->title = $meta->getTitle(); $instance->titleWasAutoResolved = $meta->titleWasAutoResolved(); $instance->crawlable = $meta->isCrawlable(); + $instance->forwardQuery = $meta->forwardQuery(); return $instance; } @@ -244,6 +245,9 @@ class ShortUrl extends AbstractEntity $this->title = $shortUrlEdit->title(); $this->titleWasAutoResolved = $shortUrlEdit->titleWasAutoResolved(); } + if ($shortUrlEdit->forwardQueryWasProvided()) { + $this->forwardQuery = $shortUrlEdit->forwardQuery(); + } } /** diff --git a/module/Core/src/Model/ShortUrlEdit.php b/module/Core/src/Model/ShortUrlEdit.php index 32c1ca1e..8e187cf6 100644 --- a/module/Core/src/Model/ShortUrlEdit.php +++ b/module/Core/src/Model/ShortUrlEdit.php @@ -32,6 +32,8 @@ final class ShortUrlEdit implements TitleResolutionModelInterface private ?bool $validateUrl = null; private bool $crawlablePropWasProvided = false; private bool $crawlable = false; + private bool $forwardQueryPropWasProvided = false; + private bool $forwardQuery = true; private function __construct() { @@ -64,6 +66,7 @@ final class ShortUrlEdit implements TitleResolutionModelInterface $this->tagsPropWasProvided = array_key_exists(ShortUrlInputFilter::TAGS, $data); $this->titlePropWasProvided = array_key_exists(ShortUrlInputFilter::TITLE, $data); $this->crawlablePropWasProvided = array_key_exists(ShortUrlInputFilter::CRAWLABLE, $data); + $this->forwardQueryPropWasProvided = array_key_exists(ShortUrlInputFilter::FORWARD_QUERY, $data); $this->longUrl = $inputFilter->getValue(ShortUrlInputFilter::LONG_URL); $this->validSince = parseDateField($inputFilter->getValue(ShortUrlInputFilter::VALID_SINCE)); @@ -73,6 +76,7 @@ final class ShortUrlEdit implements TitleResolutionModelInterface $this->tags = $inputFilter->getValue(ShortUrlInputFilter::TAGS); $this->title = $inputFilter->getValue(ShortUrlInputFilter::TITLE); $this->crawlable = $inputFilter->getValue(ShortUrlInputFilter::CRAWLABLE); + $this->forwardQuery = getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::FORWARD_QUERY) ?? true; } public function longUrl(): ?string @@ -176,4 +180,14 @@ final class ShortUrlEdit implements TitleResolutionModelInterface { return $this->crawlablePropWasProvided; } + + public function forwardQuery(): bool + { + return $this->forwardQuery; + } + + public function forwardQueryWasProvided(): bool + { + return $this->forwardQueryPropWasProvided; + } } diff --git a/module/Core/src/Model/ShortUrlMeta.php b/module/Core/src/Model/ShortUrlMeta.php index 0c982f17..74390281 100644 --- a/module/Core/src/Model/ShortUrlMeta.php +++ b/module/Core/src/Model/ShortUrlMeta.php @@ -32,6 +32,7 @@ final class ShortUrlMeta implements TitleResolutionModelInterface private ?string $title = null; private bool $titleWasAutoResolved = false; private bool $crawlable = false; + private bool $forwardQuery = true; private function __construct() { @@ -82,6 +83,7 @@ final class ShortUrlMeta implements TitleResolutionModelInterface $this->tags = $inputFilter->getValue(ShortUrlInputFilter::TAGS); $this->title = $inputFilter->getValue(ShortUrlInputFilter::TITLE); $this->crawlable = $inputFilter->getValue(ShortUrlInputFilter::CRAWLABLE); + $this->forwardQuery = getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::FORWARD_QUERY) ?? true; } public function getLongUrl(): string @@ -195,4 +197,9 @@ final class ShortUrlMeta implements TitleResolutionModelInterface { return $this->crawlable; } + + public function forwardQuery(): bool + { + return $this->forwardQuery; + } } diff --git a/module/Core/src/Validation/ShortUrlInputFilter.php b/module/Core/src/Validation/ShortUrlInputFilter.php index fd5a92e7..47f6f8ac 100644 --- a/module/Core/src/Validation/ShortUrlInputFilter.php +++ b/module/Core/src/Validation/ShortUrlInputFilter.php @@ -33,6 +33,7 @@ class ShortUrlInputFilter extends InputFilter public const TAGS = 'tags'; public const TITLE = 'title'; public const CRAWLABLE = 'crawlable'; + public const FORWARD_QUERY = 'forwardQuery'; private function __construct(array $data, bool $requireLongUrl) { @@ -89,9 +90,10 @@ class ShortUrlInputFilter extends InputFilter $this->add($this->createBooleanInput(self::FIND_IF_EXISTS, false)); - // This cannot be defined as a boolean input because it can actually have 3 values, true, false and null. - // Defining it as boolean will make null fall back to false, which is not the desired behavior. + // These cannot be defined as a boolean inputs, because they can actually have 3 values: true, false and null. + // Defining them as boolean will make null fall back to false, which is not the desired behavior. $this->add($this->createInput(self::VALIDATE_URL, false)); + $this->add($this->createInput(self::FORWARD_QUERY, false)); $domain = $this->createInput(self::DOMAIN, false); $domain->getValidatorChain()->attach(new Validation\HostAndPortValidator());