All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.websudos.util.lift.package.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013-2015 Websudos, Limited.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Explicit consent must be obtained from the copyright owner, Websudos Limited before any redistribution is made.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.websudos.util

import net.liftweb.http.rest.RestContinuation
import net.liftweb.http.{JsonResponse, LiftResponse}
import net.liftweb.json._

import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.{Failure, Success}
import scalaz.{NonEmptyList, ValidationNel}

package object lift extends LiftParsers with JsonHelpers {


  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())(item => JsonResponse(item.asJValue(), 200))
      }
    }
  }

  implicit class ResponseToFuture(val response: LiftResponse) extends AnyVal {
    def toFuture(): Future[LiftResponse] = Promise.successful(response).future
  }

  implicit class ResponseConverter(val resp: NonEmptyList[String]) extends AnyVal {

    def toError(code: Int): ApiError = ApiError(ApiErrorResponse(code, resp.list))

    def toJson(code: Int = 406): LiftResponse = JsonResponse(Extraction.decompose(toError(code))(DefaultFormats), code)
  }

  implicit class ErrorConverter(val err: Throwable) extends AnyVal {

    def toError(code: Int): ApiError = ApiError(ApiErrorResponse(code, List(err.getMessage)))

    def toJson(code: Int)(implicit formats: Formats): LiftResponse = JsonResponse(Extraction.decompose(toError(code)), code)
  }

  implicit class JsonHelper[T <: Product with Serializable](val clz: T) extends AnyVal {
    def asJson()(implicit formats: Formats, manifest: Manifest[T]): String = {
      compactRender(Extraction.decompose(clz))
    }

    def asJValue()(implicit formats: Formats, manifest: Manifest[T]): JValue = {
      Extraction.decompose(clz)
    }

    def asResponse()(implicit mf: Manifest[T], formats: DefaultFormats): LiftResponse = {
      JsonResponse(clz.asJValue(), 200)
    }
  }

  implicit class JsonSeqHelper[T <: Product with Serializable](val list: Seq[T]) extends AnyVal {
    def asJson()(implicit formats: Formats, manifest: Manifest[T]): String = {
      compactRender(Extraction.decompose(list))
    }

    def asJValue()(implicit formats: Formats, manifest: Manifest[T]): JValue = {
      Extraction.decompose(list)
    }

    def asResponse()(implicit mf: Manifest[T], formats: DefaultFormats): LiftResponse = {
      if (list.nonEmpty) {
        JsonResponse(list.asJValue(), 200)
      } else {
        JsonResponse(JArray(Nil), 204)
      }
    }
  }

  implicit class JsonListHelper[T <: Product with Serializable](val list: List[T]) extends AnyVal {
    def asJson()(implicit formats: Formats, manifest: Manifest[T]): String = {
      compactRender(Extraction.decompose(list))
    }

    def asJValue()(implicit formats: Formats, manifest: Manifest[T]): JValue = {
      Extraction.decompose(list)
    }

    def asResponse()(implicit mf: Manifest[T], formats: DefaultFormats): LiftResponse = {
      list match {
        case head :: tail => JsonResponse(list.asJValue(), 200)
        case Nil => JsonResponse(JArray(Nil), 204)
      }
    }
  }

  implicit class FutureResponseHelper(val responseFuture: Future[LiftResponse]) extends AnyVal {

    def async(failureCode: Int = 500)(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 eval: ValidationNel[String, A]) extends AnyVal {
    def respond(pf: A => LiftResponse)(code: Int = 400): LiftResponse = {
      eval.fold(_.toJson(code), 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.
     * @param code The error status code to use if the validation is a failure.
     * @return A future wrapping a Lift Response.
     */
    @deprecated("Use mapSuccess instead", "0.9.11")
    def async(pf: A => Future[LiftResponse])(code: Int = 400): Future[LiftResponse] = {
      eval.fold(_.toJson(code).toFuture(), pf)
    }

    def mapSuccess(pf: A => Future[LiftResponse])(code: Int = 400): Future[LiftResponse] = {
      eval.fold(_.toJson(code).toFuture(), pf)
    }

    def respond(pf: A => LiftResponse): LiftResponse = {
      eval.fold(_.toJson(), pf)
    }

    @deprecated("Use mapSuccess instead", "0.9.11")
    def async(pf: A => Future[LiftResponse]): Future[LiftResponse] = {
      eval.fold(_.toJson().toFuture(), pf)
    }

    def mapSuccess(pf: A => Future[LiftResponse]): Future[LiftResponse] = {
      eval.fold(_.toJson().toFuture(), pf)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy