Scala compilation fails with: Could not find implicit value for parameter W

555 Views Asked by At

I got a doobie query that doesn't want to compile:

package de.x.vmdbplanningvalues.impl.queries

import de.x.campaignplans.CampaignPlanId
import de.x.distributionbrands.DistributionBrandId
import de.x.vmdbplanningvalues._
import doobie._

object UpdateVmdbPlanningValuesQuery {
  case class VmdbPlanningUpdate(
      creditRatingRejections: Option[Double],
      goodsCoupons: Option[Double],
      customerDiscounts: Option[Double],
      campaignPlanId: String,
      distributionBrandId: String,
      country: String,
      categoryId: String,
      lane: VmdbLane
  )

  def apply(
      distributionBrand: DistributionBrandId,
      country: String,
      campaignPlanId: CampaignPlanId,
      category: String,
      updates: List[VmdbPlanningValuesForVmdbLane]
  ): ConnectionIO[Unit] = {
    for {
      _ <- updateQuery(updates.map(update =>
        VmdbPlanningUpdate(
          update.creditRatingRejections,
          update.goodsCoupons,
          update.customerDiscounts,
          campaignPlanId.id,
          distributionBrand.id,
          country,
          category,
          update.lane
        )
      ))
    } yield ()
  }

  def updateQuery[T: Write](updates: List[VmdbPlanningUpdate]): ConnectionIO[Int] = {
    val sql =
      """
         UPDATE vmdb_planning_values as vmdb
         SET vmdb.credit_rating_rejections = ?,
             vmdb.goods_coupons = ?,
             vmdb.customer_discounts = ?
         FROM campaign_plan cp
         WHERE cp.id = ?
            AND vmdb.distribution_brand_id = ?
            AND vmdb.country_id = ?
            AND vmdb.year=DATE_PART('year', cp.start_date)
            AND vmdb.quarter=DATE_PART('quarter', cp.start_date)
            AND vmdb.category_id = ?
            AND vmdb.lane = ?
      """

    Update[VmdbPlanningUpdate](sql).updateMany(updates)
  }
}

However it fails with the following error:

[error] /Users/johannesklauss/Documents/campaign-service/server/src/main/scala/de/x/vmdbplanningvalues/impl/queries/UpdateVmdbPlanningValuesQuery.scala:60:35: could not find implicit value for parameter W: doobie.Write[de.x.vmdbplanningvalues.impl.queries.UpdateVmdbPlanningValuesQuery.VmdbPlanningUpdate]
[error]     Update[VmdbPlanningUpdate](sql).updateMany(updates)
[error] 

I am not quite sure what the error message means, since I am still a bit fuzzy about implicits in Scala. Does anybody have an idea?

Edit: Added VmdbLane.scala:

package de.x.vmdbplanningvalues

import io.circe.Decoder.Result
import io.circe._

sealed trait VmdbLane {
  override def toString: String = VmdbLane.toEnum(this)
}

object VmdbLane {

  case object New extends VmdbLane
  case object CarryOver extends VmdbLane
  case object Sale extends VmdbLane
  case object Sum extends VmdbLane

  def toEnum(e: VmdbLane): String =
    e match {
      case New => "new"
      case CarryOver => "carryOver"
      case Sale => "sale"
      case Sum => "sum"
    }

  def fromEnum(s: String): Option[VmdbLane] =
    Option(s) collect {
      case "new" => New
      case "carryOver" => CarryOver
      case "sale" => Sale
      case "sum" => Sum
    }

  implicit val jsonFormat: Encoder[VmdbLane] with Decoder[VmdbLane] =
    new Encoder[VmdbLane] with Decoder[VmdbLane] {
      override def apply(a: VmdbLane): Json = Encoder.encodeString(toEnum(a))
      override def apply(c: HCursor): Result[VmdbLane] =
        c.value.asString.flatMap(s => fromEnum(s)) match {
          case Some(a) => Right(a)
          case None => Left(DecodingFailure("VmdbLane", c.history))
        }
    }
}

1

There are 1 best solutions below

0
Emiliano Martinez On BEST ANSWER

The problem was not related to Circe it is related to how the custom doobie type mapping works. If you include something like inside the VmdbLane object:

implicit val natGet: Get[VmdbLane] = Get[String].map(in => {
    in match {
      case "New"       => New
      case "CarryOver" => CarryOver
      case "Sale"      => Sale
      case "Sum"       => Sum
    }
  })

implicit val natPut: Put[VmdbLane] = Put[String].contramap {
    case New       => "New"
    case CarryOver => "CarryOver"
    case Sale      => "Sale"
    case Sum       => "Sum"
  }

The compiler should include the mapping an it should work.