algolia / algoliasearch-client-scala

⚡️ A fully-featured and blazing-fast Scala API client to interact with Algolia.
https://www.algolia.com/doc/api-client/getting-started/install/scala/
MIT License
26 stars 29 forks source link

[bug]: Scala 3 client crashes when trying to deserialize data #660

Closed Fluf22 closed 1 month ago

Fluf22 commented 1 month ago

Description

Minimal reproducible example:

import scala.io.Source
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContextExecutor}

import algoliasearch.api.SearchClient
import algoliasearch.`extension`.SearchClientExtensions

import org.json4s.native.JsonMethods
import org.json4s.jvalue2extractable

object Main {
  def main(args: Array[String]): Unit = {
    implicit val ec: ExecutionContextExecutor = scala.concurrent.ExecutionContext.global
    implicit val formats: org.json4s.Formats = org.json4s.DefaultFormats

    // Fetch sample dataset
    val url = "https://dashboard.algolia.com/sample_datasets/movie.json"
    val result = Source.fromURL(url).mkString
    val records = JsonMethods.parse(result).extract[Seq[Map[String, Any]]]

    // Connect and authenticate with your Algolia app
    val client = SearchClient("<app-id>", "<api-key>")

    // Save records in Algolia index
    try {
      Await.result(
        client.saveObjects("movie_index_scala_3", records),
        Duration(100, "sec")
      )
    } catch {
      case e: Exception => println(e.getCause.getCause.getMessage)
    }
  }
}

This will produce the following error:

java.lang.NoClassDefFoundError: scala/quoted/staging/package$
    at org.json4s.reflect.ScalaSigReader$.readConstructor(ScalaSigReader.scala:42)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.ctorParamType(Reflector.scala:181)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.$anonfun$16(Reflector.scala:264)
    at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:75)
    at scala.collection.immutable.ArraySeq.map(ArraySeq.scala:35)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.createConstructorDescriptors$$anonfun$3(Reflector.scala:241)
    at scala.collection.immutable.List.map(List.scala:246)
    at scala.collection.immutable.List.map(List.scala:79)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.createConstructorDescriptors(Reflector.scala:220)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.constructorsAndCompanion(Reflector.scala:199)
    at org.json4s.reflect.Reflector$ClassDescriptorBuilder.result(Reflector.scala:296)
    at org.json4s.reflect.Reflector$.createDescriptorWithFormats(Reflector.scala:87)
    at org.json4s.reflect.Reflector$.describeWithFormats$$anonfun$1(Reflector.scala:70)
    at org.json4s.reflect.Memo.apply(Memo.scala:12)
    at org.json4s.reflect.Reflector$.describeWithFormats(Reflector.scala:70)
    at org.json4s.Extraction$.decomposeObject$1(Extraction.scala:143)
    at org.json4s.Extraction$.internalDecomposeWithBuilder(Extraction.scala:260)
    at org.json4s.Extraction$.internalDecomposeWithBuilder(Extraction.scala:224)
    at org.json4s.Extraction$.addField$1(Extraction.scala:135)
    at org.json4s.Extraction$.decomposeObject$1(Extraction.scala:168)
    at org.json4s.Extraction$.internalDecomposeWithBuilder(Extraction.scala:260)
    at org.json4s.Extraction$.internalDecomposeWithBuilder$$anonfun$1(Extraction.scala:237)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    at scala.Option.foreach(Option.scala:437)
    at org.json4s.Extraction$.internalDecomposeWithBuilder(Extraction.scala:237)
    at org.json4s.Extraction$.decomposeWithBuilder(Extraction.scala:84)
    at org.json4s.native.Serialization$.write(Serialization.scala:47)
    at org.json4s.native.Serialization$.write(Serialization.scala:40)
    at algoliasearch.internal.JsonSerializer.serialize(JsonSerializer.scala:25)
    at algoliasearch.internal.HttpRequester$$anon$1.writeTo(HttpRequester.scala:85)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:62)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at algoliasearch.internal.interceptor.RetryStrategy.processRequest(RetryStrategy.scala:68)
    at algoliasearch.internal.interceptor.RetryStrategy.intercept$$anonfun$1(RetryStrategy.scala:38)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    at scala.collection.immutable.List.foreach(List.scala:333)
    at algoliasearch.internal.interceptor.RetryStrategy.intercept(RetryStrategy.scala:44)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at algoliasearch.internal.interceptor.UserAgentInterceptor.intercept(UserAgentInterceptor.scala:20)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at algoliasearch.internal.interceptor.AuthInterceptor.intercept(AuthInterceptor.scala:37)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at algoliasearch.internal.interceptor.HeaderInterceptor.intercept(HeaderInterceptor.scala:26)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
    at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
    at algoliasearch.internal.HttpRequester.execute(HttpRequester.scala:154)
    at algoliasearch.ApiClient.execute(ApiClient.scala:90)
    at algoliasearch.api.SearchClient.batch$$anonfun$1(SearchClient.scala:250)
    at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:687)
    at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:467)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.compute(ForkJoinTask.java:1726)
    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.compute(ForkJoinTask.java:1717)
    at java.base/java.util.concurrent.ForkJoinTask$InterruptibleTask.exec(ForkJoinTask.java:1641)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:507)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1489)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:2071)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2033)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:187)

This issue is tracked here on the json4s library repository: https://github.com/json4s/json4s/issues/1520

Client

All

Version

2.3.2

Relevant log output

No response

Fluf22 commented 1 month ago

As explained in https://github.com/json4s/json4s/issues/1520, you need to import an RC version of Scala3 staging for it to work properly. This is the only workaround for now.

build.sbt


ThisBuild / version := "0.1.0-SNAPSHOT"

ThisBuild / scalaVersion := "3.3.4-RC3"

lazy val root = (project in file(".")) .settings( name := "test" )

libraryDependencies += "com.algolia" %% "algoliasearch-scala" % "2.3.4" libraryDependencies += "org.json4s" %% "json4s-native" % "4.1.0-M7" libraryDependencies += "org.scala-lang" %% "scala3-staging" % scalaVersion.value