<commit_before>package org.broadinstitute.dsde.agora.server.webservice


import akka.actor.SupervisorStrategy.Stop
import akka.actor.{OneForOneStrategy, _}
import cromwell.parser.WdlParser.SyntaxError
import org.broadinstitute.dsde.agora.server.AgoraConfig
import org.broadinstitute.dsde.agora.server.exceptions._
import org.broadinstitute.dsde.agora.server.webservice.PerRequest._
import spray.http.HttpHeader
import spray.http.StatusCodes._
import spray.httpx.marshalling.ToResponseMarshaller
import spray.routing.RequestContext

import scala.concurrent.duration._

/**
 * This actor controls the lifecycle of a request. It is responsible for forwarding the initial message
 * to a target handling actor. This actor waits for the target actor to signal completion (via a message),
 * timeout, or handle an exception. It is this actors responsibility to respond to the request and
 * shutdown itself and child actors.
 *
 * Request completion can be signaled in 2 ways:
 * 1) with just a response object
 * 2) with a RequestComplete message which can specify http status code as well as the response
 */
trait PerRequest extends Actor {
  import context._

  // JSON Serialization Support
  import spray.httpx.SprayJsonSupport._
  import org.broadinstitute.dsde.agora.server.model.AgoraApiJsonSupport._

  def r: RequestContext
  def target: ActorRef
  def message: AnyRef
  def timeout: Duration

  setReceiveTimeout(timeout)
  target ! message

  def receive = {
    case RequestComplete_(response, marshaller) => complete(response)(marshaller)
    case RequestCompleteWithHeaders_(response, headers, marshaller) => complete(response, headers: _*)(marshaller)
    case ReceiveTimeout => complete(GatewayTimeout)
    case x =>
      system.log.error("Unsupported response message sent to PerRequest actor: " + Option(x).getOrElse("null").toString)
      complete(InternalServerError)
  }

  /**
   * Complete the request sending the given response and status code
   * @param response to send to the caller
   * @param marshaller to use for marshalling the response
   * @return
   */
  private def complete[T](response: T, headers: HttpHeader*)(implicit marshaller: ToResponseMarshaller[T]) = {
    r.withHttpResponseHeadersMapped(h => h ++ headers).complete(response)
    stop(self)
  }

  override val supervisorStrategy =
    OneForOneStrategy(loggingEnabled = AgoraConfig.supervisorLogging) {
      //Should make a single Authorization Exception trait to minimize code duplication.
      case e: AgoraEntityAuthorizationException =>
        r.complete(Unauthorized, AgoraException(e.getMessage, e.getCause, Unauthorized))
        Stop
      case e: NamespaceAuthorizationException =>
        r.complete(Unauthorized, AgoraException(e.getMessage, e.getCause, Unauthorized))
        Stop
      case e: AgoraEntityNotFoundException =>
        r.complete(NotFound, AgoraException(e.getMessage, e.getCause, NotFound))
        Stop
      case e: DockerImageNotFoundException =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: PermissionNotFoundException =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: SyntaxError =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: ValidationException =>
        r.complete(BadRequest,AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: Throwable =>
        r.complete(InternalServerError, AgoraException(e.getMessage, e.getCause, InternalServerError))
        Stop
    }
}


object PerRequest {

  sealed trait PerRequestMessage
  /**
   * Report complete, follows same pattern as spray.routing.RequestContext.complete; examples of how to call
   * that method should apply here too. E.g. even though this method has only one parameter, it can be called
   * with 2 where the first is a StatusCode: RequestComplete(StatusCode.Created, response)
   */
  case class RequestComplete[T](response: T)(implicit val marshaller: ToResponseMarshaller[T]) extends PerRequestMessage

