diff --git a/build.sbt b/build.sbt index c9eaca39..513db3a4 100644 --- a/build.sbt +++ b/build.sbt @@ -51,6 +51,7 @@ lazy val http4s = project.in(file("modules/http4s")) .settings( name := "fuuid-http4s", libraryDependencies ++= Seq( + "org.http4s" %% "http4s-core" % http4sV, "org.http4s" %% "http4s-dsl" % http4sV % Test ) ) diff --git a/modules/http4s/src/main/scala/io/chrisdavenport/fuuid/http4s/implicits.scala b/modules/http4s/src/main/scala/io/chrisdavenport/fuuid/http4s/implicits.scala new file mode 100644 index 00000000..8bd08512 --- /dev/null +++ b/modules/http4s/src/main/scala/io/chrisdavenport/fuuid/http4s/implicits.scala @@ -0,0 +1,23 @@ +package io.chrisdavenport.fuuid.http4s + +import io.chrisdavenport.fuuid.FUUID +import cats.data.ValidatedNel +import cats.syntax.either._ +import org.http4s.QueryParamDecoder +import org.http4s.QueryParameterValue +import org.http4s.ParseFailure + +object implicits { + implicit val fuuidQueryParamDecoder: QueryParamDecoder[FUUID] = + new QueryParamDecoder[FUUID] { + def decode(value: QueryParameterValue): ValidatedNel[ParseFailure, FUUID] = + FUUID + .fromString(value.value) + .leftMap( + _ => + ParseFailure( + "Failed to parse FUUID query parameter", + s"Could not parse ${value.value} as a FUUID")) + .toValidatedNel + } +} diff --git a/modules/http4s/src/test/scala/io/chrisdavenport/fuuid/http4s/FUUIDQueryParamDecoder.scala b/modules/http4s/src/test/scala/io/chrisdavenport/fuuid/http4s/FUUIDQueryParamDecoder.scala new file mode 100644 index 00000000..a1c6d843 --- /dev/null +++ b/modules/http4s/src/test/scala/io/chrisdavenport/fuuid/http4s/FUUIDQueryParamDecoder.scala @@ -0,0 +1,24 @@ +package io.chrisdavenport.fuuid.http4s + +import io.chrisdavenport.fuuid.{FUUID, FUUIDArbitraries} +import io.chrisdavenport.fuuid.http4s.implicits._ +import org.http4s.dsl.io._ +import org.scalacheck._ +import org.specs2.ScalaCheck +import org.specs2.mutable.Specification + +class FUUIDQueryParamDecoder extends Specification with ScalaCheck with FUUIDArbitraries { + + object IdQueryParamMatcher extends QueryParamDecoderMatcher[FUUID]("id") + + "FUUID QueryParamDecoder" should { + + "work properly given a valid UUID" in prop { validFuuid: FUUID => + IdQueryParamMatcher.unapply(Map("id" -> List(validFuuid.show))) must beSome(validFuuid) + } + + "fail given an invalid UUID" in prop { invalidUuid: String => + IdQueryParamMatcher.unapply(Map("id" -> List(invalidUuid))) must beNone + }.setArbitrary(Arbitrary(Gen.alphaStr)) + } +}