getshaka-org / native-converter

Easily convert between Scala.js and native JavaScript
Apache License 2.0
59 stars 5 forks source link

an implementation is missing. #13

Closed NicolasRouquette closed 3 years ago

NicolasRouquette commented 3 years ago

I am trying to use this library to parse some data from the Zenhub API.

import sttp.client3._
import sttp.model._
import org.getshaka.nativeconverter.NativeConverter

import scala.collection.immutable.Seq
import scala.scalajs.js

case class Zenhub(url: String, token: String):

  val backend = HttpURLConnectionBackend()

  /**
   * Get a Release Report.
   * @param id Zenhub release ID
   * @see https://github.com/ZenHubIO/API#get-a-release-report
   * @see https://github.com/ZenHubIO/API#authentication
   * @return Error or Json release data
   */
  def getReleaseReport(id: String): Either[String, String] =
    val request = basicRequest
      .headers(Header("Content-Type", "application/json"), Header("X-Authentication-Token", token))
      .get(uri"${url}/p1/reports/release/${id}")
    val response = request.send(backend)
    response.body

  /**
   * Get all the issues for a release report.
   * @param id Zenhub release ID
   * @see https://github.com/ZenHubIO/API#get-all-the-issues-for-a-release-report
   * @return
   */
  def getReleaseReportIssues(id: String): Either[String, Seq[Zenhub.RepoIssue]] =
    val request = basicRequest
      .headers(Header("Content-Type", "application/json"), Header("X-Authentication-Token", token))
      .get(uri"${url}/p1/reports/release/${id}/issues")
    val response = request.send(backend)
    response.body.map { (s: String) =>
      NativeConverter[Seq[Zenhub.RepoIssue]].fromJson(s)
    }

object Zenhub:

  case class RepoIssue(repo_id: Int, issue_number: Int) derives NativeConverter

At runtime, I get an error:

Exception in thread "main" scala.NotImplementedError: an implementation is missing
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:345)
    at scala.scalajs.js.JSON$.parse$default$2(JSON.scala:42)
    at org.getshaka.nativeconverter.NativeConverter.fromJson(NativeConverter.scala:51)
    at org.getshaka.nativeconverter.NativeConverter.fromJson$(NativeConverter.scala:19)
    at org.getshaka.nativeconverter.NativeConverter$immutableSeqConv.fromJson(NativeConverter.scala:304)
    at caesar.Zenhub.getReleaseReportIssues$$anonfun$1(Zenhub.scala:40)

I noticed that NativeConverter.fromJson is defined as:

  def fromJson(json: String): A = fromNative(JSON.parse(json))

which means that we get the default value for the optional reviver argument of JSON.parse:

  def parse(text: String,
      reviver: js.Function2[js.Any, js.Any, js.Any] = ???): js.Dynamic = js.native

Since the default value is ???, the exception makes sense.

What is a suitable function that we can provide here?

How is it that this problem has not happened before given that it has been several years since the scalajs JSON.parse method has required 2 arguments? -- see this change: https://github.com/scala-js/scala-js/commit/5fd2ff241040fb5d18b3a4a8176f481fa7462710

AugustNagro commented 3 years ago

Can you make a minimal reproducer?

I tried this and it works with no problem:

  case class RepoIssue(repo_id: Int, issue_number: Int) derives NativeConverter

  @Test
  def yolo: Unit =
    val issues = Seq(
      RepoIssue(1, 1),
      RepoIssue(2, 2),
      RepoIssue(3, 3)
    )
    val issuesJson = """ [{"repo_id":1,"issue_number":1},{"repo_id":2,"issue_number":2},{"repo_id":3,"issue_number":3}]  """.trim
    val nc = NativeConverter[Seq[RepoIssue]]
    assertEquals(issues, nc.fromJson(issuesJson))
    assertEquals(issuesJson, nc.toJson(issues))

I haven't used sttp before, but my guess would be that this line is the problem:

val backend = HttpURLConnectionBackend()

I don't think Scala.js would implement java.net.HttpURLConnection.

AugustNagro commented 3 years ago

Hey @NicolasRouquette going to close this, please reopen if you make a reproducer