diff --git a/docs/src/main/asciidoc/funqy-gcp-functions.adoc b/docs/src/main/asciidoc/funqy-gcp-functions.adoc index 9f1bfd35c416bd..50df2cb7fec52e 100644 --- a/docs/src/main/asciidoc/funqy-gcp-functions.adoc +++ b/docs/src/main/asciidoc/funqy-gcp-functions.adoc @@ -43,7 +43,7 @@ gcloud components install beta == The Code There is nothing special about the code and more importantly nothing Google Cloud specific. Funqy functions can be deployed to many different -environments and Google Cloud Functions is one of them. The Java code is actually the same exact code as the {quickstarts-tree-url}/funqy-quickstarts/funqy-http-quickstart[funqy-http-quickstart]. +environments and Google Cloud Functions is one of them. [[choose]] == Choose Your Function @@ -73,7 +73,7 @@ This will compile and package your code. == Create the function -In this example, we will create 3 functions, an http function and two background functions. Background functions allow to +In this example, we will create two background functions. Background functions allow to react to Google Cloud events like PubSub messages, Cloud Storage events, Firestore events, ... [source,java] @@ -89,25 +89,13 @@ public class GreetingFunctions { @Inject GreetingService service; - // <1> - @Funq - public Greeting helloHttpWorld() { - String message = service.hello("world"); - Greeting greeting = new Greeting(); - greeting.setMessage(message); - greeting.setName("world"); - return greeting; - } - - // <2> - @Funq + @Funq // <1> public void helloPubSubWorld(PubsubMessage pubSubEvent) { String message = service.hello("world"); System.out.println(pubSubEvent.messageId + " - " + message); } - // <3> - @Funq + @Funq // <2> public void helloGCSWorld(StorageEvent storageEvent) { String message = service.hello("world"); System.out.println(storageEvent.name + " - " + message); @@ -118,10 +106,9 @@ public class GreetingFunctions { NOTE: Function return type can also be Mutiny reactive types. -1. This is an HTTP function -2. This is a background function that takes as parameter a `io.quarkus.funqy.gcp.functions.event.PubsubMessage`, +1. This is a background function that takes as parameter a `io.quarkus.funqy.gcp.functions.event.PubsubMessage`, this is a convenient class to deserialize a PubSub message. -3. This is a background function that takes as parameter a `io.quarkus.funqy.gcp.functions.event.StorageEvent`, +2. This is a background function that takes as parameter a `io.quarkus.funqy.gcp.functions.event.StorageEvent`, this is a convenient class to deserialize a Google Storage event. NOTE: we provide convenience class to deserialize common Google Cloud event inside the `io.quarkus.funqy.gcp.functions.event` package. @@ -136,18 +123,10 @@ quarkus.funqy.export=helloHttpWorld == Build and Deploy to Google Cloud -To build your application, you first need to define a packaging of type `uber-jar` via your `application.properties`. - -[source] ----- -quarkus.package.uber-jar=true ----- - -Then you can package your application via `mvn clean package`. +To build your application, you can package your application via `mvn clean package`. You will have a single JAR inside the `target/deployment` repository that contains your classes and all your dependencies in it. -Then you will be able to use `gcloud` to deploy your function to Google Cloud, the `gcloud` command will be different regarding which -type of function you want to deploy (http or background), and for background function, from which event you want to be triggered. +Then you will be able to use `gcloud` to deploy your function to Google Cloud, the `gcloud` command will be different depending from which event you want to be triggered. [WARNING] ==== @@ -158,23 +137,6 @@ ERROR: (gcloud.beta.functions.deploy) OperationError: code=7, message=Build Fail This means that Cloud Build is not activated yet. To overcome this error, open the URL shown in the error, follow the instructions and then wait a few minutes before retrying the command. ==== - -=== Http Functions - -Use this command to deploy to Google Cloud Functions: - -[source] ----- -gcloud beta functions deploy quarkus-example-funky-http \ - --entry-point=io.quarkus.funqy.gcp.functions.FunqyHttpFunction \ - --runtime=java11 --trigger-http --source=target/deployment ----- - -The entry point always needs to be `io.quarkus.funqy.gcp.functions.FunqyHttpFunction` as it will be this class -that will bootstrap Quarkus. - -This command will give you as output a `httpsTrigger.url` that points to your function. - === Background Functions - PubSub Use this command to deploy to Google Cloud Functions: @@ -197,11 +159,10 @@ To trigger an event to this function, you can use the `gcloud functions call` co [source] ---- -gcloud functions call quarkus-example-funky-pubsub --data '{"data":"SGVsbG8sIFB1Yi9TdWIh"}' +gcloud functions call quarkus-example-funky-pubsub --data '{"data":"Hello, Pub/Sub"}' ---- -The `--data '{"data":"SGVsbG8sIFB1Yi9TdWIh"}'` option allow to specify the message to be send to PubSub, this needs to be BASE64 encoded. -Here we send `Hello, Pub/Sub!`. +The `--data '{"data":"Hello, Pub/Sub"}'` option allow to specify the message to be send to PubSub. === Background Functions - Cloud Storage @@ -254,25 +215,6 @@ mvn dependency:copy \ Then you can use it to launch your function locally, again, the command depends on the type of function and the type of events. -=== Http Functions - -For HTTP functions, you launch the invoker with a target class of `io.quarkus.funqy.gcp.functions.FunqyHttpFunction`. - -[source] ----- -java -jar java-function-invoker-1.0.0-beta1.jar \ - --classpath target/funqy-google-cloud-functions-1.0-SNAPSHOT-runner.jar \ - --target io.quarkus.funqy.gcp.functions.FunqyHttpFunction ----- - -Then you can call your HTTP function: - -[source] ----- -curl localhost:8080 -> {"name":"world","message":"Hello world!"} ----- - === Background Functions - PubSub For background functions, you launch the invoker with a target class of `io.quarkus.funqy.gcp.functions.FunqyBackgroundFunction`. @@ -313,6 +255,14 @@ curl localhost:8080 -d '{"data":{"name":"text"}}' This will call your PubSub background function with a Cloud Storage event `{"name":"file.txt"}`, so an event on the `file.txt` file. + +== Deploying HTTP Functions via Funqy + +You can use link:funqy-http[Funqy HTTP] on Google Cloud Functions. +This allows you to invoke on multiple Funqy functions using HTTP deployed as one Google Cloud Function. + +For this you need to include both `quarkus-funqy-http` and `quarkus-google-cloud-functions` extension. + == What's next? If you are looking for JAX-RS, Servlet or Vert.x support for Google Cloud Functions, we have it thanks to our link:gcp-functions-http[Google Cloud Functions HTTP binding]. diff --git a/extensions/funqy/funqy-google-cloud-functions/deployment/pom.xml b/extensions/funqy/funqy-google-cloud-functions/deployment/pom.xml index 7ac6dfad516e63..22dddb34c8a978 100644 --- a/extensions/funqy/funqy-google-cloud-functions/deployment/pom.xml +++ b/extensions/funqy/funqy-google-cloud-functions/deployment/pom.xml @@ -16,15 +16,15 @@ io.quarkus - quarkus-funqy-google-cloud-functions + quarkus-funqy-server-common-deployment io.quarkus - quarkus-arc-deployment + quarkus-jackson-deployment io.quarkus - quarkus-funqy-server-common-deployment + quarkus-funqy-google-cloud-functions diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/pom.xml b/extensions/funqy/funqy-google-cloud-functions/runtime/pom.xml index 5473a3d92617a2..144a5646165e85 100644 --- a/extensions/funqy/funqy-google-cloud-functions/runtime/pom.xml +++ b/extensions/funqy/funqy-google-cloud-functions/runtime/pom.xml @@ -26,7 +26,7 @@ com.google.cloud.functions functions-framework-api - compile + compile diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java index 0b32d0ae402864..5c73cad4a35b94 100644 --- a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java +++ b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java @@ -6,8 +6,6 @@ import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; import com.google.cloud.functions.Context; -import com.google.cloud.functions.HttpRequest; -import com.google.cloud.functions.HttpResponse; import io.quarkus.arc.ManagedContext; import io.quarkus.arc.runtime.BeanContainer; @@ -71,25 +69,6 @@ public void chooseInvoker(FunqyConfig config) { } } - /** - * Handle HttpFunction. - * - * @param httpRequest - * @param httpResponse - */ - public static void handle(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { - Object input = null; - if (invoker.hasInput()) { - input = reader.readValue(httpRequest.getInputStream()); - } - FunqyServerResponse response = dispatch(input); - - Object value = response.getOutput().await().indefinitely(); - if (value != null) { - writer.writeValue(httpResponse.getOutputStream(), value); - } - } - /** * Handle RawBackgroundFunction * diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyHttpFunction.java b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyHttpFunction.java deleted file mode 100644 index 38570747f9a908..00000000000000 --- a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyHttpFunction.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.quarkus.funqy.gcp.functions; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -import com.google.cloud.functions.HttpFunction; -import com.google.cloud.functions.HttpRequest; -import com.google.cloud.functions.HttpResponse; - -import io.quarkus.runtime.Application; - -public class FunqyHttpFunction implements HttpFunction { - protected static final String deploymentStatus; - protected static boolean started = false; - - static { - StringWriter error = new StringWriter(); - PrintWriter errorWriter = new PrintWriter(error, true); - if (Application.currentApplication() == null) { // were we already bootstrapped? Needed for mock unit testing. - // For GCP functions, we need to set the TCCL to the QuarkusHttpFunction classloader then restore it. - // Without this, we have a lot of classloading issues (ClassNotFoundException on existing classes) - // during static init. - ClassLoader currentCl = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(FunqyHttpFunction.class.getClassLoader()); - Class appClass = Class.forName("io.quarkus.runner.ApplicationImpl"); - String[] args = {}; - Application app = (Application) appClass.newInstance(); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - app.stop(); - } - }); - app.start(args); - errorWriter.println("Quarkus bootstrapped successfully."); - started = true; - } catch (Exception ex) { - errorWriter.println("Quarkus bootstrap failed."); - ex.printStackTrace(errorWriter); - } finally { - Thread.currentThread().setContextClassLoader(currentCl); - } - } else { - errorWriter.println("Quarkus bootstrapped successfully."); - started = true; - } - deploymentStatus = error.toString(); - } - - @Override - public void service(HttpRequest request, HttpResponse response) throws IOException { - if (!started) { - throw new IOException(deploymentStatus); - } - FunqyCloudFunctionsBindingRecorder.handle(request, response); - } -} diff --git a/integration-tests/funqy-google-cloud-functions/README.md b/integration-tests/funqy-google-cloud-functions/README.md index 0491bf843a0c74..ec44bd91b8c664 100644 --- a/integration-tests/funqy-google-cloud-functions/README.md +++ b/integration-tests/funqy-google-cloud-functions/README.md @@ -16,30 +16,13 @@ Then you need to use Maven to build the artifact, the build will automatically c mvn clean package ``` -Finally, you need to use `gcloud` to deploy the function to Google Cloud. The `gcloud` command is different for HttpFunction and +Finally, you need to use `gcloud` to deploy the function to Google Cloud. The `gcloud` command is different for each Background function so the set of instructions differs for each. This example contains multiple Funqy functions, if you want to test a different function that the one defined inside your `application.properties`, you can use the `--set-env-vars` option of `gcloud` to define the name of the function via the `QUARKUS_FUNQY_EXPORT` environment variable. -## HTTP function -To deploy the HttpFunction, you can use the following `gcloud` command: - -```shell script -gcloud beta functions deploy quarkus-funqy-http --entry-point=io.quarkus.funqy.gcp.functions.FunqyHttpFunction \ - --trigger-http \ - --runtime=java11 --source=target/deployment -``` - -After deploying your HTTP function to Google Cloud, the `gcloud` command will output the endpoint base location. - -You can issue the following `curl` commands to test it: - -```shell script -curl -v {httpsTrigger.url} -``` - ## Background function ### PubSub event diff --git a/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java b/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java index b6e03f7faddc1b..9ec3c7ee68dcf0 100644 --- a/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java +++ b/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java @@ -1,49 +1,16 @@ package io.quarkus.funqy.gcp.functions.test; -import java.time.Duration; -import java.util.Timer; -import java.util.TimerTask; - import javax.inject.Inject; import io.quarkus.funqy.Funq; import io.quarkus.funqy.gcp.functions.event.PubsubMessage; import io.quarkus.funqy.gcp.functions.event.StorageEvent; -import io.smallrye.mutiny.Uni; public class GreetingFunctions { @Inject GreetingService service; - @Funq - public Greeting helloHttpWorld() { - String message = service.hello("world"); - Greeting greeting = new Greeting(); - greeting.setMessage(message); - greeting.setName("world"); - return greeting; - } - - @Funq - public Uni helloHttpWorldAsync() { - return Uni.createFrom().emitter(uniEmitter -> { - - Timer timer = new Timer(); - timer.schedule(new TimerTask() { - @Override - public void run() { - String message = service.hello("world"); - Greeting greeting = new Greeting(); - greeting.setMessage(message); - greeting.setName("world"); - uniEmitter.complete(greeting); - timer.cancel(); - } - }, Duration.ofMillis(1).toMillis()); - }); - } - @Funq public void helloPubSubWorld(PubsubMessage pubSubEvent) { String message = service.hello("world");