com.outworkers.util.lift.package.scala Maven / Gradle / Ivy
/*
* Copyright 2013 - 2017 Outworkers Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.outworkers.util
import cats.data.{NonEmptyList, ValidatedNel}
import cats.syntax.ValidatedSyntax
import com.outworkers.util.domain.ApiError
import com.outworkers.util.parsers._
import net.liftweb.http.rest.RestContinuation
import net.liftweb.http.{JsonResponse, LiftResponse}
import net.liftweb.json._
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
package object lift extends LiftParsers with JsonHelpers with CatsOps with ValidatedSyntax {
type ValidationNel[L, R] = cats.data.ValidatedNel[L, R]
protected[this] val defaultSuccessResponse = 200
protected[this] val noContentSuccessResponse = 204
protected[this] val defaultErrorResponse = 400
protected[this] val failureResponse = 500
implicit class OptionResponseHelper[T](val opt: Option[T]) extends AnyVal {
/**
* When the Option is full, this will continue the transformation flow of an Option to a LiftResponse.
* Otherwise, the flow will short-circuit to a an unauthorized response.
*
* @param pf A partial function from a full option of type T to an async LiftResponse.
* @return A Future wrapping the obtained LiftResponse.
*/
def required(pf: T => Future[LiftResponse]): Future[LiftResponse] = {
opt.fold(Future.successful(JsonUnauthorizedResponse()))(pf)
}
}
implicit class FutureOptionTransformer[T <: Product with Serializable](val future: Future[Option[T]]) extends AnyVal {
def json()(implicit ec: ExecutionContext, formats: Formats, mf: Manifest[T]): Future[LiftResponse] = {
future map { item =>
item.fold(JsonUnauthorizedResponse())(s => JsonResponse(s.asJValue, defaultSuccessResponse))
}
}
}
implicit class ResponseToFuture(val response: LiftResponse) extends AnyVal {
def toFuture: Future[LiftResponse] = Future.successful(response)
def future(): Future[LiftResponse] = Future.successful(response)
}
implicit class ResponseConverter(val resp: NonEmptyList[String]) extends AnyVal {
def toError(code: Int): ApiError = ApiError.fromArgs(code, resp.toList)
def toJson(code: Int = defaultErrorResponse)(implicit formats: Formats): LiftResponse = {
JsonResponse(Extraction.decompose(toError(code)), code)
}
def asResponse(code: Int = defaultErrorResponse)(implicit formats: Formats): LiftResponse = {
JsonResponse(Extraction.decompose(toError(code), code))
}
}
implicit class ErrorConverter(val err: Throwable) extends AnyVal {
def toError(code: Int): ApiError = ApiError.fromArgs(code, List(err.getMessage))
def toJson(code: Int)(implicit formats: Formats): LiftResponse = {
JsonResponse(Extraction.decompose(toError(code)), code)
}
}
implicit class JsonCollection[
M[X] <: TraversableOnce[X],
T <: Product with Serializable
](val list: M[T])(implicit formats: Formats, manifest: Manifest[T]) {
def asJson: String = {
compactRender(Extraction.decompose(list.toList))
}
def asPrettyJson: String = {
JsonWrapper.prettyRender(Extraction.decompose(list.toList))
}
def asJValue: JValue = {
Extraction.decompose(list.toList)
}
def asResponse: LiftResponse = {
if (list.nonEmpty) {
JsonResponse(list.asJValue, defaultSuccessResponse)
} else {
JsonResponse(JArray(Nil), noContentSuccessResponse)
}
}
}
implicit class JsonHelper[T <: Product with Serializable](val clz: T)(
implicit formats: Formats, manifest: Manifest[T]
) {
def asJson: String = {
compactRender(Extraction.decompose(clz))
}
def asPrettyJson: String = {
JsonWrapper.prettyRender(Extraction.decompose(clz))
}
def asJValue: JValue = {
Extraction.decompose(clz)
}
def asResponse: LiftResponse = {
JsonResponse(clz.asJValue, defaultSuccessResponse)
}
}
implicit class FutureResponseHelper(val responseFuture: Future[LiftResponse]) extends AnyVal {
def async(failureCode: Int = failureResponse)(implicit context: ExecutionContext): LiftResponse = {
RestContinuation.async {
reply => {
responseFuture.onComplete {
case Success(resp) => reply(resp)
case Failure(err) => reply(err.toJson(failureCode))
}
}
}
}
}
implicit class ValidationResponseHelper[+A](val vd: ValidatedNel[String, A]) extends AnyVal {
/**
* Maps a validation to a LiftResponse if the validation is successful.
* If the validation is not successful, this method provides a default response mechanism
* which returns a JSON HTTP 400 response, where the body is an object containing the error code
* and a list of messages corresponding to each individual error in the applicative functor.
*
* @param pf The partial function that maps the successful result to a LiftResponse.
* @return A future wrapping a Lift Response.
*/
def mapSuccess(pf: A => Future[LiftResponse]): Future[LiftResponse] = vd.fold(_.toJson().future(), pf)
/**
* Maps a validation to a LiftResponse if the validation is successful.
* If the validation is not successful, this method provides a default response mechanism
* which returns a JSON HTTP 400 response, where the body is an object containing the error code
* and a list of messages corresponding to each individual error in the applicative functor.
*
* @param pf The partial function that maps the successful result to a LiftResponse.
* @return A future wrapping a Lift Response.
*/
def respond(pf: A => LiftResponse): LiftResponse = vd.fold(_.toJson(), pf)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy