Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #2321: Accept multiple query parameter values #2472

Conversation

eshu
Copy link

@eshu eshu commented Oct 7, 2023

/claim #2321

Also please check this PR: it could be a good addition to current one: eshu#1

@CLAassistant
Copy link

CLAassistant commented Oct 7, 2023

CLA assistant check
All committers have signed the CLA.

@eshu eshu changed the title Issue 2321: MonoQuery and MultiQuery Issue #2321: MonoQuery and MultiQuery Oct 7, 2023
@codecov-commenter
Copy link

codecov-commenter commented Oct 7, 2023

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Comparison is base (f2ce5cf) 64.35% compared to head (286f6de) 64.62%.

Files Patch % Lines
...src/main/scala/zio/http/codec/TextChunkCodec.scala 90.62% 3 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2472      +/-   ##
==========================================
+ Coverage   64.35%   64.62%   +0.27%     
==========================================
  Files         140      141       +1     
  Lines        8326     8359      +33     
  Branches     1517     1495      -22     
==========================================
+ Hits         5358     5402      +44     
+ Misses       2968     2957      -11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch 4 times, most recently from 17a0c11 to ca00c7b Compare October 7, 2023 10:10
@eshu eshu marked this pull request as ready for review October 7, 2023 10:23
@eshu
Copy link
Author

eshu commented Oct 7, 2023

/claim #2321

@eshu eshu closed this Oct 7, 2023
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch from 0c6b0df to 588ab4f Compare October 7, 2023 15:05
@eshu eshu reopened this Oct 7, 2023
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch from 49d58ce to c064188 Compare October 7, 2023 16:31
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch 2 times, most recently from 7d3b435 to 08e7fca Compare October 12, 2023 08:31
@eshu eshu changed the title Issue #2321: MonoQuery and MultiQuery Issue #2321: Accept multiple query parameter values Oct 12, 2023
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch 5 times, most recently from 4c05a2e to f9be11a Compare October 12, 2023 14:11
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch 2 times, most recently from 195f38c to b35eda4 Compare October 12, 2023 14:44
@eshu eshu force-pushed the issue-2321/Support-query-parameters-with-multiple-values-MultiQuery branch from b35eda4 to 75afb82 Compare October 12, 2023 15:26
def paramStr(name: String): QueryCodec[String] =
HttpCodec.Query(name, TextCodec.string)
def queryOpt[I](name: String)(implicit codec: TextCodec[I]): QueryCodec[Option[I]] =
HttpCodec.Query(name, TextChunkCodec.optional(codec))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure, that this is not necessary. We have already HttpCodec#optional that should cover this case.
Please try to replace QueryCodec.queryOpt[X](y) in the tests with QueryCodec.query[X](y).optional.
If it works, I think you can remove this constructor and TextChunkCodec.optional.
If not, please report back why it does not work.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it would be better to solve all cardinality cases in the same class and do not spread it over all code base.

import zio.prelude._
import zio.{Chunk, ChunkBuilder, NonEmptyChunk}

sealed trait TextChunkCodec[A, I] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your idea, of having more detailed errors. But I still think the whole TextChunkCodec is not necessary.
I think you could just fit it in TextCodec. You could add two new methods to TextCodec, def decodeAll(values: Chunk[String]): Chunk[A] and def encodeAll(values: Chunk[A]): Chunk[String].
For Query, add a parameter cardinality: Cardinality

sealed trait Cardinality
object Cardinality {
  case object ExactyOne extends Cardinality
  case object AtLeastOne extends Cardinality
  case object Many extends Cardinality
}

And set this according to the constructors.
Then just use the two new methods in EncoderDecoder and throw an exception if cardinality and the size of the chunk do not match.

This has imho the following benefits:

  • no extra class
  • every query codec is just one object instead of 2
  • still having better error messages, without wrapping each result of decoding a query in DecodeResult

This seems to me better in terms of maintainability and allocations during decoding.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You ask me to drop one class and replace with another one. :)
It won't decrease the number of classes in code and definitely increase complexity because we put more functionality in TextCodec unrelated to its main purpose.

TextCodec is good for encoding/decoding scalar values, this is it's speciality. TextChunkCodec do not have an intersection with it, and have another specialization. I believe it is good to follow to the single responcibility principle and do not pull all functionality in one class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, that my idea still have one new data structure. The difference is, there are only singletons and it is analysable. What you have right now, can't be translated into docs. I can't take a look at an instance of TextChunkCodec and know the cardinality.
Also I don't see any problem with extending TextCodc by these methods, since they can be added to the trait and would be the same for all implementations.
As well I'd argue that it is still single responsibility. The responsibility to transform text to values and values to text.
@vigoo @jdegoes do you have some opinion / other idea(s)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For documentation it is possible to add a method "description" like in TextCodec or add another way of tagging. This is not an issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@987Nabil I added the method cardinality to TextChunkCodec.

I can't agree that TextChunkCodec and TextCodec have the same responsibility. TextCodec solves the scalar transformation. One string - one value. TextChunkCodec solves the issue of cardinality: check amount of values and coerce it to expected cardinality. It uses TextCodec to encode each string scalar to a typed value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I offered my opinion. And I don't feel like this is my field of expertise for zio-http (yet). I'll neither ask you to change nor approve this PR but rather summon my fellow zio-http team members ༼ つ ◕◕ ༽つ

@jdegoes
Copy link
Member

jdegoes commented Jan 6, 2024

@eshu @987Nabil

I really do not like the idea of TextChunkCodec. Rather, I feel like every query parameter may have zero or more values (according to spec), so the base primitive for QueryCodec should in fact generate Chunk[A] rather than A.

Essentially, HttpCodec.Query should be changed from this:

private[http] final case class Query[A](name: String, textCodec: TextCodec[A], index: Int = 0)
      extends Atom[HttpCodecType.Query, A]  {

to this:

private[http] final case class Query[A](name: String, textCodec: TextCodec[A], index: Int = 0)
      extends Atom[HttpCodecType.Query, Chunk[A]]  {

and all types updated. Now, we shouldn't break backward compatibility: and, indeed, most people using query parameters will just be looking for one value, rather than zero to many. So we should expose the same set of QueryCodecs (QueryCodecs.scala) that we do today, but we can expose a more foundational one that provides access to 0 to many query parameter values.

How does this approach sound?

Please re-open if you get a chance to simplify this solution, and I will be happy to review and merge quickly.

@jdegoes jdegoes closed this Jan 6, 2024
@987Nabil
Copy link
Contributor

987Nabil commented Jan 6, 2024

Sounds good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants