Skip to content

Commit

Permalink
adding support for accumulating hlist mapping failures
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffjohn11 committed May 11, 2020
1 parent b6322fa commit d12e7db
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 16 deletions.
29 changes: 15 additions & 14 deletions core/src/main/scala/neotypes/implicits/mappers/ResultMappers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ trait ResultMappers extends ValueMappers {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable]): Either[Throwable, H :!: T] = {
val (headName, headValue) = value.head
val head = fieldDecoder.to(headName, Some(headValue))
val tail = tailDecoder.to(value.tail, None, errors)

head.flatMap(h => tail.map(t => h :: t))
accumulateErrorsIfExist[H, T, H](value.tail, typeHint, errors, head, identity)
}
}

Expand Down Expand Up @@ -148,17 +146,7 @@ trait ResultMappers extends ValueMappers {
}

val decodedHead = head.to(fieldName, convertedValue.find(_._1 == fieldName).map(_._2))

(tail, decodedHead) match{
case (HNilResultMapper, Left(th)) =>
Left(MultipleIncoercibleException((th +: errors).reverse))
case (HNilResultMapper, _) if errors.nonEmpty =>
Left(MultipleIncoercibleException(errors.reverse))
case (_, Left(th)) =>
tail.to(convertedValue, typeHint, th +: errors).map(t => labelled.field[K](null.asInstanceOf[H]) :: t)
case (_, Right(v)) =>
tail.to(convertedValue, typeHint, errors).map(t => labelled.field[K](v) :: t)
}
accumulateErrorsIfExist[H, T, FieldType[K, H]](value, typeHint, errors, decodedHead, h => labelled.field[K](h))
}
}
}
Expand All @@ -174,4 +162,17 @@ trait ResultMappers extends ValueMappers {

implicit final def pathRecordMarshallable[N: ResultMapper, R: ResultMapper]: ResultMapper[Path[N, R]] =
ResultMapper.fromValueMapper

def accumulateErrorsIfExist[H, T <: HList, HR](results: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable], head: Either[Throwable, H], headFunc: H => HR)(implicit resultMapper: ResultMapper[T]) = {
(resultMapper, head) match{
case (HNilResultMapper, Left(th)) =>
Left(MultipleIncoercibleException((th +: errors).reverse))
case (HNilResultMapper, _) if errors.nonEmpty =>
Left(MultipleIncoercibleException(errors.reverse))
case (_, Left(th)) =>
resultMapper.to(results, typeHint, th +: errors).map(t => headFunc(null.asInstanceOf[H]) :: t)
case (_, Right(v)) =>
resultMapper.to(results, typeHint, errors).map(t => headFunc(v) :: t)
}
}
}
20 changes: 18 additions & 2 deletions core/src/test/scala/neotypes/CompositeTypesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import neotypes.implicits.syntax.string._
import neotypes.internal.syntax.async._
import org.neo4j.driver.v1.Value
import org.scalatest.LoneElement
import shapeless._

import scala.concurrent.Future
import scala.jdk.CollectionConverters._
Expand Down Expand Up @@ -135,21 +136,36 @@ final class CompositeTypesSpec[F[_]](testkit: EffectTestkit[F]) extends BaseInte
}
}

it should "construct an MultipleIncoercibleException message with all failures" in {
it should "construct an MultipleIncoercibleException message with all case class mapping failures" in {
recoverToExceptionIf[MultipleIncoercibleException] {
executeAsFuture { s =>
"match (p:Person {name: 'Charlize Theron'}) return p"
.query[PersonIntNameBornBool]
.single(s)
}
} map { ex =>
assert(ex.errors.map(_.getMessage) == List(
assert(ex.errors.map(_.getMessage) == List(
"Cannot coerce STRING to Java int for field [name] with value [\"Charlize Theron\"]",
"Cannot coerce INTEGER to Java boolean for field [born] with value [1975]")
)
}
}

it should "construct an MultipleIncoercibleException message with all HList mapping failures" in {
recoverToExceptionIf[MultipleIncoercibleException] {
executeAsFuture { s =>
"match (p:Person {name: 'Charlize Theron'}) return p.name, p.born"
.query[Int :: Boolean :: HNil]
.single(s)
}
} map { ex =>
assert(ex.errors.map(_.getMessage) == List(
"Cannot coerce STRING to Java int for field [p.name] with value [\"Charlize Theron\"]",
"Cannot coerce INTEGER to Java boolean for field [p.born] with value [1975]")
)
}
}

override final val initQuery: String = BaseIntegrationSpec.DEFAULT_INIT_QUERY
}

Expand Down

0 comments on commit d12e7db

Please sign in to comment.