diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/Decoders.scala b/client/src/main/scala/org/scalaexercises/evaluator/Decoders.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/Decoders.scala rename to client/src/main/scala/org/scalaexercises/evaluator/Decoders.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala b/client/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala rename to client/src/main/scala/org/scalaexercises/evaluator/EvaluatorAPI.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorClient.scala b/client/src/main/scala/org/scalaexercises/evaluator/EvaluatorClient.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorClient.scala rename to client/src/main/scala/org/scalaexercises/evaluator/EvaluatorClient.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorResponses.scala b/client/src/main/scala/org/scalaexercises/evaluator/EvaluatorResponses.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/EvaluatorResponses.scala rename to client/src/main/scala/org/scalaexercises/evaluator/EvaluatorResponses.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala b/client/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala rename to client/src/main/scala/org/scalaexercises/evaluator/api/Evaluator.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala b/client/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala rename to client/src/main/scala/org/scalaexercises/evaluator/free/algebra/EvaluatorOps.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala b/client/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala rename to client/src/main/scala/org/scalaexercises/evaluator/free/interpreters/Interpreter.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/http/HttpClient.scala b/client/src/main/scala/org/scalaexercises/evaluator/http/HttpClient.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/http/HttpClient.scala rename to client/src/main/scala/org/scalaexercises/evaluator/http/HttpClient.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/http/HttpRequestBuilder.scala b/client/src/main/scala/org/scalaexercises/evaluator/http/HttpRequestBuilder.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/http/HttpRequestBuilder.scala rename to client/src/main/scala/org/scalaexercises/evaluator/http/HttpRequestBuilder.scala diff --git a/client/shared/src/main/scala/org/scalaexercises/evaluator/implicits.scala b/client/src/main/scala/org/scalaexercises/evaluator/implicits.scala similarity index 100% rename from client/shared/src/main/scala/org/scalaexercises/evaluator/implicits.scala rename to client/src/main/scala/org/scalaexercises/evaluator/implicits.scala diff --git a/project/ProjectPlugin.scala b/project/ProjectPlugin.scala index f4812a37..fa28911c 100644 --- a/project/ProjectPlugin.scala +++ b/project/ProjectPlugin.scala @@ -26,7 +26,7 @@ object ProjectPlugin extends AutoPlugin { lazy val roshttp = "2.2.4" lazy val slf4jSimple = "1.7.28" lazy val jwtCore = "4.0.0" - lazy val coursier = "2.0.0-RC3-2" + lazy val coursier = "2.0.0-RC3-4" } lazy val dockerSettings = Seq( @@ -82,9 +82,11 @@ object ProjectPlugin extends AutoPlugin { %%("http4s-circe", V.http4s), %("config"), %%("jwt-core", V.jwtCore), + %%("coursier", V.coursier), + %%("coursier-cache", V.coursier), + "io.get-coursier" %% "coursier-cats-interop" % V.coursier, %%("scalatest", V.scalatest) % "test" - ), - addSbtPlugin("io.get-coursier" % "sbt-coursier" % V.coursier) + ) ) lazy val buildInfoSettings = Seq( @@ -136,7 +138,7 @@ object ProjectPlugin extends AutoPlugin { organizationEmail = "hello@47deg.com" ), orgLicenseSetting := ApacheLicense, - scalaVersion := "2.12.10", + scalaVersion := "2.11.11", scalaOrganization := "org.scala-lang", javacOptions ++= Seq("-encoding", "UTF-8", "-Xlint:-options"), fork in Test := false, @@ -144,13 +146,11 @@ object ProjectPlugin extends AutoPlugin { cancelable in Global := true, headerLicense := Some( HeaderLicense.Custom( - s"""|/* - | * scala-exercises - ${name.value} - | * Copyright (C) 2015-2019 47 Degrees, LLC. - | */ - | - |""".stripMargin - )), - headerMappings := headerMappings.value + (HeaderFileType.scala -> HeaderCommentStyle.CStyleBlockComment) + s"""| + | scala-exercises - ${name.value} + | Copyright (C) 2015-2019 47 Degrees, LLC. + | + |""".stripMargin + )) ) ++ shellPromptSettings } diff --git a/server/src/main/scala/org/scalaexercises/evaluator/auth.scala b/server/src/main/scala/org/scalaexercises/evaluator/auth.scala index e5e36730..f8a45c93 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/auth.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/auth.scala @@ -1,19 +1,21 @@ /* - * scala-exercises - evaluator-server - * Copyright (C) 2015-2016 47 Degrees, LLC. + * + * scala-exercises - evaluator-server + * Copyright (C) 2015-2019 47 Degrees, LLC. + * */ package org.scalaexercises.evaluator -import org.http4s._, org.http4s.dsl._, org.http4s.server._ +import cats.effect.Sync import com.typesafe.config._ +import org.http4s._ +import org.http4s.syntax.kleisli.http4sKleisliResponseSyntax import org.http4s.util._ -import scala.util.{Failure, Success, Try} -import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim, JwtHeader, JwtOptions} - import org.log4s.getLogger +import pdi.jwt.{Jwt, JwtAlgorithm} -import scalaz.concurrent.Task +import scala.util.{Failure, Success} object auth { @@ -37,15 +39,14 @@ object auth { type HeaderT = `X-Scala-Eval-Api-Token` - def name: CaseInsensitiveString = "x-scala-eval-api-token".ci + def name: CaseInsensitiveString = CaseInsensitiveString("x-scala-eval-api-token") override def parse(s: String): ParseResult[`X-Scala-Eval-Api-Token`] = ParseResult.success(`X-Scala-Eval-Api-Token`(s)) - def matchHeader(header: Header): Option[HeaderT] = { + def matchHeader(header: Header): Option[HeaderT] = if (header.name == name) Some(`X-Scala-Eval-Api-Token`(header.value)) else None - } } @@ -55,20 +56,26 @@ object auth { writer.append(token) } - def apply(service: HttpService): HttpService = Service.lift { req => - req.headers.get(`X-Scala-Eval-Api-Token`) match { - case Some(header) => - Jwt.decodeRaw(header.value, secretKey, Seq(JwtAlgorithm.HS256)) match { - case Success(tokenIdentity) => - logger.info(s"Auth success with identity : $tokenIdentity") - service(req) - case Failure(ex) => - logger.warn(s"Auth failed : $ex") - Task.now(Response(Status.Unauthorized)) + def apply[F[_]: Sync](service: HttpApp[F]): HttpApp[F] = + HttpRoutes + .of[F] { + case req if req.headers.nonEmpty => { + req.headers.get(`X-Scala-Eval-Api-Token`) match { + case Some(header) => + Jwt.decodeRaw(header.token, secretKey, Seq(JwtAlgorithm.HS256)) match { + case Success(tokenIdentity) => { + logger.info(s"Auth success with identity : $tokenIdentity") + service(req) + } + case Failure(ex) => { + logger.warn(s"Auth failed : $ex") + Sync[F].pure(Response(Status.Unauthorized)) + } + } + } } - case None => Task.now(Response(Status.Unauthorized)) - } - - } + case _ => Sync[F].pure(Response(Status.Unauthorized)) + } + .orNotFound } diff --git a/server/src/main/scala/org/scalaexercises/evaluator/codecs.scala b/server/src/main/scala/org/scalaexercises/evaluator/codecs.scala index bd092209..84c71eb6 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/codecs.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/codecs.scala @@ -1,40 +1,23 @@ /* - * scala-exercises - evaluator-server - * Copyright (C) 2015-2016 47 Degrees, LLC. + * + * scala-exercises - evaluator-server + * Copyright (C) 2015-2019 47 Degrees, LLC. + * */ package org.scalaexercises.evaluator -import org.http4s._, org.http4s.dsl._ -import io.circe.{Decoder, Encoder, Json, Printer} -import org.http4s.headers.`Content-Type` -import io.circe.jawn.CirceSupportParser.facade +import cats.effect.Sync +import io.circe.{Decoder, Encoder} +import org.http4s._ +import org.http4s.circe._ /** Provides Json serialization codecs for the http4s services */ trait Http4sCodecInstances { - implicit val jsonDecoder: EntityDecoder[Json] = jawn.jawnDecoder(facade) + implicit def entityDecoderOf[F[_]: Sync, A: Decoder]: EntityDecoder[F, A] = jsonOf[F, A] - implicit def jsonDecoderOf[A](implicit decoder: Decoder[A]): EntityDecoder[A] = - jsonDecoder.flatMapR { json => - decoder - .decodeJson(json) - .fold( - failure => - DecodeResult.failure( - InvalidMessageBodyFailure(s"Could not decode JSON: $json", Some(failure))), - DecodeResult.success(_) - ) - } - - implicit val jsonEntityEncoder: EntityEncoder[Json] = EntityEncoder[String] - .contramap[Json] { json => - Printer.noSpaces.pretty(json) - } - .withContentType(`Content-Type`(MediaType.`application/json`)) - - implicit def jsonEncoderOf[A](implicit encoder: Encoder[A]): EntityEncoder[A] = - jsonEntityEncoder.contramap[A](encoder.apply) + implicit def entityEncoderOf[F[_]: Sync, A: Encoder]: EntityEncoder[F, A] = jsonEncoderOf[F, A] } diff --git a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala index 38408d03..40792151 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/evaluation.scala @@ -1,6 +1,8 @@ /* - * scala-exercises - evaluator-server - * Copyright (C) 2015-2016 47 Degrees, LLC. + * + * scala-exercises - evaluator-server + * Copyright (C) 2015-2019 47 Degrees, LLC. + * */ package org.scalaexercises.evaluator @@ -9,11 +11,13 @@ import java.io.{ByteArrayOutputStream, File} import java.math.BigInteger import java.net.URLClassLoader import java.security.MessageDigest -import java.util.concurrent.TimeoutException import java.util.jar.JarFile +import cats.effect.{Concurrent, ConcurrentEffect, ContextShift, Timer} +import cats.implicits._ import coursier._ -import monix.execution.Scheduler +import coursier.cache.{ArtifactError, FileCache} +import coursier.util.Sync import org.scalaexercises.evaluator.Eval.CompilerException import scala.concurrent.duration._ @@ -24,13 +28,11 @@ import scala.tools.nsc.reporters._ import scala.tools.nsc.{Global, Settings} import scala.util.Try import scala.util.control.NonFatal -import scalaz.Scalaz._ -import scalaz._ -import scalaz.concurrent.Task -class Evaluator(timeout: FiniteDuration = 20.seconds)( - implicit S: Scheduler -) { +class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)( + implicit CS: ContextShift[F], + F: ConcurrentEffect[F], + T: Timer[F]) { type Remote = String private[this] def convert(errors: (Position, String, String)): (String, List[CompilationInfo]) = { @@ -38,31 +40,35 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( (severity, CompilationInfo(msg, Some(RangePosition(pos.start, pos.point, pos.end))) :: Nil) } - def remoteToRepository(remote: Remote): Repository = - MavenRepository(remote) + def remoteToRepository(remote: Remote): Repository = MavenRepository(remote) def dependencyToModule(dependency: Dependency): coursier.Dependency = - coursier.Dependency( - Module(dependency.groupId, dependency.artifactId), + coursier.Dependency.of( + Module(Organization(dependency.groupId), ModuleName(dependency.artifactId)), dependency.version ) - def resolveArtifacts(remotes: Seq[Remote], dependencies: Seq[Dependency]): Task[Resolution] = { - val resolution = Resolution(dependencies.map(dependencyToModule).toSet) - val repositories: Seq[Repository] = Cache.ivy2Local +: remotes.map(remoteToRepository) - val fetch = Fetch.from(repositories, Cache.fetch()) - resolution.process.run(fetch) + val cache: FileCache[F] = FileCache[F].noCredentials + + def resolveArtifacts(remotes: Seq[Remote], dependencies: Seq[Dependency]): F[Resolution] = { + Resolve[F](cache) + .addDependencies(dependencies.map(dependencyToModule): _*) + .addRepositories(remotes.map(remoteToRepository): _*) + .addRepositories(coursier.LocalRepositories.ivy2Local) + .io } def fetchArtifacts( remotes: Seq[Remote], - dependencies: Seq[Dependency]): Task[coursier.FileError \/ List[File]] = + dependencies: Seq[Dependency]): F[Either[ArtifactError, List[File]]] = for { - resolution <- resolveArtifacts(remotes, dependencies) - artifacts <- Task.gatherUnordered( - resolution.artifacts.map(Cache.file(_).run) - ) - } yield artifacts.sequenceU + resolution <- resolveArtifacts(remotes, dependencies) + gatheredArtifacts <- resolution.artifacts().toList.traverse(cache.file(_).run) + artifacts = gatheredArtifacts.foldRight(Right(Nil): Either[ArtifactError, List[File]]) { + case (Right(file), acc) => acc.map(file :: _) + case (Left(ae), _) => Left(ae) + } + } yield artifacts def createEval(jars: Seq[File]) = { new Eval(jars = jars.toList) { @@ -124,22 +130,29 @@ class Evaluator(timeout: FiniteDuration = 20.seconds)( code: String, remotes: Seq[Remote] = Nil, dependencies: Seq[Dependency] = Nil - ): Task[EvalResult[T]] = { + ): F[EvalResult[T]] = { for { allJars <- fetchArtifacts(remotes, dependencies) result <- allJars match { - case \/-(jars) => - Task({ - evaluate(code, jars) - }).timed(timeout) - .handle({ - case err: TimeoutException => Timeout[T](timeout) - }) - case -\/(fileError) => - Task.now(UnresolvedDependency(fileError.describe)) + case Right(jars) => + val fallback: EvalResult[T] = Timeout[T](timeout) + val success: F[EvalResult[T]] = + timeoutTo(F.delay { evaluate(code, jars) }, timeout, fallback) + success + case Left(fileError) => + val failure: F[EvalResult[T]] = F.pure(UnresolvedDependency[T](fileError.describe)) + failure } } yield result } + + def timeoutTo[A](fa: F[A], after: FiniteDuration, fallback: A): F[A] = { + + Concurrent[F].race(fa, T.sleep(after)).flatMap { + case Left(a) => a.pure[F] + case Right(_) => fallback.pure[F] + } + } } /** @@ -170,7 +183,7 @@ private class StringCompiler( val settings = StringCompiler.this.settings val messages = new scala.collection.mutable.ListBuffer[List[String]] - def display(pos: Position, message: String, severity: Severity) { + def display(pos: Position, message: String, severity: Severity) = { severity.count += 1 val severityName = severity match { case ERROR => "error: " @@ -193,11 +206,11 @@ private class StringCompiler( }) } - def displayPrompt { + def displayPrompt = { // no. } - override def reset { + override def reset = { super.reset messages.clear() } @@ -205,7 +218,7 @@ private class StringCompiler( val global = new Global(settings, reporter) - def reset() { + def reset() = { targetDir match { case None => { output.asInstanceOf[VirtualDirectory].clear() @@ -239,7 +252,7 @@ private class StringCompiler( /** * Compile scala code. It can be found using the above class loader. */ - def apply(code: String) { + def apply(code: String) = { // if you're looking for the performance hit, it's 1/2 this line... val compiler = new global.Run val sourceFiles = List(new BatchSourceFile("(inline)", code)) @@ -366,7 +379,7 @@ class Eval(target: Option[File] = None, jars: List[File] = Nil) { * Check if code is Eval-able. * @throws CompilerException if not Eval-able. */ - def check(code: String) { + def check(code: String) = { val id = uniqueId(code) val className = "Evaluator__" + id val wrappedCode = wrapCodeInClass(className, code) diff --git a/server/src/main/scala/org/scalaexercises/evaluator/services.scala b/server/src/main/scala/org/scalaexercises/evaluator/services.scala index aa5dfe8f..c0ba6a35 100644 --- a/server/src/main/scala/org/scalaexercises/evaluator/services.scala +++ b/server/src/main/scala/org/scalaexercises/evaluator/services.scala @@ -1,15 +1,25 @@ /* - * scala-exercises - evaluator-server - * Copyright (C) 2015-2016 47 Degrees, LLC. + * + * scala-exercises - evaluator-server + * Copyright (C) 2015-2019 47 Degrees, LLC. + * */ package org.scalaexercises.evaluator -import monix.execution.Scheduler +import cats.effect.{ConcurrentEffect, ContextShift, ExitCode, IO, IOApp, Timer} +import cats.implicits._ +import codecs._ +import coursier.util.Sync +import coursier.interop.cats._ +import io.circe.generic.auto._ +import io.circe.syntax._ import org.http4s._ import org.http4s.dsl._ +import org.http4s.headers.Allow import org.http4s.server.blaze._ import org.log4s.getLogger +import org.http4s.syntax.kleisli.http4sKleisliResponseSyntax import scala.concurrent.duration._ import scala.language.postfixOps @@ -17,14 +27,8 @@ import scala.language.postfixOps object services { import EvalResponse.messages._ - import codecs._ - import io.circe.generic.auto._ - private val logger = getLogger - - implicit val scheduler: Scheduler = Scheduler.io("scala-evaluator") - - val evaluator = new Evaluator(20 seconds) + def evaluatorInstance[F[_]: ConcurrentEffect: ContextShift: Timer: Sync] = new Evaluator[F](20 seconds) val corsHeaders = Seq( Header("Vary", "Origin,Access-Control-Request-Methods"), @@ -34,95 +38,87 @@ object services { 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: EvalResult[_]) => - val response = result match { - case EvalSuccess(cis, res, out) => - EvalResponse( - `ok`, - Option(res.toString), - Option(res.asInstanceOf[AnyRef].getClass.getName), - Option(out), - cis) - case Timeout(_) => - EvalResponse(`Timeout Exceded`, None, None, None, Map.empty) - case UnresolvedDependency(msg) => - EvalResponse( - `Unresolved Dependency` + " : " + msg, - None, - None, - None, - Map.empty) - case EvalRuntimeError(cis, runtimeError) => - EvalResponse( - `Runtime Error`, - runtimeError map (_.error.getMessage), - runtimeError map (_.error.getClass.getName), - None, - cis) - case CompilationError(cis) => - EvalResponse(`Compilation Error`, None, None, None, cis) - case GeneralError(err) => - EvalResponse(`Unforeseen Exception`, None, None, None, Map.empty) + def service[F[_]: ConcurrentEffect: ContextShift: Timer: Sync] = new Http4sDsl[F] { + def httpApp(evaluator: Evaluator[F]) = + HttpRoutes + .of[F] { + // Evaluator service + case req @ POST -> Root / "eval" => + 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), + Option(out), + cis) + case Timeout(_) => + EvalResponse(`Timeout Exceded`, None, None, None, Map.empty) + case UnresolvedDependency(msg) => + EvalResponse( + `Unresolved Dependency` + " : " + msg, + None, + None, + None, + Map.empty) + case EvalRuntimeError(cis, runtimeError) => + EvalResponse( + `Runtime Error`, + runtimeError map (_.error.getMessage), + runtimeError map (_.error.getClass.getName), + None, + cis) + case CompilationError(cis) => + EvalResponse(`Compilation Error`, None, None, None, cis) + case GeneralError(err) => + EvalResponse(`Unforeseen Exception`, None, None, None, Map.empty) + } + Ok(response.asJson) } - Ok(response.asJson) } - } - .map((r: Response) => r.putHeaders(corsHeaders: _*)) - }) - - def loaderIOService = HttpService { - - case _ -> Root => - MethodNotAllowed() - - case GET -> Root / "loaderio-1318d1b3e06b7bc96dd5de5716f57496" => - 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: _*) + .map((r: Response[F]) => r.putHeaders(corsHeaders: _*)) + // LoaderIO service + case _ -> Root => MethodNotAllowed(Allow(GET, POST, OPTIONS)) + case GET -> Root / "loaderio-1318d1b3e06b7bc96dd5de5716f57496" => + Ok("loaderio-1318d1b3e06b7bc96dd5de5716f57496") + // Options service + // CORS middleware in http4s can't be combined with our `auth` middleware. We need to handle CORS calls ourselves. + case OPTIONS -> Root / "eval" => + Ok().map(res => res.withHeaders(corsHeaders: _*)) //putHeaders(corsHeaders: _*) + } + .orNotFound } } -object EvaluatorServer extends App { +object EvaluatorServer extends IOApp { import services._ private[this] val logger = getLogger - val ip = Option(System.getenv("HOST")).getOrElse("0.0.0.0") + lazy val ip = Option(System.getenv("HOST")).getOrElse("0.0.0.0") - val port = (Option(System.getenv("PORT")) orElse + lazy val port = (Option(System.getenv("PORT")) orElse Option(System.getProperty("http.port"))).map(_.toInt).getOrElse(8080) - 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 - .awaitShutdown() + override def run(args: List[String]): IO[ExitCode] = { + logger.info(s"Initializing Evaluator at $ip:$port") + BlazeServerBuilder[IO] + .bindHttp(port, ip) + .withHttpApp(auth[IO](service[IO].httpApp(evaluatorInstance[IO]))) + .serve + .compile + .lastOrError + } } diff --git a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala b/shared/src/main/scala/org/scalaexercises/evaluator/types.scala similarity index 93% rename from shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala rename to shared/src/main/scala/org/scalaexercises/evaluator/types.scala index 81d8046d..fe793f71 100644 --- a/shared/shared/src/main/scala/org/scalaexercises/evaluator/types.scala +++ b/shared/src/main/scala/org/scalaexercises/evaluator/types.scala @@ -1,6 +1,8 @@ /* - * scala-exercises - evaluator-shared - * Copyright (C) 2015-2016 47 Degrees, LLC. + * + * scala-exercises - evaluator-shared + * Copyright (C) 2015-2019 47 Degrees, LLC. + * */ package org.scalaexercises.evaluator