  /**<commit_msg>Log error if unexpected exception bubbles up to PerRequest<commit_after>package org.broadinstitute.dsde.agora.server.webservice


import akka.actor.SupervisorStrategy.Stop
import akka.actor.{OneForOneStrategy, _}
import com.typesafe.scalalogging.slf4j.LazyLogging
import cromwell.parser.WdlParser.SyntaxError
import org.broadinstitute.dsde.agora.server.AgoraConfig
import org.broadinstitute.dsde.agora.server.exceptions._
import org.broadinstitute.dsde.agora.server.webservice.PerRequest._
import spray.http.HttpHeader
import spray.http.StatusCodes._
import spray.httpx.marshalling.ToResponseMarshaller
import spray.routing.RequestContext

import scala.concurrent.duration._

/**
 * This actor controls the lifecycle of a request. It is responsible for forwarding the initial message
 * to a target handling actor. This actor waits for the target actor to signal completion (via a message),
 * timeout, or handle an exception. It is this actors responsibility to respond to the request and
 * shutdown itself and child actors.
 *
 * Request completion can be signaled in 2 ways:
 * 1) with just a response object
 * 2) with a RequestComplete message which can specify http status code as well as the response
 */
trait PerRequest extends Actor with LazyLogging {
  import context._

  // JSON Serialization Support
  import spray.httpx.SprayJsonSupport._
  import org.broadinstitute.dsde.agora.server.model.AgoraApiJsonSupport._

  def r: RequestContext
  def target: ActorRef
  def message: AnyRef
  def timeout: Duration

  setReceiveTimeout(timeout)
  target ! message

  def receive = {
    case RequestComplete_(response, marshaller) => complete(response)(marshaller)
    case RequestCompleteWithHeaders_(response, headers, marshaller) => complete(response, headers: _*)(marshaller)
    case ReceiveTimeout => complete(GatewayTimeout)
    case x =>
      system.log.error("Unsupported response message sent to PerRequest actor: " + Option(x).getOrElse("null").toString)
      complete(InternalServerError)
  }

  /**
   * Complete the request sending the given response and status code
   * @param response to send to the caller
   * @param marshaller to use for marshalling the response
   * @return
   */
  private def complete[T](response: T, headers: HttpHeader*)(implicit marshaller: ToResponseMarshaller[T]) = {
    r.withHttpResponseHeadersMapped(h => h ++ headers).complete(response)
    stop(self)
  }

  override val supervisorStrategy =
    OneForOneStrategy(loggingEnabled = AgoraConfig.supervisorLogging) {
      //Should make a single Authorization Exception trait to minimize code duplication.
      case e: AgoraEntityAuthorizationException =>
        r.complete(Unauthorized, AgoraException(e.getMessage, e.getCause, Unauthorized))
        Stop
      case e: NamespaceAuthorizationException =>
        r.complete(Unauthorized, AgoraException(e.getMessage, e.getCause, Unauthorized))
        Stop
      case e: AgoraEntityNotFoundException =>
        r.complete(NotFound, AgoraException(e.getMessage, e.getCause, NotFound))
        Stop
      case e: DockerImageNotFoundException =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: PermissionNotFoundException =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: SyntaxError =>
        r.complete(BadRequest, AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: ValidationException =>
        r.complete(BadRequest,AgoraException(e.getMessage, e.getCause, BadRequest))
        Stop
      case e: Throwable =>
        logger.error("Exception caught by PerRequest: ", e)
        r.complete(InternalServerError, AgoraException(e.getMessage, e.getCause, InternalServerError))
        Stop
    }
}


object PerRequest {

  sealed trait PerRequestMessage
  /**
   * Report complete, follows same pattern as spray.routing.RequestContext.complete; examples of how to call
   * that method should apply here too. E.g. even though this method has only one parameter, it can be called
   * with 2 where the first is a StatusCode: RequestComplete(StatusCode.Created, response)
   */
  case class RequestComplete[T](response: T)(implicit val marshaller: ToResponseMarshaller[T]) extends PerRequestMessage

  /**
{'fraction_non_alphanumeric': 0.08039192539251565, 'fraction_numerical': 0.0010624483532050526, 'mean_word_length': 4.103614457831325, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3959783', 'n_tokens_mistral': 2293, 'n_tokens_neox': 2292, 'n_words': 689}