From abf4d5a20663dafad98b1bd37ceef5e52a2e4e7e Mon Sep 17 00:00:00 2001 From: Ian Forsey Date: Tue, 16 Mar 2021 23:52:10 +0000 Subject: [PATCH 01/17] Add equalsUnordered method for comparing Urls without considering query parameter order #282 (#283) --- README.md | 56 ++++++++ build.sbt | 3 +- .../scala/io/lemonlabs/uri/QueryString.scala | 10 ++ .../src/main/scala/io/lemonlabs/uri/Uri.scala | 81 ++++++++++++ .../scala/io/lemonlabs/uri/CatsTests.scala | 87 +++++++++++++ .../io/lemonlabs/uri/EqualityTests.scala | 121 ++++++++++++++++++ 6 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 shared/src/test/scala/io/lemonlabs/uri/EqualityTests.scala diff --git a/README.md b/README.md index f495e3fc..d42e1e98 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,62 @@ val url = Url.parse("http://user:password@example.com?secret=123&other=true") url.toRedactedString(Redact.byRemoving.allParams().userInfo()) ``` +## Url Equality + +By default scala-uri only considers `Url`s equal if query parameters are in the same order: + +```scala mdoc:reset +import io.lemonlabs.uri._ + +val urlOne = Url.parse("https://example.com?a=1&b=2") +val urlTwo = Url.parse("https://example.com?b=2&a=1") + +urlOne == urlTwo // this is false + +val urlThree = Url.parse("https://example.com?a=1&b=2") + +urlOne == urlThree // this is true +``` + +For use-cases where query parameter order is not important, the `equalsUnordered` can be used + +```scala mdoc +urlOne.equalsUnordered(urlTwo) // this is true +``` + +When using cats for equality testing, parameter order will also be considered by default + +```scala mdoc +import cats.implicits._ + +urlOne === urlTwo // this is false +urlOne === urlThree // this is true +``` + +With cats, query parameter order can be ignored for equality checks with the following import: + +```scala mdoc +import io.lemonlabs.uri.Url.unordered._ + +urlOne === urlTwo // this is true +urlOne === urlThree // this is true +``` + +Note: depending on the type you are comparing, you will need to import a different cats `Eq` instance. +The following are available: + +```scala mdoc:reset +import io.lemonlabs.uri.Uri.unordered._ +import io.lemonlabs.uri.Url.unordered._ +import io.lemonlabs.uri.RelativeUrl.unordered._ +import io.lemonlabs.uri.UrlWithAuthority.unordered._ +import io.lemonlabs.uri.ProtocolRelativeUrl.unordered._ +import io.lemonlabs.uri.AbsoluteUrl.unordered._ +import io.lemonlabs.uri.UrlWithoutAuthority.unordered._ +import io.lemonlabs.uri.SimpleUrlWithoutAuthority.unordered._ +import io.lemonlabs.uri.QueryString.unordered._ +``` + ## Pattern Matching URIs ```scala mdoc:reset diff --git a/build.sbt b/build.sbt index cdcecac5..c1502717 100644 --- a/build.sbt +++ b/build.sbt @@ -117,7 +117,8 @@ val previousVersions = (0 to 0).map(v => s"3.$v.0").toSet val mimaExcludes = Seq( ProblemFilters.exclude[ReversedMissingMethodProblem]("io.lemonlabs.uri.typesafe.QueryValueInstances1.*"), - ProblemFilters.exclude[ReversedMissingMethodProblem]("io.lemonlabs.uri.Url.*") + ProblemFilters.exclude[ReversedMissingMethodProblem]("io.lemonlabs.uri.Url.*"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("io.lemonlabs.uri.Uri.*") ) val mimaSettings = Seq( diff --git a/shared/src/main/scala/io/lemonlabs/uri/QueryString.scala b/shared/src/main/scala/io/lemonlabs/uri/QueryString.scala index 161d6141..25b29557 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/QueryString.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/QueryString.scala @@ -229,6 +229,11 @@ case class QueryString(params: Vector[(String, Option[String])])(implicit config else paramsAsString.mkString("&") } + private def paramsCardinality = params.groupBy(identity) + + def equalsUnordered(other: QueryString): Boolean = + this.paramsCardinality == other.paramsCardinality + /** Returns the query string with no encoding taking place (e.g. non ASCII characters will not be percent encoded) * @return String containing the raw query string for this Uri */ @@ -263,4 +268,9 @@ object QueryString { implicit val eqQueryString: Eq[QueryString] = Eq.fromUniversalEquals implicit val showQueryString: Show[QueryString] = Show.fromToString implicit val orderQueryString: Order[QueryString] = Order.by(_.params) + + object unordered { + implicit val eqQueryString: Eq[QueryString] = + (x: QueryString, y: QueryString) => x.equalsUnordered(y) + } } diff --git a/shared/src/main/scala/io/lemonlabs/uri/Uri.scala b/shared/src/main/scala/io/lemonlabs/uri/Uri.scala index 50ed2d69..5b5bb4e1 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/Uri.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/Uri.scala @@ -22,6 +22,7 @@ import io.lemonlabs.uri.typesafe.PathPart.ops._ import io.lemonlabs.uri.typesafe.TraversablePathParts.ops._ import io.lemonlabs.uri.typesafe.Fragment.ops._ +import java.util import scala.util.Try /** Represents a URI. See [[https://www.ietf.org/rfc/rfc3986 RFC 3986]] @@ -71,6 +72,10 @@ sealed trait Uri extends Product with Serializable { def toJavaURI: java.net.URI = new java.net.URI(toString(config)) + /** Similar to `==` but ignores the ordering of any query string parameters + */ + def equalsUnordered(other: Uri): Boolean + /** Returns the path with no encoders taking place (e.g. non ASCII characters will not be percent encoded) * @return String containing the raw path for this Uri */ @@ -102,6 +107,11 @@ object Uri { implicit val eqUri: Eq[Uri] = Eq.fromUniversalEquals implicit val showUri: Show[Uri] = Show.fromToString implicit val orderUri: Order[Uri] = Order.by(_.toString()) + + object unordered { + implicit val eqUri: Eq[Uri] = + (x: Uri, y: Uri) => x.equalsUnordered(y) + } } /** Represents a URL, which will be one of these forms: @@ -476,6 +486,15 @@ sealed trait Url extends Uri { def toRedactedString(redactor: Redactor)(implicit conf: UriConfig = UriConfig.default): String = redactor.apply(this).toString(conf) + + /** Similar to `==` but ignores the ordering of any query string parameters + */ + def equalsUnordered(other: Uri): Boolean = other match { + case otherUrl: Url => + this.removeQueryString() == otherUrl.removeQueryString() && query.equalsUnordered(otherUrl.query) + case _ => + false + } } object Url { @@ -519,6 +538,11 @@ object Url { implicit val eqUrl: Eq[Url] = Eq.fromUniversalEquals implicit val showUrl: Show[Url] = Show.fromToString implicit val orderUrl: Order[Url] = Order.by(_.toString()) + + object unordered { + implicit val eqUrl: Eq[Url] = + (x: Url, y: Url) => x.equalsUnordered(y) + } } /** Represents Relative URLs which do not contain an authority. Examples include: @@ -602,6 +626,11 @@ object RelativeUrl { implicit val orderRelUrl: Order[RelativeUrl] = Order.by { url => (url.path, url.query, url.fragment) } + + object unordered { + implicit val eqRelUrl: Eq[RelativeUrl] = + (x: RelativeUrl, y: RelativeUrl) => x.equalsUnordered(y) + } } /** Represents absolute URLs with an authority (i.e. URLs with a host), examples include: @@ -768,6 +797,11 @@ object UrlWithAuthority { implicit val eqUrlWithAuthority: Eq[UrlWithAuthority] = Eq.fromUniversalEquals implicit val showUrlWithAuthority: Show[UrlWithAuthority] = Show.fromToString implicit val orderUrlWithAuthority: Order[UrlWithAuthority] = Order.by(_.toString()) + + object unordered { + implicit val eqUrlWithAuthority: Eq[UrlWithAuthority] = + (x: UrlWithAuthority, y: UrlWithAuthority) => x.equalsUnordered(y) + } } /** Represents protocol relative URLs, for example: `//example.com` @@ -827,6 +861,11 @@ object ProtocolRelativeUrl { implicit val orderProtocolRelUrl: Order[ProtocolRelativeUrl] = Order.by { url => (url.authority, url.path, url.query, url.fragment) } + + object unordered { + implicit val eqProtocolRelUrl: Eq[ProtocolRelativeUrl] = + (x: ProtocolRelativeUrl, y: ProtocolRelativeUrl) => x.equalsUnordered(y) + } } /** Represents absolute URLs, for example: `http://example.com` @@ -887,6 +926,11 @@ object AbsoluteUrl { implicit val orderAbsUrl: Order[AbsoluteUrl] = Order.by { url => (url.scheme, url.authority, url.path, url.query, url.fragment) } + + object unordered { + implicit val eqAbsUrl: Eq[AbsoluteUrl] = + (x: AbsoluteUrl, y: AbsoluteUrl) => x.equalsUnordered(y) + } } /** Represents URLs that do not have an authority, for example: @@ -940,6 +984,11 @@ object UrlWithoutAuthority { implicit val orderUrlWithoutAuthority: Order[UrlWithoutAuthority] = Order.by { url => (url.scheme, url.path, url.query, url.fragment) } + + object unordered { + implicit val eqUrlWithoutAuthority: Eq[UrlWithoutAuthority] = + (x: UrlWithoutAuthority, y: UrlWithoutAuthority) => x.equalsUnordered(y) + } } /** Represents URLs that do not have an authority, for example: `mailto:example@example.com` @@ -1009,6 +1058,11 @@ object SimpleUrlWithoutAuthority { implicit val orderSimpleUrlWithoutAuthority: Order[SimpleUrlWithoutAuthority] = Order.by { url => (url.scheme, url.path, url.query, url.fragment) } + + object unordered { + implicit val eqSimpleUrlWithoutAuthority: Eq[SimpleUrlWithoutAuthority] = + (x: SimpleUrlWithoutAuthority, y: SimpleUrlWithoutAuthority) => x.equalsUnordered(y) + } } /** Represents URLs with the data scheme, for example: `data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678` @@ -1086,6 +1140,23 @@ final case class DataUrl(mediaType: MediaType, base64: Boolean, data: Array[Byte private[uri] def toString(c: UriConfig): String = scheme + ":" + pathString(c) + + override def equals(obj: Any): Boolean = obj match { + case other: DataUrl => + other.canEqual(this) && + mediaType == other.mediaType && + base64 == other.base64 && + util.Arrays.equals(data, other.data) + case _ => false + } + + override def hashCode(): Int = + 41 * (41 * (41 + mediaType.hashCode()) + base64.hashCode()) + util.Arrays.hashCode(data) + + /** For DataUrls this method is exactly the same as `==` + */ + override def equalsUnordered(other: Uri): Boolean = + this == other } object DataUrl { @@ -1169,6 +1240,11 @@ final case class ScpLikeUrl(override val user: Option[String], override val host // Don't do percent encoding. Can't find any reference to it being user.fold("")(_ + "@") + hostToString(host) + ":" + path.toString(config.withNoEncoding) } + + /** For ScpLikeUrls this method is exactly the same as `==` + */ + override def equalsUnordered(other: Uri): Boolean = + this == other } object ScpLikeUrl { @@ -1217,6 +1293,11 @@ final case class Urn(path: UrnPath)(implicit val config: UriConfig = UriConfig.d private[uri] def toString(c: UriConfig): String = scheme + ":" + path.toString(c) + + /** For URNs this method is exactly the same as `==` + */ + def equalsUnordered(other: Uri): Boolean = + this == other } object Urn { diff --git a/shared/src/test/scala/io/lemonlabs/uri/CatsTests.scala b/shared/src/test/scala/io/lemonlabs/uri/CatsTests.scala index d1c2ccea..29855770 100644 --- a/shared/src/test/scala/io/lemonlabs/uri/CatsTests.scala +++ b/shared/src/test/scala/io/lemonlabs/uri/CatsTests.scala @@ -17,6 +17,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(true) } + it should "be supported for unordered query params Uri" in new CatsTestCase { + import Uri.unordered._ + + val uri = Uri.parse("https://typelevel.org/cats/?a=1&b=two") + val uri2: Uri = AbsoluteUrl.parse("https://typelevel.org/cats/?b=two&a=1") + + (uri === uri2) should equal(true) + } + it should "be supported for Url" in new CatsTestCase { val uri = Url.parse("https://typelevel.org/cats/") val uri2: Url = AbsoluteUrl.parse("https://typelevel.org/cats/") @@ -24,6 +33,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(true) } + it should "be supported unordered query params Url" in new CatsTestCase { + import Url.unordered._ + + val uri = Url.parse("https://typelevel.org/cats/?a=1&b=two") + val uri2: Url = AbsoluteUrl.parse("https://typelevel.org/cats/?b=two&a=1") + + (uri === uri2) should equal(true) + } + it should "be supported for RelativeUrl" in new CatsTestCase { val uri: Url = RelativeUrl.parse("/cats2/") val uri2: Url = RelativeUrl.parse("/cats/") @@ -31,6 +49,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(false) } + it should "be supported for unordered query params RelativeUrl" in new CatsTestCase { + import RelativeUrl.unordered._ + + val uri = RelativeUrl.parse("/cats2/?b=two&a=1") + val uri2 = RelativeUrl.parse("/cats/?a=1&b=two") + + (uri === uri2) should equal(false) + } + it should "be supported for UrlWithAuthority" in new CatsTestCase { val uri = UrlWithAuthority.parse("https://typelevel.org/cats/") val uri2: UrlWithAuthority = AbsoluteUrl.parse("https://typelevel.org/cats/") @@ -38,6 +65,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(true) } + it should "be supported for unordered query params UrlWithAuthority" in new CatsTestCase { + import UrlWithAuthority.unordered._ + + val uri = UrlWithAuthority.parse("https://typelevel.org/cats/?a=1&b=two") + val uri2: UrlWithAuthority = AbsoluteUrl.parse("https://typelevel.org/cats/?b=two&a=1") + + (uri === uri2) should equal(true) + } + it should "be supported for ProtocolRelativeUrl" in new CatsTestCase { val uri = ProtocolRelativeUrl.parse("//typelevel.org/cats/") val uri2 = ProtocolRelativeUrl.parse("//typelevel.org/cats/?different=true") @@ -45,6 +81,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri =!= uri2) should equal(true) } + it should "be supported for unordered query params ProtocolRelativeUrl" in new CatsTestCase { + import ProtocolRelativeUrl.unordered._ + + val uri = ProtocolRelativeUrl.parse("//typelevel.org/cats/?b=two&a=1") + val uri2 = ProtocolRelativeUrl.parse("//typelevel.org/cats/?a=1&b=two") + + (uri =!= uri2) should equal(false) + } + it should "be supported for AbsoluteUrl" in new CatsTestCase { val uri = AbsoluteUrl.parse("https://typelevel.org/cats/") val uri2 = AbsoluteUrl.parse("https://typelevel.org/cats/") @@ -52,6 +97,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(true) } + it should "be supported for unordered query params AbsoluteUrl" in new CatsTestCase { + import AbsoluteUrl.unordered._ + + val uri = AbsoluteUrl.parse("https://typelevel.org/cats/?a=1&b=two") + val uri2 = AbsoluteUrl.parse("https://typelevel.org/cats/?b=two&a=1") + + (uri === uri2) should equal(true) + } + it should "be supported for UrlWithoutAuthority" in new CatsTestCase { val uri = UrlWithoutAuthority.parse("mailto:someone@somewhere.com") val uri2 = UrlWithoutAuthority.parse("mailto:someoneelse@somewhereelse.com") @@ -59,6 +113,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(false) } + it should "be supported for unordered query params UrlWithoutAuthority" in new CatsTestCase { + import UrlWithoutAuthority.unordered._ + + val uri = UrlWithoutAuthority.parse("mailto:someone@somewhere.com?b=two&a=1") + val uri2 = UrlWithoutAuthority.parse("mailto:someoneelse@somewhereelse.com?a=1&b=two") + + (uri === uri2) should equal(false) + } + it should "be supported for SimpleUrlWithoutAuthority" in new CatsTestCase { val uri = SimpleUrlWithoutAuthority.parse("mailto:someone@somewhere.com") val uri2 = SimpleUrlWithoutAuthority.parse("mailto:someone@somewhere.com") @@ -66,6 +129,15 @@ class CatsTests extends AnyFlatSpec with Matchers { (uri === uri2) should equal(true) } + it should "be supported for unordered query params SimpleUrlWithoutAuthority" in new CatsTestCase { + import SimpleUrlWithoutAuthority.unordered._ + + val uri = SimpleUrlWithoutAuthority.parse("mailto:someone@somewhere.com?a=1&b=two") + val uri2 = SimpleUrlWithoutAuthority.parse("mailto:someone@somewhere.com?b=two&a=1") + + (uri === uri2) should equal(true) + } + it should "be supported for DataUrl" in new CatsTestCase { val uri = DataUrl.parse("data:,A%20brief%20note") val uri2 = DataUrl.parse("data:,Another%20brief%20note") @@ -185,6 +257,21 @@ class CatsTests extends AnyFlatSpec with Matchers { (qs === qs2) should equal(true) } + it should "be supported for unordered QueryString" in new CatsTestCase { + val qs = QueryString.parse("a=1&b=2") + val qs2 = QueryString.parse("b=2&a=1") + + import QueryString.unordered._ + (qs === qs2) should equal(true) + } + + it should "be supported for ordered QueryString" in new CatsTestCase { + val qs = QueryString.parse("a=1&b=2") + val qs2 = QueryString.parse("b=2&a=1") + + (qs === qs2) should equal(false) + } + "Show" should "be supported for Uri" in { val uri = Uri.parse("https://typelevel.org/cats/") uri.show should equal("https://typelevel.org/cats/") diff --git a/shared/src/test/scala/io/lemonlabs/uri/EqualityTests.scala b/shared/src/test/scala/io/lemonlabs/uri/EqualityTests.scala new file mode 100644 index 00000000..f91b2fa6 --- /dev/null +++ b/shared/src/test/scala/io/lemonlabs/uri/EqualityTests.scala @@ -0,0 +1,121 @@ +package io.lemonlabs.uri + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks + +class EqualityTests extends AnyFlatSpec with Matchers with ScalaCheckDrivenPropertyChecks { + "QueryString.equalsUnordered" should "require the same number of identical parameters" in { + val qsOne = QueryString.parse("a=1&a=1&a=1&a=1") + val qsTwo = QueryString.parse("a=1&a=1&a=1") + qsOne.equalsUnordered(qsTwo) should equal(false) + } + + it should "require the same number of value-less parameters" in { + val qsOne = QueryString.parse("a=1&a") + val qsTwo = QueryString.parse("a=1") + qsOne.equalsUnordered(qsTwo) should equal(false) + } + + it should "require the same number of empty parameters" in { + val qsOne = QueryString.parse("a=1&a=") + val qsTwo = QueryString.parse("a=1") + qsOne.equalsUnordered(qsTwo) should equal(false) + } + + it should "match when in the same order" in { + val qsOne = QueryString.parse("a=1&b=2&b=2") + val qsTwo = QueryString.parse("a=1&b=2&b=2") + qsOne.equalsUnordered(qsTwo) should equal(true) + } + + it should "match when in a different order" in { + val qsOne = QueryString.parse("a=1&b=2&b=2") + val qsTwo = QueryString.parse("b=2&a=1&b=2") + qsOne.equalsUnordered(qsTwo) should equal(true) + } + + "Uri.equalsUnordered" should "require the same number of identical parameters" in { + val urlOne = Url.parse("http://example.com?a=1&a=1&a=1&a=1") + val urlTwo = Url.parse("http://example.com?a=1&a=1&a=1") + urlOne.equalsUnordered(urlTwo) should equal(false) + } + + it should "require the same number of value-less parameters" in { + val urlOne = Url.parse("http://example.com?a=1&a") + val urlTwo = Url.parse("http://example.com?a=1") + urlOne.equalsUnordered(urlTwo) should equal(false) + } + + it should "require the same number of empty parameters" in { + val urlOne = Url.parse("http://example.com?a=1&a=") + val urlTwo = Url.parse("http://example.com?a=1") + urlOne.equalsUnordered(urlTwo) should equal(false) + } + + it should "match when in the same order" in { + val urlOne = Url.parse("http://example.com?a=1&b=2&b=2") + val urlTwo = Url.parse("http://example.com?a=1&b=2&b=2") + urlOne.equalsUnordered(urlTwo) should equal(true) + } + + it should "match when in a different order" in { + val urlOne = Url.parse("http://example.com?a=1&b=2&b=2") + val urlTwo = Url.parse("http://example.com?b=2&a=1&b=2") + urlOne.equalsUnordered(urlTwo) should equal(true) + } + + it should "not match URLs with URNs" in { + val urlOne = Url.parse("http://example.com") + val urnTwo = Urn.parse("urn:example:com") + urlOne.equalsUnordered(urnTwo) should equal(false) + } + + it should "not match different types of URL" in { + val urlOne = Url.parse("http://example.com?a=1&b=2") + val urlTwo = Url.parse("//example.com?a=1&b=2") + urlOne.equalsUnordered(urlTwo) should equal(false) + } + + it should "be the same as == for DataUrls" in { + val urlOne = DataUrl.parse("data:,A%20brief%20note") + val urlTwo = Url.parse("data:,A%20brief%20note") + urlOne.equalsUnordered(urlTwo) should equal(true) + } + + it should "be the same as == for ScpUrls" in { + val urlOne = ScpLikeUrl.parse("root@host:/root/file.tar.gz") + val urlTwo = ScpLikeUrl.parse("root@host:/root/file.tar.gz") + urlOne.equalsUnordered(urlTwo) should equal(true) + } + + it should "be the same as == for Urns" in { + val urnOne = Urn.parse("urn:example:com") + val urnTwo = Urn.parse("urn:example:com") + urnOne.equalsUnordered(urnTwo) should equal(true) + } + + "DataUrl.equals" should "equal itself" in new UriScalaCheckGenerators { + forAll { dataUrl: DataUrl => + (dataUrl == dataUrl) should equal(true) + } + } + + it should "not equal another DataUrl" in new UriScalaCheckGenerators { + forAll { (dataUrl: DataUrl, dataUrl2: DataUrl) => + (dataUrl == dataUrl2) should equal(false) + } + } + + "DataUrl.hashCode" should "equal itself" in new UriScalaCheckGenerators { + forAll { dataUrl: DataUrl => + dataUrl.hashCode() should equal(dataUrl.hashCode()) + } + } + + it should "not equal another DataUrl" in new UriScalaCheckGenerators { + forAll { (dataUrl: DataUrl, dataUrl2: DataUrl) => + dataUrl.hashCode() should not equal dataUrl2.hashCode() + } + } +} From 32995ce3be10fb5efc5b82d9759810c89d4a8322 Mon Sep 17 00:00:00 2001 From: Ian Forsey Date: Tue, 16 Mar 2021 23:53:14 +0000 Subject: [PATCH 02/17] Update public suffixes --- .../main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala index a65a2c72..d87c8d87 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala @@ -83,6 +83,8 @@ object PublicSuffixes { "owo.codes", "platformsh.site", "dweb.link", + "pythonanywhere.com", + "eu.pythonanywhere.com", "sys.qcx.io", "quipelements.com", "on-k3s.io", @@ -7543,6 +7545,7 @@ object PublicSuffixes { "blackbaudcdn.net", "of.je", "boomla.net", + "boutir.com", "boxfuse.io", "square7.ch", "bplaced.com", @@ -7554,6 +7557,7 @@ object PublicSuffixes { "uk0.bigv.io", "dh.bytemark.co.uk", "vm.bytemark.co.uk", + "cafjs.com", "mycd.eu", "carrd.co", "crd.co", @@ -8923,6 +8927,7 @@ object PublicSuffixes { "spdns.org", "seidat.net", "senseering.net", + "magnet.page", "biz.ua", "co.ua", "pp.ua", From 9f64f5ee1f40d186c4fe0f3275608a1d802428a6 Mon Sep 17 00:00:00 2001 From: Ian Forsey Date: Tue, 16 Mar 2021 23:53:24 +0000 Subject: [PATCH 03/17] Bump version to 3.2.0 --- .travis.yml | 2 +- README.md | 8 ++++---- version.sbt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6166d6e9..c3672b0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ script: > cd target && git clone https://github.com/lemonlabsuk/scala-uri-demo.git && cd scala-uri-demo && - sbt -Dscala.ver=$TRAVIS_SCALA_VERSION -Dscala.uri.ver=3.1.0 test && + sbt -Dscala.ver=$TRAVIS_SCALA_VERSION -Dscala.uri.ver=3.2.0 test && cd "$TRAVIS_BUILD_DIR" jdk: diff --git a/README.md b/README.md index d42e1e98..4c7b2a0c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ To include it in your SBT project from maven central: ```scala -"io.lemonlabs" %% "scala-uri" % "3.1.0" +"io.lemonlabs" %% "scala-uri" % "3.2.0" ``` ## Migration Guides @@ -900,13 +900,13 @@ The type class instances exist in the companion objects for these types. * For `2.11.x` support use `scala-uri` `1.4.10` from branch [`1.4.x`](https://github.com/lemonlabsuk/scala-uri/tree/1.4.x) * For `2.10.x` support use `scala-uri` `0.4.17` from branch [`0.4.x`](https://github.com/lemonlabsuk/scala-uri/tree/0.4.x) * For `2.9.x` support use `scala-uri` `0.3.6` from branch [`0.3.x`](https://github.com/lemonlabsuk/scala-uri/tree/0.3.x) - * For Scala.js `1.x.x` support, use `scala-uri` `3.1.0` + * For Scala.js `1.x.x` support, use `scala-uri` `3.2.0` * For Scala.js `0.6.x` support, use `scala-uri` `2.2.3` Release builds are available in maven central. For SBT users just add the following dependency: ```scala -"io.lemonlabs" %% "scala-uri" % "3.1.0" +"io.lemonlabs" %% "scala-uri" % "3.2.0" ``` For maven users you should use (for 2.13.x): @@ -915,7 +915,7 @@ For maven users you should use (for 2.13.x): io.lemonlabs scala-uri_2.13 - 3.1.0 + 3.2.0 ``` diff --git a/version.sbt b/version.sbt index 3271bb8f..c83229bb 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "3.1.0" +version in ThisBuild := "3.2.0" From 3eac9402e8197a1e23a62ad19a314c4d5c24d548 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 30 Mar 2021 21:46:10 +0200 Subject: [PATCH 04/17] Update sbt-scalafix to 0.9.27 (#284) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 99cfa218..4c5ad33d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -12,4 +12,4 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2") addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.18") -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.26") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.27") From ccd0a832e0779150dbf9503191222e0e41174c81 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 30 Mar 2021 21:46:25 +0200 Subject: [PATCH 05/17] Update cats-core, cats-laws to 2.5.0 (#286) --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c1502717..ac6e6a86 100644 --- a/build.sbt +++ b/build.sbt @@ -25,7 +25,7 @@ val sharedSettings = Seq( "org.scalatest" %%% "scalatest" % "3.2.6" % Test, "org.scalatestplus" %%% "scalacheck-1-14" % "3.2.2.0" % Test, "org.scalacheck" %%% "scalacheck" % "1.15.3" % Test, - "org.typelevel" %%% "cats-laws" % "2.4.2" % Test + "org.typelevel" %%% "cats-laws" % "2.5.0" % Test ), scalacOptions := Seq( "-unchecked", @@ -63,7 +63,7 @@ val scalaUriSettings = Seq( libraryDependencies ++= Seq( "org.parboiled" %%% "parboiled" % "2.2.1", "com.chuusai" %%% "shapeless" % "2.3.3", - "org.typelevel" %%% "cats-core" % "2.4.2" + "org.typelevel" %%% "cats-core" % "2.5.0" ), pomPostProcess := { node => new RuleTransformer(new RewriteRule { From 220733a9ad8db1a3f9d1ea8762f17a4644842390 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 30 Mar 2021 22:50:53 +0200 Subject: [PATCH 06/17] Update sbt to 1.4.9 (#281) --- .travis.yml | 12 ++++++++++++ project/build.properties | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c3672b0a..5a2ba53e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,17 @@ --- language: scala + +install: + - | + # From: https://eed3si9n.com/sbt-1.4.9 + # Remove this when https://github.com/travis-ci/travis-build/pull/1980 is fixed + export SBT_LAUNCHER=1.4.9 + export SBT_OPTS="-Dfile.encoding=UTF-8" + curl -L --silent "https://github.com/sbt/sbt/releases/download/v$SBT_LAUNCHER/sbt-$SBT_LAUNCHER.tgz" > $HOME/sbt.tgz + tar zxf $HOME/sbt.tgz -C $HOME + sudo rm /usr/local/bin/sbt + sudo ln -s $HOME/sbt/bin/sbt /usr/local/bin/sbt + script: > sbt $COVERAGE ++$TRAVIS_SCALA_VERSION check mdoc coverage $PROJECT/test coverageReport && cd target && diff --git a/project/build.properties b/project/build.properties index 0b2e09c5..dbae93bc 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.7 +sbt.version=1.4.9 From 18a99f02b84d8aa27f629ab11a3f40ff0e35fc64 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Thu, 8 Apr 2021 10:53:47 +0200 Subject: [PATCH 07/17] Update sbt to 1.5.0 (#289) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index dbae93bc..e67343ae 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.9 +sbt.version=1.5.0 From 955c3c4163804d73e1e941e501f4d4b9e5ccd800 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Thu, 8 Apr 2021 10:54:00 +0200 Subject: [PATCH 08/17] Update scalatest to 3.2.7 (#288) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index ac6e6a86..46edb5a2 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ val sharedSettings = Seq( organization := "io.lemonlabs", libraryDependencies ++= Seq( "org.typelevel" %%% "simulacrum-scalafix-annotations" % "0.5.4", - "org.scalatest" %%% "scalatest" % "3.2.6" % Test, + "org.scalatest" %%% "scalatest" % "3.2.7" % Test, "org.scalatestplus" %%% "scalacheck-1-14" % "3.2.2.0" % Test, "org.scalacheck" %%% "scalacheck" % "1.15.3" % Test, "org.typelevel" %%% "cats-laws" % "2.5.0" % Test From 642c32dff07e31d1f0399802347cc60856b602b1 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Fri, 9 Apr 2021 10:01:04 +0200 Subject: [PATCH 09/17] Update sbt-scalajs, scalajs-compiler, ... to 1.5.1 (#287) Co-authored-by: Ian Forsey --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4c5ad33d..feb6916d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") From 7906cfc3b170579828720adcf220dc56f1bf8125 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:30:22 +0200 Subject: [PATCH 10/17] Update shapeless to 2.3.4 (#290) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 46edb5a2..9c726b24 100644 --- a/build.sbt +++ b/build.sbt @@ -62,7 +62,7 @@ val scalaUriSettings = Seq( description := "Simple scala library for building and parsing URIs", libraryDependencies ++= Seq( "org.parboiled" %%% "parboiled" % "2.2.1", - "com.chuusai" %%% "shapeless" % "2.3.3", + "com.chuusai" %%% "shapeless" % "2.3.4", "org.typelevel" %%% "cats-core" % "2.5.0" ), pomPostProcess := { node => From 6fd1c80859ae276dcce32701893b0e48e0918e0a Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 20 Apr 2021 10:00:57 +0200 Subject: [PATCH 11/17] Update cats-core, cats-laws to 2.6.0 (#292) --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 9c726b24..00e81625 100644 --- a/build.sbt +++ b/build.sbt @@ -25,7 +25,7 @@ val sharedSettings = Seq( "org.scalatest" %%% "scalatest" % "3.2.7" % Test, "org.scalatestplus" %%% "scalacheck-1-14" % "3.2.2.0" % Test, "org.scalacheck" %%% "scalacheck" % "1.15.3" % Test, - "org.typelevel" %%% "cats-laws" % "2.5.0" % Test + "org.typelevel" %%% "cats-laws" % "2.6.0" % Test ), scalacOptions := Seq( "-unchecked", @@ -63,7 +63,7 @@ val scalaUriSettings = Seq( libraryDependencies ++= Seq( "org.parboiled" %%% "parboiled" % "2.2.1", "com.chuusai" %%% "shapeless" % "2.3.4", - "org.typelevel" %%% "cats-core" % "2.5.0" + "org.typelevel" %%% "cats-core" % "2.6.0" ), pomPostProcess := { node => new RuleTransformer(new RewriteRule { From fa24c846734b02fc3225c34acc526855bd1a2f4a Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Fri, 23 Apr 2021 10:23:14 +0200 Subject: [PATCH 12/17] Update scalatest to 3.2.8 (#294) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 00e81625..657862dc 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ val sharedSettings = Seq( organization := "io.lemonlabs", libraryDependencies ++= Seq( "org.typelevel" %%% "simulacrum-scalafix-annotations" % "0.5.4", - "org.scalatest" %%% "scalatest" % "3.2.7" % Test, + "org.scalatest" %%% "scalatest" % "3.2.8" % Test, "org.scalatestplus" %%% "scalacheck-1-14" % "3.2.2.0" % Test, "org.scalacheck" %%% "scalacheck" % "1.15.3" % Test, "org.typelevel" %%% "cats-laws" % "2.6.0" % Test From 9fcefc956dd0cca99948dc836fe022c6f49b160e Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 27 Apr 2021 10:13:59 +0200 Subject: [PATCH 13/17] Update sbt to 1.5.1 (#296) --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e67343ae..f0be67b9 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.0 +sbt.version=1.5.1 From e7366a98387491d60ccc570902599b33cfc1a1ef Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Tue, 27 Apr 2021 10:14:13 +0200 Subject: [PATCH 14/17] Update parboiled to 2.3.0 (#295) --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 657862dc..d5ac2f49 100644 --- a/build.sbt +++ b/build.sbt @@ -61,7 +61,7 @@ val scalaUriSettings = Seq( name := "scala-uri", description := "Simple scala library for building and parsing URIs", libraryDependencies ++= Seq( - "org.parboiled" %%% "parboiled" % "2.2.1", + "org.parboiled" %%% "parboiled" % "2.3.0", "com.chuusai" %%% "shapeless" % "2.3.4", "org.typelevel" %%% "cats-core" % "2.6.0" ), From b2db659293cbef804a949006d24a7aa021ebd72b Mon Sep 17 00:00:00 2001 From: Ian Forsey Date: Tue, 27 Apr 2021 09:15:36 +0100 Subject: [PATCH 15/17] Cats friendly badge broken link again. Going to remove for now --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 4c7b2a0c..e80d2859 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/lemonlabsuk/scala-uri.svg)](http://isitmaintained.com/project/lemonlabsuk/scala-uri "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/lemonlabsuk/scala-uri.svg)](http://isitmaintained.com/project/lemonlabsuk/scala-uri "Percentage of issues still open") -[![Cats Friendly Badge](https://typelevel.org/cats/img/cats-badge-tiny.png)](#cats-support) - `scala-uri` is a small Scala library that helps you work with URIs. It has the following features: * A [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt) compliant [parser](#parsing) to parse URLs and URNs from Strings From 9113f57601a737810f47eb663be2196b68fd46b5 Mon Sep 17 00:00:00 2001 From: Ian Forsey Date: Tue, 27 Apr 2021 09:34:54 +0100 Subject: [PATCH 16/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e80d2859..866d6dcd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # scala-uri -[![Build Status](https://travis-ci.org/lemonlabsuk/scala-uri.svg?branch=master)](https://travis-ci.org/lemonlabsuk/scala-uri) +[![Build Status](https://api.travis-ci.org/lemonlabsuk/scala-uri.svg?branch=master)](https://travis-ci.org/lemonlabsuk/scala-uri) [![codecov.io](http://codecov.io/github/lemonlabsuk/scala-uri/coverage.svg?branch=master)](https://codecov.io/gh/lemonlabsuk/scala-uri/branch/master) [![Slack](https://lemonlabs.io/slack/badge.svg)](https://lemonlabs.io/slack) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.lemonlabs/scala-uri_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.lemonlabs/scala-uri_2.12) From b42a08f0eebaf211481e06289fa5658635083c59 Mon Sep 17 00:00:00 2001 From: Scala Steward <43047562+scala-steward@users.noreply.github.com> Date: Thu, 29 Apr 2021 10:05:13 +0200 Subject: [PATCH 17/17] Update sbt-scoverage to 1.7.0 (#297) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index feb6916d..5cdaaccf 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.7.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")