MeltwaterArchive / dropwizard-scala

Scala language integration for Dropwizard
Apache License 2.0
73 stars 24 forks source link

QueryParam of Option of scala primitive results in error #22

Open joekarl opened 7 years ago

joekarl commented 7 years ago

Been scratching my head at this one for a bit now after doing an upgrade from the old massrelevance version to the datasift maintained version.

Specifically the error I'm running into is whenever I try to add something like QueryParam("foo") foo: Option[Int] to a resource.

I've done some digging, and similar combinations of QueryParam("foo") foo: Option[java.lang.Integer] and QueryParam("foo") foo: Int work as expected.

This can be replicated by adding the following test/resource to ScalaApplicationSpecIT.scala (and actually just adding that resource path with the parameter Option[Int] will fail validation of the resource on test suite startup.

--- a/core/src/test/scala/com/datasift/dropwizard/scala/ScalaApplicationSpecIT.scala
+++ b/core/src/test/scala/com/datasift/dropwizard/scala/ScalaApplicationSpecIT.scala
@@ -37,6 +37,15 @@ case class ScalaTestConfiguration(
   def greetOrNotFound(@QueryParam("name") name: Option[String]): Option[List[String]] =
     name.map(greeting.format(_)).map(List(_))

+
+  @GET @Path("/opt-int-param")
+  def greetWithOptionInt(@QueryParam("i") i: Option[Int]): Option[Int] =
+    i
+
   @GET @Path("/option")
   def greetWithOption(@QueryParam("name") name: Option[String]): List[String] =
     name.map(greeting.format(_)).toList
@@ -313,6 +322,28 @@ class ScalaApplicationSpecIT extends FlatSpec with BeforeAndAfterAllMulti {
     assert(result === Success(expected))
   }

+
+  "GET /opt-int-param" should "yield results" in {
+    val fixture = 2
+    val expected = Option(2)
+    val result = request("/opt-int-param").map {
+      _.queryParam("i", fixture.toString)
+        .request(MediaType.APPLICATION_JSON)
+        .get(classOf[Option[Int]])
+    }
+    assert(result === Success(expected))
+  }
+
   "GET /complex_scala" should "yield results" in {
     val fixture: Set[BigDecimal] = Set(BigDecimal(1), BigDecimal(2))
     val expected = 2

I think this has something to do with some weirdness with either type resolution or erasure as by introspecting ScalaParamConvertersProvider during test time the call to getConverter as the parameter type is being listed at type=class scala.Option and the generic type at runtime is scala.Option[java.lang.Object]

Actual error the container spits out

Caused by: org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type public scala.Option com.datasift.dropwizard.scala.ScalaTestResource.greetWithOptionInt(scala.Option) at index 0.; source='ResourceMethod{httpMethod=GET, consumedTypes=[application/json], producedTypes=[application/json], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class com.datasift.dropwizard.scala.ScalaTestResource, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@6097f225]}, definitionMethod=public scala.Option com.datasift.dropwizard.scala.ScalaTestResource.greetWithOptionInt(scala.Option), parameters=[Parameter [type=class scala.Option, source=i, defaultValue=null]], responseType=scala.Option<java.lang.Object>}, nameBindings=[]}']
joekarl commented 7 years ago

Unsurprisingly the same behavior is noted with scala Longs and Booleans.

anant-indix commented 6 years ago

Option[java.lang.Long] is working but not Option[scala.Long] because java.lang.Long implements java.lang.reflect.Type whereas scala.Long doesn't

com.datasift.dropwizard.scala.jersey.inject.ScalaParamConvertersProvider overrides method override def getConverter[T](rawType: Class[T], genericType: Type, annotations: Array[Annotation]): ParamConverter[T] = { So essentially none of the native classes in "scala" package would work.

G1GC commented 6 years ago

Essentially, we need reflection compatibility between scala types and java types, to get the above to work, I guess - there's an open ticket - https://issues.scala-lang.org/browse/SI-7317