Skip to content

Commit

Permalink
fixes and formatting changes, reverting imports to previous style
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffjohn11 committed May 5, 2020
1 parent 813400d commit 3e57862
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 55 deletions.
25 changes: 8 additions & 17 deletions cats-data/src/test/scala/neotypes/cats/data/CatsDataSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,13 @@ package neotypes.cats.data

import neotypes.{CleaningIntegrationSpec, FutureTestkit}
import neotypes.cats.data.implicits._
import neotypes.exceptions.IncoercibleException
import neotypes.exceptions.{IncoercibleException, MultipleIncoercibleException}
import neotypes.implicits.mappers.all._
import neotypes.implicits.syntax.cypher._
import neotypes.implicits.syntax.string._

import cats.data.{
Chain,
Const,
NonEmptyChain,
NonEmptyList,
NonEmptyMap,
NonEmptySet,
NonEmptyVector
}
import _root_.cats.instances.string._ // Brings the implicit Order[String] instance into the scope.
import _root_.cats.instances.int._ // Brings the implicit Order[Int] instance into the scope.
import cats.data.{Chain, Const, NonEmptyChain, NonEmptyList, NonEmptyMap, NonEmptySet, NonEmptyVector}
import _root_.cats.instances.string._
import _root_.cats.instances.int._

import scala.concurrent.Future

Expand Down Expand Up @@ -64,7 +55,7 @@ final class CatsDataSpec extends CleaningIntegrationSpec[Future](FutureTestkit)
}

it should "fail if retrieving an empty list as a NonEmptyChain" in execute { s =>
recoverToSucceededIf[IncoercibleException] {
recoverToSucceededIf[MultipleIncoercibleException] {
for {
_ <- "CREATE (stackTrace: StackTrace { line: 1, errors: [] })".query[Unit].execute(s)
stackTrace <- "MATCH (stackTrace: StackTrace) RETURN stackTrace".query[StackTrace].single(s)
Expand All @@ -86,7 +77,7 @@ final class CatsDataSpec extends CleaningIntegrationSpec[Future](FutureTestkit)
}

it should "fail if retrieving an empty list as a NonEmptyList" in execute { s =>
recoverToSucceededIf[IncoercibleException] {
recoverToSucceededIf[MultipleIncoercibleException] {
for {
_ <- "CREATE (player: Player { name: \"Luis\", items: [] })".query[Unit].execute(s)
player <- "MATCH (player: Player) RETURN player".query[Player].single(s)
Expand Down Expand Up @@ -129,7 +120,7 @@ final class CatsDataSpec extends CleaningIntegrationSpec[Future](FutureTestkit)
}

it should "fail if retrieving an empty list as a NonEmptySet" in execute { s =>
recoverToSucceededIf[IncoercibleException] {
recoverToSucceededIf[MultipleIncoercibleException] {
for {
_ <- "CREATE (set: Set { name: \"favourites\", numbers: [] })".query[Unit].execute(s)
set <- "MATCH (set: Set) RETURN set".query[MySet].single(s)
Expand All @@ -151,7 +142,7 @@ final class CatsDataSpec extends CleaningIntegrationSpec[Future](FutureTestkit)
}

it should "fail if retrieving an empty list as a NonEmptyVector" in execute { s =>
recoverToSucceededIf[IncoercibleException] {
recoverToSucceededIf[MultipleIncoercibleException] {
for {
_ <- "CREATE (purchase: Purchase { total: 12.5, groceries: [] })".query[Unit].execute(s)
purchase <- "MATCH (purchase: Purchase) RETURN purchase".query[Purchase].single(s)
Expand Down
37 changes: 18 additions & 19 deletions core/src/main/scala/neotypes/implicits/mappers/ResultMappers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ package implicits.mappers
import java.time._
import java.util.UUID

import neotypes.exceptions.MultipleIncoercibleException
import neotypes.mappers.{ResultMapper, TypeHint, ValueMapper}
import neotypes.types.Path
import mappers.{ResultMapper, TypeHint, ValueMapper}
import exceptions.MultipleIncoercibleException
import types.Path

import org.neo4j.driver.internal.types.InternalTypeSystem
import org.neo4j.driver.internal.value.IntegerValue
import org.neo4j.driver.v1.Value
import org.neo4j.driver.v1.types.{Entity, IsoDuration, Node, Point, Relationship, Path => NPath}
import shapeless.labelled.FieldType
import org.neo4j.driver.v1.types.{Entity, IsoDuration, Node, Path => NPath, Point, Relationship}
import shapeless.{HList, HNil, LabelledGeneric, Lazy, Witness, labelled, :: => :!:}
import shapeless.labelled.FieldType

import scala.collection.compat.Factory
import scala.reflect.ClassTag
Expand Down Expand Up @@ -100,14 +101,14 @@ trait ResultMappers extends ValueMappers {
reprDecoder: Lazy[ResultMapper[R]],
ct: ClassTag[A]): ResultMapper[A] =
new ResultMapper[A] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable]): Either[Throwable, A] =
reprDecoder.value.to(value, Some(TypeHint(ct))).map(gen.from)
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable]): Either[Throwable, A] =
reprDecoder.value.to(value, Some(TypeHint(ct)), Nil).map(gen.from)
}

implicit final def hlistMarshallable[H, T <: HList, LR <: HList](implicit fieldDecoder: ValueMapper[H],
tailDecoder: ResultMapper[T]): ResultMapper[H :!: T] =
new ResultMapper[H :!: T] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable]): Either[Throwable, H :!: T] = {
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)
Expand All @@ -123,7 +124,7 @@ trait ResultMappers extends ValueMappers {
private def collectEntityFields(entity: Entity): List[(String, Value)] =
(Constants.ID_FIELD_NAME -> new IntegerValue(entity.id)) :: getKeyValuesFrom(entity).toList

override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable]): Either[Throwable, FieldType[K, H] :!: T] = {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable]): Either[Throwable, FieldType[K, H] :!: T] = {
val fieldName = key.value.name

typeHint match {
Expand All @@ -148,23 +149,21 @@ trait ResultMappers extends ValueMappers {

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

tail match{
case HNilResultMapper if errors.nonEmpty =>
Left(MultipleIncoercibleException(errors.toList.reverse))
case _ =>
decodedHead match{
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)
}
(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)
}
}
}
}

implicit final def optionResultMapper[T](implicit mapper: ResultMapper[T]): ResultMapper[Option[T]] =
new ResultMapper[Option[T]] {
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable]): Either[Throwable, Option[T]] =
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable]): Either[Throwable, Option[T]] =
if (fields.isEmpty)
Right(None)
else
Expand Down
20 changes: 10 additions & 10 deletions core/src/main/scala/neotypes/mappers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import scala.util.Try
object mappers {
@annotation.implicitNotFound("Could not find the ResultMapper for ${A}")
trait ResultMapper[A] { self =>
def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, A]
def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, A]

