GCP Cloud functions in Scala 3

Lets face it, Scala 3 is excellent, but how to make it run in the cloud.

Lets start by running the cloud function locally.

package com.jgibbons.eg1

import com.google.cloud.functions.HttpFunction
import com.google.cloud.functions.HttpRequest
import com.google.cloud.functions.HttpResponse
import java.io.BufferedWriter
import java.io.IOException

// https://cloud.google.com/functions/docs/first-java#gradle
@main def RunLocallyHelloWorldScala3 : Unit =
  println(s"Test Runner for HelloWorldScala3")
  com.google.cloud.functions.invoker.runner.Invoker.main(Array("--target", classOf[HelloWorldScala3].getName))

class HelloWorldScala3 extends HttpFunction :
  override def service(request: HttpRequest, response: HttpResponse) =
    val writer = response.getWriter()
    writer.write("Hello World from Google Cloud Function in Scala 3!")

That was easy.

And we all use SBT and don’t get pulled back to gradle or maven, so, surely that is hard.

build.sbt

ThisBuild / scalaVersion := "3.0.0"
ThisBuild / organization := "com.jgibbons"

lazy val gcpKowFunctions = (project in file("."))
  .settings(
    name := "gcp-kow-functions",
    libraryDependencies += "org.scalatest" % "scalatest_3" % "3.2.9" % Test,

    // Every function needs this dependency to get the Functions Framework API.
    libraryDependencies += "com.google.cloud.functions" % "functions-framework-api" % "1.0.1",

    // To run function locally using Functions Framework's local invoker
    libraryDependencies += "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.0.0-alpha-2-rc5",
  )

project/build.properties

sbt.version=1.5.3

project/plugins.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0")

So, well, the rest of it is the normal build and deployment using gcloud commands which are well covered by others.

eg https://cloud.google.com/functions/docs/first-java#gradle

But, for my notes:

Run the assembly target to create:

target/scala-3.0.0/gcp-kow-functions.assembly-0.1.0-SNAPSHOT.jar

cd target/scala-3.0.0/
gcloud functions deploy scala3HelloWorld --entry-point com.jgibbons.eg1.HelloWorldScala3 --runtime java11 --trigger-http --memory 512MB --allow-unauthenticated

gcloud functions describe scala3HelloWorld

Look for the url, and browse to it.

Magic, have a local runner for the function, and a deployed cloud function.

Time to ditch Kubernetes, Http4s, Rho, and the rest of it. Back to simpler Scala 3, and joy.

On last thing

Obviously the java-function-invoker isn’t needed in the cloud, so change the build.sbt to have % Test on the end.

    libraryDependencies += "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.0.0-alpha-2-rc5" % Test,

Then move the runner into the test package, such as

package com.jgibbons.eg1

// https://cloud.google.com/functions/docs/first-java#gradle
@main def RunLocallyHelloWorldScala3 : Unit =
  println(s"Test Runner for HelloWorldScala3")
  com.google.cloud.functions.invoker.runner.Invoker.main(Array("--target", classOf[HelloWorldScala3].getName))

And that reduced the jar down to 6.73M from the original 12.2M.