diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/README.tpl.qute.md b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/README.tpl.qute.md deleted file mode 100644 index 26809cc0bf4ec..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/README.tpl.qute.md +++ /dev/null @@ -1,3 +0,0 @@ -{#include readme-header /} - -> :warning: **INCOMPATIBLE WITH NATIVE**: Azure Functions Integration is not compatible with native yet! diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/host.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/host.json deleted file mode 100644 index 9f633be4856b6..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/pom.tpl.qute.xml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/pom.tpl.qute.xml deleted file mode 100644 index 58b634e17a6b2..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/pom.tpl.qute.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 1.19.0 - 11 - $\{artifactId}-{gen-info.time} - {app-region} - {resource-group} - - - - - com.microsoft.azure - azure-functions-maven-plugin - $\{azure.functions.maven.plugin.version} - - - $\{functionAppName} - - $\{functionResourceGroup} - - java-functions-app-service-plan - - - $\{functionAppRegion} - - - - - - - - - {function-os} - 11 - - - - FUNCTIONS_EXTENSION_VERSION - ~4 - - - - - - package-functions - - package - - - - - - - maven-clean-plugin - 3.1.0 - - - - obj - - - - - - - diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml deleted file mode 100644 index 8d74884950fe6..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/src/main/resources/application.tpl.qute.yml +++ /dev/null @@ -1,3 +0,0 @@ -quarkus: - http: - root-path: {root-context-path} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-codestart/base/host.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-codestart/base/host.json deleted file mode 100644 index 9f633be4856b6..0000000000000 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-codestart/base/host.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2.0", - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[3.*, 4.0.0)" - } -} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/local.settings.json b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/base/local.settings.json similarity index 100% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/base/local.settings.json rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/base/local.settings.json diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/base/src/main/resources/application.tpl.qute.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/base/src/main/resources/application.tpl.qute.yml new file mode 100644 index 0000000000000..a04a91b22e344 --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/base/src/main/resources/application.tpl.qute.yml @@ -0,0 +1,5 @@ +quarkus: + http: + root-path: {root-context-path} + azure-functions: + app-name: {project.artifact-id}-{gen-info.time} \ No newline at end of file diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/codestart.yml b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/codestart.yml similarity index 82% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/codestart.yml rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/codestart.yml index 90d341f05b794..20018f15077a1 100644 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/codestart.yml +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/codestart.yml @@ -1,9 +1,7 @@ name: azure-functions-http-example ref: azure-functions-http type: code -tags: - - example - - maven-only +tags: extension-codestart metadata: title: Azure Functions HTTP Integration example description: This example contains a HTTP function ready for Azure. @@ -15,12 +13,6 @@ language: data: # /api is the default root context azure functions will prepend root-context-path: "/api" - app-region: - eastus - resource-group: - quarkus-examples - function-os: - linux dependencies: - io.quarkus:quarkus-azure-functions-http - io.quarkus:quarkus-resteasy-reactive diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/integrationTest/java/org/acme/GreetingIT.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/integrationTest/java/org/acme/GreetingIT.java new file mode 100644 index 0000000000000..7cb5a77aa344d --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/integrationTest/java/org/acme/GreetingIT.java @@ -0,0 +1,19 @@ +package org.acme; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import org.junit.jupiter.api.Test; +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +public class GreetingIT { + + @Test + public void testIt() { + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(is("hello")); + } +} diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/java/src/main/java/org/acme/GreetingResource.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/main/java/org/acme/GreetingResource.java similarity index 100% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/java/src/main/java/org/acme/GreetingResource.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/main/java/org/acme/GreetingResource.java diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/java/src/test/java/org/acme/GreetingTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/test/java/org/acme/GreetingTest.java similarity index 100% rename from devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/azure-functions-http-example/java/src/test/java/org/acme/GreetingTest.java rename to devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/azure-functions-http-codestart/java/src/test/java/org/acme/GreetingTest.java diff --git a/docs/src/main/asciidoc/azure-functions-http.adoc b/docs/src/main/asciidoc/azure-functions-http.adoc index b6f281f358237..4496bd3014380 100644 --- a/docs/src/main/asciidoc/azure-functions-http.adoc +++ b/docs/src/main/asciidoc/azure-functions-http.adoc @@ -11,7 +11,8 @@ include::_attributes.adoc[] The `quarkus-azure-functions-http` extension allows you to write microservices with RESTEasy Reactive (our Jakarta REST implementation), Undertow (servlet), Reactive Routes, or xref:funqy-http.adoc[Funqy HTTP] and make these microservices deployable to the Azure Functions runtime. - +In other words, this extension is a bridge from the Azure Functions HttpTrigger and the Quarkus family +of HTTP APIs. One azure function deployment can represent any number of Jakarta REST, servlet, Reactive Routes, or xref:funqy-http.adoc[Funqy HTTP] endpoints. include::{includes}/extension-status.adoc[] @@ -30,9 +31,9 @@ include::{includes}/prerequisites.adoc[] This guide walks you through running a maven project that can deploy a Resteasy Reactive endpoint to Azure Functions. While only Jakarta REST is provided as an example, you can easily replace it with the HTTP framework of your choice. -== Creating the Maven Deployment Project +== Creating the Maven/Gradle Project -You can download the example code from Quarkus's application generator at https://code.quarkus.io/d?e=azure-functions-http&cn=code.quarkus.io[this link]. +You can generate the example code from Quarkus's online application generator at https://code.quarkus.io/d?e=azure-functions-http&cn=code.quarkus.io[this link]. You can also generate this example with the Quarkus CLI: @@ -41,6 +42,8 @@ You can also generate this example with the Quarkus CLI: quarkus create app --extension=quarkus-azure-functions-http ---- +Add the `--gradle` switch if you want to generate a gradle project. + == Login to Azure If you don't log in to Azure you won't be able to deploy. @@ -52,66 +55,139 @@ az login == Quarkus dev mode -Quarkus dev mode works by just running your application as a HTTP endpoint. +Quarkus dev mode works by just running your application as a HTTP endpoint. In dev mode +there is no special interaction with the Azure Functions local runtime. [source,bash,subs=attributes+] ---- ./mvnw clean package quarkus:dev ---- +== Examining the project + +If you open the `pom.xml` or `build.gradle` build file of the generated project you'll see that +the project is similar to any other Quarkus project. +The `quarkus-azure-functions-http` extension is the integration point between +Quarkus and Azure Functions. + +The current implementation of the `quarkus-azure-functions-http` extension no longer requires the +`azure-functions-maven-plugin` or gradle equivalent. Local development and Azure Functions packaging and +deployment is now all done by Quarkus. + +Build configuration is now all within `application.properties`. The only required configuration switch +is `quarkus.azure-functions.app-name`. + +== Azure Deployment Descriptors + +The Azure Functions `host.json` deployment descriptor is automatically +generated, but if you need to override it, declare it in the root directory of the project and +rerun the build when you are ready. + +[#config-azure-paths] +== Configuring Root Paths + +The default route prefix for an Azure Function is `/api`. All of your Jakarta REST, Servlet, Reactive Routes, and xref:funqy-http.adoc[Funqy HTTP] endpoints must +explicitly take this into account. In the generated project this is handled by the +`quarkus.http.root-path` switch in `application.properties` + +== Login to Azure + +If you don't log in to Azure you won't be able to deploy. + +[source,bash,subs=attributes+] +---- +az login +---- + +== Quarkus dev mode +Quarkus dev mode does not work currently with Azure Functions. -== Run locally in Azure Functions simulated environment +== Run locally in Azure Functions local environment -If you want to try your app with a simulated local Azure Functions environment, you can +If you want to try this example within the local Azure Functions environment, you can use this command [source,bash,subs=attributes+] ---- -./mvnw clean package azure-functions:run +./mvnw quarkus:run +---- + +or + +[source,bash,subs=attributes+] +---- +./gradlew --info --no-daemon quarkusRun ---- +Gradle is a bit quirky with process management, so you need the `--no-daemon` switch or control-c will not +destroy the process cleanly and you'll have open ports. + Note that you must have the https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local#v2[Azure Functions Core Tools] installed for this to work! -== Deploy to Azure +The URL to access the example would be: + +http://localhost:8081/api/hello + +== Quarkus Integration Testing + +You can implement integration tests using `@QuarkusIntegrationTest` functionality. When these +integration tests run, the local Azure Functions environment will be spun up for the duration of integration testing. -The `pom.xml` you generated in the previous step pulls in the `azure-functions-maven-plugin`. Running maven package -generates config files and a staging directory required by the `azure-functions-maven-plugin`. Here's -how to execute it. +For maven: [source,bash,subs=attributes+] ---- -./mvnw clean package azure-functions:deploy +./mvnw -DskipITs=false verify ---- -If deployment is a success, the azure plugin will tell you the base URL to access your function. +Make sure any integration tests you execute with maven use the `*IT.java` file pattern so that regular builds do not execute +the test. -i.e. -[source] +For Gradle: +[source,bash,subs=attributes+] ---- -[INFO] HTTP Trigger Urls: -[INFO] QuarkusHttp : https://{appName}.azurewebsites.net/api/{*path} +./gradlew --info quarkusIntTest ---- -The URL to access the service would be +Make sure any integration tests you execute with Gradle are located within `src/integrationTest/java`. Integration +tests that exist in `src/test` will run with normal build and fail. -https://{appName}.azurewebsites.net/api/hello +== Deploy to Azure -== Extension maven dependencies +The `quarkus-azure-functions-http` extension handles all the work to deploy to Azure. By default, +Quarkus will use the Azure CLI in the background to authenticate and deploy to Azure. If you have +multiple subscriptions associated with your account, you must set the `quarkus.azure-functions.subscription-id` +property in your `application.properties` file to the subscription you want to use. +For other authentication mechanisms and deployment options see our config properties https://quarkus.io/guides/all-config[here]. -You must include the `quarkus-azure-functions-http` extension as this is a generic bridge between the Azure Functions -runtime and the HTTP framework you are writing your microservices in. +To run the deploy, after you build your project execute: -== Azure Deployment Descriptors +[source,bash,subs=attributes+] +---- +./mvnw quarkus:deploy +---- -Templates for Azure Functions deployment descriptors (`host.json`, `function.json`) are within -the base directory of the project. Edit them as you need to. Rerun the build when you are ready. +or -[#config-azure-paths] -== Configuring Root Paths +[source,bash,subs=attributes+] +---- +./gradlew --info deploy +---- + +If deployment is a success, Quarkus will output the endpoint URL of the example function to the console +For Gradle, you must use the `--info` switch to see this output! + +i.e. +[source] +---- +[INFO] HTTP Trigger Urls: +[INFO] HttpExample : https://{appName}.azurewebsites.net/api/{*path} +---- + +The URL to access the service would be something like + +https://{appName}.azurewebsites.net/api/hello -The default route prefix for an Azure Function is `/api`. All of your Jakarta REST, Servlet, Reactive Routes, and xref:funqy-http.adoc[Funqy HTTP] endpoints must -explicitly take this into account. In the generated project this is handled by the -`quarkus.http.root-path` switch in `application.properties` diff --git a/docs/src/main/asciidoc/azure-functions.adoc b/docs/src/main/asciidoc/azure-functions.adoc index 17f5bd61e559d..df4d9a81a7523 100644 --- a/docs/src/main/asciidoc/azure-functions.adoc +++ b/docs/src/main/asciidoc/azure-functions.adoc @@ -100,15 +100,11 @@ deployment is now all done by Quarkus. Build configuration is now all within `application.properties`. The only required configuration switch is `quarkus.azure-functions.app-name`. +== Azure Deployment Descriptors -== Login to Azure - -If you don't log in to Azure you won't be able to deploy. - -[source,bash,subs=attributes+] ----- -az login ----- +The Azure Functions `host.json` deployment descriptor is automatically +generated, but if you need to override it, declare it in the root directory of the project and +rerun the build when you are ready. == Quarkus dev mode @@ -137,6 +133,11 @@ destroy the process cleanly and you'll have open ports. Note that you must have the https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local#v2[Azure Functions Core Tools] installed for this to work! +The URL to access the example would be: + +https://localhost:8081/HttpExample?name=Bill + + == Quarkus Integration Testing You can implement integration tests using `@QuarkusIntegrationTest` functionality. When these @@ -161,6 +162,15 @@ For Gradle: Make sure any integration tests you execute with Gradle are located within `src/integrationTest/java`. Integration tests that exist in `src/test` will run with normal build and fail. +== Login to Azure + +If you don't log in to Azure you won't be able to deploy. + +[source,bash,subs=attributes+] +---- +az login +---- + == Deploy to Azure The `quarkus-azure-functions` extension handles all the work to deploy to Azure. By default, @@ -197,9 +207,5 @@ The URL to access the service would be https://{appName}.azurewebsites.net/api/HttpExample -== Azure Deployment Descriptors - -Templates for Azure Functions deployment descriptors (`host.json`, `function.json`) are within -base directory of the project. Edit them as you need to. Rerun the build when you are ready. diff --git a/extensions/azure-functions-http/deployment/pom.xml b/extensions/azure-functions-http/deployment/pom.xml index fa285088c0a76..459f7d344df8d 100644 --- a/extensions/azure-functions-http/deployment/pom.xml +++ b/extensions/azure-functions-http/deployment/pom.xml @@ -17,6 +17,10 @@ io.quarkus quarkus-azure-functions-http + + io.quarkus + quarkus-azure-functions-deployment + io.quarkus quarkus-core-deployment diff --git a/extensions/azure-functions-http/deployment/src/main/java/io/quarkus/azure/functions/resteasy/deployment/AzureFunctionsHttpProcessor.java b/extensions/azure-functions-http/deployment/src/main/java/io/quarkus/azure/functions/resteasy/deployment/AzureFunctionsHttpProcessor.java index c8e0f1f65169d..6575a6ae588d1 100644 --- a/extensions/azure-functions-http/deployment/src/main/java/io/quarkus/azure/functions/resteasy/deployment/AzureFunctionsHttpProcessor.java +++ b/extensions/azure-functions-http/deployment/src/main/java/io/quarkus/azure/functions/resteasy/deployment/AzureFunctionsHttpProcessor.java @@ -1,17 +1,14 @@ package io.quarkus.azure.functions.resteasy.deployment; -import java.util.Collections; -import java.util.List; +import java.lang.reflect.Method; import org.jboss.logging.Logger; -import io.quarkus.builder.BuildException; -import io.quarkus.deployment.Feature; +import io.quarkus.azure.functions.deployment.AzureFunctionBuildItem; +import io.quarkus.azure.functions.resteasy.runtime.Function; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; -import io.quarkus.deployment.pkg.PackageConfig; -import io.quarkus.deployment.pkg.builditem.LegacyJarRequiredBuildItem; import io.quarkus.runtime.LaunchMode; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; @@ -19,21 +16,19 @@ public class AzureFunctionsHttpProcessor { private static final Logger log = Logger.getLogger(AzureFunctionsHttpProcessor.class); @BuildStep - public LegacyJarRequiredBuildItem forceLegacy(List features, PackageConfig config) throws BuildException { - for (FeatureBuildItem item : features) { - if (Feature.AZURE_FUNCTIONS.getName().equals(item.getName())) { - throw new BuildException( - "quarkus-azure-functions-http extension is incompatible with quarkus-azure-functions extension. Remove quarkus-azure-functions from your build.", - Collections.EMPTY_LIST); - } - } - // Azure Functions need a legacy jar and no runner - config.addRunnerSuffix = false; - return new LegacyJarRequiredBuildItem(); + public RequireVirtualHttpBuildItem requestVirtualHttp(LaunchModeBuildItem launchMode) { + return launchMode.getLaunchMode() == LaunchMode.NORMAL ? RequireVirtualHttpBuildItem.MARKER : null; } @BuildStep - public RequireVirtualHttpBuildItem requestVirtualHttp(LaunchModeBuildItem launchMode) { - return launchMode.getLaunchMode() == LaunchMode.NORMAL ? RequireVirtualHttpBuildItem.MARKER : null; + public void registerFunction(BuildProducer producer) { + Method functionMethod = null; + for (Method method : Function.class.getMethods()) { + if (method.getName().equals("run")) { + functionMethod = method; + break; + } + } + producer.produce(new AzureFunctionBuildItem(Function.QUARKUS_HTTP, Function.class, functionMethod)); } } diff --git a/extensions/azure-functions-http/runtime/pom.xml b/extensions/azure-functions-http/runtime/pom.xml index aac16f0ab69d8..8416a0cbae4ac 100644 --- a/extensions/azure-functions-http/runtime/pom.xml +++ b/extensions/azure-functions-http/runtime/pom.xml @@ -18,6 +18,10 @@ io.quarkus quarkus-vertx-http + + io.quarkus + quarkus-azure-functions + io.quarkus quarkus-core diff --git a/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/BaseFunction.java b/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/BaseFunction.java index 7d1967fe1fcb5..4d62c27cea433 100644 --- a/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/BaseFunction.java +++ b/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/BaseFunction.java @@ -26,7 +26,6 @@ import io.netty.util.ReferenceCountUtil; import io.quarkus.netty.runtime.virtual.VirtualClientConnection; import io.quarkus.netty.runtime.virtual.VirtualResponseHandler; -import io.quarkus.runtime.Quarkus; import io.quarkus.vertx.http.runtime.VertxHttpRecorder; public class BaseFunction { @@ -34,30 +33,6 @@ public class BaseFunction { private static final int BUFFER_SIZE = 8096; - protected static void ensureQuarkusInitialized() { - // The following will atomically call initQuarkus if this hasn't been done before, - // and therefore make sure that deploymentStatus, started and bootstrapError are all set as necessary - QuarkusInitializer.ensureQuarkusInitialized(); - } - - // needed for mock tests - public static boolean throwException = true; - - private static void initQuarkus() { - if (throwException) { - Quarkus.manualInitialize(); - Quarkus.manualStart(); - } else { - try { - Quarkus.manualInitialize(); - Quarkus.manualStart(); - - } catch (Exception ex) { - - } - } - } - protected HttpResponseMessage dispatch(HttpRequestMessage> request) { try { return nettyDispatch(request); @@ -169,19 +144,4 @@ public void close() { future.completeExceptionally(new RuntimeException("Connection closed")); } } - - private static final class QuarkusInitializer { - - static { - // Using an initializer block ensures that initQuarkus is called exactly once, - // and is called atomically, thereby making it thread-safe. - - initQuarkus(); - } - - private static void ensureQuarkusInitialized() { - // No code needed; the static initializer block will take care of the initialization. - // This method exists to ensure that this class is loaded, and therefore Quarkus is initialized. - } - } } diff --git a/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/Function.java b/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/Function.java index dc199a5e7b384..dde4d70e945c0 100644 --- a/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/Function.java +++ b/extensions/azure-functions-http/runtime/src/main/java/io/quarkus/azure/functions/resteasy/runtime/Function.java @@ -12,15 +12,15 @@ public class Function extends BaseFunction { - @FunctionName("QuarkusHttp") + public static final String QUARKUS_HTTP = "QuarkusHttp"; + + @FunctionName(QUARKUS_HTTP) public HttpResponseMessage run( @HttpTrigger(name = "req", dataType = "binary", methods = { HttpMethod.GET, HttpMethod.HEAD, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS }, route = "{*path}", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, final ExecutionContext context) { - ensureQuarkusInitialized(); - return dispatch(request); } } diff --git a/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml index f0bf92a0142b2..cf314451f2bfd 100644 --- a/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/azure-functions-http/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -11,6 +11,5 @@ metadata: status: "preview" codestart: name: "azure-functions-http" - kind: "example" languages: "java" artifact: "io.quarkus:quarkus-project-core-extension-codestarts" \ No newline at end of file diff --git a/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java b/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java index 4dc3a61516975..3e37c618f42d0 100644 --- a/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java +++ b/extensions/azure-functions-http/runtime/src/test/java/io/quarkus/azure/functions/resteasy/runtime/FunctionTest.java @@ -56,7 +56,6 @@ public class FunctionTest { private final HttpResponseMessage response = mock(HttpResponseMessage.class); static { - BaseFunction.throwException = false; } @BeforeEach diff --git a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsAppNameBuildItem.java b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsAppNameBuildItem.java new file mode 100644 index 0000000000000..66995bd53fdc5 --- /dev/null +++ b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsAppNameBuildItem.java @@ -0,0 +1,15 @@ +package io.quarkus.azure.functions.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +final public class AzureFunctionsAppNameBuildItem extends SimpleBuildItem { + private final String appName; + + public AzureFunctionsAppNameBuildItem(String appName) { + this.appName = appName; + } + + public String getAppName() { + return appName; + } +} diff --git a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsConfig.java b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsConfig.java index 95c95a97110a6..049684836a21d 100644 --- a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsConfig.java +++ b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsConfig.java @@ -39,10 +39,12 @@ public class AzureFunctionsConfig { /** - * App name for azure function project. This is required setting + * App name for azure function project. This is required setting. + * + * Defaults to the base artifact name */ @ConfigItem - public String appName; + public Optional appName; /** * Azure Resource Group for your Azure Functions @@ -141,7 +143,7 @@ public static class RuntimeConfig { } - public FunctionAppConfig toFunctionAppConfig(String subscriptionId) { + public FunctionAppConfig toFunctionAppConfig(String subscriptionId, String appName) { Map appSettings = this.appSettings; if (appSettings.isEmpty()) { appSettings = new HashMap<>(); diff --git a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsDeployCommand.java b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsDeployCommand.java index 8430504515e35..aff10a43c92f2 100644 --- a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsDeployCommand.java +++ b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsDeployCommand.java @@ -98,12 +98,13 @@ public void declare(BuildProducer producer) { @BuildStep public void deploy(DeployConfig deployConfig, AzureFunctionsConfig config, + AzureFunctionsAppNameBuildItem appName, OutputTargetBuildItem output, BuildProducer producer) throws Exception { if (!deployConfig.isEnabled(AZURE_FUNCTIONS)) return; - validateParameters(config); + validateParameters(config, appName.getAppName()); setCurrentOperation(); AzureMessager.setDefaultMessager(new QuarkusAzureMessager()); Azure.az().config().setLogLevel(HttpLogDetailLevel.NONE.name()); @@ -111,9 +112,10 @@ public void deploy(DeployConfig deployConfig, AzureFunctionsConfig config, AzureTaskManager.register(new QuarkusAzureTaskManager()); initAzureAppServiceClient(config); - final FunctionAppBase target = createOrUpdateResource(config.toFunctionAppConfig(subscriptionId)); + final FunctionAppBase target = createOrUpdateResource( + config.toFunctionAppConfig(subscriptionId, appName.getAppName())); Path outputDirectory = output.getOutputDirectory(); - Path functionStagingDir = outputDirectory.resolve("azure-functions").resolve(config.appName); + Path functionStagingDir = outputDirectory.resolve("azure-functions").resolve(appName.getAppName()); deployArtifact(functionStagingDir, target); producer.produce(new DeployCommandActionBuildItem(AZURE_FUNCTIONS, true)); @@ -156,12 +158,12 @@ public AzureString getDescription() { } } - protected void validateParameters(AzureFunctionsConfig config) throws BuildException { + protected void validateParameters(AzureFunctionsConfig config, String appName) throws BuildException { // app name - if (StringUtils.isBlank(config.appName)) { + if (StringUtils.isBlank(appName)) { throw new BuildException(EMPTY_APP_NAME); } - if (config.appName.startsWith("-") || !config.appName.matches(APP_NAME_PATTERN)) { + if (appName.startsWith("-") || !appName.matches(APP_NAME_PATTERN)) { throw new BuildException(INVALID_APP_NAME); } // resource group diff --git a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsProcessor.java b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsProcessor.java index 15386c5b75c50..9c23eec031b80 100644 --- a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsProcessor.java +++ b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsProcessor.java @@ -76,14 +76,23 @@ FeatureBuildItem feature() { return new FeatureBuildItem(Feature.AZURE_FUNCTIONS); } + @BuildStep + AzureFunctionsAppNameBuildItem appName(OutputTargetBuildItem output, AzureFunctionsConfig functionsConfig) { + String appName = functionsConfig.appName.orElse(output.getBaseName()); + return new AzureFunctionsAppNameBuildItem(appName); + } + @BuildStep(onlyIf = IsNormal.class, onlyIfNot = NativeBuild.class) public ArtifactResultBuildItem packageFunctions(List functions, OutputTargetBuildItem target, AzureFunctionsConfig functionsConfig, PackageConfig packageConfig, + AzureFunctionsAppNameBuildItem appName, JarBuildItem jar) throws Exception { - if (functions == null || functions.isEmpty()) + if (functions == null || functions.isEmpty()) { + log.warn("No azure functions exist in deployment"); return null; + } AnnotationHandler handler = new AnnotationHandlerImpl(); HashSet methods = new HashSet<>(); for (AzureFunctionBuildItem item : functions) @@ -97,7 +106,7 @@ public ArtifactResultBuildItem packageFunctions(List fun Path rootPath = target.getOutputDirectory().resolve(".."); Path outputDirectory = target.getOutputDirectory(); - Path functionStagingDir = outputDirectory.resolve("azure-functions").resolve(functionsConfig.appName); + Path functionStagingDir = outputDirectory.resolve("azure-functions").resolve(appName.getAppName()); copyHostJson(rootPath, functionStagingDir); diff --git a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsRunCommand.java b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsRunCommand.java index a8a5f7daf5b63..7797fd7b49901 100644 --- a/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsRunCommand.java +++ b/extensions/azure-functions/deployment/src/main/java/io/quarkus/azure/functions/deployment/AzureFunctionsRunCommand.java @@ -37,8 +37,9 @@ public class AzureFunctionsRunCommand { @BuildStep public RunCommandActionBuildItem run(List functions, OutputTargetBuildItem target, + AzureFunctionsAppNameBuildItem appName, AzureFunctionsConfig config) throws Exception { - Path stagingDir = getDeploymentStagingDirectoryPath(target, config); + Path stagingDir = getDeploymentStagingDirectoryPath(target, appName.getAppName()); File file = stagingDir.toFile(); if (!file.exists() || !file.isDirectory()) { throw new BuildException("Staging directory does not exist. Rebuild the app", Collections.emptyList()); @@ -57,8 +58,8 @@ public RunCommandActionBuildItem run(List functions, Out return launcher; } - protected Path getDeploymentStagingDirectoryPath(OutputTargetBuildItem target, AzureFunctionsConfig config) { - return target.getOutputDirectory().resolve("azure-functions").resolve(config.appName); + protected Path getDeploymentStagingDirectoryPath(OutputTargetBuildItem target, String appName) { + return target.getOutputDirectory().resolve("azure-functions").resolve(appName); } protected void checkRuntimeExistence(final CommandHandler handler) throws AzureExecutionException { diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/FailOnDuplicateCodestartFileStrategyHandler.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/FailOnDuplicateCodestartFileStrategyHandler.java index c3b30e9e9ab24..5e3ded6653fbe 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/FailOnDuplicateCodestartFileStrategyHandler.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/FailOnDuplicateCodestartFileStrategyHandler.java @@ -40,6 +40,8 @@ public void copyStaticFile(Source source, Path targetPath) throws IOException { + targetPath); } Files.createDirectories(targetPath.getParent()); + source.copyTo(targetPath); } + } diff --git a/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartBuildIT.java b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartBuildIT.java index 415070a21d879..32b90968cf96e 100644 --- a/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartBuildIT.java +++ b/integration-tests/devtools/src/test/java/io/quarkus/devtools/codestarts/quarkus/QuarkusCodestartBuildIT.java @@ -52,6 +52,10 @@ private static boolean isExcluded(String codestart) { return true; } + if (codestart.startsWith("azure-functions")) { + return true; + } + return EXCLUDED.contains(codestart); } diff --git a/integration-tests/virtual-http-resteasy/src/test/java/io/quarkus/it/virtual/FunctionTest.java b/integration-tests/virtual-http-resteasy/src/test/java/io/quarkus/it/virtual/FunctionTest.java index 1a48ead8b5ec1..e6923d736cabd 100644 --- a/integration-tests/virtual-http-resteasy/src/test/java/io/quarkus/it/virtual/FunctionTest.java +++ b/integration-tests/virtual-http-resteasy/src/test/java/io/quarkus/it/virtual/FunctionTest.java @@ -18,7 +18,6 @@ import com.microsoft.azure.functions.HttpResponseMessage; import com.microsoft.azure.functions.HttpStatus; -import io.quarkus.azure.functions.resteasy.runtime.BaseFunction; import io.quarkus.azure.functions.resteasy.runtime.Function; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; @@ -30,7 +29,6 @@ public class FunctionTest { @BeforeAll public static void setFlag() { - BaseFunction.throwException = false; } @Test diff --git a/integration-tests/virtual-http/src/test/java/io/quarkus/it/virtual/FunctionTest.java b/integration-tests/virtual-http/src/test/java/io/quarkus/it/virtual/FunctionTest.java index 0782c35e7ec3f..08adf8fab8f88 100644 --- a/integration-tests/virtual-http/src/test/java/io/quarkus/it/virtual/FunctionTest.java +++ b/integration-tests/virtual-http/src/test/java/io/quarkus/it/virtual/FunctionTest.java @@ -10,7 +10,6 @@ import jakarta.ws.rs.core.MediaType; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.microsoft.azure.functions.ExecutionContext; @@ -18,7 +17,6 @@ import com.microsoft.azure.functions.HttpResponseMessage; import com.microsoft.azure.functions.HttpStatus; -import io.quarkus.azure.functions.resteasy.runtime.BaseFunction; import io.quarkus.azure.functions.resteasy.runtime.Function; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; @@ -28,11 +26,6 @@ */ @QuarkusTest public class FunctionTest { - @BeforeAll - public static void setFlag() { - BaseFunction.throwException = false; - } - @Test public void testFunqy() { final HttpRequestMessageMock req = new HttpRequestMessageMock();