From 25d968edfdcba8999e08d835901cf0fcd086fe28 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 16 May 2021 23:16:51 +0200 Subject: [PATCH 1/4] Include .mill-version file in repo to help Metals with Scala 3 --- .mill-version | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .mill-version diff --git a/.mill-version b/.mill-version new file mode 100644 index 0000000000..13f88c27db --- /dev/null +++ b/.mill-version @@ -0,0 +1,2 @@ +0.9.7 + From 64f20ebc27a72bd278e14081e8c3a9ac0ae9ab36 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 16 May 2021 23:23:41 +0200 Subject: [PATCH 2/4] ignore metals directories --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 8d3ee552d6..e992878ed0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.bloop/ +.metals/ +.vscode/ target/ *.iml .idea From 4644880b48e1a41ce4927eae75ba9a2a3370b4fe Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 17 May 2021 13:12:16 +0200 Subject: [PATCH 3/4] Rename testFrameworks -> testFramework --- build.sc | 2 +- example/compress/build.sc | 2 +- example/compress2/build.sc | 2 +- example/compress3/build.sc | 2 +- example/cookies/build.sc | 2 +- example/decorated/build.sc | 2 +- example/decorated2/build.sc | 2 +- example/endpoints/build.sc | 2 +- example/formJsonPost/build.sc | 2 +- example/httpMethods/build.sc | 2 +- example/minimalApplication/build.sc | 2 +- example/minimalApplication2/build.sc | 2 +- example/redirectAbort/build.sc | 2 +- example/scalatags/build.sc | 2 +- example/staticFiles/build.sc | 2 +- example/staticFiles2/build.sc | 2 +- example/todo/build.sc | 2 +- example/todoApi/build.sc | 2 +- example/todoDb/build.sc | 2 +- example/twirl/build.sc | 2 +- example/variableRoutes/build.sc | 2 +- example/websockets/build.sc | 2 +- example/websockets2/build.sc | 2 +- example/websockets3/build.sc | 2 +- example/websockets4/build.sc | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/build.sc b/build.sc index e02af644a6..37efd9d82d 100644 --- a/build.sc +++ b/build.sc @@ -60,7 +60,7 @@ class CaskMainModule(val crossScalaVersion: String) extends CaskModule { def scalacPluginIvyDeps = T{ if (!isDotty) Agg(ivy"com.lihaoyi::acyclic:0.2.0") else Agg() } object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", ivy"com.lihaoyi::requests::0.6.9" diff --git a/example/compress/build.sc b/example/compress/build.sc index 23660c667b..0d6016b1d1 100644 --- a/example/compress/build.sc +++ b/example/compress/build.sc @@ -7,7 +7,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/compress2/build.sc b/example/compress2/build.sc index 46a9034788..f19e434305 100644 --- a/example/compress2/build.sc +++ b/example/compress2/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/compress3/build.sc b/example/compress3/build.sc index 46a9034788..f19e434305 100644 --- a/example/compress3/build.sc +++ b/example/compress3/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/cookies/build.sc b/example/cookies/build.sc index 46a9034788..f19e434305 100644 --- a/example/cookies/build.sc +++ b/example/cookies/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/decorated/build.sc b/example/decorated/build.sc index 46a9034788..f19e434305 100644 --- a/example/decorated/build.sc +++ b/example/decorated/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/decorated2/build.sc b/example/decorated2/build.sc index 46a9034788..f19e434305 100644 --- a/example/decorated2/build.sc +++ b/example/decorated2/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/endpoints/build.sc b/example/endpoints/build.sc index 46a9034788..f19e434305 100644 --- a/example/endpoints/build.sc +++ b/example/endpoints/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/formJsonPost/build.sc b/example/formJsonPost/build.sc index 78b60c17a4..a7264cea6f 100644 --- a/example/formJsonPost/build.sc +++ b/example/formJsonPost/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/httpMethods/build.sc b/example/httpMethods/build.sc index 46a9034788..f19e434305 100644 --- a/example/httpMethods/build.sc +++ b/example/httpMethods/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/minimalApplication/build.sc b/example/minimalApplication/build.sc index 46a9034788..f19e434305 100644 --- a/example/minimalApplication/build.sc +++ b/example/minimalApplication/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/minimalApplication2/build.sc b/example/minimalApplication2/build.sc index 46a9034788..f19e434305 100644 --- a/example/minimalApplication2/build.sc +++ b/example/minimalApplication2/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/redirectAbort/build.sc b/example/redirectAbort/build.sc index 46a9034788..f19e434305 100644 --- a/example/redirectAbort/build.sc +++ b/example/redirectAbort/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/scalatags/build.sc b/example/scalatags/build.sc index 5f6953cd7d..62d5b7f7bc 100644 --- a/example/scalatags/build.sc +++ b/example/scalatags/build.sc @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{ ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/staticFiles/build.sc b/example/staticFiles/build.sc index 2e99d1d4ab..e3a97174c3 100644 --- a/example/staticFiles/build.sc +++ b/example/staticFiles/build.sc @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/staticFiles2/build.sc b/example/staticFiles2/build.sc index 2e99d1d4ab..e3a97174c3 100644 --- a/example/staticFiles2/build.sc +++ b/example/staticFiles2/build.sc @@ -8,7 +8,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/todo/build.sc b/example/todo/build.sc index 7da6daecb1..aa367b29ce 100644 --- a/example/todo/build.sc +++ b/example/todo/build.sc @@ -10,7 +10,7 @@ trait AppModule extends CrossScalaModule{ ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/todoApi/build.sc b/example/todoApi/build.sc index 46a9034788..f19e434305 100644 --- a/example/todoApi/build.sc +++ b/example/todoApi/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/todoDb/build.sc b/example/todoDb/build.sc index 13d1191ead..f5bf4c92dc 100644 --- a/example/todoDb/build.sc +++ b/example/todoDb/build.sc @@ -9,7 +9,7 @@ trait AppModule extends CrossScalaModule{ ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/twirl/build.sc b/example/twirl/build.sc index b42194622a..d140d39832 100644 --- a/example/twirl/build.sc +++ b/example/twirl/build.sc @@ -12,7 +12,7 @@ trait AppModule extends CrossScalaModule with mill.twirllib.TwirlModule{ ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/variableRoutes/build.sc b/example/variableRoutes/build.sc index 46a9034788..f19e434305 100644 --- a/example/variableRoutes/build.sc +++ b/example/variableRoutes/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/websockets/build.sc b/example/websockets/build.sc index f2968378a4..8677052576 100644 --- a/example/websockets/build.sc +++ b/example/websockets/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/websockets2/build.sc b/example/websockets2/build.sc index f2968378a4..8677052576 100644 --- a/example/websockets2/build.sc +++ b/example/websockets2/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/websockets3/build.sc b/example/websockets3/build.sc index f2968378a4..8677052576 100644 --- a/example/websockets3/build.sc +++ b/example/websockets3/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", diff --git a/example/websockets4/build.sc b/example/websockets4/build.sc index f2968378a4..8677052576 100644 --- a/example/websockets4/build.sc +++ b/example/websockets4/build.sc @@ -6,7 +6,7 @@ trait AppModule extends CrossScalaModule{ def ivyDeps = Agg[Dep]( ) object test extends Tests{ - def testFrameworks = Seq("utest.runner.Framework") + def testFramework = "utest.runner.Framework" def ivyDeps = Agg( ivy"com.lihaoyi::utest::0.7.10", From d7f7d55a5b468f02c02298fe05b4401b4580153e Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 17 May 2021 13:08:16 +0200 Subject: [PATCH 4/4] Prevent cask from responding with 405 for an undefined route. Fixes #51. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to this commit, `prepareRouteTries` created a mapping from method-name to DispatchTrie (Map[String, DispatchTrie[…]]). This commit instead creates a DispatchTrie[Map[String, …]], basically an inversion of the previous result. The updated tests in minimalApplication and minimalApplication2 have been updated to cover the differences. --- cask/src/cask/main/Main.scala | 79 +++++++++---------- .../app/test/src/ExampleTests.scala | 2 +- .../app/test/src/ExampleTests.scala | 2 +- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index b76a2e9768..f273f4b894 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -46,10 +46,10 @@ abstract class Main{ implicit def log: cask.util.Logger = new cask.util.Logger.Console() - def routeTries = Main.prepareRouteTries(allRoutes) + def dispatchTrie = Main.prepareDispatchTrie(allRoutes) def defaultHandler = new BlockingHandler( - new Main.DefaultHandler(routeTries, mainDecorators, debugMode, handleNotFound, handleMethodNotAllowed, handleEndpointError) + new Main.DefaultHandler(dispatchTrie, mainDecorators, debugMode, handleNotFound, handleMethodNotAllowed, handleEndpointError) ) def handleNotFound() = Main.defaultHandleNotFound() @@ -74,7 +74,7 @@ abstract class Main{ } object Main{ - class DefaultHandler(routeTries: Map[String, DispatchTrie[(Routes, EndpointMetadata[_])]], + class DefaultHandler(dispatchTrie: DispatchTrie[Map[String, (Routes, EndpointMetadata[_])]], mainDecorators: Seq[Decorator[_, _, _]], debugMode: Boolean, handleNotFound: () => Response.Raw, @@ -101,35 +101,30 @@ object Main{ (r: Any) => Main.writeResponse(exchange, r.asInstanceOf[Response.Raw]) ) - val dispatchTrie: DispatchTrie[(Routes, EndpointMetadata[_])] = routeTries.get(effectiveMethod) match { - case None => - Main.writeResponse(exchange, handleMethodNotAllowed()) - return - case Some(trie) => trie - } - dispatchTrie.lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match { case None => Main.writeResponse(exchange, handleNotFound()) - case Some(((routes, metadata), routeBindings, remaining)) => - Decorator.invoke( - Request(exchange, remaining), - metadata.endpoint, - metadata.entryPoint.asInstanceOf[EntryPoint[Routes, _]], - routes, - routeBindings, - (mainDecorators ++ routes.decorators ++ metadata.decorators).toList, - Nil - ) match{ - case Result.Success(res) => runner(res) - case e: Result.Error => - Main.writeResponse( - exchange, - handleError(routes, metadata, e) - ) - None + case Some((methodMap, routeBindings, remaining)) => + methodMap.get(effectiveMethod) match { + case None => Main.writeResponse(exchange, handleMethodNotAllowed()) + case Some((routes, metadata)) => + Decorator.invoke( + Request(exchange, remaining), + metadata.endpoint, + metadata.entryPoint.asInstanceOf[EntryPoint[Routes, _]], + routes, + routeBindings, + (mainDecorators ++ routes.decorators ++ metadata.decorators).toList, + Nil + ) match { + case Result.Success(res) => runner(res) + case e: Result.Error => + Main.writeResponse( + exchange, + handleError(routes, metadata, e) + ) + } } } - // println("Completed Request: " + exchange.getRequestPath) }catch{case e: Throwable => e.printStackTrace() } @@ -149,23 +144,25 @@ object Main{ ) } - def prepareRouteTries(allRoutes: Seq[Routes]): Map[String, DispatchTrie[(Routes, EndpointMetadata[_])]] = { - val routeList = for{ + def prepareDispatchTrie(allRoutes: Seq[Routes]): DispatchTrie[Map[String, (Routes, EndpointMetadata[_])]] = { + val flattenedRoutes = for { routes <- allRoutes - route <- routes.caskMetadata.value.map(x => x: EndpointMetadata[_]) - } yield (routes, route) + metadata <- routes.caskMetadata.value + } yield { + val segments = Util.splitPath(metadata.endpoint.path) + val methodMap = metadata.endpoint.methods.map(_ -> (routes, metadata: EndpointMetadata[_])).toMap + (segments, methodMap, metadata.endpoint.subpath) + } - val allMethods: Set[String] = - routeList.flatMap(_._2.endpoint.methods).map(_.toLowerCase).toSet + val dispatchInputs = flattenedRoutes.groupBy(_._1).map { case (segments, values) => + val methodMap = values.map(_._2).flatten.toMap + val hasSubpath = values.map(_._3).contains(true) + (segments, methodMap, hasSubpath) + }.toSeq - allMethods - .map { method => - method -> DispatchTrie.construct[(Routes, EndpointMetadata[_])](0, - for ((route, metadata) <- routeList if metadata.endpoint.methods.contains(method)) - yield (Util.splitPath(metadata.endpoint.path): collection.IndexedSeq[String], (route, metadata), metadata.endpoint.subpath) - ) - }.toMap + DispatchTrie.construct(0, dispatchInputs) } + def writeResponse(exchange: HttpServerExchange, response: Response.Raw) = { response.data.headers.foreach{case (k, v) => exchange.getResponseHeaders.put(new HttpString(k), v) diff --git a/example/minimalApplication/app/test/src/ExampleTests.scala b/example/minimalApplication/app/test/src/ExampleTests.scala index 986fc617b3..cd04898c05 100644 --- a/example/minimalApplication/app/test/src/ExampleTests.scala +++ b/example/minimalApplication/app/test/src/ExampleTests.scala @@ -27,7 +27,7 @@ object ExampleTests extends TestSuite{ requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh" - requests.get(s"$host/do-thing", check = false).statusCode ==> 404 + requests.delete(s"$host/do-thing", check = false).statusCode ==> 405 } } } diff --git a/example/minimalApplication2/app/test/src/ExampleTests.scala b/example/minimalApplication2/app/test/src/ExampleTests.scala index 5bbee09e9f..7d5ad2873e 100644 --- a/example/minimalApplication2/app/test/src/ExampleTests.scala +++ b/example/minimalApplication2/app/test/src/ExampleTests.scala @@ -27,7 +27,7 @@ object ExampleTests extends TestSuite{ requests.post(s"$host/do-thing", data = "hello").text() ==> "olleh" - requests.get(s"$host/do-thing", check = false).statusCode ==> 404 + requests.delete(s"$host/do-thing", check = false).statusCode ==> 405 } } }