Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Li Haoyi
    @lihaoyi
    you can use custom decorators for that
    jetaggart
    @jetaggart
    do you have an example by chance?
    I'm confused on how a decorator for a POST method, that receives a pre-flight check from the browser with a OPTIONS method can be handled by a decorator
    Li Haoyi
    @lihaoyi
    I don't think the decorators can have state spanning multiple requests
    you'll have to implement that out of band in your own code
    jetaggart
    @jetaggart
    gotcha, thank you!
    Objektwerks
    @objektwerks
    A Cask/Undertow server doesn't block in main. Why not?
    Is there a more elegant solution? What am I missing? Thanks in advance! :)
    Objektwerks
    @objektwerks
    Also, what is the suggested method of testing a Cask server. I have a few ideas , but would like to hear from the gurus. Thanks in advance!
    Li Haoyi
    @lihaoyi
    the cask examples/ folder projects have unit tests
    you can use that as a starting point and evolve the tests from there
    Objektwerks
    @objektwerks
    @lihaoyi Ok, I'll examine those examples more closely. Thanks!
    urbanchr
    @urbanchr

    I am trying to adapt the chatbot example from Hands-on-Scala to use some buttons and to modify a counter. At the moment I have

    @cask.get("/")
      def hello() = 
          doctype("html")(
          html(
            head(href := bootstrap, script(src := "/static/app2.js")), 
            body( 
                div(id := "messageList")(i(color.red)(cnt)),
                form(onsubmit := "submitForm(); return false")(
                    button(`type` := "submit", id := "nameInput",  value :="1")("1")
                    //button(`type` := "submit", id := "nameInput",  value :="2")("2")
                )
            )
          ))
    
      @cask.postJson("/")
      def postChatMsg(name: String) = {
        cnt = cnt + 1
        ujson.Obj("success" -> true, "txt" -> frag(i(color.red)(cnt)).render, "err" -> "oops")
      }

    However, when I uncomment the second button, I receive a 400 (Bad Request). The helper app2.js is as follows:

    function submitForm() {
      fetch(
        "/",
        {method: "POST", body: JSON.stringify({name: nameInput.value})}
      ).then(response => response.json())
       .then(json => {
          if (json["success"]) {
            messageList.innerHTML = json["txt"]
          }
      })
    }

    What is the right approach to get more than one button to work? Thanks a lot!

    Objektwerks
    @objektwerks
    What is the colloquial Cask way to build a route(s) that serves up 1) an index.html; and 2) associated resources from a Cask server's resource classpath? I've tried staticResources ("/") with no luck. I've used get("/") to manually classpath load an index.html, which works fine ... with 404 errors on all relative resources. This is pretty straightforward with Http4s and Akka-Http. TIA for any help!
    Li Haoyi
    @lihaoyi
    Have you looked at the examples? I'm pretty sure we have one that does what you deacribe
    Objektwerks
    @objektwerks
    Yes, thanks, I've poured all over the examples. I couldn't find a winning route combo that would 1) load an index.html and 2) load associated resources of the jvm classpath. I've come up with a manual hack to load resources - but the index.html doesn't really load correctly. I'll give it a go again today; and if that fails, I'll likely move my web assets to the js project in my classic Scalajs crossproject ( then I can deal with same origin headers ). Cheers! :)
    Objektwerks
    @objektwerks
    I looked at that yesterday; specifically:
    @cask.staticResources("/static")
      def static() = "todo"
    My resources are in /public/ I couldn't get anything to work. Also I don't understand the return value from def static().
    Currently I manually load index.html and associated resources. My index.html page loads correctly. All resources are loaded correctly less the ico and png files, which don't render in the browser. So I'm doing something wrong there.
    Objektwerks
    @objektwerks
    object Router extends LazyLogging:
      private val utf8 = Codec.UTF8.name
      private val contentType = "Content-Type"
      private val basePath = "/public/"
      private val indexHtml = loadResource("index.html")
      private val indexHtmlHeader = contentType -> "text/html; charset=UTF-8"
    
      def loadResource(resource: String): String =
        val path = s"$basePath$resource"
        logger.debug(s"*** load resource: $path")
        Using( Source.fromInputStream(getClass.getResourceAsStream(path), utf8) ) { 
          source => source.mkString
        }.getOrElse("")
    
      def toHeader(resource: String): (String, String) =
        logger.debug(s"*** to header: ${resource.split('.').last}")
        resource.split('.').last match
          case "css"  => contentType -> "text/css"
          case "ico"  => contentType -> "image/x-icon"
          case "png"  => contentType -> "image/png"
          case "js"   => contentType -> "text/javascript"
          case "map"  => contentType -> "application/json"
          case "html" => indexHtmlHeader
          case _      => contentType -> "text/plain"
    
    class Router(dispatcher: Dispatcher) extends Routes with LazyLogging:
      import Router._
    
      @cask.get("/")
      def index() = Response(indexHtml, 200, Seq(indexHtmlHeader))
    
      @cask.get(Router.basePath, subpath = true)
      def resources(request: Request) =
        val resource = request.remainingPathSegments.head
        val content = loadResource(resource)
        val headers = Seq(toHeader(resource))
        logger.debug(s"*** headers: $headers")
        Response(content, 200, headers)
    
      @cask.post("/command")
      def command(request: Request) =
        val command = read[Command](request.text())
        logger.debug(s"*** Command: $command")
    
        val event = dispatcher.dispatch(command)
        logger.debug(s"*** Event: $event")
        write[Event](event)
    
      initialize()
    Objektwerks
    @objektwerks
    Using cask.staticResources I attempted to recreate the above 1) index and 2) resources routes, but with no success.
    Objektwerks
    @objektwerks
    I added ImageIO code, so png files are loaded correctly now. I will use 12 Monkeys imageio library, tomorrow, to load the favicon.ico file. I'll keep you posted.
    Objektwerks
    @objektwerks
    Well I added the twelve monkeys dependency, which provides extended ImageIO support for ico and the like. So, now, I no longer get a NPE; yet the ico is not loaded by the browser correctly. I'm on an Apple M1. Here's the code:
    Objektwerks
    @objektwerks
    import cask.main.Routes
    import cask.model.{Request, Response}
    import com.typesafe.scalalogging.LazyLogging
    
    import java.awt.image.BufferedImage
    import java.io.ByteArrayOutputStream
    import javax.imageio.ImageIO
    
    import scala.io.{Codec, Source}
    import scala.util.{Try, Using}
    
    trait Resources(val basePath: String) extends LazyLogging:
      val utf8 = Codec.UTF8.name
      val contentType = "Content-Type"
      val indexHtml = loadResource("index.html")
      val indexHtmlHeader = contentType -> "text/html; charset=UTF-8"
    
      def toContentType(resource: String): String = resource.split('.').last
    
      def toPath(resource: String): String = s"$basePath$resource"
    
      def toHeader(resource: String): (String, String) =
        logger.debug(s"*** to header: ${toContentType(resource)}")
        toContentType(resource) match
          case "css"  => contentType -> "text/css"
          case "ico"  => contentType -> "image/x-icon"
          case "png"  => contentType -> "image/png"
          case "js"   => contentType -> "text/javascript"
          case "map"  => contentType -> "application/json"
          case "html" => indexHtmlHeader
          case _      => contentType -> "text/plain"
    
      def isImage(resource: String): Boolean =
        toContentType(resource) match
          case "ico" | "png"  => true
          case _              => false
    
      def loadResource(resource: String): Array[Byte] =
        val path = toPath(resource)
        logger.debug(s"*** load resource: $path")
        Using( Source.fromInputStream(getClass.getResourceAsStream(path), utf8) ) {
          source => source.mkString.getBytes
        }.getOrElse(Array.empty[Byte])
    
      def loadImage(resource: String): Array[Byte] =
        val path = toPath(resource)
        logger.debug(s"*** load image: $path")
        val url = getClass.getResource(path)
        val image = ImageIO.read(url)
        val baos = new ByteArrayOutputStream()
        val contentType = toContentType(resource)
        ImageIO.write(image, contentType, baos)
        baos.toByteArray
    
    class Router(dispatcher: Dispatcher) extends Routes with LazyLogging with Resources("/public/"):
      @cask.get("/")
      def index() = Response(indexHtml, 200, Seq(indexHtmlHeader))
    
      @cask.get(basePath, subpath = true)
      def resources(request: Request) =
        val resource = request.remainingPathSegments.head
        val headers = Seq(toHeader(resource))
        if isImage(resource) then Response(loadImage(resource), 200, headers)
        else Response(loadResource(resource), 200, headers)
    
      initialize()
    That's effectively what I was hoping cask.staticResources would do for me. Cheers!
    Objektwerks
    @objektwerks
    I corrected the favicon.ico loading error by replacing the true ico file with a png file --- still named favicon.ico, of course. :)
    Objektwerks
    @objektwerks
    FWIW, this project contains, among other examples, an updated Resources and ResourceRouter: https://github.com/objektwerks/cask
    Stewart Stewart
    @stewSquared

    Suppose I wanted to use a custom type in a path, like

    @cask.get("/user/:id")
    def user(id: java.util.UUID)

    Would it be appropriate to implement a custom cask.endpoints.PathReader[UUID]?
    Or is it better to parse an id string in the body and return a 400?

    Li Haoyi
    @lihaoyi
    either should work
    Objektwerks
    @objektwerks
    FYI, I've built a CorsHandler that works in initial tests. See:
    https://github.com/objektwerks/cask/blob/master/src/main/scala/objektwerks/handler/CorsHandler.scala
    https://github.com/objektwerks/cask/blob/master/src/main/scala/objektwerks/Server.scala
    Naturally, this can be built multiple ways. Note how a CorsHandler is initialized in Server, which extends cask.Main.
    I've posted the above info to cask/github #31 should someone want to run with this initial effort. Cheers!
    discobaba
    @uncleweirdo_twitter
    the autowire project seemed a little bit awesome. Is there some way to get that kind of strong typing with cask?
    rom1dep
    @rom1dep:kde.org
    [m]
    Hello there!
    do you happen to have good resources/idiomatic examples showing how to manage users, authentication and sessions with cask?
    Lucas Della Bella
    @lucasdellabella
    Hey there! I'm looking for a web server to serve a sangria (graphql) api from. I like the simplicity of cask but I'm not really sure what trade-offs I'd need to be considering. Does anyone have thoughts or opinions?
    Li Haoyi
    @lihaoyi
    Cask is pretty bare bones; it basically only does routing and not much else. That can be good or bad depending on what you want
    rom1dep
    @rom1dep:kde.org
    [m]
    @lihaoyi: I'm a bit wary to roll my own authentication and session management, I don't trust myself to do that safely. Could that eventually be absorbed in the framework's features set as an optional thing?
    An unrelated question, has anyone attempted to compile a cask app to native (via GraalVM's native-image)? That would be super interesting for me
    1 reply
    Li Haoyi
    @lihaoyi
    it could, if someone contributes. But right now, Cask only knows about requests, responses, headers and cookies. Higher level concepts like auth or sessions would have to be built on top
    rom1dep
    @rom1dep:kde.org
    [m]
    and that requires state, hence persistence, hence caching and loads of opinionated ways to do it :)
    raul782
    @raul782:matrix.org
    [m]
    I’m testing cask from my iPad+pi and sbt, is there a way to keep the app running(It auto closed after hitting sbt run), or I necessarily need to use mill?
    Raul Rodriguez
    @raul782

    Hello guys, do you know how can I run a cask project with sbt.

    object RadarApp extends cask.Main:
      val allRoutes: Seq[ApiRoutes] = Seq(ApiRoutes())
    
      override def port: Int = 8888
      override def main(args: Array[String]): Unit = super.main(args)

    When running it as

    sbt "run com.example.RadarApp"

    I get the following message:

    [info] running com.example.RadarApp com.example.RadarApp
    Jun 10, 2022 8:36:28 PM org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.1.0.Final
    [success] Total time: 1 s, completed Jun 10, 2022, 8:36:28 PM
    Raul Rodriguez
    @raul782
    running sbt "~run com.example.RadarApp" keeps the server running, though any change in the source code throws an address error
    aduialthin
    @aduialthin:matrix.org
    [m]

    Sorry to post the same question again (didn't know this channel exists).

    I am learning to use cask (https://com-lihaoyi.github.io/cask/#getting-started). But I don't use mill as build tool. Instead I use sbt. In that case, after typing sbt run in the command line, following messages are returned. However accessing to http://localhost:8080 shows Unable to connect, unless run sbt process first, and then type run or runMain .... How can I launch the server without executing sbt process? Thanks

    info] running metricsapp.App 
    Jun 24, 2022 3:42:34 PM org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.1.0.Final
    [success] Total time: 1 s, completed Jun 24, 2022, 3:42:34 PM
    Siddhartha Gadgil
    @siddhartha-gadgil

    I was trying to upgrade scala from 2.13.3 to 2.13.8 and various dependencies, and I am getting a strange error because of a change in behaviour for static resources, which I am using for css and javascript (the latter built with mill and copied).

    • when I go to the path to my css file, http://localhost:8080/public/css/bootstrap.min.css the browser insists on downloading the file, not showing it.
    • it seems that (probably for the same reason) bootstrap is not loading in the page calling it.

    The code with the static resource is:

    @cask.staticResources("/public")
      def staticResourceRoutes() = "."

    The strangest thing is I got this error before I updated cask (but updated other stuff) and also after. The changes are in scalaVersion, scalaJSVersion, os-lib, upickle and scalatags.

    It seems to me that I should set some header, but I cannot figure out what. Any help is appreciated.