Skip to content

Commit

Permalink
Merge pull request #13 from lihaoyi-databricks/wip
Browse files Browse the repository at this point in the history
Upstream some fixes
  • Loading branch information
lihaoyi authored Sep 14, 2019
2 parents 90b6806 + edd1f62 commit dedeed3
Show file tree
Hide file tree
Showing 62 changed files with 302 additions and 218 deletions.
2 changes: 1 addition & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ object cask extends ScalaModule with PublishModule {

def testFrameworks = Seq("utest.runner.Framework")
def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.6.9",
ivy"com.lihaoyi::utest::0.7.1",
ivy"com.lihaoyi::requests::0.2.0",
// ivy"org.xerial:sqlite-jdbc:3.18.0",
// ivy"io.getquill::quill-jdbc:2.6.0"
Expand Down
2 changes: 1 addition & 1 deletion cask/src/cask/decorators/compress.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import cask.internal.Router
import cask.model.{Request, Response}

import collection.JavaConverters._
class compress extends cask.Decorator{
class compress extends cask.RawDecorator{
def wrapFunction(ctx: Request, delegate: Delegate) = {
val acceptEncodings = ctx.exchange.getRequestHeaders.get("Accept-Encoding").asScala.flatMap(_.split(", "))
delegate(Map()).map{ v =>
Expand Down
14 changes: 7 additions & 7 deletions cask/src/cask/endpoints/FormEndpoint.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cask.endpoints

import cask.internal.{Router, Util}
import cask.main.Endpoint
import cask.main.HttpEndpoint
import cask.model._
import io.undertow.server.handlers.form.FormParserFactory

Expand Down Expand Up @@ -43,14 +43,13 @@ object FormReader{
def read(ctx: Request, label: String, input: Seq[FormEntry]) = input.map(_.asInstanceOf[FormFile])
}
}
class postForm(val path: String, override val subpath: Boolean = false) extends Endpoint {
type Output = Response
class postForm(val path: String, override val subpath: Boolean = false)
extends HttpEndpoint[Response.Raw, Seq[FormEntry]] {

val methods = Seq("post")
type Input = Seq[FormEntry]
type InputParser[T] = FormReader[T]
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
delegate: Delegate): Router.Result[Response.Raw] = {
try {
val formData = FormParserFactory.builder().build().createParser(ctx.exchange).parseBlocking()
delegate(
Expand All @@ -62,11 +61,12 @@ class postForm(val path: String, override val subpath: Boolean = false) extends
)
} catch{case e: Exception =>
Router.Result.Success(cask.model.Response(
"Unable to parse form data: " + e + "\n" + Util.stackTraceString(e)
"Unable to parse form data: " + e + "\n" + Util.stackTraceString(e),
statusCode = 400
))
}
}

def wrapPathSegment(s: String): Input = Seq(FormValue(s, new io.undertow.util.HeaderMap))
def wrapPathSegment(s: String): Seq[FormEntry] = Seq(FormValue(s, new io.undertow.util.HeaderMap))
}

52 changes: 41 additions & 11 deletions cask/src/cask/endpoints/JsonEndpoint.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package cask.endpoints

import java.io.ByteArrayOutputStream
import java.io.{ByteArrayOutputStream, InputStream, OutputStream, OutputStreamWriter}

import cask.internal.{Router, Util}
import cask.main.Endpoint
import cask.main.HttpEndpoint
import cask.model.Response.DataCompanion
import cask.model.{Request, Response}

import collection.JavaConverters._

sealed trait JsReader[T] extends Router.ArgReader[ujson.Value, T, cask.model.Request]
object JsReader{
Expand All @@ -26,13 +28,24 @@ object JsReader{
}
}
}
class postJson(val path: String, override val subpath: Boolean = false) extends Endpoint{
type Output = Response
trait JsonData extends Response.Data
object JsonData extends DataCompanion[JsonData]{
implicit class JsonDataImpl[T: upickle.default.Writer](t: T) extends JsonData{
def write(out: OutputStream) = {
val writer = new OutputStreamWriter(out)
implicitly[upickle.default.Writer[T]].write(new ujson.BaseRenderer(writer), t)
writer.flush()
}
}
}

class postJson(val path: String, override val subpath: Boolean = false)
extends HttpEndpoint[Response[JsonData], ujson.Value]{
val methods = Seq("post")
type Input = ujson.Js.Value
type InputParser[T] = JsReader[T]
override type OuterReturned = Router.Result[Response.Raw]
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {
delegate: Delegate): Router.Result[Response.Raw] = {
val obj = for{
str <-
try {
Expand All @@ -41,21 +54,38 @@ class postJson(val path: String, override val subpath: Boolean = false) extends
Right(new String(boas.toByteArray))
}
catch{case e: Throwable => Left(cask.model.Response(
"Unable to deserialize input JSON text: " + e + "\n" + Util.stackTraceString(e)
"Unable to deserialize input JSON text: " + e + "\n" + Util.stackTraceString(e),
statusCode = 400
))}
json <-
try Right(ujson.read(str))
catch{case e: Throwable => Left(cask.model.Response(
"Input text is invalid JSON: " + e + "\n" + Util.stackTraceString(e)
"Input text is invalid JSON: " + e + "\n" + Util.stackTraceString(e),
statusCode = 400
))}
obj <-
try Right(json.obj)
catch {case e: Throwable => Left(cask.model.Response("Input JSON must be a dictionary"))}
catch {case e: Throwable => Left(cask.model.Response(
"Input JSON must be a dictionary",
statusCode = 400
))}
} yield obj.toMap
obj match{
case Left(r) => Router.Result.Success(r)
case Left(r) => Router.Result.Success(r.map(Response.Data.StringData))
case Right(params) => delegate(params)
}
}
def wrapPathSegment(s: String): Input = ujson.Js.Str(s)
def wrapPathSegment(s: String): ujson.Value = ujson.Str(s)
}

class getJson(val path: String, override val subpath: Boolean = false)
extends HttpEndpoint[Response[JsonData], Seq[String]]{
val methods = Seq("get")
type InputParser[T] = QueryParamReader[T]
override type OuterReturned = Router.Result[Response.Raw]
def wrapFunction(ctx: Request, delegate: Delegate): Router.Result[Response.Raw] = {

delegate(WebEndpoint.buildMapFromQueryParams(ctx))
}
def wrapPathSegment(s: String) = Seq(s)
}
19 changes: 8 additions & 11 deletions cask/src/cask/endpoints/StaticEndpoints.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package cask.endpoints

import cask.main.Endpoint
import cask.main.HttpEndpoint
import cask.model.Request

class staticFiles(val path: String) extends Endpoint{
type Output = String
class staticFiles(val path: String) extends HttpEndpoint[String, Seq[String]]{
val methods = Seq("get")
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = {
delegate(Map()).map(t =>
cask.model.StaticFile(
(cask.internal.Util.splitPath(t) ++ ctx.remainingPathSegments)
Expand All @@ -19,16 +17,15 @@ class staticFiles(val path: String) extends Endpoint{
)
}

def wrapPathSegment(s: String): Input = Seq(s)
def wrapPathSegment(s: String): Seq[String] = Seq(s)
}

class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader) extends Endpoint{
type Output = String
class staticResources(val path: String, resourceRoot: ClassLoader = getClass.getClassLoader)
extends HttpEndpoint[String, Seq[String]]{
val methods = Seq("get")
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
override def subpath = true
def wrapFunction(ctx: Request, delegate: Delegate): Returned = {
def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = {
delegate(Map()).map(t =>
cask.model.StaticResource(
(cask.internal.Util.splitPath(t) ++ ctx.remainingPathSegments)
Expand All @@ -39,5 +36,5 @@ class staticResources(val path: String, resourceRoot: ClassLoader = getClass.get
)
}

def wrapPathSegment(s: String): Input = Seq(s)
def wrapPathSegment(s: String): Seq[String] = Seq(s)
}
18 changes: 10 additions & 8 deletions cask/src/cask/endpoints/WebEndpoints.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package cask.endpoints

import cask.internal.Router
import cask.main.Endpoint
import cask.main.HttpEndpoint
import cask.model.{Request, Response}

import collection.JavaConverters._


trait WebEndpoint extends Endpoint{
type Output = Response
type Input = Seq[String]
trait WebEndpoint extends HttpEndpoint[Response.Raw, Seq[String]]{
type InputParser[T] = QueryParamReader[T]
def wrapFunction(ctx: Request,
delegate: Map[String, Input] => Router.Result[Output]): Router.Result[Response] = {

delegate: Delegate): Router.Result[Response.Raw] = {
delegate(WebEndpoint.buildMapFromQueryParams(ctx))
}
def wrapPathSegment(s: String) = Seq(s)
}
object WebEndpoint{
def buildMapFromQueryParams(ctx: Request) = {
val b = Map.newBuilder[String, Seq[String]]
val queryParams = ctx.exchange.getQueryParameters
for(k <- queryParams.keySet().iterator().asScala){
Expand All @@ -22,9 +25,8 @@ trait WebEndpoint extends Endpoint{
deque.toArray(arr)
b += (k -> (arr: Seq[String]))
}
delegate(b.result())
b.result()
}
def wrapPathSegment(s: String) = Seq(s)
}
class get(val path: String, override val subpath: Boolean = false) extends WebEndpoint{
val methods = Seq("get")
Expand Down
22 changes: 12 additions & 10 deletions cask/src/cask/endpoints/WebSocketEndpoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@ package cask.endpoints
import cask.internal.Router
import cask.model.Request
import io.undertow.websockets.WebSocketConnectionCallback

import collection.JavaConverters._
sealed trait WebsocketResult
object WebsocketResult{
implicit class Response(val value: cask.model.Response) extends WebsocketResult
implicit class Response[T](value0: cask.model.Response[T])
(implicit f: T => cask.model.Response.Data) extends WebsocketResult{
def value = value0.map(f)
}
implicit class Listener(val value: WebSocketConnectionCallback) extends WebsocketResult
}

class websocket(val path: String, override val subpath: Boolean = false) extends cask.main.BaseEndpoint{
type Output = WebsocketResult
class websocket(val path: String, override val subpath: Boolean = false)
extends cask.main.Endpoint[WebsocketResult, Seq[String]]{
val methods = Seq("websocket")
type Input = Seq[String]
type InputParser[T] = QueryParamReader[T]
type Returned = Router.Result[WebsocketResult]
def wrapFunction(ctx: Request, delegate: Delegate): Returned = delegate(Map())

def wrapPathSegment(s: String): Input = Seq(s)

type OuterReturned = Router.Result[WebsocketResult]
def wrapFunction(ctx: Request, delegate: Delegate): OuterReturned = {
delegate(WebEndpoint.buildMapFromQueryParams(ctx))
}

def wrapPathSegment(s: String): Seq[String] = Seq(s)
}
9 changes: 9 additions & 0 deletions cask/src/cask/internal/Conversion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cask.internal

import scala.annotation.implicitNotFound

@implicitNotFound("Cannot return ${T} as a ${V}")
class Conversion[T, V](val f: T => V)
object Conversion{
implicit def create[T, V](implicit f: T => V) = new Conversion(f)
}
7 changes: 6 additions & 1 deletion cask/src/cask/internal/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import scala.collection.mutable
import java.io.OutputStream

import scala.annotation.switch
import scala.concurrent.{ExecutionContext, Future, Promise}

object Util {

def firstFutureOf[T](futures: Seq[Future[T]])(implicit ec: ExecutionContext) = {
val p = Promise[T]
futures.foreach(_.foreach(p.trySuccess))
p.future
}
/**
* Convert a string to a C&P-able literal. Basically
* copied verbatim from the uPickle source code.
Expand Down
Loading

0 comments on commit dedeed3

Please sign in to comment.