/**
* Allows supplying a secondary [[ResultMapper]] to try if the original fails.
Expand All @@ -23,7 +23,7 @@ object mappers {
* @return A new [[ResultMapper]] that returns the type of the supplied secondary mapper.
*/
def or[AA >: A](mapper: => ResultMapper[AA]): ResultMapper[AA] = new ResultMapper[AA] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, AA] = {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, AA] = {
self.to(value, typeHint).left.flatMap(_ => mapper.to(value, typeHint))
}
}
Expand All @@ -36,7 +36,7 @@ object mappers {
* @return A new ResultMapper that applies your function to the result.
*/
def map[B](f: A => B): ResultMapper[B] = new ResultMapper[B] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, B] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, B] =
self.to(value, typeHint).map(f)
}

Expand All @@ -49,7 +49,7 @@ object mappers {
* @return A new [[ResultMapper]] derived from the value your original [[ResultMapper]] outputs.
*/
def flatMap[B](f: A => ResultMapper[B]): ResultMapper[B] = new ResultMapper[B] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, B] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, B] =
self.to(value, typeHint).flatMap(a => f(a).to(value, typeHint))
}

Expand All @@ -61,7 +61,7 @@ object mappers {
* @return A [[ResultMapper]] that produces a pair of values.
*/
def product[B](fa: ResultMapper[B]): ResultMapper[(A, B)] = new ResultMapper[(A, B)] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, (A, B)] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, (A, B)] =
self.flatMap(t => fa.map(a => (t, a))).to(value, typeHint)
}

Expand All @@ -74,7 +74,7 @@ object mappers {
* @return A [[ResultMapper]] that, if sucessful, will return a value of either the original or secondary type.
*/
def either[B](fa: ResultMapper[B]): ResultMapper[Either[A, B]] = new ResultMapper[Either[A, B]] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, Either[A, B]] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, Either[A, B]] =
self.to(value, typeHint) match {
case Right(r) => Right(Left(r))
case Left(_) => fa.to(value, typeHint) match {
Expand Down Expand Up @@ -103,7 +103,7 @@ object mappers {
* @return A [[ResultMapper]] that always returns the supplied value and never errors.
*/
def const[A](a: A): ResultMapper[A] = new ResultMapper[A] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, A] = Right(a)
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, A] = Right(a)
}

/**
Expand All @@ -117,7 +117,7 @@ object mappers {
* @return A new [[ResultMapper]] that parses query results with the supplied function.
*/
def instance[A](f: (List[(String, Value)], Option[TypeHint]) => Either[Throwable, A]): ResultMapper[A] = new ResultMapper[A] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, A] = f(value, typeHint)
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, A] = f(value, typeHint)
}

/**
Expand All @@ -128,7 +128,7 @@ object mappers {
* @return A [[ResultMapper]] that always returns a throwable error.
*/
def failed[A](failure: Throwable): ResultMapper[A] = new ResultMapper[A] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, A] = Left(failure)
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, A] = Left(failure)
}

