Comments (5)
This happens because you are defining custom codecs from String
to your type (Option[List[CarName]]
in your case, but from tapir's point of view this is just a type T
):
implicit val carNameCodec: Codec[String, Option[List[CarName]], TextPlain] =
Codec.string.mapDecode(h => decode[CarName](h, CarName))(encode[CarName](_, _.name))
So we have a String <-> T
conversion, where the base type is required (a String
). If you then create a query[T]
input, tapir it's trying to construct a List[String] <-> T
codec, and to do that, it's using the following chain: List[String] -> get the only element (or fail) -> T
.
I think defining your codec as String <-> List[CarName]
, and then creating the input as an optional one: query[Option[List[CarName]]]
should work.
from tapir.
This seems to work:
package sttp.tapir.examples
import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.http.scaladsl.Http
import org.apache.pekko.http.scaladsl.server.Route
import sttp.tapir.*
import sttp.tapir.CodecFormat.TextPlain
import sttp.tapir.server.pekkohttp.PekkoHttpServerInterpreter
import scala.concurrent.duration.*
import scala.concurrent.{Await, Future}
import scala.io.StdIn
object HelloWorldPekkoServer extends App {
implicit val actorSystem: ActorSystem = ActorSystem()
import actorSystem.dispatcher
case class CarName(name: String)
def decode[T](s: String, fromString: String => T): DecodeResult[List[T]] = DecodeResult.Value(s.split(",", -1).toList.map(fromString))
def encode[T](list: List[T], asString: T => String): String = list.map(asString).mkString(",")
implicit val carNameCodec: Codec[String, List[CarName], TextPlain] =
Codec.string.mapDecode(h => decode[CarName](h, CarName))(encode[CarName](_, _.name))
val helloWorld: PublicEndpoint[Option[List[CarName]], Unit, String, Any] =
endpoint.get.in("hello").in(query[Option[List[CarName]]]("name")).out(stringBody)
val helloWorldRoute: Route =
PekkoHttpServerInterpreter().toRoute(helloWorld.serverLogicSuccess(cars => Future.successful(s"Got: $cars!")))
Await.result(Http().newServerAt("localhost", 8080).bindFlow(helloWorldRoute), 1.minute)
StdIn.readLine()
}
Test:
[~]% curl "http://localhost:8080/hello?name=x"
Got: Some(List(CarName(x)))!%
[~]% curl "http://localhost:8080/hello?name=x,y"
Got: Some(List(CarName(x), CarName(y)))!%
[~]% curl "http://localhost:8080/hello"
Got: None!%
from tapir.
First - thank you for such a fast answer.
Second - works like a charm.
Third - I am aware of type erasure in JVM, but not understand how removing Option from a codec helps to make a parameter optional. It's counterintuitive. Still, I can live with that as long as it works.
Best regards,
PK
from tapir.
This doesn't reach type erasure, everything is done at compiler level (where we have full type info). There are no run-time checks if something is an instance of an option:
For a query[T]
input we need a Codec[List[String], T]
(the parameter might appear multiple times in the url). So the compiler tries to find the appropriate instance in the implicit scope.
In your case, if you have query[Option[List[CarName]]]
, the compiler will try to find Codec[List[String], Option[List[CarName]]]
. So it tries to look that up - it will try several available options, among them this one, which is a rule on how to construct a Codec[List[T], Option[U]]
given a Codec[T, U]
. Substituting our types for T
and U
, we reduce the problem to looking up Codec[String, List[CarName]]
- which you provide in your own code base. Problem solved!
Now in your original example, you provided a Codec[String, Option[List[CarName]]
. So this can only be used, if we have the String
to decode (tapir can't, and doesn't inspect the exact shape of the right-hand side - it can be whatever; here it's an option, but that's completely opaque to tapir). While constructing the Codec[List[String], Option[List[CarName]]]
required for the query paramter, another rule was used, to get the single element from the list, and if it's not there - report a missing element.
from tapir.
Thank you very much for such a detailed and clear response. Now I understand the Option should be only in endpoint input declaration (and why input is List not single String)!
from tapir.
Related Issues (20)
- Enchancement of the Endpoint DSL to support multiple path variables or combination of path variable and literal within single path segment
- AsyncApi schema - broken rendering of examples
- AsyncApi schema - use of `.withDiscriminator` for `Schema` renders illegal async api spec HOT 1
- Add `const` attribute to Schema HOT 10
- Add a flag into `TSchemaToASchema` to distinguish between `mappings` or `consts` for discriminators. HOT 4
- Can we add `SchemaType.Intersection` for `SProduct`s? HOT 1
- Allow for third-party routing logic to tap into the Tapir backends HOT 3
- [BUG] Security path inputs always goes before regular input segments HOT 3
- [BUG] Netty servers shouldn't reply to WS Close frame immediately
- io.netty.handler.timeout.ReadTimeoutException logged for a simple endpoint HOT 13
- Auto-derive default values
- [BUG] NoContent with emptyOutput generates string body example
- Can we provide better error messages for jsoniter?
- confusing validation message when validating all query params HOT 2
- Can we provide some integration which would make it easier to work with htmx projects?
- Can we support custom error message for Enumeration Validation? HOT 3
- [BUG] In documentation collection refined as NonEmpty does not appear as required
- [BUG] Not[Empty] iron constraint on collection generates a "Could not find Schema for type io.github.iltotore.iron.IronType" compile error
- [BUG] Enhance iron library support in Tapir
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tapir.