Skip to content

Commit 066947f

Browse files
committed
Endpoint Documentation
1 parent 683f1fd commit 066947f

File tree

3 files changed

+156
-4
lines changed

3 files changed

+156
-4
lines changed

README.md

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,142 @@
1-
# evaluator
2-
A remote Scala code evaluator
1+
# Remote Scala Eval
2+
3+
The remote Scala evaluator is a server based application that
4+
allows remote evaluation of arbitrary Scala code.
5+
6+
# Run from sources
7+
8+
```bash
9+
sbt run
10+
```
11+
12+
# Authentication
13+
14+
The remote Scala eval uses [JWT](https://jwt.io/) to encode / decode tokens.
15+
16+
## Generate an auth token
17+
18+
In order to generate an auth token you may use the scala console and invoke
19+
the `org.scalaexercises.evaluator.auth#generateToken` like so
20+
21+
```
22+
sbt console
23+
24+
scala> import org.scalaexercises.evaluator.auth._
25+
26+
scala> generateToken("your identity")
27+
res0: String = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eW91ciBpZGVudGl0eQ.cfH43Wa7k_w1i0W2pQhV1k21t2JqER9lw5EpJcENRMI
28+
```
29+
30+
Note `your identity` is exclusively to identify incoming requests for logging purposes.
31+
The Scala evaluator will authorize any incoming request generated with the `secretKey`
32+
33+
# Request
34+
35+
Requests are sent in JSON format via HTTP POST and are authenticated via the `x-scala-eval-api-token` header.
36+
37+
# Sample Request
38+
39+
Given the token above a sample request may look like:
40+
41+
```bash
42+
curl -X POST -H "Content-Type: application/json" -H "x-scala-eval-api-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eW91ciBpZGVudGl0eQ.cfH43Wa7k_w1i0W2pQhV1k21t2JqER9lw5EpJcENRMI" -d '{
43+
"resolvers":[
44+
"https://oss.sonatype.org/content/repositories/releases"
45+
],
46+
"dependencies":[
47+
{
48+
"groupId":"org.typelevel",
49+
"artifactId":"cats-core_2.11",
50+
"version":"0.4.1"
51+
}
52+
],
53+
"code":"{import cats._; Monad[Id].pure(42)}"
54+
}
55+
' "http://localhost:8080/eval"
56+
```
57+
58+
## Headers
59+
60+
```
61+
Content-Type : application/json
62+
x-scala-eval-api-token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eW91ciBpZGVudGl0eQ.cfH43Wa7k_w1i0W2pQhV1k21t2JqER9lw5EpJcENRMI
63+
```
64+
65+
## Body
66+
67+
```json
68+
{
69+
"resolvers":[
70+
"https://oss.sonatype.org/content/repositories/releases"
71+
],
72+
"dependencies":[
73+
{
74+
"groupId":"org.typelevel",
75+
"artifactId":"cats-core_2.11",
76+
"version":"0.4.1"
77+
}
78+
],
79+
"code":"{import cats._; Monad[Id].pure(42)}"
80+
}
81+
```
82+
83+
- `resolvers` : A list of resolvers where artifacts dependencies are hosted
84+
- `dependencies` : A List of artifacts required to eval the code
85+
- `code` : Some Scala Code
86+
87+
## Response
88+
89+
After compiling and attempting to evaluate the server will return a response payload with the following fields:
90+
91+
- `msg` : A message indicating the result of the compilation and evaluation. Note failing to compile still yields http status 200 as the purpose of the service is to output a result without judging if the input was correct or not.
92+
- `value` : The result of the evaluation or `null` if it didn't compile or an exception is thrown.
93+
- `valueType` : The type of result or `null` if it didn't compile or an exception is thrown
94+
- `compilationInfos` : A map of compilation severity errors and their associated message
95+
96+
For ilustration purposes here is a few Response Payload examples:
97+
98+
Successful compilation and Evaluation
99+
100+
```json
101+
{
102+
"msg": "Ok",
103+
"value": "42",
104+
"valueType": "java.lang.Integer",
105+
"compilationInfos": {}
106+
}
107+
```
108+
109+
Compilation Failure
110+
111+
```json
112+
{
113+
"msg": "Compilation Error",
114+
"value": null,
115+
"valueType": null,
116+
"compilationInfos": {
117+
"ERROR": [
118+
{
119+
"message": "value x is not a member of cats.Monad[cats.Id]",
120+
"pos": {
121+
"start": 165,
122+
"point": 165,
123+
"end": 165
124+
}
125+
}
126+
]
127+
}
128+
}
129+
```
130+
131+
Evaluating code that may result in a thrown exception
132+
133+
```json
134+
{
135+
"msg": "Runtime Error",
136+
"value": null,
137+
"valueType": null,
138+
"compilationInfos": {}
139+
}
140+
```
141+
142+

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ enablePlugins(JavaAppPackaging)
3232
addCompilerPlugin(
3333
"org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full
3434
)
35+

src/main/scala/auth.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import org.http4s.util._
66
import scala.util.{Try, Success, Failure}
77
import pdi.jwt.{Jwt, JwtAlgorithm, JwtHeader, JwtClaim, JwtOptions}
88

9+
import org.log4s.getLogger
10+
911
import scalaz.concurrent.Task
1012

1113
object auth {
1214

15+
private [this] val logger = getLogger
16+
1317
val config = ConfigFactory.load()
1418

1519
val SecretKeyPath = "eval.auth.secretKey"
@@ -20,6 +24,9 @@ object auth {
2024
throw new IllegalStateException("Missing -Deval.auth.secretKey=[YOUR_KEY_HERE] or env var [EVAL_SECRET_KEY] ")
2125
}
2226

27+
def generateToken(value : String = "{}") =
28+
Jwt.encode(value, secretKey, JwtAlgorithm.HS256)
29+
2330
object `X-Scala-Eval-Api-Token` extends HeaderKey.Singleton {
2431

2532
type HeaderT = `X-Scala-Eval-Api-Token`
@@ -46,8 +53,12 @@ object auth {
4653
req.headers.get(`X-Scala-Eval-Api-Token`) match {
4754
case Some(header) =>
4855
Jwt.decodeRaw(header.value, secretKey, Seq(JwtAlgorithm.HS256)) match {
49-
case Success(_) => service(req)
50-
case Failure(_) => Task.now(Response(Status.Unauthorized))
56+
case Success(tokenIdentity) =>
57+
logger.info(s"Auth success with identity : $tokenIdentity")
58+
service(req)
59+
case Failure(ex) =>
60+
logger.warn(s"Auth failed : $ex")
61+
Task.now(Response(Status.Unauthorized))
5162
}
5263
case None => Task.now(Response(Status.Unauthorized))
5364
}

0 commit comments

Comments
 (0)