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))
}
}
}
CodePudding user response:
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.