/**
Expand All @@ -139,7 +139,7 @@ object mappers {
*/
def fromValueMapper[A](implicit marshallable: ValueMapper[A]): ResultMapper[A] =
new ResultMapper[A] {
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, A] =
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, A] =
fields
.headOption
.fold(ifEmpty = marshallable.to("", None)) {
Expand Down
13 changes: 9 additions & 4 deletions core/src/test/scala/neotypes/CompositeTypesSpec.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package neotypes

import neotypes.exceptions.{MultipleIncoercibleException}
import neotypes.exceptions.MultipleIncoercibleException
import neotypes.implicits.mappers.results._
import neotypes.implicits.syntax.string._
import neotypes.internal.syntax.async._
import org.neo4j.driver.v1.Value
import org.scalatest.LoneElement

import scala.concurrent.Future
import scala.jdk.CollectionConverters._

/** Base class for testing queries which produce complex types. */
final class CompositeTypesSpec[F[_]](testkit: EffectTestkit[F]) extends BaseIntegrationSpec(testkit) {
final class CompositeTypesSpec[F[_]](testkit: EffectTestkit[F]) extends BaseIntegrationSpec(testkit) with LoneElement {
behavior of s"Extracting complex types using: ${effectName}"

import CompositeTypesSpec._
Expand Down Expand Up @@ -129,7 +131,7 @@ final class CompositeTypesSpec[F[_]](testkit: EffectTestkit[F]) extends BaseInte
.single(s)
}
} map { ex =>
assert(ex.errors.map(_.getMessage) == List("Cannot coerce STRING to Java int for field [name] with value [\"Charlize Theron\"]"))
assert(ex.errors.loneElement.getMessage == "Cannot coerce STRING to Java int for field [name] with value [\"Charlize Theron\"]")
}
}

Expand All @@ -141,7 +143,10 @@ final class CompositeTypesSpec[F[_]](testkit: EffectTestkit[F]) extends BaseInte
.single(s)
}
} map { ex =>
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]"))
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]")
)
}
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/test/scala/neotypes/MapperSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class MapperSpec extends AnyFreeSpec {
}
"should allow defining a secondary mapper" in {
val failingMapper = new ResultMapper[String] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, String] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, String] =
Left(new Exception)
}
val result = failingMapper.or(StringResultMapper).to(List(("value", new StringValue("string"))), None)
Expand All @@ -51,7 +51,7 @@ final class MapperSpec extends AnyFreeSpec {
"should be flatmappable" in {
val flatMappedMapper = ResultMapper[String].flatMap { str =>
new ResultMapper[Int] {
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable]): Either[Throwable, Int] =
override def to(value: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable]): Either[Throwable, Int] =
Right(str.length)
}
}
Expand Down
2 changes: 1 addition & 1 deletion refined/src/main/scala/neotypes/refined/Refined.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ trait RefinedMappers {

implicit final def refinedResultMapper[T, P](implicit marshallable: ValueMapper[Refined[T, P]]): ResultMapper[Refined[T, P]] =
new ResultMapper[Refined[T, P]] {
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: Seq[Throwable] = Nil): Either[Throwable, Refined[T, P]] =
override def to(fields: List[(String, Value)], typeHint: Option[TypeHint], errors: List[Throwable] = Nil): Either[Throwable, Refined[T, P]] =
fields
.headOption
.fold(ifEmpty = marshallable.to("", None)) {
Expand Down
8 changes: 6 additions & 2 deletions refined/src/test/scala/neotypes/refined/RefinedSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package neotypes.refined

import neotypes.{CleaningIntegrationSpec, FutureTestkit}
import neotypes.exceptions.IncoercibleException
import neotypes.exceptions.MultipleIncoercibleException
import neotypes.implicits.mappers.all._
import neotypes.implicits.syntax.cypher._
import neotypes.implicits.syntax.string._
Expand All @@ -11,10 +12,11 @@ import eu.timepit.refined.W
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric.Interval
import org.scalatest.LoneElement

import scala.concurrent.Future

final class RefinedSpec extends CleaningIntegrationSpec[Future](FutureTestkit) {
final class RefinedSpec extends CleaningIntegrationSpec[Future](FutureTestkit) with LoneElement {
import RefinedSpec.{Level, User}

it should "insert and retrieve one refined value" in execute { s =>
Expand Down Expand Up @@ -85,11 +87,13 @@ final class RefinedSpec extends CleaningIntegrationSpec[Future](FutureTestkit) {
}

it should "fail if at least one value inside a case class does not satisfy the refinement condition" in execute { s =>
recoverToSucceededIf[IncoercibleException] {
recoverToExceptionIf[MultipleIncoercibleException] {
for {
_ <- "CREATE (user: User { name: \"???\", level: -1 })".query[Unit].execute(s)
user <- "MATCH (user: User { name: \"???\" }) RETURN user".query[User].single(s)
} yield user
}.map{ex =>
assert(ex.errors.loneElement.getMessage == "Left predicate of (!(-1 < 1) && !(-1 > 99)) failed: Predicate (-1 < 1) did not fail.")
}
}
}
Expand Down

0 comments on commit 3e57862

Please sign in to comment.