From fbedda8c43b5b4282c10330da04f9fc80094374c Mon Sep 17 00:00:00 2001 From: dominv Date: Tue, 7 Mar 2017 14:14:41 +0100 Subject: [PATCH 01/11] Including the compilerFlags in the EvalRequest model and adding dding the ability to process them --- .../evaluator/api/Evaluator.scala | 5 +-- .../evaluator/free/algebra/EvaluatorOps.scala | 8 +++-- .../free/interpreters/Interpreter.scala | 4 +-- .../scalaexercises/evaluator/evaluation.scala | 17 ++++++---- .../scalaexercises/evaluator/services.scala | 31 +++++++++++++++++-- .../org/scalaexercises/evaluator/types.scala | 3 +- 6 files changed, 52 insertions(+), 16 deletions(-) diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala index 05db870a..dbb68b99 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala @@ -23,10 +23,11 @@ class Evaluator { authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String): Future[EvaluationResponse[EvalResponse]] = + code: String, + compilerFlags: List[String] = Nil): Future[EvaluationResponse[EvalResponse]] = httpClient.post[EvalResponse]( url = url, secretKey = authKey, - data = EvalRequest(resolvers, dependencies, code).asJson.noSpaces) + data = EvalRequest(resolvers, dependencies, code, compilerFlags).asJson.noSpaces) } diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala index 11b3e555..3276ea22 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala @@ -14,7 +14,8 @@ final case class Evaluates(url: String, authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String) + code: String, + compilerFlags: List[String] = Nil) extends EvaluatorOp[EvaluationResponse[EvalResponse]] class EvaluatorOps[F[_]](implicit I: Inject[EvaluatorOp, F]) { @@ -24,10 +25,11 @@ class EvaluatorOps[F[_]](implicit I: Inject[EvaluatorOp, F]) { authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String + code: String, + compilerFlags: List[String] = Nil ): Free[F, EvaluationResponse[EvalResponse]] = Free.inject[EvaluatorOp, F]( - Evaluates(url, authKey, resolvers, dependencies, code)) + Evaluates(url, authKey, resolvers, dependencies, code, compilerFlags)) } diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala index 687bbceb..e48df867 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala @@ -22,8 +22,8 @@ trait Interpreter { val evaluator = new Evaluator() def apply[A](fa: EvaluatorOp[A]): Future[A] = fa match { - case Evaluates(url, authKey, resolvers, dependencies, code) ⇒ - evaluator.eval(url, authKey, resolvers, dependencies, code) + case Evaluates(url, authKey, resolvers, dependencies, code, compilerFlags) ⇒ + evaluator.eval(url, authKey, resolvers, dependencies, code, compilerFlags) } } diff --git a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala index 9c73411e..03a9b135 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala @@ -73,15 +73,17 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( ) } yield artifacts.sequenceU - def createEval(jars: Seq[File]) = { + def createEval(jars: Seq[File], compilerFlags: List[String]) = { new Eval(jars = jars.toList) { @volatile var errors: Map[String, List[CompilationInfo]] = Map.empty override lazy val compilerSettings: Settings = new EvalSettings(None) { - if (!jars.isEmpty) { + if (jars.nonEmpty) { val newJars = jars.mkString(File.pathSeparator) classpath.value = newJars + File.pathSeparator + classpath.value } + + processArguments(compilerFlags, processAll = true) } override lazy val compilerMessageHandler: Option[Reporter] = Some( @@ -101,8 +103,10 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( } } - private[this] def evaluate[T](code: String, jars: Seq[File]): EvalResult[T] = { - val eval = createEval(jars) + private[this] def evaluate[T](code: String, + jars: Seq[File], + compilerFlags: List[String]): EvalResult[T] = { + val eval = createEval(jars, compilerFlags) val result = for { _ ← Try(eval.check(code)) @@ -126,14 +130,15 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( def eval[T]( code: String, remotes: Seq[Remote] = Nil, - dependencies: Seq[Dependency] = Nil + dependencies: Seq[Dependency] = Nil, + compilerFlags: List[String] = Nil ): Task[EvalResult[T]] = { for { allJars <- fetchArtifacts(remotes, dependencies) result <- allJars match { case \/-(jars) => Task({ - evaluate(code, jars) + evaluate(code, jars, compilerFlags) }).timed(timeout) .handle({ case err: TimeoutException => Timeout[T](timeout) diff --git a/server/src/main/scala/org/scalaexercises/evaluator/services.scala b/server/src/main/scala/org/scalaexercises/evaluator/services.scala index e1679773..eed0ccd4 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/services.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/services.scala @@ -5,6 +5,7 @@ package org.scalaexercises.evaluator +import io.circe.Decoder import org.http4s._ import org.http4s.dsl._ import org.http4s.server._ @@ -42,13 +43,39 @@ object services { auth(HttpService { case req @ POST -> Root / "eval" => import io.circe.syntax._ + + val decoder: EntityDecoder[EvalRequest] = { + implicit val evalRequestDecoder: Decoder[EvalRequest] = + Decoder.instance { + cursor => + for { + resolvers <- cursor.downField("resolvers").as[List[String]] + dependencies <- cursor + .downField("dependencies") + .as[List[Dependency]] + code <- cursor.downField("code").as[String] + compilerFlags <- cursor + .downField("compilerFlags") + .as[Option[List[String]]] + } yield + EvalRequest( + resolvers, + dependencies, + code, + compilerFlags.getOrElse(Nil)) + } + + jsonDecoderOf(evalRequestDecoder) + } + req - .decode[EvalRequest] { + .decodeWith[EvalRequest](decoder, strict = false) { evalRequest => evaluator.eval[Any]( code = evalRequest.code, remotes = evalRequest.resolvers, - dependencies = evalRequest.dependencies + dependencies = evalRequest.dependencies, + compilerFlags = evalRequest.compilerFlags ) flatMap { (result: EvalResult[_]) => val response = result match { diff --git a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala b/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala index 7abdb336..67678570 100644 --- a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala +++ b/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala @@ -46,7 +46,8 @@ final case class Dependency(groupId: String, final case class EvalRequest(resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String) + code: String, + compilerFlags: List[String] = Nil) final case class EvalResponse(msg: String, value: Option[String] = None, From 897b0589d47832cdcf38d935bad0c99dee33a466 Mon Sep 17 00:00:00 2001 From: dominv Date: Tue, 7 Mar 2017 16:23:21 +0100 Subject: [PATCH 02/11] Adding tests to EvalEndpointSpec --- .../evaluator/EvalEndpointSpec.scala | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala index 79cc03fa..c7513be2 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala @@ -183,5 +183,52 @@ class EvalEndpointSpec extends FunSpec with Matchers { `Accept-Ranges`(Nil)).status should be(HttpStatus.Unauthorized) } + it("evaluates the code when no compilerFlags are provided") { + verifyEvalResponse( + response = serve( + EvalRequest( + code = "{import cats._; Eval.now(42).value}", + resolvers = sonatypeReleases, + dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil + ), + `X-Scala-Eval-Api-Token`(validToken)), + expectedStatus = HttpStatus.Ok, + expectedValue = Some("42"), + expectedMessage = `ok` + ) + } + + it("evaluates the code when an empty list of compilerFlags is provided") { + verifyEvalResponse( + response = serve( + EvalRequest( + code = "{import cats._; Eval.now(42).value}", + resolvers = sonatypeReleases, + dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, + compilerFlags = Nil + ), + `X-Scala-Eval-Api-Token`(validToken)), + expectedStatus = HttpStatus.Ok, + expectedValue = Some("42"), + expectedMessage = `ok` + ) + } + + it("evaluates the code when a list of compilerFlags is provided") { + verifyEvalResponse( + response = serve( + EvalRequest( + code = "{import cats._; Eval.now(42).value}", + resolvers = sonatypeReleases, + dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, + compilerFlags = List("-print", "-optimise", "-help") + ), + `X-Scala-Eval-Api-Token`(validToken)), + expectedStatus = HttpStatus.Ok, + expectedValue = Some("42"), + expectedMessage = `ok` + ) + } + } } From 1975a8e3273ac2b68608ddb9a931195fe381ca7e Mon Sep 17 00:00:00 2001 From: dominv Date: Tue, 7 Mar 2017 16:23:53 +0100 Subject: [PATCH 03/11] Removing ignore from some EvaluatorSpec's tests --- .../evaluator/EvaluatorSpec.scala | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala index f259e99d..15d777d8 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala @@ -33,12 +33,11 @@ class EvaluatorSpec extends FunSpec with Matchers { } } - ignore("can load dependencies for an evaluation") { + describe("can load dependencies for an evaluation") { val code = """ -import cats._ - -Eval.now(42).value - """ + import cats._ + Eval.now(42).value + """ val remotes = List("https://oss.sonatype.org/content/repositories/releases/") val dependencies = List( @@ -58,11 +57,11 @@ Eval.now(42).value } } - ignore("can load different versions of a dependency across evaluations") { + describe("can load different versions of a dependency across evaluations") { val code = """ -import cats._ -Eval.now(42).value - """ + import cats._ + Eval.now(42).value + """ val remotes = List("https://oss.sonatype.org/content/repositories/releases/") val dependencies1 = List( @@ -95,11 +94,11 @@ Eval.now(42).value } } - ignore("can run code from the exercises content") { + describe("can run code from the exercises content") { val code = """ -import stdlib._ -Asserts.scalaTestAsserts(true) -""" + import stdlib._ + Asserts.scalaTestAsserts(true) + """ val remotes = List("https://oss.sonatype.org/content/repositories/releases/") val dependencies = List( @@ -119,11 +118,11 @@ Asserts.scalaTestAsserts(true) } } - ignore("captures exceptions when running the exercises content") { + describe("captures exceptions when running the exercises content") { val code = """ -import stdlib._ -Asserts.scalaTestAsserts(false) -""" + import stdlib._ + Asserts.scalaTestAsserts(false) + """ val remotes = List("https://oss.sonatype.org/content/repositories/releases/") val dependencies = List( From 283c8db4e8653f61d4232fc95bb1508381040411 Mon Sep 17 00:00:00 2001 From: dominv Date: Tue, 7 Mar 2017 16:37:31 +0100 Subject: [PATCH 04/11] Optimizing imports --- .../evaluator/free/algebra/EvaluatorOps.scala | 2 +- .../scalaexercises/evaluator/evaluation.scala | 39 ++++++++----------- .../scalaexercises/evaluator/services.scala | 9 ++--- .../evaluator/EvalEndpointSpec.scala | 24 ++++++------ .../evaluator/EvaluatorSpec.scala | 5 ++- 5 files changed, 35 insertions(+), 44 deletions(-) diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala index 3276ea22..c928c736 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala @@ -6,8 +6,8 @@ package org.scalaexercises.evaluator.free.algebra import cats.free.{Free, Inject} -import org.scalaexercises.evaluator.{Dependency, EvalResponse} import org.scalaexercises.evaluator.EvaluatorResponses.EvaluationResponse +import org.scalaexercises.evaluator.{Dependency, EvalResponse} sealed trait EvaluatorOp[A] final case class Evaluates(url: String, diff --git a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala index 03a9b135..291b684a 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala @@ -5,34 +5,29 @@ package org.scalaexercises.evaluator -import scala.language.reflectiveCalls - -import java.io.{File, InputStream} +import java.io.File +import java.math.BigInteger import java.net.URLClassLoader -import java.nio.file.Path -import java.util.jar.JarFile -import java.util.concurrent.TimeoutException import java.security.MessageDigest -import java.math.BigInteger +import java.util.concurrent.TimeoutException +import java.util.jar.JarFile -import scala.tools.nsc.{Global, Settings} -import scala.tools.nsc.reporters._ -import scala.tools.nsc.io.{VirtualDirectory, AbstractFile} -import scala.reflect.internal.util.{Position, NoPosition, BatchSourceFile, AbstractFileClassLoader} +import coursier._ +import monix.execution.Scheduler +import org.scalaexercises.evaluator.Eval.CompilerException -import scalaz._; import Scalaz._ +import scala.concurrent.duration._ +import scala.language.reflectiveCalls +import scala.reflect.internal.util.{AbstractFileClassLoader, BatchSourceFile, Position} +import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} +import scala.tools.nsc.reporters._ +import scala.tools.nsc.{Global, Settings} import scala.util.Try import scala.util.control.NonFatal -import scala.concurrent._ -import scala.concurrent.duration._ +import scalaz.Scalaz._ +import scalaz._ import scalaz.concurrent.Task -import monix.execution.Scheduler - -import coursier._ - -import org.scalaexercises.evaluator._ - class Evaluator(timeout: FiniteDuration = 20.seconds)( implicit S: Scheduler ) { @@ -119,7 +114,7 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( case scala.util.Success(r) ⇒ EvalSuccess[T](errors, r, "") case scala.util.Failure(t) ⇒ t match { - case e: Eval.CompilerException ⇒ CompilationError(errors) + case e: CompilerException ⇒ CompilationError(errors) case NonFatal(e) ⇒ EvalRuntimeError(errors, Option(RuntimeError(e, None))) case e ⇒ GeneralError(e) @@ -258,7 +253,7 @@ private class StringCompiler( case _ => List(List(reporter.toString)) } - throw new Eval.CompilerException(msgs) + throw new CompilerException(msgs) } } diff --git a/server/src/main/scala/org/scalaexercises/evaluator/services.scala b/server/src/main/scala/org/scalaexercises/evaluator/services.scala index eed0ccd4..467bbe5d 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/services.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/services.scala @@ -6,23 +6,20 @@ package org.scalaexercises.evaluator import io.circe.Decoder +import monix.execution.Scheduler 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.language.postfixOps -import scalaz.concurrent.Task -import scalaz._ import scala.concurrent.duration._ +import scala.language.postfixOps object services { + import EvalResponse.messages._ import codecs._ import io.circe.generic.auto._ - import EvalResponse.messages._ private val logger = getLogger diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala index c7513be2..a5be7b2a 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala @@ -5,27 +5,25 @@ package org.scalaexercises.evaluator -import org.scalatest._ -import org.http4s._ -import org.http4s.headers._ -import org.http4s.dsl._ -import org.http4s.server._ +import java.nio.charset.StandardCharsets -import io.circe.syntax._ import io.circe.generic.auto._ -import scalaz.stream.Process.emit -import java.nio.charset.StandardCharsets +import io.circe.syntax._ +import org.http4s.dsl._ +import org.http4s.headers._ +import org.http4s.{Status => HttpStatus, _} +import org.scalatest._ +import pdi.jwt.{Jwt, JwtAlgorithm} import scodec.bits.ByteVector -import pdi.jwt.{Jwt, JwtAlgorithm, JwtHeader, JwtClaim, JwtOptions} -import org.http4s.{Status => HttpStatus} +import scalaz.stream.Process.emit class EvalEndpointSpec extends FunSpec with Matchers { - import services._ - import codecs._ - import auth._ import EvalResponse.messages._ + import auth._ + import codecs._ + import services._ val sonatypeReleases = "https://oss.sonatype.org/content/repositories/releases/" :: Nil diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala index 15d777d8..b96b0a48 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala @@ -5,12 +5,13 @@ package org.scalaexercises.evaluator -import scala.concurrent.duration._ -import scala.language.postfixOps import monix.execution.Scheduler import org.scalatest._ import org.scalatest.exceptions.TestFailedException +import scala.concurrent.duration._ +import scala.language.postfixOps + class EvaluatorSpec extends FunSpec with Matchers { implicit val scheduler: Scheduler = Scheduler.io("exercises-spec") val evaluator = new Evaluator(20 seconds) From e78eaec47f79e4b4a7798860069be757491605e8 Mon Sep 17 00:00:00 2001 From: dominv Date: Tue, 7 Mar 2017 17:01:20 +0100 Subject: [PATCH 05/11] Adding tests to EvaluatorSpec --- .../evaluator/EvaluatorSpec.scala | 163 +++++++++++------- 1 file changed, 100 insertions(+), 63 deletions(-) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala index b96b0a48..dd23d822 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala @@ -16,6 +16,8 @@ class EvaluatorSpec extends FunSpec with Matchers { implicit val scheduler: Scheduler = Scheduler.io("exercises-spec") val evaluator = new Evaluator(20 seconds) + val remotes = "https://oss.sonatype.org/content/repositories/releases/" :: Nil + describe("evaluation") { it("can evaluate simple expressions") { val result: EvalResult[Int] = evaluator.eval("{ 41 + 1 }").run @@ -35,22 +37,12 @@ class EvaluatorSpec extends FunSpec with Matchers { } describe("can load dependencies for an evaluation") { - val code = """ - import cats._ - Eval.now(42).value - """ - val remotes = - List("https://oss.sonatype.org/content/repositories/releases/") - val dependencies = List( - Dependency("org.typelevel", "cats_2.11", "0.6.0") - ) + val code = "{import cats._; Eval.now(42).value}" + + val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil val result: EvalResult[Int] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies - ) + .eval(code, remotes = remotes, dependencies = dependencies) .run result should matchPattern { @@ -59,32 +51,17 @@ class EvaluatorSpec extends FunSpec with Matchers { } describe("can load different versions of a dependency across evaluations") { - val code = """ - import cats._ - Eval.now(42).value - """ - val remotes = - List("https://oss.sonatype.org/content/repositories/releases/") - val dependencies1 = List( - Dependency("org.typelevel", "cats_2.11", "0.4.1") - ) - val dependencies2 = List( - Dependency("org.typelevel", "cats_2.11", "0.6.0") - ) + val code = "{import cats._; Eval.now(42).value}" + + val dependencies1 = Dependency("org.typelevel", "cats_2.11", "0.4.1") :: Nil + + val dependencies2 = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil val result1: EvalResult[Int] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies1 - ) + .eval(code, remotes = remotes, dependencies = dependencies1) .run val result2: EvalResult[Int] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies2 - ) + .eval(code, remotes = remotes, dependencies = dependencies2) .run result1 should matchPattern { @@ -96,52 +73,112 @@ class EvaluatorSpec extends FunSpec with Matchers { } describe("can run code from the exercises content") { - val code = """ - import stdlib._ - Asserts.scalaTestAsserts(true) - """ - val remotes = - List("https://oss.sonatype.org/content/repositories/releases/") - val dependencies = List( - Dependency("org.scala-exercises", "exercises-stdlib_2.11", "0.2.0") - ) + val code = "{import stdlib._; Asserts.scalaTestAsserts(true)}" + + val dependencies = Dependency( + "org.scala-exercises", + "exercises-stdlib_2.11", + "0.2.0") :: Nil + + val result: EvalResult[Unit] = evaluator + .eval(code, remotes = remotes, dependencies = dependencies) + .run + + result should matchPattern { + case EvalSuccess(_, (), _) => + } + } + + describe("captures exceptions when running the exercises content") { + val code = "{import stdlib._; Asserts.scalaTestAsserts(false)}" + + val dependencies = Dependency( + "org.scala-exercises", + "exercises-stdlib_2.11", + "0.2.0") :: Nil + + val result: EvalResult[Unit] = evaluator + .eval(code, remotes = remotes, dependencies = dependencies) + .run + + result should matchPattern { + case EvalRuntimeError( + _, + Some(RuntimeError(err: TestFailedException, _))) => + } + } + + describe("can run code from the exercises content") { + val code = "{import cats._; Eval.now(42).value}" + + val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil + + val compilerFlags = Nil val result: EvalResult[Unit] = evaluator .eval( code, remotes = remotes, - dependencies = dependencies - ) + dependencies = dependencies, + compilerFlags = compilerFlags) .run result should matchPattern { - case EvalSuccess(_, (), _) => + case EvalSuccess(_, 42, _) => } } - describe("captures exceptions when running the exercises content") { - val code = """ - import stdlib._ - Asserts.scalaTestAsserts(false) - """ - val remotes = - List("https://oss.sonatype.org/content/repositories/releases/") - val dependencies = List( - Dependency("org.scala-exercises", "exercises-stdlib_2.11", "0.2.0") - ) + describe("can evaluate code without any compiler flags provided") { + val code = "{import cats._; Eval.now(42).value}" + + val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil + + val result: EvalResult[Unit] = evaluator + .eval(code, remotes = remotes, dependencies = dependencies) + .run + + result should matchPattern { + case EvalSuccess(_, 42, _) => + } + } + + describe("can evaluate code with an empty list of compiler flags provided") { + val code = "{import cats._; Eval.now(42).value}" + + val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil + + val compilerFlags = Nil val result: EvalResult[Unit] = evaluator .eval( code, remotes = remotes, - dependencies = dependencies - ) + dependencies = dependencies, + compilerFlags = compilerFlags) .run result should matchPattern { - case EvalRuntimeError( - _, - Some(RuntimeError(err: TestFailedException, _))) => + case EvalSuccess(_, 42, _) => + } + } + + describe("can evaluate code with a list of compiler flags provided") { + val code = "{import cats._; Eval.now(42).value}" + + val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil + + val compilerFlags = List("-X", "-help") + + val result: EvalResult[Unit] = evaluator + .eval( + code, + remotes = remotes, + dependencies = dependencies, + compilerFlags = compilerFlags) + .run + + result should matchPattern { + case EvalSuccess(_, 42, _) => } } } From ed1e56e512932a13987e7bc5c6c30c81448e2afd Mon Sep 17 00:00:00 2001 From: dominv Date: Wed, 8 Mar 2017 10:59:55 +0100 Subject: [PATCH 06/11] Removing compilerFlag from EvalEndpointSpec --- .../scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala index a5be7b2a..9c5119c1 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala @@ -219,7 +219,7 @@ class EvalEndpointSpec extends FunSpec with Matchers { code = "{import cats._; Eval.now(42).value}", resolvers = sonatypeReleases, dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, - compilerFlags = List("-print", "-optimise", "-help") + compilerFlags = List("-optimise", "-help") ), `X-Scala-Eval-Api-Token`(validToken)), expectedStatus = HttpStatus.Ok, From da2b1afcec3c331f24ef7fef221835ef186b6215 Mon Sep 17 00:00:00 2001 From: dominv Date: Wed, 8 Mar 2017 11:00:03 +0100 Subject: [PATCH 07/11] Updating EvaluatorApi to accept compilerFlags --- .../scala/org/scalaexercises/evaluator/EvaluatorAPI.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala index 7f9b92e2..28b63fc7 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala @@ -14,6 +14,7 @@ class EvaluatorAPI[F[_]](url: String, authKey: String)( def evaluates(resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String): Free[F, EvaluationResponse[EvalResponse]] = - O.evaluates(url, authKey, resolvers, dependencies, code) -} + code: String, + compilerFlags: List[String] = Nil): Free[F, EvaluationResponse[EvalResponse]] = + O.evaluates(url, authKey, resolvers, dependencies, code, compilerFlags) +} \ No newline at end of file From ceebe6c5f1872e1383fe1c009741396f8e0bfbc4 Mon Sep 17 00:00:00 2001 From: dominv Date: Wed, 8 Mar 2017 16:58:31 +0100 Subject: [PATCH 08/11] Allowing a pre-established set of compiler flags --- server/src/main/resources/application.conf | 26 ++++- .../scalaexercises/evaluator/services.scala | 98 +++++++++++-------- 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/server/src/main/resources/application.conf b/server/src/main/resources/application.conf index b0a8cb24..97fad0b0 100644 --- a/server/src/main/resources/application.conf +++ b/server/src/main/resources/application.conf @@ -12,4 +12,28 @@ eval.auth { # Loader IO Configuration loaderio.verificationToken=loaderio-changeme -loaderio.verificationToken=${?LOADERIO_VERIFICATION_TOKEN} \ No newline at end of file +loaderio.verificationToken=${?LOADERIO_VERIFICATION_TOKEN} + +allowed.compiler{ + flags = ["-deprecation", + "-encoding", "UTF-8", + "-feature", + "-language:existentials", + "-language:experimental.macros", + "-language:higherKinds", + "-language:implicitConversions", + "-language:reflectiveCalls", + "-unchecked", + "-Xfatal-warnings", + "-Xfuture", + "-Xlint", + "-Xplugin-require:macroparadise", + "-Yinline-warnings", + "-Yliteral-types", + "-Yno-adapted-args", + "-Ywarn-dead-code", + "-Ywarn-numeric-widen", + "-Ywarn-value-discard", + "-Ypartial-unification"] + flags = ${?ALLOWED_COMPILER_FLAGS} +} \ No newline at end of file diff --git a/server/src/main/scala/org/scalaexercises/evaluator/services.scala b/server/src/main/scala/org/scalaexercises/evaluator/services.scala index 467bbe5d..66f64d74 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/services.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/services.scala @@ -5,6 +5,7 @@ package org.scalaexercises.evaluator +import com.typesafe.config.ConfigFactory import io.circe.Decoder import monix.execution.Scheduler import org.http4s._ @@ -12,6 +13,7 @@ import org.http4s.dsl._ import org.http4s.server.blaze._ import org.log4s.getLogger +import scala.collection.JavaConversions._ import scala.concurrent.duration._ import scala.language.postfixOps @@ -23,6 +25,17 @@ object services { private val logger = getLogger + val config = ConfigFactory.load() + + val AllowedCompilerFlags = "allowed.compiler.flags" + + val allowedCompilerFlags = if (config.hasPath(AllowedCompilerFlags)) { + config.getStringList(AllowedCompilerFlags).toList + } else { + throw new IllegalStateException( + "Missing -D allowed.compiler.flags=[YOUR_KEY_HERE] or env var [ALLOWED_COMPILER_FLAGS]") + } + implicit val scheduler: Scheduler = Scheduler.io("scala-evaluator") val evaluator = new Evaluator(20 seconds) @@ -68,45 +81,52 @@ object services { req .decodeWith[EvalRequest](decoder, strict = false) { evalRequest => - evaluator.eval[Any]( - code = evalRequest.code, - remotes = evalRequest.resolvers, - dependencies = evalRequest.dependencies, - compilerFlags = evalRequest.compilerFlags - ) 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) - } + val (validCompilerFlags, invalidCompilerFlags) = + evalRequest.compilerFlags.partition( + allowedCompilerFlags.contains(_)) + if (invalidCompilerFlags.isEmpty) { + evaluator.eval[Any]( + code = evalRequest.code, + remotes = evalRequest.resolvers, + dependencies = evalRequest.dependencies, + compilerFlags = validCompilerFlags + ) 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) + } + } else + BadRequest( + s"Invalid compiler flags: ${invalidCompilerFlags.mkString(",")}") } .map((r: Response) => r.putHeaders(corsHeaders: _*)) }) From bc123a667c467845be4c34bc83237260e62bed4c Mon Sep 17 00:00:00 2001 From: dominv Date: Wed, 8 Mar 2017 17:06:21 +0100 Subject: [PATCH 09/11] Updating tests in EvalEndpointSpec and in EvaluatorSpec --- .../evaluator/EvalEndpointSpec.scala | 16 ++++++++++++++-- .../scalaexercises/evaluator/EvaluatorSpec.scala | 5 +++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala index 9c5119c1..09c04dfa 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala @@ -212,14 +212,14 @@ class EvalEndpointSpec extends FunSpec with Matchers { ) } - it("evaluates the code when a list of compilerFlags is provided") { + it("evaluates the code when a list of allowed compilerFlags is provided") { verifyEvalResponse( response = serve( EvalRequest( code = "{import cats._; Eval.now(42).value}", resolvers = sonatypeReleases, dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, - compilerFlags = List("-optimise", "-help") + compilerFlags = List("-deprecation", "-feature") ), `X-Scala-Eval-Api-Token`(validToken)), expectedStatus = HttpStatus.Ok, @@ -228,5 +228,17 @@ class EvalEndpointSpec extends FunSpec with Matchers { ) } + it("rejects requests with invalid compilerFlags") { + serve( + EvalRequest( + code = "1", + resolvers = Nil, + dependencies = Nil, + compilerFlags = List("-optimise", "-help") + ), + `X-Scala-Eval-Api-Token`(validToken)).status should be( + HttpStatus.BadRequest) + } + } } diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala index dd23d822..a009ebe3 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala @@ -162,12 +162,13 @@ class EvaluatorSpec extends FunSpec with Matchers { } } - describe("can evaluate code with a list of compiler flags provided") { + describe( + "can evaluate code with a list of allowed compiler flags provided") { val code = "{import cats._; Eval.now(42).value}" val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - val compilerFlags = List("-X", "-help") + val compilerFlags = List("-deprecation", "-feature") val result: EvalResult[Unit] = evaluator .eval( From 228a85c44768e5cb19d2f7c42e3fe545b1dbaf20 Mon Sep 17 00:00:00 2001 From: dominv Date: Thu, 9 Mar 2017 16:51:07 +0100 Subject: [PATCH 10/11] Adding the compiler plugin to the compiler settings --- .../main/scala/org/scalaexercises/evaluator/evaluation.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala index 291b684a..d10e6d1d 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala @@ -76,6 +76,11 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( if (jars.nonEmpty) { val newJars = jars.mkString(File.pathSeparator) classpath.value = newJars + File.pathSeparator + classpath.value + + (jars map (_.toString)).find(_.contains("paradise")) match { + case Some(compilerJar) => plugin.appendToValue(compilerJar) + case None => + } } processArguments(compilerFlags, processAll = true) From ab282e253d6d1dede832462dd7fe1d44ec8c4383 Mon Sep 17 00:00:00 2001 From: dominv Date: Mon, 13 Mar 2017 11:46:12 +0100 Subject: [PATCH 11/11] Removing unnecessary compilerFlags processing --- .../evaluator/EvaluatorAPI.scala | 5 +- .../evaluator/api/Evaluator.scala | 11 +- .../evaluator/free/algebra/EvaluatorOps.scala | 8 +- .../free/interpreters/Interpreter.scala | 4 +- server/src/main/resources/application.conf | 24 ---- .../scalaexercises/evaluator/evaluation.scala | 15 +-- .../scalaexercises/evaluator/services.scala | 125 ++++++------------ .../evaluator/EvalEndpointSpec.scala | 59 --------- .../evaluator/EvaluatorSpec.scala | 60 --------- .../org/scalaexercises/evaluator/types.scala | 3 +- 10 files changed, 57 insertions(+), 257 deletions(-) diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala index 28b63fc7..757381a6 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala @@ -14,7 +14,6 @@ class EvaluatorAPI[F[_]](url: String, authKey: String)( def evaluates(resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String, - compilerFlags: List[String] = Nil): Free[F, EvaluationResponse[EvalResponse]] = - O.evaluates(url, authKey, resolvers, dependencies, code, compilerFlags) + code: String): Free[F, EvaluationResponse[EvalResponse]] = + O.evaluates(url, authKey, resolvers, dependencies, code) } \ No newline at end of file diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala index dbb68b99..9449a273 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala @@ -5,11 +5,11 @@ package org.scalaexercises.evaluator.api -import org.scalaexercises.evaluator.EvaluatorResponses.EvaluationResponse -import org.scalaexercises.evaluator.{Decoders, Dependency, EvalRequest, EvalResponse} -import org.scalaexercises.evaluator.http.HttpClient import io.circe.generic.auto._ import io.circe.syntax._ +import org.scalaexercises.evaluator.EvaluatorResponses.EvaluationResponse +import org.scalaexercises.evaluator.http.HttpClient +import org.scalaexercises.evaluator.{Decoders, Dependency, EvalRequest, EvalResponse} import scala.concurrent.Future @@ -23,11 +23,10 @@ class Evaluator { authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String, - compilerFlags: List[String] = Nil): Future[EvaluationResponse[EvalResponse]] = + code: String): Future[EvaluationResponse[EvalResponse]] = httpClient.post[EvalResponse]( url = url, secretKey = authKey, - data = EvalRequest(resolvers, dependencies, code, compilerFlags).asJson.noSpaces) + data = EvalRequest(resolvers, dependencies, code).asJson.noSpaces) } diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala index c928c736..0a91a708 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala @@ -14,8 +14,7 @@ final case class Evaluates(url: String, authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String, - compilerFlags: List[String] = Nil) + code: String) extends EvaluatorOp[EvaluationResponse[EvalResponse]] class EvaluatorOps[F[_]](implicit I: Inject[EvaluatorOp, F]) { @@ -25,11 +24,10 @@ class EvaluatorOps[F[_]](implicit I: Inject[EvaluatorOp, F]) { authKey: String, resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String, - compilerFlags: List[String] = Nil + code: String ): Free[F, EvaluationResponse[EvalResponse]] = Free.inject[EvaluatorOp, F]( - Evaluates(url, authKey, resolvers, dependencies, code, compilerFlags)) + Evaluates(url, authKey, resolvers, dependencies, code)) } diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala index e48df867..687bbceb 100644 --- a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala +++ b/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala @@ -22,8 +22,8 @@ trait Interpreter { val evaluator = new Evaluator() def apply[A](fa: EvaluatorOp[A]): Future[A] = fa match { - case Evaluates(url, authKey, resolvers, dependencies, code, compilerFlags) ⇒ - evaluator.eval(url, authKey, resolvers, dependencies, code, compilerFlags) + case Evaluates(url, authKey, resolvers, dependencies, code) ⇒ + evaluator.eval(url, authKey, resolvers, dependencies, code) } } diff --git a/server/src/main/resources/application.conf b/server/src/main/resources/application.conf index 97fad0b0..c028de2d 100644 --- a/server/src/main/resources/application.conf +++ b/server/src/main/resources/application.conf @@ -13,27 +13,3 @@ eval.auth { # Loader IO Configuration loaderio.verificationToken=loaderio-changeme loaderio.verificationToken=${?LOADERIO_VERIFICATION_TOKEN} - -allowed.compiler{ - flags = ["-deprecation", - "-encoding", "UTF-8", - "-feature", - "-language:existentials", - "-language:experimental.macros", - "-language:higherKinds", - "-language:implicitConversions", - "-language:reflectiveCalls", - "-unchecked", - "-Xfatal-warnings", - "-Xfuture", - "-Xlint", - "-Xplugin-require:macroparadise", - "-Yinline-warnings", - "-Yliteral-types", - "-Yno-adapted-args", - "-Ywarn-dead-code", - "-Ywarn-numeric-widen", - "-Ywarn-value-discard", - "-Ypartial-unification"] - flags = ${?ALLOWED_COMPILER_FLAGS} -} \ No newline at end of file diff --git a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala index d10e6d1d..c1d17ec6 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala @@ -68,7 +68,7 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( ) } yield artifacts.sequenceU - def createEval(jars: Seq[File], compilerFlags: List[String]) = { + def createEval(jars: Seq[File]) = { new Eval(jars = jars.toList) { @volatile var errors: Map[String, List[CompilationInfo]] = Map.empty @@ -82,8 +82,6 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( case None => } } - - processArguments(compilerFlags, processAll = true) } override lazy val compilerMessageHandler: Option[Reporter] = Some( @@ -103,10 +101,8 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( } } - private[this] def evaluate[T](code: String, - jars: Seq[File], - compilerFlags: List[String]): EvalResult[T] = { - val eval = createEval(jars, compilerFlags) + private[this] def evaluate[T](code: String, jars: Seq[File]): EvalResult[T] = { + val eval = createEval(jars) val result = for { _ ← Try(eval.check(code)) @@ -130,15 +126,14 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( def eval[T]( code: String, remotes: Seq[Remote] = Nil, - dependencies: Seq[Dependency] = Nil, - compilerFlags: List[String] = Nil + dependencies: Seq[Dependency] = Nil ): Task[EvalResult[T]] = { for { allJars <- fetchArtifacts(remotes, dependencies) result <- allJars match { case \/-(jars) => Task({ - evaluate(code, jars, compilerFlags) + evaluate(code, jars) }).timed(timeout) .handle({ case err: TimeoutException => Timeout[T](timeout) diff --git a/server/src/main/scala/org/scalaexercises/evaluator/services.scala b/server/src/main/scala/org/scalaexercises/evaluator/services.scala index 66f64d74..bd42b9ee 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/services.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/services.scala @@ -5,15 +5,12 @@ package org.scalaexercises.evaluator -import com.typesafe.config.ConfigFactory -import io.circe.Decoder import monix.execution.Scheduler import org.http4s._ import org.http4s.dsl._ import org.http4s.server.blaze._ import org.log4s.getLogger -import scala.collection.JavaConversions._ import scala.concurrent.duration._ import scala.language.postfixOps @@ -25,17 +22,6 @@ object services { private val logger = getLogger - val config = ConfigFactory.load() - - val AllowedCompilerFlags = "allowed.compiler.flags" - - val allowedCompilerFlags = if (config.hasPath(AllowedCompilerFlags)) { - config.getStringList(AllowedCompilerFlags).toList - } else { - throw new IllegalStateException( - "Missing -D allowed.compiler.flags=[YOUR_KEY_HERE] or env var [ALLOWED_COMPILER_FLAGS]") - } - implicit val scheduler: Scheduler = Scheduler.io("scala-evaluator") val evaluator = new Evaluator(20 seconds) @@ -53,80 +39,47 @@ object services { auth(HttpService { case req @ POST -> Root / "eval" => import io.circe.syntax._ - - val decoder: EntityDecoder[EvalRequest] = { - implicit val evalRequestDecoder: Decoder[EvalRequest] = - Decoder.instance { - cursor => - for { - resolvers <- cursor.downField("resolvers").as[List[String]] - dependencies <- cursor - .downField("dependencies") - .as[List[Dependency]] - code <- cursor.downField("code").as[String] - compilerFlags <- cursor - .downField("compilerFlags") - .as[Option[List[String]]] - } yield - EvalRequest( - resolvers, - dependencies, - code, - compilerFlags.getOrElse(Nil)) - } - - jsonDecoderOf(evalRequestDecoder) - } - req - .decodeWith[EvalRequest](decoder, strict = false) { + .decode[EvalRequest] { evalRequest => - val (validCompilerFlags, invalidCompilerFlags) = - evalRequest.compilerFlags.partition( - allowedCompilerFlags.contains(_)) - if (invalidCompilerFlags.isEmpty) { - evaluator.eval[Any]( - code = evalRequest.code, - remotes = evalRequest.resolvers, - dependencies = evalRequest.dependencies, - compilerFlags = validCompilerFlags - ) 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) - } - } else - BadRequest( - s"Invalid compiler flags: ${invalidCompilerFlags.mkString(",")}") + 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: _*)) }) diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala index 09c04dfa..a021830c 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvalEndpointSpec.scala @@ -181,64 +181,5 @@ class EvalEndpointSpec extends FunSpec with Matchers { `Accept-Ranges`(Nil)).status should be(HttpStatus.Unauthorized) } - it("evaluates the code when no compilerFlags are provided") { - verifyEvalResponse( - response = serve( - EvalRequest( - code = "{import cats._; Eval.now(42).value}", - resolvers = sonatypeReleases, - dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - ), - `X-Scala-Eval-Api-Token`(validToken)), - expectedStatus = HttpStatus.Ok, - expectedValue = Some("42"), - expectedMessage = `ok` - ) - } - - it("evaluates the code when an empty list of compilerFlags is provided") { - verifyEvalResponse( - response = serve( - EvalRequest( - code = "{import cats._; Eval.now(42).value}", - resolvers = sonatypeReleases, - dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, - compilerFlags = Nil - ), - `X-Scala-Eval-Api-Token`(validToken)), - expectedStatus = HttpStatus.Ok, - expectedValue = Some("42"), - expectedMessage = `ok` - ) - } - - it("evaluates the code when a list of allowed compilerFlags is provided") { - verifyEvalResponse( - response = serve( - EvalRequest( - code = "{import cats._; Eval.now(42).value}", - resolvers = sonatypeReleases, - dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil, - compilerFlags = List("-deprecation", "-feature") - ), - `X-Scala-Eval-Api-Token`(validToken)), - expectedStatus = HttpStatus.Ok, - expectedValue = Some("42"), - expectedMessage = `ok` - ) - } - - it("rejects requests with invalid compilerFlags") { - serve( - EvalRequest( - code = "1", - resolvers = Nil, - dependencies = Nil, - compilerFlags = List("-optimise", "-help") - ), - `X-Scala-Eval-Api-Token`(validToken)).status should be( - HttpStatus.BadRequest) - } - } } diff --git a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala index a009ebe3..d643ec9a 100644 --- a/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala +++ b/server/src/test/scala/org/scalaexercises/evaluator/EvaluatorSpec.scala @@ -113,26 +113,6 @@ class EvaluatorSpec extends FunSpec with Matchers { val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - val compilerFlags = Nil - - val result: EvalResult[Unit] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies, - compilerFlags = compilerFlags) - .run - - result should matchPattern { - case EvalSuccess(_, 42, _) => - } - } - - describe("can evaluate code without any compiler flags provided") { - val code = "{import cats._; Eval.now(42).value}" - - val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - val result: EvalResult[Unit] = evaluator .eval(code, remotes = remotes, dependencies = dependencies) .run @@ -142,45 +122,5 @@ class EvaluatorSpec extends FunSpec with Matchers { } } - describe("can evaluate code with an empty list of compiler flags provided") { - val code = "{import cats._; Eval.now(42).value}" - - val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - - val compilerFlags = Nil - - val result: EvalResult[Unit] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies, - compilerFlags = compilerFlags) - .run - - result should matchPattern { - case EvalSuccess(_, 42, _) => - } - } - - describe( - "can evaluate code with a list of allowed compiler flags provided") { - val code = "{import cats._; Eval.now(42).value}" - - val dependencies = Dependency("org.typelevel", "cats_2.11", "0.6.0") :: Nil - - val compilerFlags = List("-deprecation", "-feature") - - val result: EvalResult[Unit] = evaluator - .eval( - code, - remotes = remotes, - dependencies = dependencies, - compilerFlags = compilerFlags) - .run - - result should matchPattern { - case EvalSuccess(_, 42, _) => - } - } } } diff --git a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala b/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala index 67678570..7abdb336 100644 --- a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala +++ b/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala @@ -46,8 +46,7 @@ final case class Dependency(groupId: String, final case class EvalRequest(resolvers: List[String] = Nil, dependencies: List[Dependency] = Nil, - code: String, - compilerFlags: List[String] = Nil) + code: String) final case class EvalResponse(msg: String, value: Option[String] = None,