Ry2254
Ry2254

Reputation: 1009

Could not find or construct Param[shapeless.::[String,shapeless.::[io.circe.Json,shapeless.HNil]]]

I am working on a basic job-board app for practice and when trying to compile my Scala code I am getting the following error.

[warn] insecure HTTP request is deprecated 'http://repo.typesafe.com/typesafe/releases/'; switch to HTTPS or opt-in as ("Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/").withAllowInsecureProtocol(true)
[info] Compiling 8 Scala sources to /Users/ryanmcavoy/Code/job-board/target/scala-2.11/classes ...
[error] /Users/ryanmcavoy/Code/job-board/src/main/scala/io/github/jobboard/database/JobQueries.scala:26:5: Could not find or construct Param[shapeless.::[String,shapeless.::[io.github.jobboard.model.JobPostDetails,shapeless.HNil]]].
[error] Ensure that this type is an atomic type with an Meta instance in scope, or is an HList whose members
[error] have Meta instances in scope. You can usually diagnose this problem by trying to summon the Meta
[error] instance for each element in the REPL. See the FAQ in the Book of Doobie for more hints.
[error]     sql"""
[error]     ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed

It seems to be complaining about the below method

  def insert(jobPost: JobPost): doobie.Update0 = {
    sql"""
         |INSERT INTO jobs (
         |  id,
         |  details
         |)
         |VALUES (
         |  ${jobPost.id},
         |  ${jobPost.details}
         |)
        """.stripMargin
      .update
  }

I have had a look at the docs here: https://tpolecat.github.io/doobie-0.2.3/15-FAQ.html but haven't been able to figure it out.

For additional info I'll include the below:

case class JobPost(id: String, details: JobPostDetails)

case class JobPostDetails(title: String, description: String, salary: Double, employmentType: String, employer: String)
    def createTable: doobie.Update0 = {
      sql"""
           |CREATE TABLE IF NOT EXISTS jobs (
           |  id UUID PRIMARY KEY,
           |  details JSON NOT NULL
           |)
       """.stripMargin
        .update
    }

Upvotes: 1

Views: 238

Answers (1)

Krzysztof Atłasik
Krzysztof Atłasik

Reputation: 22595

In your case, Doobie doesn't know how to serialize JobPostDetails into JSON column on your Postgres database.

By default, Doobie doesn't know how to change case class into JSON. Arguably the most popular library for serializing JSON in scala is circe. You'd have to add additional dependencies in your build.sbt for circe and circe-Postgres-doobie integration.

"org.tpolecat" %% "doobie-postgres" % "0.8.8"
"org.tpolecat" %% "doobie-postgres-circe" % "0.8.8"
"io.circe" %% "circe-core" % "0.13.0"

Then you should create instances of Encoder, Decoder, Put and Get typeclasses in companion object of JobPostDetails:

import io.circe.{ Decoder, Encoder }
import io.circe.generic.semiauto.{ deriveDecoder, deriveEncoder }
import doobie.postgres.circe.json.implicits._
import doobie._
import io.circe.syntax._

case class JobPostDetails(title: String, description: String, salary: Double, employmentType: String, employer: String)

object JobPostDetails {

  //for decoding json into JobPostDetails
  implicit val circeDecoder: Decoder[JobPostDetails] =
    deriveDecoder[JobPostDetails]

  //for encoding JobPostDetails into json
  implicit val circeEncoder: Encoder[JobPostDetails] =
    deriveEncoder[JobPostDetails]

  //tells doobie to put JobPostDetails as json to details column
  implicit val put: Put[JobPostDetails] =
    Put[Json].contramap(_.asJson) 

  //tells doobie how to read JobPostDetails from json column
  implicit val get: Get[JobPostDetails] =
    Get[Json].temap(_.as[JobPostDetails].leftMap(_.show)) 
}

After that, you should be able to save JobPost.

Upvotes: 1

Related Questions