MiniDigger/Hangar

View on GitHub
orePlayCommon/app/controllers/sugar/CircePlayController.scala

Summary

Maintainability
A
0 mins
Test Coverage
package controllers.sugar

import scala.concurrent.{ExecutionContext, Future}

import play.api.http.{ContentTypes, HttpErrorHandler, Writeable}
import play.api.libs.{json => playjson}
import play.api.mvc.{BaseControllerHelpers, BodyParser, RequestHeader, Result}

import akka.util.ByteString
import io.circe._
import io.circe.syntax._
import cats.syntax.all._

trait CircePlayController { self: BaseControllerHelpers =>

  def errorHandler: HttpErrorHandler

  implicit val jsonWriteable: Writeable[Json] = Writeable(js => ByteString(js.noSpaces), Some(ContentTypes.JSON))

  implicit def asJsonWriteable[A: Encoder]: Writeable[A] =
    Writeable(obj => ByteString(obj.asJson.noSpaces), Some(ContentTypes.JSON))

  object parseCirce {

    private def convertPlayJson(js: playjson.JsValue): Json = js match {
      case playjson.JsString(value) => value.asJson
      case playjson.JsBoolean(bool) => bool.asJson
      case playjson.JsNumber(value) => value.asJson
      case playjson.JsNull          => Json.Null
      case playjson.JsArray(value)  => Json.arr(value.map(convertPlayJson): _*)
      case playjson.JsObject(underlying) =>
        Json.obj(underlying.map(t => t._1 -> convertPlayJson(t._2))(collection.breakOut): _*)
    }

    //Easiest way to do stuff
    def json(implicit ec: ExecutionContext): BodyParser[Json] =
      BodyParser("circe json")(parse.json(_).map(_.map(convertPlayJson)))

    def decodeJson[A: Decoder](implicit ec: ExecutionContext): BodyParser[A] =
      BodyParser("circe json decoder") { request =>
        json.apply(request).mapFuture {
          case Left(value) => Future.successful(Left(value))
          case Right(value) =>
            value.as[A].fold(e => createBadResult(e.show)(request).map(Left.apply), a => Future.successful(Right(a)))
        }
      }

    protected def createBadResult(msg: String, statusCode: Int = BAD_REQUEST): RequestHeader => Future[Result] =
      request => errorHandler.onClientError(request, statusCode, msg)
  }
}