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

com.algorithmia.algo.AlgoResponse.scala Maven / Gradle / Ivy

The newest version!
package com.algorithmia.algo

import java.io.IOException
import org.apache.commons.codec.binary.Base64
import play.api.libs.json._

/**
  * Represents an algorithm response.
  */
sealed trait AlgoResponse {
  def isSuccess: Boolean
  def as[T](implicit reads: Reads[T]): T
  def asOpt[T](implicit reads: Reads[T]): Option[T]
  def asString: String
  def asBytes: Array[Byte]
  def map[T](f: AlgoSuccess => T): Option[T]
  def metadata: Metadata

  /** Returns the full response from the server */
  def rawOutput: String
}

case class AlgoSuccess(result: JsValue, metadata: Metadata, rawOutput: String) extends AlgoResponse {
  override def isSuccess: Boolean = true
  override def as[T](implicit reads: Reads[T]): T = {
    metadata.content_type match {
      case ContentTypeJson | ContentTypeText => {
        Json.fromJson[T](result) match {
          case JsSuccess(obj, _) => obj
          case JsError(errors) => {
            val errorMsg = errors.map(_._2.mkString(",")).mkString("\n")
            throw new IOException("Failed to parse algorithm response: " + errorMsg)
          }
        }
      }
      case _ => throw new IOException("Cannot cast algorithm response")
    }
  }
  override def asOpt[T](implicit reads: Reads[T]): Option[T] = Json.fromJson[T](result).asOpt
  override def asString: String = result match {
    case JsString(str) => str
    case _ => result.toString
  }
  override def asBytes: Array[Byte] = metadata.content_type match {
    case ContentTypeBinary =>
      result match {
        case JsString(str) => Base64.decodeBase64(str) // Decode base 64
        case _ => throw new IOException("Algorithm returned successfully, but cannot be converted to byte array")
      }
    case _ => throw new IOException("Algorithm returned successfully, but cannot be converted to byte array")
  }
  override def map[T](f: AlgoSuccess => T): Option[T] = Some(f(this))
}

case class AlgoFailure(message: String, metadata: Metadata, rawOutput: String) extends AlgoResponse {
  override def isSuccess: Boolean = false
  override def as[T](implicit reads: Reads[T]): T =
    throw new ClassCastException("Algorithm returned an error, and cannot be converted to type")
  override def asOpt[T](implicit reads: Reads[T]): Option[T] = None
  override def asString: String =
    throw new ClassCastException("Algorithm returned an error, and cannot be converted to  string")
  override def asBytes: Array[Byte] =
    throw new ClassCastException("Algorithm returned an error, and cannot be converted to byte array")
  override def map[T](f: AlgoSuccess => T): Option[T] = None
}

object AlgoResponse {

  private implicit val algoResponseErrorReads = Json.reads[AlgoResponseError]
  private implicit val algoResponseMetadataReads = Json.reads[AlgoResponseMetadata]
  private implicit val algoResponseRawReads = Json.reads[AlgoResponseRaw]

  /**
    * Parse server response into an AlgoResponse
    */
  def apply(rawOutput: String, outputType: AlgorithmOutputType): AlgoResponse = {
    val json = Json.parse(rawOutput)
    Json.fromJson[AlgoResponseRaw](json) match {
      case JsSuccess(AlgoResponseRaw(result, None, metadata), _) => AlgoSuccess(result, metadata.metadata, rawOutput)
      case JsSuccess(AlgoResponseRaw(_, Some(error), metadata), _) =>
        AlgoFailure(error.message, metadata.metadata, rawOutput)
      case JsError(_) =>
        AlgoFailure("Failed to parse algorithm response", Metadata(0, ContentTypeVoid, None), rawOutput)
    }
  }

  /**
    * Case classes used to help with JSON parsing
    */
  private case class AlgoResponseRaw(
      result: JsValue,
      error: Option[AlgoResponseError],
      metadata: AlgoResponseMetadata
  )
  private case class AlgoResponseError(message: String)
  private case class AlgoResponseMetadata(
      duration: Double,
      content_type: Option[String],
      stdout: Option[String]
  ) {
    def metadata: Metadata = Metadata(
      duration,
      ContentType(content_type.getOrElse("")),
      stdout
    )
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy