Skip to content

Commit

Permalink
Merge pull request #31 from scala-exercises/js-CORS-support
Browse files Browse the repository at this point in the history
Added support for CORS
  • Loading branch information
juanpedromoreno committed Sep 28, 2016
2 parents 2abe135 + 9406ddf commit 5fa54cd
Showing 1 changed file with 67 additions and 41 deletions.
108 changes: 67 additions & 41 deletions server/src/main/scala/org/scalaexercises/evaluator/services.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@

package org.scalaexercises.evaluator

import org.http4s._, org.http4s.dsl._, org.http4s.server._
import org.http4s._
import org.http4s.dsl._
import org.http4s.server._
import org.http4s.server.blaze._
import org.log4s.getLogger

import monix.execution.Scheduler

import scala.concurrent.duration._
import scala.language.postfixOps

import scalaz.concurrent.Task
import scalaz._
import scala.concurrent.duration._

object services {

Expand All @@ -29,47 +29,62 @@ object services {

val evaluator = new Evaluator(20 seconds)

val corsHeaders = Seq(
Header("Vary", "Origin,Access-Control-Request-Methods"),
Header("Access-Control-Allow-Methods", "POST"),
Header("Access-Control-Allow-Origin", "*"),
Header(
"Access-Control-Allow-Headers",
"x-scala-eval-api-token, Content-Type"),
Header("Access-Control-Max-Age", 1.day.toSeconds.toString()))

def evalService =
auth(HttpService {
case req @ POST -> Root / "eval" =>
import io.circe.syntax._
req.decode[EvalRequest] {
evalRequest =>
evaluator.eval[Any](
code = evalRequest.code,
remotes = evalRequest.resolvers,
dependencies = evalRequest.dependencies
) flatMap {
result =>
val response = result match {
case EvalSuccess(cis, res, out) =>
EvalResponse(
`ok`,
Option(res.toString),
Option(res.asInstanceOf[AnyRef].getClass.getName),
cis)
case Timeout(_) =>
EvalResponse(`Timeout Exceded`, None, None, Map.empty)
case UnresolvedDependency(msg) =>
EvalResponse(
`Unresolved Dependency` + " : " + msg,
None,
None,
Map.empty)
case EvalRuntimeError(cis, runtimeError) =>
EvalResponse(
`Runtime Error`,
runtimeError map (_.error.getMessage),
runtimeError map (_.error.getClass.getName),
cis)
case CompilationError(cis) =>
EvalResponse(`Compilation Error`, None, None, cis)
case GeneralError(err) =>
EvalResponse(`Unforeseen Exception`, None, None, Map.empty)
}
Ok(response.asJson)
}
}
req
.decode[EvalRequest] {
evalRequest =>
evaluator.eval[Any](
code = evalRequest.code,
remotes = evalRequest.resolvers,
dependencies = evalRequest.dependencies
) flatMap {
(result: EvalResult[_]) =>
val response = result match {
case EvalSuccess(cis, res, out) =>
EvalResponse(
`ok`,
Option(res.toString),
Option(res.asInstanceOf[AnyRef].getClass.getName),
cis)
case Timeout(_) =>
EvalResponse(`Timeout Exceded`, None, None, Map.empty)
case UnresolvedDependency(msg) =>
EvalResponse(
`Unresolved Dependency` + " : " + msg,
None,
None,
Map.empty)
case EvalRuntimeError(cis, runtimeError) =>
EvalResponse(
`Runtime Error`,
runtimeError map (_.error.getMessage),
runtimeError map (_.error.getClass.getName),
cis)
case CompilationError(cis) =>
EvalResponse(`Compilation Error`, None, None, cis)
case GeneralError(err) =>
EvalResponse(
`Unforeseen Exception`,
None,
None,
Map.empty)
}
Ok(response.asJson)
}
}
.map((r: Response) => r.putHeaders(corsHeaders: _*))
})

def loaderIOService = HttpService {
Expand All @@ -81,6 +96,12 @@ object services {
Ok("loaderio-1318d1b3e06b7bc96dd5de5716f57496")
}

// CORS middleware in http4s can't be combined with our `auth` middleware. We need to handle CORS calls ourselves.
def optionsService = HttpService {
case OPTIONS -> Root / "eval" =>
Ok().putHeaders(corsHeaders: _*)
}

}

object EvaluatorServer extends App {
Expand All @@ -96,9 +117,14 @@ object EvaluatorServer extends App {

logger.info(s"Initializing Evaluator at $ip:$port")

// The order in which services are mounted is really important. They're executed from bottom to top, and they won't be
// checked for repeated methods or routes. That's why we set the `optionsService` before the `evalService` one, as if
// not, execution of a OPTIONS call to /eval would lead to `evalService`, even if that service doesn't recognize the
// OPTIONS verb.
BlazeBuilder
.bindHttp(port, ip)
.mountService(evalService)
.mountService(optionsService)
.mountService(loaderIOService)
.start
.run
Expand Down

0 comments on commit 5fa54cd

Please sign in to comment.