http4s / http4s

A minimal, idiomatic Scala interface for HTTP

Home Page:https://http4s.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Uri.setQueryParams() doesn't seem to overwrite existing params

invadergir opened this issue · comments

Hi, I was attempting to use Uri.setQueryParams() and noticed it doesn't behave according to the comments:

  /** Creates maybe a new `Self` with the specified parameters. The entire
    * [[Query]] will be replaced with the given one.
    */

This munit test passes on 0.23.19-RC3 and 0.23.4 (in a test project that imports http4s), and I would expect it to fail:

  test("setQueryParams() will overwrite any existing query parameters if they exist") {
    val uri = Uri.unsafeFromString("http://example.com/a/b/c?m=5&n=6")
    val origQP = Map("m" -> Seq("5"), "n" -> Seq("6"))
    assertEquals(uri.query.multiParams, origQP)

    val uriSetQP = uri.setQueryParams(Map("X" -> Seq("10")))
    val reallyExpected = Map("X" -> Seq("10")) // we only expect the new QPs to be set, the others lost
    val expected = origQP ++ reallyExpected  // this is what we actually get, see below:
    assertEquals(uriSetQP.query.multiParams, expected )
}

Thanks! I guess the bug is here?

val vec = params.foldLeft(query.toVector) {

My humble opinion is that we better need to fix the scaladoc and add a new method rather than change the behaviour of that function.

Yeah, I wouldn't disagree. It looks like it's been working this way since v0.20.0. dd7e192

Just fyi: there's another method that modifies query parameters in Uri: withQueryParams:

scala> val uri = uri"https://typelevel.org?foo=123&bar=456"
val uri: org.http4s.Uri = https://typelevel.org?foo=123&bar=456

scala> uri.setQueryParams(Map("bar" -> Seq("789"), "car" -> Seq("abc")))
val res2: org.http4s.Uri = https://typelevel.org?foo=123&bar=456&bar=789&car=abc

scala> uri.withQueryParams(Map("bar" -> "789", "car" -> "abc"))
val res4: org.http4s.Uri = https://typelevel.org?foo=123&bar=789&car=abc

So neither of those two methods does replace query parameters completely.
It seems that the only way to get them replaced is uri.copy(query = ...), which may not be that bad but...

Now compare to a Request's header methods:

  • withHeadersreplaces all the headers in a request.
  • putHeaders – actually, just adds given headers to existing ones.
  • addHeader – does the same thing as putHeaders but just for a single header.

So in my opinion, current naming conventions in the library are quite confusing and inconsistent.

  • withSomething – may or may not replace everything;
  • setSomething – despite its name, may append stuff instead of replacing it;
  • putSomething, addSomething – well, not always obvious from their names too.

Moreover, although the withSomething version is quite ubiquitous, but it may have different behavior. Whereas other names like set, put, add, etc may or may not exist in every particular model.

Perhaps, it would be nice if the naming consistency could be addressed in 1.x version at least, wdyt?