Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions project/ProjectPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ object ProjectPlugin extends AutoPlugin {

object V {
lazy val cats = "2.7.0"
lazy val catsEffect = "2.5.4"
lazy val http4s = "0.21.31"
lazy val catsEffect = "3.3.4"
lazy val http4s = "0.23.8"
lazy val circe = "0.14.1"
lazy val log4s = "1.7.0"
lazy val scalatest = "3.2.10"
lazy val scalatestplusScheck = "3.2.2.0"
lazy val jodaTime = "2.10.13"
lazy val slf4j = "1.7.35"
lazy val jwtCore = "9.0.3"
lazy val coursier = "2.0.16"
lazy val coursier = "2.1.0-M2-19-g5a34ba7c1"
lazy val config = "1.4.1"
lazy val scala = "2.13.8"
}
Expand Down
31 changes: 7 additions & 24 deletions server/src/main/scala/org/scalaexercises/evaluator/auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@
package org.scalaexercises.evaluator

import cats.effect.Sync
import cats.syntax.list._
import com.typesafe.config._
import org.http4s._
import org.http4s.syntax.kleisli._
import org.http4s.util._
import org.log4s.getLogger
import pdi.jwt.{Jwt, JwtAlgorithm}
import org.typelevel.ci.CIString

import scala.util.{Failure, Success}

object auth {

private[this] val logger = getLogger

private[this] val tokenHeaderKey = CIString("X-Scala-Eval-Api-Token")

val config = ConfigFactory.load()

val SecretKeyPath = "eval.auth.secretKey"
Expand All @@ -46,34 +50,13 @@ object auth {
def generateToken(value: String = "{}") =
Jwt.encode(value, secretKey, JwtAlgorithm.HS256)

object `X-Scala-Eval-Api-Token` extends HeaderKey.Singleton {

type HeaderT = `X-Scala-Eval-Api-Token`

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] =
if (header.name == name) Some(`X-Scala-Eval-Api-Token`(header.value))
else None

}

final case class `X-Scala-Eval-Api-Token`(token: String) extends Header.Parsed {
override def key = `X-Scala-Eval-Api-Token`
override def renderValue(writer: Writer): writer.type =
writer.append(token)
}

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 req if req.headers.headers.nonEmpty =>
req.headers.get(tokenHeaderKey) match {
case Some(header) =>
Jwt.decodeRaw(header.token, secretKey, Seq(JwtAlgorithm.HS256)) match {
Jwt.decodeRaw(header.head.value, secretKey, Seq(JwtAlgorithm.HS256)) match {
case Success(tokenIdentity) =>
logger.info(s"Auth success with identity : $tokenIdentity")
service(req)
Expand Down
35 changes: 0 additions & 35 deletions server/src/main/scala/org/scalaexercises/evaluator/codecs.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import java.security.MessageDigest
import java.util.jar.JarFile

import cats.effect._
import cats.effect.syntax.concurrent.catsEffectSyntaxConcurrent
import cats.implicits._
import coursier._
import coursier.cache.{ArtifactError, FileCache}
Expand All @@ -39,10 +38,13 @@ import scala.tools.nsc.reporters._
import scala.tools.nsc.{Global, Settings}
import scala.util.{Failure, Success, Try}
import scala.util.control.NonFatal
import cats.effect.Temporal
import cats.effect.unsafe.IORuntime

class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(implicit
F: ConcurrentEffect[F],
T: Timer[F]
F: Async[F],
T: Temporal[F],
runtime: IORuntime
) {
type Remote = String

Expand Down Expand Up @@ -143,8 +145,11 @@ class Evaluator[F[_]: Sync](timeout: FiniteDuration = 20.seconds)(implicit
allJars <- fetch(remotes, dependencies)
result <- allJars match {
case Right(jars) =>
evaluate(code, jars)
.timeoutTo(timeout, Timeout[T](timeout).asInstanceOf[EvalResult[T]].pure[F])
Clock[F].timeoutTo(
evaluate[T](code, jars),
timeout,
Timeout[T](timeout).asInstanceOf[EvalResult[T]].pure[F]
)
case Left(ex) => F.pure(UnresolvedDependency[T](ex.getMessage))
}
} yield result
Expand Down Expand Up @@ -201,7 +206,7 @@ private class StringCompiler(
Nil)
}

override def reset = {
override def reset() = {
super.reset
messages.clear()
}
Expand Down Expand Up @@ -296,7 +301,7 @@ private class StringCompiler(
* - contruct an instance of that class
* - return the result of `apply()`
*/
class Eval(target: Option[File] = None, jars: List[File] = Nil) {
class Eval(target: Option[File] = None, jars: List[File] = Nil)(implicit runtime: IORuntime) {
private lazy val compilerPath =
try classPathOfClass("scala.tools.nsc.Interpreter")
catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,37 @@

package org.scalaexercises.evaluator

import cats.effect.{ConcurrentEffect, ContextShift, ExitCode, IO, IOApp, Timer}
import cats.effect.{ExitCode, IO, IOApp}
import cats.implicits._
import coursier.interop.cats._
import coursier.util.Sync
import io.circe.Json
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s._
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.dsl._
import org.http4s.headers.Allow
import org.http4s.server.blaze._
import org.http4s.syntax.kleisli.http4sKleisliResponseSyntaxOptionT
import org.log4s.getLogger
import org.scalaexercises.evaluator.codecs._

import scala.concurrent.duration._
import cats.effect.Temporal
import org.http4s.circe.JsonDecoder

object services {

import EvalResponse.messages._

val corsHeaders = Seq(
val corsHeaders: Seq[Header.ToRaw] = 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 service[F[_]: ConcurrentEffect: ContextShift: Timer: Sync](evaluator: Evaluator[F]) = {
def service[F[_]: Temporal: Sync: JsonDecoder](evaluator: Evaluator[F]) = {

object dsl extends Http4sDsl[F]

Expand Down
16 changes: 16 additions & 0 deletions server/src/main/scala/org/scalaexercises/evaluator/types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package org.scalaexercises.evaluator

import io.circe.generic.semiauto.deriveDecoder

import scala.concurrent.duration._
import io.circe.Decoder

final case class RangePosition(start: Int, point: Int, end: Int)

Expand Down Expand Up @@ -47,19 +50,32 @@ final case class CompilationError[A](compilationInfos: CI) extends EvalResult[A]
final case class GeneralError[A](stack: Throwable) extends EvalResult[A]

final case class Exclusion(organization: String, moduleName: String)

object Exclusion {
implicit val decExclusion: Decoder[Exclusion] = deriveDecoder
}

final case class Dependency(
groupId: String,
artifactId: String,
version: String,
exclusions: Option[List[Exclusion]] = None
)

object Dependency {
implicit val decDependency: Decoder[Dependency] = deriveDecoder
}

final case class EvalRequest(
resolvers: List[String] = Nil,
dependencies: List[Dependency] = Nil,
code: String
)

object EvalRequest {
implicit val decEvalRequest: Decoder[EvalRequest] = deriveDecoder
}

final case class EvalResponse(
msg: String,
value: Option[String] = None,
Expand Down