diff --git a/azdevops-pipeline/build-release-artifacts.yml b/azdevops-pipeline/build-release-artifacts.yml deleted file mode 100644 index 728dde4b..00000000 --- a/azdevops-pipeline/build-release-artifacts.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Gradle -# Build your Java project and run tests with Gradle using a Gradle wrapper script. -# Add steps that analyze code, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/java - -trigger: none - -pool: - vmImage: ubuntu-latest - -steps: -- checkout: self - -- task: Gradle@3 - inputs: - # Specifies the working directory to run the Gradle build. The task uses the repository root directory if the working directory is not specified. - workingDirectory: '' - # Specifies the gradlew wrapper's location within the repository that will be used for the build. - gradleWrapperFile: 'gradlew' - # Sets the GRADLE_OPTS environment variable, which is used to send command-line arguments to start the JVM. The xmx flag specifies the maximum memory available to the JVM. - gradleOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '$(jdkVersion)' - jdkArchitectureOption: 'x64' - publishJUnitResults: false - tasks: clean assemble - displayName: Assemble durabletask-client and durabletask-azure-functions - -# the secring.gpg file is required to sign the artifacts, it's generated from GnuPG, and it's stored in the library of the durabletaskframework ADO -- task: DownloadSecureFile@1 - name: gpgSecretFile - displayName: 'Download GPG secret file' - inputs: - secureFile: 'secring.gpg' - -- task: Gradle@3 - inputs: - workingDirectory: '' - gradleWrapperFile: 'gradlew' - gradleOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '$(jdkVersion)' - jdkArchitectureOption: 'x64' - tasks: publish - options: '-Psigning.keyId=$(gpgSignKey) -Psigning.password=$(gpgSignPassword) -Psigning.secretKeyRingFile=$(gpgSecretFile.secureFilePath)' - displayName: Publish durabletask-client and durabletask-azure-functions - -- task: CopyFiles@2 - displayName: 'Copy publish file to Artifact Staging Directory' - inputs: - SourceFolder: repo/com/microsoft - Contents: '**/*.*' - TargetFolder: $(Build.ArtifactStagingDirectory) - -- task: PublishPipelineArtifact@1 - name: PublishPipelineArtifact1 - displayName: 'Publish Artifact: Build Outputs' - inputs: - ArtifactName: BuildOutputs - TargetPath: $(Build.ArtifactStagingDirectory) \ No newline at end of file diff --git a/azurefunctions/README.md b/azurefunctions/README.md deleted file mode 100644 index 132daa00..00000000 --- a/azurefunctions/README.md +++ /dev/null @@ -1,230 +0,0 @@ -# Azure Durable Functions setup for Java - -In this article, you follow steps to create and run a simple azure durable functions in Java. - -## Configure your local environment - -The following are the requirements for you local environment: - -- The Java Developer Kit, version 8 or 11, is required. Between the two, JDK 11 is recommended. -- The `JAVA_HOME` environment variable must be set to the install location of the correct version of the JDK. -- [Apache Maven](https://maven.apache.org/), version 3.0 or above for azure function app creation, is required for using automatic project creation tools. - - If Maven isn't your preferred development tool, check out our similar tutorials to [create a function app](https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser). This README also contains instructions for [Gradle](https://gradle.org/). -- [Install Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash) version 3.0.4585+ or 4.0.4590+ - - -## Prerequisite check -In a terminal or command window, run the following commands to check if the correct versions are installed. - -- `java -version` -- `mvn -version` -- `func --version` - -## Using the latest Java worker - -The version of the Java worker that supports Durable Functions is available starting in the following versions of the Azure Functions Core Tools: - -- v3.0.4585 or greater -- v4.0.4590 or greater - -If you aren't able to get a version of the Core Tools in one of these version ranges, you can still use Durable Functions for Java, but you'll need to manually update the version of the Java worker used by your existing Core Tools installation. The following are steps describing how you can update to a compatible version of the Java worker. You can skip these steps if you already have a required Azure Functions Core Tools version installed locally. - -### Download the Java Worker for your particular Core Tools version. - -The Azure Functions Java worker is a single jar file that can be downloaded locally on your machine using one of the following links. - -| Azure Functions Core Tools Version | Java Worker Version | -| - | - | -| v3.x | v1.11.0 ([Download](https://javaworkerrelease.blob.core.windows.net/release/1.11.0/azure-functions-java-worker.jar?sp=r&st=2022-06-01T22:17:09Z&se=2022-12-31T07:17:09Z&spr=https&sv=2020-08-04&sr=b&sig=Lrp0sJ2q91Q2QeNIxAoy4Ulf3ewx1KdNR1td9BXDuxI%3D)) -| v4.x | v2.2.3 ([Download](https://javaworkerrelease.blob.core.windows.net/release/2.2.3/azure-functions-java-worker.jar?sp=r&st=2022-06-01T22:19:48Z&se=2022-12-31T07:19:48Z&spr=https&sv=2020-08-04&sr=b&sig=N4gcAP0jVKqAdo59WxZVFDjtfaBiwCJt%2BMBajkv%2FudI%3D)) - -### Replace the existing core tools Java worker with the downloaded version. - -The location of the file to replace is different depending on which operating system you're using: - -| OS | Java Worker Location | -| - | - | -| Windows | `"%PROGRAMFILES%\Microsoft\Azure Functions Core Tools"` | -| macOS | `/usr/local/Cellar/azure-functions-core-tools@4/4.0.4544/workers` | -| Linux | `/usr/lib/azure-functions-core-tools-` | - -Replace the `azure-functions-java-worker.jar` file from one of the above locations with the downloaded copy. - -## Create a new Azure Functions Java app - -If you use Maven for building Java apps, then you can use the following `mvn` command to create a new Java Functions app (targeting Java 11): - -```bash -mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=11 -``` -**_NOTE:_** You can change the `FUNCTIONS_EXTENSION_VERSION` app setting in `pom.xml` to ~3 to build and deploy a V3 app. - -For more information about creating Azure Function apps using Maven, see the [create a Java function in Azure from the command line](https://docs.microsoft.com/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser) documentation. - -If you're using Gradle, then we recommend copying the [Azure Functions sample app](/samples-azure-functions/) in this repo as the starting point for your app. - -## Use the Durable Task Client SDK and Azure Functions library for Java - -To get access to the classes, interfaces, and annotations required to build Durable Functions, update the pom.xml or build.gradle of your project to reference the [durabletask-azure-functions](https://mvnrepository.com/artifact/com.microsoft/durabletask-azure-functions) and [durabletask-client](https://mvnrepository.com/artifact/com.microsoft/durabletask-client) dependencies. - -## Add a simple Durable Functions orchestration to your project - -Copy the following Java code into a new Java file in your project. It defines three different function types: an HTTP trigger for starting a Durable Functions orchestration, an orchestration trigger function, and an activity trigger function. - -```java -package com.functions; - -import java.util.Optional; - -import com.microsoft.azure.functions.*; -import com.microsoft.azure.functions.annotation.*; - -import com.microsoft.durabletask.*; -import com.microsoft.durabletask.azurefunctions.*; - -public class DurableFunctionsSample { - /** - * This HTTP-triggered function starts the orchestration. - */ - @FunctionName("StartHelloCities") - public HttpResponseMessage startHelloCities( - @HttpTrigger(name = "req", methods = {HttpMethod.POST}) HttpRequestMessage> req, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("HelloCities"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(req, instanceId); - } - - /** - * This is the orchestrator function, which can schedule activity functions, create durable timers, - * or wait for external events in a way that's completely fault-tolerant. The OrchestrationRunner.loadAndRun() - * static method is used to take the function input and execute the orchestrator logic. - */ - @FunctionName("HelloCities") - public String helloCitiesOrchestrator(@DurableOrchestrationTrigger(name = "runtimeState") String runtimeState) { - return OrchestrationRunner.loadAndRun(runtimeState, ctx -> { - String result = ""; - result += ctx.callActivity("SayHello", "Tokyo", String.class).await() + ", "; - result += ctx.callActivity("SayHello", "London", String.class).await() + ", "; - result += ctx.callActivity("SayHello", "Seattle", String.class).await(); - return result; - }); - } - - /** - * This is the activity function that gets invoked by the orchestrator function. - */ - @FunctionName("SayHello") - public String sayHello(@DurableActivityTrigger(name = "name") String name) { - return String.format("Hello %s!", name); - } -} -``` - -### Update host.json - -Update your host.json file to look similar to the following: - -```json -{ - "version": "2.0", - "logging": { - "logLevel": { - "DurableTask.AzureStorage": "Warning", - "DurableTask.Core": "Warning" - } - }, - "extensions": { - "durableTask": { - "hubName": "JavaTestHub" - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle.Preview", - "version": "[4.*, 5.0.0)" - } -} -``` - -The important thing to note is the value for `extensionBundle`, which points to the Azure Functions v4 *Preview* bundle. This is the only bundle that currently has the necessary support for Durable Functions for Java. - -### Update local.settings.json - -Ensure your `local.settings.json` file has a connection string configured for `AzureWebJobsStorage`. If you're using a local storage emulator, like [Azurite](https://docs.microsoft.com/azure/storage/common/storage-use-azurite?tabs=npm), you can use the example below: - -```json -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "java" - } -} -``` - -## Test the Durable Functions orchestration - -Use one of the following commands to start your function app locally using the Azure Functions Core Tools: - -### Maven - -```bash -mvn clean package -mvn azure-functions:run -``` - -### Gradle - -```bash -./gradlew azureFunctionsRun -``` - -### Running the orchestration - -You can use a cURL command from your terminal to test the Durable Functions orchestration. - -```bash -curl -i -X POST http://localhost:7071/api/StartHelloCities -``` - -The output should look something like the following: - -```http -HTTP/1.1 201 Created -Content-Type: application/json; charset=utf-8 -Date: Thu, 02 Jun 2022 05:43:00 GMT -Server: Kestrel -Location: http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866?code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ== -Transfer-Encoding: chunked - -{ - "id": "2461b25e-ece5-4e3d-a22e-593e1f47f866", - "purgeHistoryDeleteUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866?code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ==", - "sendEventPostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866/raiseEvent/{eventName}?code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ==", - "statusQueryGetUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866?code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ==", - "terminatePostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866/terminate?reason={text}&code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ==" -} -``` - -To see whether the orchestration completed, send an HTTP GET request to the URL in the `Location` header from the response of the previous command: - -```bash -curl -i "http://localhost:7071/runtime/webhooks/durabletask/instances/2461b25e-ece5-4e3d-a22e-593e1f47f866?code=yibxD0qEC8MY9hd0UrBR68a8KPkRhuFZAGRkuX5oN5vJ6bLHLjqrhQ==" -``` - -If successful, the response should look something like the following: - -```http -HTTP/1.1 200 OK -Content-Length: 266 -Content-Type: application/json; charset=utf-8 -Date: Thu, 02 Jun 2022 05:45:01 GMT -Server: Kestrel - -{"name":"HelloCities","instanceId":"2461b25e-ece5-4e3d-a22e-593e1f47f866","runtimeStatus":"Completed","input":null,"customStatus":"","output":"Hello Tokyo!, Hello London!, Hello Seattle!","createdTime":"2022-06-02T05:43:00Z","lastUpdatedTime":"2022-06-02T05:43:00Z"} -``` - -At this point, you now have a fully function Durable Functions for Java development environment! diff --git a/azurefunctions/build.gradle b/azurefunctions/build.gradle deleted file mode 100644 index 17d1dd2b..00000000 --- a/azurefunctions/build.gradle +++ /dev/null @@ -1,120 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'signing' - id 'com.github.spotbugs' version '5.2.1' -} - -group 'com.microsoft' -version = '1.5.1' -archivesBaseName = 'durabletask-azure-functions' - -def protocVersion = '3.12.0' - -repositories { - maven { - url "https://oss.sonatype.org/content/repositories/snapshots/" - } -} - -dependencies { - api project(':client') - implementation group: 'com.microsoft.azure.functions', name: 'azure-functions-java-library', version: '3.0.0' - implementation "com.google.protobuf:protobuf-java:${protocVersion}" - compileOnly "com.microsoft.azure.functions:azure-functions-java-spi:1.0.0" -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -publishing { - repositories { - maven { - url "file://$project.rootDir/repo" - } - } - publications { - mavenJava(MavenPublication) { - from components.java - artifactId = archivesBaseName - pom { - name = 'Azure Durable Functions SDK for Java' - description = 'This package contains classes, interfaces, and annotations for developing with Azure Durable Functions in Java.' - url = "https://github.com/microsoft/durabletask-java/tree/main/azurefunctions" - licenses { - license { - name = "MIT License" - url = "https://opensource.org/licenses/MIT" - distribution = "repo" - } - } - developers { - developer { - id = "Microsoft" - name = "Microsoft Corporation" - } - } - scm { - connection = "scm:git:https://github.com/microsoft/durabletask-java" - developerConnection = "scm:git:git@github.com:microsoft/durabletask-java" - url = "https://github.com/microsoft/durabletask-java/tree/main/azurefunctions" - } - // use below script to include compile-only dependencies when generated pom file. - // This is pain point when we onboard API docs as the missing compile-only dependencies crash the - // API doc's team onboarding pipeline. - withXml { - project.configurations.compileOnly.allDependencies.each { dependency -> - asNode().dependencies[0].appendNode("dependency").with { - it.appendNode("groupId", dependency.group) - it.appendNode("artifactId", dependency.name) - it.appendNode("version", dependency.version) - it.appendNode("scope", "provided") - } - } - } - } - } - } -} - -signing { - required = !project.hasProperty("skipSigning") - sign publishing.publications.mavenJava -} - -java { - withSourcesJar() - withJavadocJar() -} - -spotbugs { - toolVersion = '4.9.2' - effort = 'max' - reportLevel = 'high' - ignoreFailures = true - excludeFilter = file('spotbugs-exclude.xml') -} - -spotbugsMain { - reports { - html { - required = true - stylesheet = 'fancy-hist.xsl' - } - xml { - required = true - } - } -} - -spotbugsTest { - reports { - html { - required = true - stylesheet = 'fancy-hist.xsl' - } - xml { - required = true - } - } -} diff --git a/azurefunctions/spotbugs-exclude.xml b/azurefunctions/spotbugs-exclude.xml deleted file mode 100644 index eee5ac08..00000000 --- a/azurefunctions/spotbugs-exclude.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableActivityTrigger.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableActivityTrigger.java deleted file mode 100644 index 700305b4..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableActivityTrigger.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for - * license information. - */ - -package com.microsoft.durabletask.azurefunctions; - -import com.microsoft.azure.functions.annotation.CustomBinding; -import com.microsoft.azure.functions.annotation.HasImplicitOutput; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - *

- * Azure Functions attribute for binding a function parameter to a Durable Task activity input. - *

- * The following is an example of an activity trigger function that accepts a String input and returns a String output. - *

- *
- * {@literal @}FunctionName("SayHello")
- * public String sayHello(
- *         {@literal @}DurableActivityTrigger(name = "name") String name,
- *         final ExecutionContext context) {
- *     context.getLogger().info("Saying hello to: " + name);
- *     return String.format("Hello %s!", name);
- * }
- * 
- * - * @since 2.0.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@CustomBinding(direction = "in", name = "", type = "activityTrigger") -@HasImplicitOutput -public @interface DurableActivityTrigger { - /** - *

The name of the activity function.

- *

If not specified, the function name is used as the name of the activity.

- *

This property supports binding parameters.

- * - * @return The name of the orchestrator function. - */ - String activity() default ""; - - /** - * The variable name used in function.json. - * - * @return The variable name used in function.json. - */ - String name(); - - /** - *

- * Defines how Functions runtime should treat the parameter value. Possible values are: - *

- * - * - * @return The dataType which will be used by the Functions runtime. - */ - String dataType() default ""; -} diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java deleted file mode 100644 index a952db68..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azurefunctions; - -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; - -import com.microsoft.azure.functions.HttpStatus; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.DurableTaskGrpcClientBuilder; -import com.microsoft.durabletask.OrchestrationMetadata; -import com.microsoft.durabletask.OrchestrationRuntimeStatus; - -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -/** - * The binding value type for the {@literal @}DurableClientInput parameter. - */ -public class DurableClientContext { - // These fields are populated via GSON deserialization by the Functions Java worker. - private String rpcBaseUrl; - private String taskHubName; - private String requiredQueryStringParameters; - private DurableTaskClient client; - - /** - * Gets the name of the client binding's task hub. - * - * @return the name of the client binding's task hub. - */ - public String getTaskHubName() { - return this.taskHubName; - } - - /** - * Gets the durable task client associated with the current function invocation. - * - * @return the Durable Task client object associated with the current function invocation. - */ - public DurableTaskClient getClient() { - if (this.rpcBaseUrl == null || this.rpcBaseUrl.length() == 0) { - throw new IllegalStateException("The client context wasn't populated with an RPC base URL!"); - } - - URL rpcURL; - try { - rpcURL = new URL(this.rpcBaseUrl); - } catch (MalformedURLException ex) { - throw new IllegalStateException("The client context RPC base URL was invalid!", ex); - } - - this.client = new DurableTaskGrpcClientBuilder().port(rpcURL.getPort()).build(); - return this.client; - } - - /** - * Creates an HTTP response which either contains a payload of management URLs for a non-completed instance - * or contains the payload containing the output of the completed orchestration. - *

- * If the orchestration instance completes within the specified timeout, then the HTTP response payload will - * contains the output of the orchestration instance formatted as JSON. However, if the orchestration does not - * complete within the specified timeout, then the HTTP response will be identical to that of the - * {@link #createCheckStatusResponse(HttpRequestMessage, String)} API. - *

- * @param request the HTTP request that triggered the current function - * @param instanceId the unique ID of the instance to check - * @param timeout total allowed timeout for output from the durable function - * @return an HTTP response which may include a 202 and location header or a 200 with the durable function output in the response body - */ - public HttpResponseMessage waitForCompletionOrCreateCheckStatusResponse( - HttpRequestMessage request, - String instanceId, - Duration timeout) { - if (this.client == null) { - this.client = getClient(); - } - OrchestrationMetadata orchestration; - try { - orchestration = this.client.waitForInstanceCompletion(instanceId, timeout, true); - return request.createResponseBuilder(HttpStatus.ACCEPTED) - .header("Content-Type", "application/json") - .body(orchestration.getSerializedOutput()) - .build(); - } catch (TimeoutException e) { - return createCheckStatusResponse(request, instanceId); - } - } - - /** - * Creates an HTTP response that is useful for checking the status of the specified instance. - *

- * The payload of the returned - * @see HttpResponseMessage - * contains HTTP API URLs that can be used to query the status of the orchestration, raise events to the orchestration, or - * terminate the orchestration. - * @param request the HTTP request that triggered the current orchestration instance - * @param instanceId the ID of the orchestration instance to check - * @return an HTTP 202 response with a Location header and a payload containing instance control URLs - */ - public HttpResponseMessage createCheckStatusResponse(HttpRequestMessage request, String instanceId) { - // TODO: To better support scenarios involving proxies or application gateways, this - // code should take the X-Forwarded-Host, X-Forwarded-Proto, and Forwarded HTTP - // request headers into consideration and generate the base URL accordingly. - // More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded. - // One potential workaround is to set ASPNETCORE_FORWARDEDHEADERS_ENABLED to true. - - // Construct the response as an HTTP 202 with a JSON object payload - return request.createResponseBuilder(HttpStatus.ACCEPTED) - .header("Location", this.getInstanceStatusURL(request, instanceId) + "?" + this.requiredQueryStringParameters) - .header("Content-Type", "application/json") - .body(this.getClientResponseLinks(request, instanceId)) - .build(); - } - - /** - * Creates a {@link HttpManagementPayload} that is useful for checking the status of the specified instance. - * - * @param request The HTTP request that triggered the current orchestration instance. - * @param instanceId The ID of the orchestration instance to check. - * @return The {@link HttpManagementPayload} with URLs that can be used to query the status of the orchestration, - * raise events to the orchestration, or terminate the orchestration. - */ - public HttpManagementPayload createHttpManagementPayload(HttpRequestMessage request, String instanceId) { - return this.getClientResponseLinks(request, instanceId); - } - - private HttpManagementPayload getClientResponseLinks(HttpRequestMessage request, String instanceId) { - String instanceStatusURL = this.getInstanceStatusURL(request, instanceId); - return new HttpManagementPayload(instanceId, instanceStatusURL, this.requiredQueryStringParameters); - } - - private String getInstanceStatusURL(HttpRequestMessage request, String instanceId) { - String baseUrl = request.getUri().getScheme() + "://" + request.getUri().getAuthority(); - String encodedInstanceId; - - try { - encodedInstanceId = URLEncoder.encode(instanceId, StandardCharsets.UTF_8.toString()); - } catch (UnsupportedEncodingException ex) { - throw new IllegalArgumentException("Failed to encode the instance ID: " + instanceId, ex); - } - - return baseUrl + "/runtime/webhooks/durabletask/instances/" + encodedInstanceId; - } -} diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientInput.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientInput.java deleted file mode 100644 index 9cab3b74..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientInput.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for - * license information. - */ - -package com.microsoft.durabletask.azurefunctions; - -import com.microsoft.azure.functions.annotation.CustomBinding; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - *

- * Azure Functions attribute for binding a function parameter to a {@literal @}DurableClientContext object. - *

- * The following is an example of an HTTP-trigger function that uses this input binding to start a new - * orchestration instance. - *

- *
- * {@literal @}FunctionName("StartHelloCities")
- * public HttpResponseMessage startHelloCities(
- *         {@literal @}HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage{@literal <}Optional{@literal <}String{@literal >}{@literal >} request,
- *         {@literal @}DurableClientInput(name = "durableContext") DurableClientContext durableContext,
- *         final ExecutionContext context) {
- * 
- *     DurableTaskClient client = durableContext.getClient();
- *     String instanceId = client.scheduleNewOrchestrationInstance("HelloCities");
- *     context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId);
- *     return durableContext.createCheckStatusResponse(request, instanceId);
- * }
- * 
- * - * @since 2.0.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@CustomBinding(direction = "in", name = "", type = "durableClient") -public @interface DurableClientInput { - /** - * The variable name used in function.json. - * - * @return The variable name used in function.json. - */ - String name(); - - /** - *

- * Defines how Functions runtime should treat the parameter value. Possible values are: - *

- * - * - * @return The dataType which will be used by the Functions runtime. - */ - String dataType() default ""; - - /** - *

- * Optional. The name of the task hub in which the orchestration data lives. - *

- *

- * If not specified, the task hub name used by this binding will be the value specified in host.json. - * If a task hub name is not configured in host.json and if the function app is running in the - * Azure Functions hosted service, then task hub name is derived from the function app's name. - * Otherwise, a constant value is used for the task hub name. - *

- *

- * In general, you should not set a value for the task hub name here unless you intend to configure - * the client to interact with orchestrations in another app. - *

- * - * @return The task hub name to use for the client. - */ - String taskHub() default ""; -} diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableOrchestrationTrigger.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableOrchestrationTrigger.java deleted file mode 100644 index fbcefac9..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableOrchestrationTrigger.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for - * license information. - */ - -package com.microsoft.durabletask.azurefunctions; - -import com.microsoft.azure.functions.annotation.CustomBinding; -import com.microsoft.azure.functions.annotation.HasImplicitOutput; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - *

- * Azure Functions attribute for binding a function parameter to a Durable Task orchestration request. - *

- * The following is an example of an orchestrator function that calls three activity functions in sequence. - *

- *
- * {@literal @}FunctionName("HelloCities")
- * public String helloCitiesOrchestrator(
- *         {@literal @}DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
- *     String result = "";
- *     result += ctx.callActivity("SayHello", "Tokyo", String.class).await() + ", ";
- *     result += ctx.callActivity("SayHello", "London", String.class).await() + ", ";
- *     result += ctx.callActivity("SayHello", "Seattle", String.class).await();
- *     return result;
- * }
- * 
- * - * @since 2.0.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@CustomBinding(direction = "in", name = "", type = "orchestrationTrigger") -@HasImplicitOutput -public @interface DurableOrchestrationTrigger { - /** - *

The name of the orchestrator function.

- *

If not specified, the function name is used as the name of the orchestration.

- *

This property supports binding parameters.

- * @return The name of the orchestrator function. - */ - String orchestration() default ""; - - /** - * The variable name used in function.json. - * - * @return The variable name used in function.json. - */ - String name(); - - /** - *

- * Defines how Functions runtime should treat the parameter value. Possible values are: - *

- * - * - * @return The dataType which will be used by the Functions runtime. - */ - String dataType() default "string"; -} diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/HttpManagementPayload.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/HttpManagementPayload.java deleted file mode 100644 index f78c00f9..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/HttpManagementPayload.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for - * license information. - */ - -package com.microsoft.durabletask.azurefunctions; - -/** - * The class that holds the webhook URLs to manage orchestration instances. - */ -public class HttpManagementPayload { - private final String id; - private final String purgeHistoryDeleteUri; - private final String restartPostUri; - private final String sendEventPostUri; - private final String statusQueryGetUri; - private final String terminatePostUri; - private final String resumePostUri; - private final String suspendPostUri; - - /** - * Creates a {@link HttpManagementPayload} to manage orchestration instances - * - * @param instanceId The ID of the orchestration instance - * @param instanceStatusURL The base webhook url of the orchestration instance - * @param requiredQueryStringParameters The query parameters to include with the http request - */ - public HttpManagementPayload( - String instanceId, - String instanceStatusURL, - String requiredQueryStringParameters) { - this.id = instanceId; - this.purgeHistoryDeleteUri = instanceStatusURL + "?" + requiredQueryStringParameters; - this.restartPostUri = instanceStatusURL + "/restart?" + requiredQueryStringParameters; - this.sendEventPostUri = instanceStatusURL + "/raiseEvent/{eventName}?" + requiredQueryStringParameters; - this.statusQueryGetUri = instanceStatusURL + "?" + requiredQueryStringParameters; - this.terminatePostUri = instanceStatusURL + "/terminate?reason={text}&" + requiredQueryStringParameters; - this.resumePostUri = instanceStatusURL + "/resume?reason={text}&" + requiredQueryStringParameters; - this.suspendPostUri = instanceStatusURL + "/suspend?reason={text}&" + requiredQueryStringParameters; - } - - /** - * Gets the ID of the orchestration instance. - * - * @return The ID of the orchestration instance. - */ - public String getId() { - return this.id; - } - - /** - * Gets the HTTP GET status query endpoint URL. - * - * @return The HTTP URL for fetching the instance status. - */ - public String getStatusQueryGetUri() { - return this.statusQueryGetUri; - } - - /** - * Gets the HTTP POST external event sending endpoint URL. - * - * @return The HTTP URL for posting external event notifications. - */ - public String getSendEventPostUri() { - return this.sendEventPostUri; - } - - /** - * Gets the HTTP POST instance termination endpoint. - * - * @return The HTTP URL for posting instance termination commands. - */ - public String getTerminatePostUri() { - return this.terminatePostUri; - } - - /** - * Gets the HTTP DELETE purge instance history by instance ID endpoint. - * - * @return The HTTP URL for purging instance history by instance ID. - */ - public String getPurgeHistoryDeleteUri() { - return this.purgeHistoryDeleteUri; - } - - /** - * Gets the HTTP POST instance restart endpoint. - * - * @return The HTTP URL for posting instance restart commands. - */ - public String getRestartPostUri() { - return restartPostUri; - } - -} diff --git a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/internal/middleware/OrchestrationMiddleware.java b/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/internal/middleware/OrchestrationMiddleware.java deleted file mode 100644 index 02be55f4..00000000 --- a/azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/internal/middleware/OrchestrationMiddleware.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for - * license information. - */ - -package com.microsoft.durabletask.azurefunctions.internal.middleware; - -import com.microsoft.azure.functions.internal.spi.middleware.Middleware; -import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain; -import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext; -import com.microsoft.durabletask.CompositeTaskFailedException; -import com.microsoft.durabletask.DataConverter; -import com.microsoft.durabletask.OrchestrationRunner; -import com.microsoft.durabletask.TaskFailedException; -import com.microsoft.durabletask.interruption.ContinueAsNewInterruption; -import com.microsoft.durabletask.interruption.OrchestratorBlockedException; - -/** - * Durable Function Orchestration Middleware - * - *

This class is internal and is hence not for public use. Its APIs are unstable and can change - * at any time. - */ -public class OrchestrationMiddleware implements Middleware { - - private static final String ORCHESTRATION_TRIGGER = "DurableOrchestrationTrigger"; - - @Override - public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exception { - String parameterName = context.getParameterName(ORCHESTRATION_TRIGGER); - if (parameterName == null){ - chain.doNext(context); - return; - } - String orchestratorRequestEncodedProtoBytes = (String) context.getParameterValue(parameterName); - String orchestratorOutputEncodedProtoBytes = OrchestrationRunner.loadAndRun(orchestratorRequestEncodedProtoBytes, taskOrchestrationContext -> { - try { - context.updateParameterValue(parameterName, taskOrchestrationContext); - chain.doNext(context); - return context.getReturnValue(); - } catch (Exception e) { - // The OrchestratorBlockedEvent will be wrapped into InvocationTargetException by using reflection to - // invoke method. Thus get the cause to check if it's OrchestratorBlockedEvent. - Throwable cause = e.getCause(); - if (cause instanceof OrchestratorBlockedException) { - throw (OrchestratorBlockedException) cause; - } - // The ContinueAsNewInterruption will be wrapped into InvocationTargetException by using reflection to - // invoke method. Thus get the cause to check if it's ContinueAsNewInterruption. - if (cause instanceof ContinueAsNewInterruption) { - throw (ContinueAsNewInterruption) cause; - } - // Below types of exception are raised by the client sdk, they data should be correctly pass back to - // durable function host. We need to cast them to the correct type so later when build the FailureDetails - // the correct exception data can be saved and pass back. - if (cause instanceof TaskFailedException) { - throw (TaskFailedException) cause; - } - - if (cause instanceof CompositeTaskFailedException) { - throw (CompositeTaskFailedException) cause; - } - - if (cause instanceof DataConverter.DataConverterException) { - throw (DataConverter.DataConverterException) cause; - } - // e will be InvocationTargetException as using reflection, so we wrap it into a RuntimeException, so it - // won't change the current OrchestratorFunction API. We cannot throw the cause which is a Throwable, it - // requires update on OrchestratorFunction API. - throw new RuntimeException("Unexpected failure in the task execution", e); - } - }); - context.updateReturnValue(orchestratorOutputEncodedProtoBytes); - } -} diff --git a/azurefunctions/src/main/resources/META-INF/services/com.microsoft.azure.functions.internal.spi.middleware.Middleware b/azurefunctions/src/main/resources/META-INF/services/com.microsoft.azure.functions.internal.spi.middleware.Middleware deleted file mode 100644 index 26168496..00000000 --- a/azurefunctions/src/main/resources/META-INF/services/com.microsoft.azure.functions.internal.spi.middleware.Middleware +++ /dev/null @@ -1 +0,0 @@ -com.microsoft.durabletask.azurefunctions.internal.middleware.OrchestrationMiddleware \ No newline at end of file diff --git a/azuremanaged/build.gradle b/azuremanaged/build.gradle deleted file mode 100644 index 6ede2393..00000000 --- a/azuremanaged/build.gradle +++ /dev/null @@ -1,180 +0,0 @@ -/* - * This build file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Java project to get you started. - * For more details take a look at the Java Quickstart chapter in the Gradle - * user guide available at https://docs.gradle.org/4.4.1/userguide/tutorial_java_projects.html - */ - -plugins { - id 'java' - id 'com.google.protobuf' version '0.8.16' - id 'idea' - id 'maven-publish' - id 'signing' - id 'com.github.spotbugs' version '5.2.1' -} - -archivesBaseName = 'durabletask-azuremanaged' -group 'com.microsoft' -version = '1.5.1-preview.1' - -def grpcVersion = '1.59.0' -def azureCoreVersion = '1.45.0' -def azureIdentityVersion = '1.11.1' -// When build on local, you need to set this value to your local jdk11 directory. -// Java11 is used to compile and run all the tests. -def PATH_TO_TEST_JAVA_RUNTIME = System.env.JDK_11 ?: System.getProperty("java.home") - -repositories { - mavenCentral() -} - -dependencies { - implementation project(':client') - implementation "io.grpc:grpc-protobuf:${grpcVersion}" - implementation "io.grpc:grpc-stub:${grpcVersion}" - runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}" - - implementation "com.azure:azure-core:${azureCoreVersion}" - implementation "com.azure:azure-identity:${azureIdentityVersion}" - - // Test dependencies - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0' - testImplementation 'org.mockito:mockito-core:5.3.1' - testImplementation 'org.mockito:mockito-junit-jupiter:5.3.1' - testImplementation "io.grpc:grpc-netty:${grpcVersion}" - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' -} - -compileJava { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -compileTestJava { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - options.fork = true - options.forkOptions.executable = "${PATH_TO_TEST_JAVA_RUNTIME}/bin/javac" -} - -test { - useJUnitPlatform() - include '**/*Test.class' -} - -publishing { - repositories { - maven { - url "file://$project.rootDir/repo" - } - } - publications { - mavenJava(MavenPublication) { - from components.java - artifactId = archivesBaseName - pom { - name = 'Durable Task Azure Managed SDK for Java' - description = 'This package contains classes and interfaces for building Durable Task orchestrations in Java using Azure Managed mode.' - url = "https://github.com/microsoft/durabletask-java/tree/main/azuremanaged" - licenses { - license { - name = "MIT License" - url = "https://opensource.org/licenses/MIT" - distribution = "repo" - } - } - developers { - developer { - id = "Microsoft" - name = "Microsoft Corporation" - } - } - scm { - connection = "scm:git:https://github.com/microsoft/durabletask-java" - developerConnection = "scm:git:git@github.com:microsoft/durabletask-java" - url = "https://github.com/microsoft/durabletask-java/tree/main/azuremanaged" - } - withXml { - project.configurations.compileOnly.allDependencies.each { dependency -> - asNode().dependencies[0].appendNode("dependency").with { - it.appendNode("groupId", dependency.group) - it.appendNode("artifactId", dependency.name) - it.appendNode("version", dependency.version) - it.appendNode("scope", "provided") - } - } - } - } - } - } -} - -signing { - required = !project.hasProperty("skipSigning") - sign publishing.publications.mavenJava -} - -java { - withSourcesJar() - withJavadocJar() -} - -spotbugs { - toolVersion = '4.9.2' - effort = 'max' - reportLevel = 'high' - ignoreFailures = true - excludeFilter = file('spotbugs-exclude.xml') -} - -spotbugsMain { - reports { - html { - required = true - stylesheet = 'fancy-hist.xsl' - } - xml { - required = true - } - } -} - -spotbugsTest { - reports { - html { - required = true - stylesheet = 'fancy-hist.xsl' - } - xml { - required = true - } - } -} - -// Add this after the plugins block -def generatedSourcesDir = file("$buildDir/generated/sources/version/java/main") - -sourceSets { - main { - java { - srcDirs += generatedSourcesDir - } - } -} - -task generateVersionInfo(type: Copy) { - from 'src/main/resources/VersionInfo.java.template' - into generatedSourcesDir - expand(version: project.version) - rename { 'VersionInfo.java' } -} - -compileJava.dependsOn generateVersionInfo - -tasks.named('sourcesJar') { - dependsOn generateVersionInfo -} - diff --git a/azuremanaged/spotbugs-exclude.xml b/azuremanaged/spotbugs-exclude.xml deleted file mode 100644 index eee5ac08..00000000 --- a/azuremanaged/spotbugs-exclude.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/AccessTokenCache.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/AccessTokenCache.java deleted file mode 100644 index 888d20b3..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/AccessTokenCache.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenRequestContext; - -import java.time.Duration; -import java.time.OffsetDateTime; - -/** - * Caches access tokens for Azure authentication. - * This class is used by both client and worker components to authenticate with Azure-managed Durable Task Scheduler. - */ -public final class AccessTokenCache { - private final TokenCredential credential; - private final TokenRequestContext context; - private final Duration margin; - private AccessToken cachedToken; - - /** - * Creates a new instance of the AccessTokenCache. - * - * @param credential The token credential to use for obtaining tokens. - * @param context The token request context specifying the scopes. - * @param margin The time margin before token expiration to refresh the token. - */ - public AccessTokenCache(TokenCredential credential, TokenRequestContext context, Duration margin) { - this.credential = credential; - this.context = context; - this.margin = margin; - } - - /** - * Gets a valid access token, refreshing it if necessary. - * - * @return A valid access token. - */ - public AccessToken getToken() { - OffsetDateTime nowWithMargin = OffsetDateTime.now().plus(margin); - - if (cachedToken == null - || cachedToken.getExpiresAt().isBefore(nowWithMargin)) { - this.cachedToken = credential.getToken(context).block(); - } - - return cachedToken; - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensions.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensions.java deleted file mode 100644 index ffc7d446..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensions.java +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.microsoft.durabletask.DurableTaskGrpcClientBuilder; -import com.azure.core.credential.TokenCredential; -import io.grpc.Channel; -import java.util.Objects; -import javax.annotation.Nullable; - -/** - * Extension methods for creating DurableTaskClient instances that connect to Azure-managed Durable Task Scheduler. - * This class provides various methods to create and configure clients using either connection strings or explicit parameters. - */ -public final class DurableTaskSchedulerClientExtensions { - - private DurableTaskSchedulerClientExtensions() {} - - /** - * Configures a DurableTaskGrpcClientBuilder to use Azure-managed Durable Task Scheduler with a connection string. - * - * @param builder The builder to configure. - * @param connectionString The connection string for Azure-managed Durable Task Scheduler. - * @throws NullPointerException if builder or connectionString is null - */ - public static void useDurableTaskScheduler( - DurableTaskGrpcClientBuilder builder, - String connectionString) { - Objects.requireNonNull(builder, "builder must not be null"); - Objects.requireNonNull(connectionString, "connectionString must not be null"); - - configureBuilder(builder, - DurableTaskSchedulerClientOptions.fromConnectionString(connectionString)); - } - - /** - * Configures a DurableTaskGrpcClientBuilder to use Azure-managed Durable Task Scheduler with explicit parameters. - * - * @param builder The builder to configure. - * @param endpoint The endpoint address for Azure-managed Durable Task Scheduler. - * @param taskHubName The name of the task hub to connect to. - * @param tokenCredential The token credential for authentication, or null for anonymous access. - * @throws NullPointerException if builder, endpoint, or taskHubName is null - */ - public static void useDurableTaskScheduler( - DurableTaskGrpcClientBuilder builder, - String endpoint, - String taskHubName, - @Nullable TokenCredential tokenCredential) { - Objects.requireNonNull(builder, "builder must not be null"); - Objects.requireNonNull(endpoint, "endpoint must not be null"); - Objects.requireNonNull(taskHubName, "taskHubName must not be null"); - - configureBuilder(builder, new DurableTaskSchedulerClientOptions() - .setEndpointAddress(endpoint) - .setTaskHubName(taskHubName) - .setCredential(tokenCredential)); - } - - /** - * Creates a DurableTaskGrpcClientBuilder configured for Azure-managed Durable Task Scheduler using a connection string. - * - * @param connectionString The connection string for Azure-managed Durable Task Scheduler. - * @return A new configured DurableTaskGrpcClientBuilder instance. - * @throws NullPointerException if connectionString is null - */ - public static DurableTaskGrpcClientBuilder createClientBuilder( - String connectionString) { - Objects.requireNonNull(connectionString, "connectionString must not be null"); - return createBuilderFromOptions( - DurableTaskSchedulerClientOptions.fromConnectionString(connectionString)); - } - - /** - * Creates a DurableTaskGrpcClientBuilder configured for Azure-managed Durable Task Scheduler using explicit parameters. - * - * @param endpoint The endpoint address for Azure-managed Durable Task Scheduler. - * @param taskHubName The name of the task hub to connect to. - * @param tokenCredential The token credential for authentication, or null for anonymous access. - * @return A new configured DurableTaskGrpcClientBuilder instance. - * @throws NullPointerException if endpoint or taskHubName is null - */ - public static DurableTaskGrpcClientBuilder createClientBuilder( - String endpoint, - String taskHubName, - @Nullable TokenCredential tokenCredential) { - Objects.requireNonNull(endpoint, "endpoint must not be null"); - Objects.requireNonNull(taskHubName, "taskHubName must not be null"); - - return createBuilderFromOptions(new DurableTaskSchedulerClientOptions() - .setEndpointAddress(endpoint) - .setTaskHubName(taskHubName) - .setCredential(tokenCredential)); - } - - // Private helper methods to reduce code duplication - private static DurableTaskGrpcClientBuilder createBuilderFromOptions(DurableTaskSchedulerClientOptions options) { - Channel grpcChannel = options.createGrpcChannel(); - return new DurableTaskGrpcClientBuilder().grpcChannel(grpcChannel); - } - - private static void configureBuilder(DurableTaskGrpcClientBuilder builder, DurableTaskSchedulerClientOptions options) { - Channel grpcChannel = options.createGrpcChannel(); - builder.grpcChannel(grpcChannel); - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptions.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptions.java deleted file mode 100644 index 0e6cd24b..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptions.java +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.TokenRequestContext; -import io.grpc.*; -import java.net.MalformedURLException; -import java.time.Duration; -import java.util.Objects; -import java.net.URL; - -/** - * Options for configuring the Durable Task Scheduler. - */ -public class DurableTaskSchedulerClientOptions { - private String endpointAddress = ""; - private String taskHubName = ""; - - private TokenCredential credential; - private String resourceId = "https://durabletask.io"; - private boolean allowInsecureCredentials = false; - private Duration tokenRefreshMargin = Duration.ofMinutes(5); - - /** - * Creates a new instance of DurableTaskSchedulerClientOptions. - */ - public DurableTaskSchedulerClientOptions() { - } - - /** - * Creates a new instance of DurableTaskSchedulerClientOptions from a connection string. - * - * @param connectionString The connection string to parse. - * @return A new DurableTaskSchedulerClientOptions object. - */ - public static DurableTaskSchedulerClientOptions fromConnectionString(String connectionString) { - DurableTaskSchedulerConnectionString parsedConnectionString = new DurableTaskSchedulerConnectionString(connectionString); - return fromConnectionString(parsedConnectionString); - } - - /** - * Creates a new instance of DurableTaskSchedulerClientOptions from a parsed connection string. - * - * @param connectionString The parsed connection string. - * @return A new DurableTaskSchedulerClientOptions object. - */ - static DurableTaskSchedulerClientOptions fromConnectionString(DurableTaskSchedulerConnectionString connectionString) { - // TODO: Parse different credential types from connection string - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - options.setEndpointAddress(connectionString.getEndpoint()); - options.setTaskHubName(connectionString.getTaskHubName()); - options.setCredential(connectionString.getCredential()); - options.setAllowInsecureCredentials(options.getCredential() == null); - return options; - } - - /** - * Gets the endpoint address. - * - * @return The endpoint address. - */ - public String getEndpointAddress() { - return endpointAddress; - } - - /** - * Sets the endpoint address. - * - * @param endpointAddress The endpoint address. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setEndpointAddress(String endpointAddress) { - this.endpointAddress = endpointAddress; - return this; - } - - /** - * Gets the task hub name. - * - * @return The task hub name. - */ - public String getTaskHubName() { - return taskHubName; - } - - /** - * Sets the task hub name. - * - * @param taskHubName The task hub name. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setTaskHubName(String taskHubName) { - this.taskHubName = taskHubName; - return this; - } - - /** - * Gets the credential used for authentication. - * - * @return The credential. - */ - public TokenCredential getCredential() { - return credential; - } - - /** - * Sets the credential used for authentication. - * - * @param credential The credential. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setCredential(TokenCredential credential) { - this.credential = credential; - return this; - } - - /** - * Gets the resource ID. - * - * @return The resource ID. - */ - public String getResourceId() { - return resourceId; - } - - /** - * Sets the resource ID. - * - * @param resourceId The resource ID. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setResourceId(String resourceId) { - this.resourceId = resourceId; - return this; - } - - /** - * Gets whether insecure credentials are allowed. - * - * @return True if insecure credentials are allowed. - */ - public boolean isAllowInsecureCredentials() { - return allowInsecureCredentials; - } - - /** - * Sets whether insecure credentials are allowed. - * - * @param allowInsecureCredentials True to allow insecure credentials. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setAllowInsecureCredentials(boolean allowInsecureCredentials) { - this.allowInsecureCredentials = allowInsecureCredentials; - return this; - } - - /** - * Gets the token refresh margin. - * - * @return The token refresh margin. - */ - public Duration getTokenRefreshMargin() { - return tokenRefreshMargin; - } - - /** - * Sets the token refresh margin. - * - * @param tokenRefreshMargin The token refresh margin. - * @return This options object. - */ - public DurableTaskSchedulerClientOptions setTokenRefreshMargin(Duration tokenRefreshMargin) { - this.tokenRefreshMargin = tokenRefreshMargin; - return this; - } - - /** - * Creates a gRPC channel using the configured options. - * - * @return A configured gRPC channel for communication with the Durable Task service. - */ - public Channel createGrpcChannel() { - // Create token cache only if credential is not null - AccessTokenCache tokenCache = null; - if (credential != null) { - TokenRequestContext context = new TokenRequestContext(); - context.addScopes(new String[] { this.resourceId + "/.default" }); - tokenCache = new AccessTokenCache(this.credential, context, this.tokenRefreshMargin); - } - - // Parse and normalize the endpoint URL - String endpoint = endpointAddress; - // Add https:// prefix if no protocol is specified - if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) { - endpoint = "https://" + endpoint; - } - - URL url; - try { - url = new URL(endpoint); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Invalid endpoint URL: " + endpoint); - } - - String authority = url.getHost(); - if (url.getPort() != -1) { - authority += ":" + url.getPort(); - } - - // Create metadata interceptor to add task hub name and auth token - AccessTokenCache finalTokenCache = tokenCache; - ClientInterceptor metadataInterceptor = new ClientInterceptor() { - @Override - public ClientCall interceptCall( - MethodDescriptor method, - CallOptions callOptions, - Channel next) { - return new ForwardingClientCall.SimpleForwardingClientCall( - next.newCall(method, callOptions)) { - @Override - public void start(ClientCall.Listener responseListener, Metadata headers) { - headers.put( - Metadata.Key.of("taskhub", Metadata.ASCII_STRING_MARSHALLER), - taskHubName - ); - - headers.put( - Metadata.Key.of("x-user-agent", Metadata.ASCII_STRING_MARSHALLER), - DurableTaskUserAgentUtil.getUserAgent() - ); - - // Add authorization token if credentials are configured - if (finalTokenCache != null) { - String token = finalTokenCache.getToken().getToken(); - headers.put( - Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER), - "Bearer " + token - ); - } - - super.start(responseListener, headers); - } - }; - } - }; - - ChannelCredentials credentials; - if (!this.allowInsecureCredentials) { - credentials = io.grpc.TlsChannelCredentials.create(); - } else { - credentials = InsecureChannelCredentials.create(); - } - - // Create channel with credentials - return Grpc.newChannelBuilder(authority, credentials) - .intercept(metadataInterceptor) - .build(); - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionString.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionString.java deleted file mode 100644 index eb2e622c..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionString.java +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nullable; - -import com.azure.core.credential.TokenCredential; -import com.azure.identity.AzureCliCredentialBuilder; -import com.azure.identity.AzurePowerShellCredentialBuilder; -import com.azure.identity.DefaultAzureCredentialBuilder; -import com.azure.identity.EnvironmentCredentialBuilder; -import com.azure.identity.IntelliJCredentialBuilder; -import com.azure.identity.InteractiveBrowserCredentialBuilder; -import com.azure.identity.ManagedIdentityCredentialBuilder; -import com.azure.identity.VisualStudioCodeCredentialBuilder; -import com.azure.identity.WorkloadIdentityCredentialBuilder; - -/** - * Represents the constituent parts of a connection string for a Durable Task Scheduler service. - */ -public class DurableTaskSchedulerConnectionString { - private final Map properties; - - /** - * Initializes a new instance of the DurableTaskSchedulerConnectionString class. - * - * @param connectionString A connection string for a Durable Task Scheduler service. - * @throws IllegalArgumentException If the connection string is invalid or missing required properties. - */ - public DurableTaskSchedulerConnectionString(String connectionString) { - if (connectionString == null || connectionString.trim().isEmpty()) { - throw new IllegalArgumentException("connectionString must not be null or empty"); - } - this.properties = parseConnectionString(connectionString); - - // Validate required properties - this.getAuthentication(); - this.getTaskHubName(); - this.getEndpoint(); - } - - /** - * Gets the authentication method specified in the connection string. - * - * @return The authentication method. - */ - public String getAuthentication() { - return getRequiredValue("Authentication"); - } - - /** - * Gets the managed identity or workload identity client ID specified in the connection string. - * - * @return The client ID, or null if not specified. - */ - public String getClientId() { - return getValue("ClientID"); - } - - /** - * Gets the "AdditionallyAllowedTenants" property, optionally used by Workload Identity. - * Multiple values can be separated by a comma. - * - * @return List of allowed tenants, or null if not specified. - */ - public List getAdditionallyAllowedTenants() { - String value = getValue("AdditionallyAllowedTenants"); - if (value == null || value.isEmpty()) { - return null; - } - return Arrays.asList(value.split(",")); - } - - /** - * Gets the "TenantId" property, optionally used by Workload Identity. - * - * @return The tenant ID, or null if not specified. - */ - public String getTenantId() { - return getValue("TenantId"); - } - - /** - * Gets the "TokenFilePath" property, optionally used by Workload Identity. - * - * @return The token file path, or null if not specified. - */ - public String getTokenFilePath() { - return getValue("TokenFilePath"); - } - - /** - * Gets the endpoint specified in the connection string. - * - * @return The endpoint URL. - */ - public String getEndpoint() { - return getRequiredValue("Endpoint"); - } - - /** - * Gets the task hub name specified in the connection string. - * - * @return The task hub name. - */ - public String getTaskHubName() { - return getRequiredValue("TaskHub"); - } - - private String getValue(String name) { - return properties.get(name); - } - - private String getRequiredValue(String name) { - String value = getValue(name); - if (value == null || value.isEmpty()) { - throw new IllegalArgumentException("The connection string must contain a " + name + " property"); - } - return value; - } - - private static Map parseConnectionString(String connectionString) { - Map properties = new HashMap<>(); - - String[] pairs = connectionString.split(";"); - for (String pair : pairs) { - int equalsIndex = pair.indexOf('='); - if (equalsIndex > 0) { - String key = pair.substring(0, equalsIndex).trim(); - String value = pair.substring(equalsIndex + 1).trim(); - properties.put(key, value); - } - } - - return properties; - } - - /** - * Gets a TokenCredential based on the authentication type specified in the connection string. - * - * @return A TokenCredential instance based on the specified authentication type, or null if authentication type is "none". - * @throws IllegalArgumentException If the connection string contains an unsupported authentication type. - */ - public @Nullable TokenCredential getCredential() { - String authType = getAuthentication(); - - // Parse the supported auth types in a case-insensitive way - switch (authType.toLowerCase().trim()) { - case "defaultazure": - return new DefaultAzureCredentialBuilder().build(); - case "managedidentity": - return new ManagedIdentityCredentialBuilder().clientId(getClientId()).build(); - case "workloadidentity": - WorkloadIdentityCredentialBuilder builder = new WorkloadIdentityCredentialBuilder(); - if (getClientId() != null && !getClientId().isEmpty()) { - builder.clientId(getClientId()); - } - - if (getTenantId() != null && !getTenantId().isEmpty()) { - builder.tenantId(getTenantId()); - } - - if (getTokenFilePath() != null && !getTokenFilePath().isEmpty()) { - builder.tokenFilePath(getTokenFilePath()); - } - - if (getAdditionallyAllowedTenants() != null) { - for (String tenant : getAdditionallyAllowedTenants()) { - builder.additionallyAllowedTenants(tenant); - } - } - - return builder.build(); - case "environment": - return new EnvironmentCredentialBuilder().build(); - case "azurecli": - return new AzureCliCredentialBuilder().build(); - case "azurepowershell": - return new AzurePowerShellCredentialBuilder().build(); - case "visualstudiocode": - return new VisualStudioCodeCredentialBuilder().build(); - case "intellij": - return new IntelliJCredentialBuilder().build(); - case "interactivebrowser": - return new InteractiveBrowserCredentialBuilder().build(); - case "none": - return null; - default: - throw new IllegalArgumentException( - String.format("The connection string contains an unsupported authentication type '%s'.", authType)); - } - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensions.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensions.java deleted file mode 100644 index 0ea9b5be..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensions.java +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.microsoft.durabletask.DurableTaskGrpcWorkerBuilder; - -import io.grpc.Channel; -import java.util.Objects; -import javax.annotation.Nullable; - -/** - * Extension methods for creating DurableTaskWorker instances that connect to Azure-managed Durable Task Scheduler. - * This class provides various methods to create and configure workers using either connection strings or explicit parameters. - */ -public final class DurableTaskSchedulerWorkerExtensions { - private DurableTaskSchedulerWorkerExtensions() {} - - /** - * Configures a DurableTaskGrpcWorkerBuilder to use Azure-managed Durable Task Scheduler with a connection string. - * - * @param builder The builder to configure. - * @param connectionString The connection string for Azure-managed Durable Task Scheduler. - * @throws NullPointerException if builder or connectionString is null - */ - public static void useDurableTaskScheduler( - DurableTaskGrpcWorkerBuilder builder, - String connectionString) { - Objects.requireNonNull(builder, "builder must not be null"); - Objects.requireNonNull(connectionString, "connectionString must not be null"); - - configureBuilder(builder, - DurableTaskSchedulerWorkerOptions.fromConnectionString(connectionString)); - } - - /** - * Configures a DurableTaskGrpcWorkerBuilder to use Azure-managed Durable Task Scheduler with explicit parameters. - * - * @param builder The builder to configure. - * @param endpoint The endpoint address for Azure-managed Durable Task Scheduler. - * @param taskHubName The name of the task hub to connect to. - * @param tokenCredential The token credential for authentication, or null for anonymous access. - * @throws NullPointerException if builder, endpoint, or taskHubName is null - */ - public static void useDurableTaskScheduler( - DurableTaskGrpcWorkerBuilder builder, - String endpoint, - String taskHubName, - @Nullable TokenCredential tokenCredential) { - Objects.requireNonNull(builder, "builder must not be null"); - Objects.requireNonNull(endpoint, "endpoint must not be null"); - Objects.requireNonNull(taskHubName, "taskHubName must not be null"); - - configureBuilder(builder, new DurableTaskSchedulerWorkerOptions() - .setEndpointAddress(endpoint) - .setTaskHubName(taskHubName) - .setCredential(tokenCredential)); - } - - /** - * Creates a DurableTaskGrpcWorkerBuilder configured for Azure-managed Durable Task Scheduler using a connection string. - * - * @param connectionString The connection string for Azure-managed Durable Task Scheduler. - * @return A new configured DurableTaskGrpcWorkerBuilder instance. - * @throws NullPointerException if connectionString is null - */ - public static DurableTaskGrpcWorkerBuilder createWorkerBuilder( - String connectionString) { - Objects.requireNonNull(connectionString, "connectionString must not be null"); - return createBuilderFromOptions( - DurableTaskSchedulerWorkerOptions.fromConnectionString(connectionString)); - } - - /** - * Creates a DurableTaskGrpcWorkerBuilder configured for Azure-managed Durable Task Scheduler using explicit parameters. - * - * @param endpoint The endpoint address for Azure-managed Durable Task Scheduler. - * @param taskHubName The name of the task hub to connect to. - * @param tokenCredential The token credential for authentication, or null for anonymous access. - * @return A new configured DurableTaskGrpcWorkerBuilder instance. - * @throws NullPointerException if endpoint or taskHubName is null - */ - public static DurableTaskGrpcWorkerBuilder createWorkerBuilder( - String endpoint, - String taskHubName, - @Nullable TokenCredential tokenCredential) { - Objects.requireNonNull(endpoint, "endpoint must not be null"); - Objects.requireNonNull(taskHubName, "taskHubName must not be null"); - - return createBuilderFromOptions(new DurableTaskSchedulerWorkerOptions() - .setEndpointAddress(endpoint) - .setTaskHubName(taskHubName) - .setCredential(tokenCredential)); - } - - // Private helper methods to reduce code duplication - private static DurableTaskGrpcWorkerBuilder createBuilderFromOptions(DurableTaskSchedulerWorkerOptions options) { - Channel grpcChannel = options.createGrpcChannel(); - return new DurableTaskGrpcWorkerBuilder().grpcChannel(grpcChannel); - } - - private static void configureBuilder(DurableTaskGrpcWorkerBuilder builder, DurableTaskSchedulerWorkerOptions options) { - Channel grpcChannel = options.createGrpcChannel(); - builder.grpcChannel(grpcChannel); - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptions.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptions.java deleted file mode 100644 index d0c34af3..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptions.java +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.TokenRequestContext; -import io.grpc.Channel; -import io.grpc.ChannelCredentials; -import io.grpc.Grpc; -import io.grpc.InsecureChannelCredentials; -import io.grpc.TlsChannelCredentials; -import io.grpc.ClientInterceptor; -import io.grpc.ClientCall; -import io.grpc.Metadata; -import io.grpc.MethodDescriptor; -import io.grpc.CallOptions; -import io.grpc.ForwardingClientCall; - -import java.time.Duration; -import java.util.Objects; -import java.net.URL; -import java.net.MalformedURLException; - -/** - * Options for configuring the Durable Task Scheduler worker. - */ -public class DurableTaskSchedulerWorkerOptions { - private String endpointAddress = ""; - private String taskHubName = ""; - - private TokenCredential credential; - private String resourceId = "https://durabletask.io"; - private boolean allowInsecureCredentials = false; - private Duration tokenRefreshMargin = Duration.ofMinutes(5); - - /** - * Creates a new instance of DurableTaskSchedulerWorkerOptions. - */ - public DurableTaskSchedulerWorkerOptions() { - } - - /** - * Creates a new instance of DurableTaskSchedulerWorkerOptions from a connection string. - * - * @param connectionString The connection string to parse. - * @return A new DurableTaskSchedulerWorkerOptions object. - */ - public static DurableTaskSchedulerWorkerOptions fromConnectionString(String connectionString) { - DurableTaskSchedulerConnectionString parsedConnectionString = new DurableTaskSchedulerConnectionString(connectionString); - return fromConnectionString(parsedConnectionString); - } - - /** - * Creates a new instance of DurableTaskSchedulerWorkerOptions from a parsed connection string. - * - * @param connectionString The parsed connection string. - * @return A new DurableTaskSchedulerWorkerOptions object. - */ - static DurableTaskSchedulerWorkerOptions fromConnectionString(DurableTaskSchedulerConnectionString connectionString) { - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - options.setEndpointAddress(connectionString.getEndpoint()); - options.setTaskHubName(connectionString.getTaskHubName()); - options.setCredential(connectionString.getCredential()); - options.setAllowInsecureCredentials(options.getCredential() == null); - return options; - } - - /** - * Gets the endpoint address. - * - * @return The endpoint address. - */ - public String getEndpointAddress() { - return endpointAddress; - } - - /** - * Sets the endpoint address. - * - * @param endpointAddress The endpoint address. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setEndpointAddress(String endpointAddress) { - this.endpointAddress = endpointAddress; - return this; - } - - /** - * Gets the task hub name. - * - * @return The task hub name. - */ - public String getTaskHubName() { - return taskHubName; - } - - /** - * Sets the task hub name. - * - * @param taskHubName The task hub name. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setTaskHubName(String taskHubName) { - this.taskHubName = taskHubName; - return this; - } - - /** - * Gets the credential used for authentication. - * - * @return The credential. - */ - public TokenCredential getCredential() { - return credential; - } - - /** - * Sets the credential used for authentication. - * - * @param credential The credential. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setCredential(TokenCredential credential) { - this.credential = credential; - return this; - } - - /** - * Gets the resource ID. - * - * @return The resource ID. - */ - public String getResourceId() { - return resourceId; - } - - /** - * Sets the resource ID. - * - * @param resourceId The resource ID. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setResourceId(String resourceId) { - this.resourceId = resourceId; - return this; - } - - /** - * Gets whether insecure credentials are allowed. - * - * @return True if insecure credentials are allowed. - */ - public boolean isAllowInsecureCredentials() { - return allowInsecureCredentials; - } - - /** - * Sets whether insecure credentials are allowed. - * - * @param allowInsecureCredentials True to allow insecure credentials. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setAllowInsecureCredentials(boolean allowInsecureCredentials) { - this.allowInsecureCredentials = allowInsecureCredentials; - return this; - } - - /** - * Gets the token refresh margin. - * - * @return The token refresh margin. - */ - public Duration getTokenRefreshMargin() { - return tokenRefreshMargin; - } - - /** - * Sets the token refresh margin. - * - * @param tokenRefreshMargin The token refresh margin. - * @return This options object. - */ - public DurableTaskSchedulerWorkerOptions setTokenRefreshMargin(Duration tokenRefreshMargin) { - this.tokenRefreshMargin = tokenRefreshMargin; - return this; - } - - /** - * Creates a gRPC channel using the configured options. - * - * @return A configured gRPC channel. - */ - public Channel createGrpcChannel() { - // Create token cache only if credential is not null - AccessTokenCache tokenCache = null; - if (credential != null) { - TokenRequestContext context = new TokenRequestContext(); - context.addScopes(new String[] { this.resourceId + "/.default" }); - tokenCache = new AccessTokenCache(this.credential, context, this.tokenRefreshMargin); - } - - // Parse and normalize the endpoint URL - String endpoint = endpointAddress; - // Add https:// prefix if no protocol is specified - if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) { - endpoint = "https://" + endpoint; - } - - URL url; - try { - url = new URL(endpoint); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Invalid endpoint URL: " + endpoint); - } - String authority = url.getHost(); - if (url.getPort() != -1) { - authority += ":" + url.getPort(); - } - - // Create metadata interceptor to add task hub name and auth token - AccessTokenCache finalTokenCache = tokenCache; - ClientInterceptor metadataInterceptor = new ClientInterceptor() { - @Override - public ClientCall interceptCall( - MethodDescriptor method, - CallOptions callOptions, - Channel next) { - return new ForwardingClientCall.SimpleForwardingClientCall( - next.newCall(method, callOptions)) { - @Override - public void start(ClientCall.Listener responseListener, Metadata headers) { - headers.put( - Metadata.Key.of("taskhub", Metadata.ASCII_STRING_MARSHALLER), - taskHubName - ); - - headers.put( - Metadata.Key.of("x-user-agent", Metadata.ASCII_STRING_MARSHALLER), - DurableTaskUserAgentUtil.getUserAgent() - ); - - // Add authorization token if credentials are configured - if (finalTokenCache != null) { - String token = finalTokenCache.getToken().getToken(); - headers.put( - Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER), - "Bearer " + token - ); - } - - super.start(responseListener, headers); - } - }; - } - }; - - // Configure channel credentials based on endpoint protocol - ChannelCredentials credentials; - if (endpoint.toLowerCase().startsWith("https://")) { - credentials = TlsChannelCredentials.create(); - } else { - credentials = InsecureChannelCredentials.create(); - } - - // Create channel with credentials - return Grpc.newChannelBuilder(authority, credentials) - .intercept(metadataInterceptor) - .build(); - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskUserAgentUtil.java b/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskUserAgentUtil.java deleted file mode 100644 index cde2c104..00000000 --- a/azuremanaged/src/main/java/com/microsoft/durabletask/azuremanaged/DurableTaskUserAgentUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -/** - * Utility class for generating the user agent string for the Durable Task SDK. - */ -public final class DurableTaskUserAgentUtil { - /** - * The name of the SDK used in the user agent string. - */ - private static final String SDK_NAME = "durabletask-java"; - - private DurableTaskUserAgentUtil() { - // Private constructor to prevent instantiation - } - - /** - * Generates the user agent string for the Durable Task SDK based on a fixed name and the package version. - * - * @return The user agent string. - */ - public static String getUserAgent() { - return String.format("%s/%s", SDK_NAME, VersionInfo.VERSION); - } -} \ No newline at end of file diff --git a/azuremanaged/src/main/resources/VersionInfo.java.template b/azuremanaged/src/main/resources/VersionInfo.java.template deleted file mode 100644 index f4acb56d..00000000 --- a/azuremanaged/src/main/resources/VersionInfo.java.template +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -/** - * Auto-generated version information for the Durable Task SDK. - * This file is generated during the build process. - */ -public final class VersionInfo { - /** - * The version of the SDK. - */ - public static final String VERSION = "${version}"; - - private VersionInfo() { - // Private constructor to prevent instantiation - } -} \ No newline at end of file diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/AccessTokenCacheTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/AccessTokenCacheTest.java deleted file mode 100644 index f900f2e9..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/AccessTokenCacheTest.java +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.TokenRequestContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import reactor.core.publisher.Mono; - -import java.time.Duration; -import java.time.OffsetDateTime; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -/** - * Unit tests for {@link AccessTokenCache}. - */ -@ExtendWith(MockitoExtension.class) -public class AccessTokenCacheTest { - - @Mock - private TokenCredential mockCredential; - - private TokenRequestContext context; - private Duration margin; - private AccessTokenCache tokenCache; - - @BeforeEach - public void setup() { - context = new TokenRequestContext().addScopes("https://durabletask.io/.default"); - margin = Duration.ofMinutes(5); - tokenCache = new AccessTokenCache(mockCredential, context, margin); - } - - @Test - @DisplayName("getToken should fetch a new token when cache is empty") - public void getToken_WhenCacheEmpty_FetchesNewToken() { - // Arrange - AccessToken expectedToken = new AccessToken("token1", OffsetDateTime.now().plusHours(1)); - when(mockCredential.getToken(any(TokenRequestContext.class))).thenReturn(Mono.just(expectedToken)); - - // Act - AccessToken result = tokenCache.getToken(); - - // Assert - assertEquals(expectedToken, result); - verify(mockCredential, times(1)).getToken(context); - } - - @Test - @DisplayName("getToken should reuse cached token when not expired") - public void getToken_WhenTokenNotExpired_ReusesCachedToken() { - // Arrange - AccessToken expectedToken = new AccessToken("token1", OffsetDateTime.now().plusHours(1)); - when(mockCredential.getToken(any(TokenRequestContext.class))).thenReturn(Mono.just(expectedToken)); - - // Act - tokenCache.getToken(); // First call to cache the token - AccessToken result = tokenCache.getToken(); // Second call should use cached token - - // Assert - assertEquals(expectedToken, result); - verify(mockCredential, times(1)).getToken(context); // Should only be called once - } - - @Test - @DisplayName("getToken should fetch a new token when current token is expired") - public void getToken_WhenTokenExpired_FetchesNewToken() { - // Arrange - AccessToken expiredToken = new AccessToken("expired", OffsetDateTime.now().minusMinutes(1)); - AccessToken newToken = new AccessToken("new", OffsetDateTime.now().plusHours(1)); - - when(mockCredential.getToken(any(TokenRequestContext.class))) - .thenReturn(Mono.just(expiredToken)) - .thenReturn(Mono.just(newToken)); - - // Act - AccessToken firstResult = tokenCache.getToken(); - AccessToken secondResult = tokenCache.getToken(); - - // Assert - assertEquals(expiredToken, firstResult); - assertEquals(newToken, secondResult); - verify(mockCredential, times(2)).getToken(context); - } - - @Test - @DisplayName("getToken should fetch a new token when current token is about to expire within margin") - public void getToken_WhenTokenAboutToExpire_FetchesNewToken() { - // Arrange - AccessToken expiringToken = new AccessToken("expiring", OffsetDateTime.now().plus(margin.minusMinutes(1))); - AccessToken newToken = new AccessToken("new", OffsetDateTime.now().plusHours(1)); - - when(mockCredential.getToken(any(TokenRequestContext.class))) - .thenReturn(Mono.just(expiringToken)) - .thenReturn(Mono.just(newToken)); - - // Act - AccessToken firstResult = tokenCache.getToken(); - AccessToken secondResult = tokenCache.getToken(); - - // Assert - assertEquals(expiringToken, firstResult); - assertEquals(newToken, secondResult); - verify(mockCredential, times(2)).getToken(context); - } -} diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsTest.java deleted file mode 100644 index a486dd4d..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsTest.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.microsoft.durabletask.DurableTaskGrpcClientBuilder; -import io.grpc.Channel; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * Unit tests for {@link DurableTaskSchedulerClientExtensions}. - */ -@ExtendWith(MockitoExtension.class) -public class DurableTaskSchedulerClientExtensionsTest { - - private static final String VALID_CONNECTION_STRING = - "Endpoint=https://example.com;Authentication=ManagedIdentity;TaskHub=myTaskHub"; - private static final String VALID_ENDPOINT = "https://example.com"; - private static final String VALID_TASKHUB = "myTaskHub"; - - @Mock - private DurableTaskGrpcClientBuilder mockBuilder; - - @Mock - private TokenCredential mockCredential; - - @Test - @DisplayName("useDurableTaskScheduler with connection string should configure builder correctly") - public void useDurableTaskScheduler_WithConnectionString_ConfiguresBuilder() { - // Arrange - when(mockBuilder.grpcChannel(any(Channel.class))).thenReturn(mockBuilder); - - // Act - DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - mockBuilder, VALID_CONNECTION_STRING); - - // Assert - verify(mockBuilder).grpcChannel(any(Channel.class)); - } - - @Test - @DisplayName("useDurableTaskScheduler with connection string should throw for null builder") - public void useDurableTaskScheduler_WithConnectionString_ThrowsForNullBuilder() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - null, VALID_CONNECTION_STRING)); - } - - @Test - @DisplayName("useDurableTaskScheduler with connection string should throw for null connection string") - public void useDurableTaskScheduler_WithConnectionString_ThrowsForNullConnectionString() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - mockBuilder, null)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should configure builder correctly") - public void useDurableTaskScheduler_WithExplicitParameters_ConfiguresBuilder() { - // Arrange - when(mockBuilder.grpcChannel(any(Channel.class))).thenReturn(mockBuilder); - - // Act - DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - mockBuilder, VALID_ENDPOINT, VALID_TASKHUB, mockCredential); - - // Assert - verify(mockBuilder).grpcChannel(any(Channel.class)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null builder") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullBuilder() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - null, VALID_ENDPOINT, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null endpoint") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullEndpoint() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - mockBuilder, null, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null task hub name") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullTaskHubName() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.useDurableTaskScheduler( - mockBuilder, VALID_ENDPOINT, null, mockCredential)); - } - - @Test - @DisplayName("createClientBuilder with connection string should create valid builder") - public void createClientBuilder_WithConnectionString_CreatesValidBuilder() { - // Act - DurableTaskGrpcClientBuilder result = - DurableTaskSchedulerClientExtensions.createClientBuilder(VALID_CONNECTION_STRING); - - // Assert - assertNotNull(result); - } - - @Test - @DisplayName("createClientBuilder with connection string should throw for null connection string") - public void createClientBuilder_WithConnectionString_ThrowsForNullConnectionString() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.createClientBuilder(null)); - } - - @Test - @DisplayName("createClientBuilder with explicit parameters should create valid builder") - public void createClientBuilder_WithExplicitParameters_CreatesValidBuilder() { - // Act - DurableTaskGrpcClientBuilder result = - DurableTaskSchedulerClientExtensions.createClientBuilder( - VALID_ENDPOINT, VALID_TASKHUB, mockCredential); - - // Assert - assertNotNull(result); - } - - @Test - @DisplayName("createClientBuilder with explicit parameters should throw for null endpoint") - public void createClientBuilder_WithExplicitParameters_ThrowsForNullEndpoint() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.createClientBuilder( - null, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("createClientBuilder with explicit parameters should throw for null task hub name") - public void createClientBuilder_WithExplicitParameters_ThrowsForNullTaskHubName() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerClientExtensions.createClientBuilder( - VALID_ENDPOINT, null, mockCredential)); - } -} \ No newline at end of file diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsUserAgentTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsUserAgentTest.java deleted file mode 100644 index 24a2513c..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientExtensionsUserAgentTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.DurableTaskGrpcClientBuilder; -import com.microsoft.durabletask.NewOrchestrationInstanceOptions; -import io.grpc.*; -import io.grpc.stub.ServerCalls; -import io.grpc.stub.StreamObserver; -import io.grpc.netty.NettyServerBuilder; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.*; - -public class DurableTaskSchedulerClientExtensionsUserAgentTest { - private Server server; - private Channel channel; - private final AtomicReference capturedUserAgent = new AtomicReference<>(); - private static final String EXPECTED_USER_AGENT_PREFIX = "durabletask-java/"; - - // Dummy gRPC service definition - public static class DummyService implements io.grpc.BindableService { - @Override - public ServerServiceDefinition bindService() { - return ServerServiceDefinition.builder("TaskHubSidecarService") - .addMethod( - MethodDescriptor.newBuilder() - .setType(MethodDescriptor.MethodType.UNARY) - .setFullMethodName(MethodDescriptor.generateFullMethodName("TaskHubSidecarService", "StartInstance")) - .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(com.google.protobuf.Empty.getDefaultInstance())) - .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(com.google.protobuf.Empty.getDefaultInstance())) - .build(), - ServerCalls.asyncUnaryCall( - new ServerCalls.UnaryMethod() { - @Override - public void invoke(com.google.protobuf.Empty request, StreamObserver responseObserver) { - // Mock response for StartInstance - responseObserver.onNext(com.google.protobuf.Empty.getDefaultInstance()); - responseObserver.onCompleted(); - } - } - ) - ) - .build(); - } - } - - @BeforeEach - public void setUp() throws IOException { - // Use NettyServerBuilder to expose the server via HTTP - server = NettyServerBuilder.forPort(0) - .addService(new DummyService()) // Register DummyService - .intercept(new ServerInterceptor() { - @Override - public ServerCall.Listener interceptCall( - ServerCall call, Metadata headers, ServerCallHandler next) { - String userAgent = headers.get(Metadata.Key.of("x-user-agent", Metadata.ASCII_STRING_MARSHALLER)); - capturedUserAgent.set(userAgent); - return next.startCall(call, headers); - } - }) - .directExecutor() - .build() - .start(); - int port = server.getPort(); - String endpoint = "localhost:" + port; - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - options.setEndpointAddress(endpoint); - options.setTaskHubName("testHub"); - options.setAllowInsecureCredentials(true); // Netty is insecure for localhost - channel = options.createGrpcChannel(); - } - - @AfterEach - public void tearDown() { - if (server != null) server.shutdownNow(); - if (channel != null && channel instanceof ManagedChannel) { - ((ManagedChannel) channel).shutdownNow(); - } - } - - @Test - public void testUserAgentHeaderIsSet() { - DurableTaskGrpcClientBuilder builder = new DurableTaskGrpcClientBuilder(); - builder.grpcChannel(channel); - DurableTaskClient client = builder.build(); - - // Schedule a new orchestration instance - String instanceId = client.scheduleNewOrchestrationInstance( - "TestOrchestration", - new NewOrchestrationInstanceOptions().setInput("TestInput")); - - // Make a dummy call to trigger the request - String userAgent = capturedUserAgent.get(); - assertNotNull(userAgent, "X-User-Agent header should be set"); - assertTrue(userAgent.startsWith(EXPECTED_USER_AGENT_PREFIX), "X-User-Agent should start with durabletask-java/"); - } -} - diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptionsTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptionsTest.java deleted file mode 100644 index 8508b56b..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerClientOptionsTest.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import io.grpc.Channel; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.time.Duration; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Unit tests for {@link DurableTaskSchedulerClientOptions}. - */ -@ExtendWith(MockitoExtension.class) -public class DurableTaskSchedulerClientOptionsTest { - - private static final String VALID_CONNECTION_STRING = - "Endpoint=https://example.com;Authentication=ManagedIdentity;TaskHub=myTaskHub"; - private static final String VALID_ENDPOINT = "https://example.com"; - private static final String VALID_TASKHUB = "myTaskHub"; - private static final String CUSTOM_RESOURCE_ID = "https://custom.resource"; - private static final Duration CUSTOM_REFRESH_MARGIN = Duration.ofMinutes(10); - - @Mock - private TokenCredential mockCredential; - - @Test - @DisplayName("fromConnectionString should create valid options") - public void fromConnectionString_CreatesValidOptions() { - // Act - DurableTaskSchedulerClientOptions options = - DurableTaskSchedulerClientOptions.fromConnectionString(VALID_CONNECTION_STRING); - - // Assert - assertNotNull(options); - assertEquals(VALID_ENDPOINT, options.getEndpointAddress()); - assertEquals(VALID_TASKHUB, options.getTaskHubName()); - } - - @Test - @DisplayName("setEndpointAddress should update endpoint") - public void setEndpointAddress_UpdatesEndpoint() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setEndpointAddress(VALID_ENDPOINT); - - // Assert - assertEquals(VALID_ENDPOINT, options.getEndpointAddress()); - } - - @Test - @DisplayName("setTaskHubName should update task hub name") - public void setTaskHubName_UpdatesTaskHubName() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setTaskHubName(VALID_TASKHUB); - - // Assert - assertEquals(VALID_TASKHUB, options.getTaskHubName()); - } - - @Test - @DisplayName("setCredential should update credential") - public void setCredential_UpdatesCredential() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setCredential(mockCredential); - - // Assert - assertEquals(mockCredential, options.getCredential()); - } - - @Test - @DisplayName("setResourceId should update resource ID") - public void setResourceId_UpdatesResourceId() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setResourceId(CUSTOM_RESOURCE_ID); - - // Assert - assertEquals(CUSTOM_RESOURCE_ID, options.getResourceId()); - } - - @Test - @DisplayName("setAllowInsecureCredentials should update insecure credentials flag") - public void setAllowInsecureCredentials_UpdatesFlag() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setAllowInsecureCredentials(true); - - // Assert - assertTrue(options.isAllowInsecureCredentials()); - } - - @Test - @DisplayName("setTokenRefreshMargin should update token refresh margin") - public void setTokenRefreshMargin_UpdatesMargin() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions(); - - // Act - options.setTokenRefreshMargin(CUSTOM_REFRESH_MARGIN); - - // Assert - assertEquals(CUSTOM_REFRESH_MARGIN, options.getTokenRefreshMargin()); - } - - @Test - @DisplayName("createGrpcChannel should create valid channel") - public void createGrpcChannel_CreatesValidChannel() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions() - .setEndpointAddress(VALID_ENDPOINT) - .setTaskHubName(VALID_TASKHUB); - - // Act - Channel channel = options.createGrpcChannel(); - - // Assert - assertNotNull(channel); - } - - @Test - @DisplayName("createGrpcChannel should handle endpoint without protocol") - public void createGrpcChannel_HandlesEndpointWithoutProtocol() { - // Arrange - DurableTaskSchedulerClientOptions options = new DurableTaskSchedulerClientOptions() - .setEndpointAddress("example.com") - .setTaskHubName(VALID_TASKHUB); - - // Act - Channel channel = options.createGrpcChannel(); - - // Assert - assertNotNull(channel); - } -} \ No newline at end of file diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionStringTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionStringTest.java deleted file mode 100644 index 59d974eb..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerConnectionStringTest.java +++ /dev/null @@ -1,287 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.azure.identity.*; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.junit.jupiter.params.provider.NullAndEmptySource; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Unit tests for {@link DurableTaskSchedulerConnectionString}. - */ -public class DurableTaskSchedulerConnectionStringTest { - - private static final String VALID_CONNECTION_STRING = - "Endpoint=https://example.com;Authentication=ManagedIdentity;TaskHub=myTaskHub"; - - @Test - @DisplayName("Constructor should parse valid connection string") - public void constructor_ParsesValidConnectionString() { - // Arrange & Act - DurableTaskSchedulerConnectionString connectionString = - new DurableTaskSchedulerConnectionString(VALID_CONNECTION_STRING); - - // Assert - assertEquals("https://example.com", connectionString.getEndpoint()); - assertEquals("ManagedIdentity", connectionString.getAuthentication()); - assertEquals("myTaskHub", connectionString.getTaskHubName()); - } - - @Test - @DisplayName("Constructor should handle connection string with whitespace") - public void constructor_HandlesWhitespace() { - // Arrange - String connectionStringWithSpaces = - "Endpoint = https://example.com ; Authentication = ManagedIdentity ; TaskHub = myTaskHub"; - - // Act - DurableTaskSchedulerConnectionString connectionString = - new DurableTaskSchedulerConnectionString(connectionStringWithSpaces); - - // Assert - assertEquals("https://example.com", connectionString.getEndpoint()); - assertEquals("ManagedIdentity", connectionString.getAuthentication()); - assertEquals("myTaskHub", connectionString.getTaskHubName()); - } - - @ParameterizedTest - @NullAndEmptySource - @ValueSource(strings = {" "}) - @DisplayName("Constructor should throw for null or empty connection string") - public void constructor_ThrowsForNullOrEmptyConnectionString(String invalidInput) { - // Act & Assert - assertThrows(IllegalArgumentException.class, - () -> new DurableTaskSchedulerConnectionString(invalidInput)); - } - - @Test - @DisplayName("Constructor should throw when missing required Endpoint property") - public void constructor_ThrowsWhenMissingEndpoint() { - // Arrange - String missingEndpoint = "Authentication=ManagedIdentity;TaskHub=myTaskHub"; - - // Act & Assert - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> new DurableTaskSchedulerConnectionString(missingEndpoint)); - - assertTrue(exception.getMessage().contains("Endpoint")); - } - - @Test - @DisplayName("Constructor should throw when missing required Authentication property") - public void constructor_ThrowsWhenMissingAuthentication() { - // Arrange - String missingAuthentication = "Endpoint=https://example.com;TaskHub=myTaskHub"; - - // Act & Assert - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> new DurableTaskSchedulerConnectionString(missingAuthentication)); - - assertTrue(exception.getMessage().contains("Authentication")); - } - - @Test - @DisplayName("Constructor should throw when missing required TaskHub property") - public void constructor_ThrowsWhenMissingTaskHub() { - // Arrange - String missingTaskHub = "Endpoint=https://example.com;Authentication=ManagedIdentity"; - - // Act & Assert - IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, - () -> new DurableTaskSchedulerConnectionString(missingTaskHub)); - - assertTrue(exception.getMessage().contains("TaskHub")); - } - - @Test - @DisplayName("getAdditionallyAllowedTenants should return split comma-separated values") - public void getAdditionallyAllowedTenants_ShouldSplitCommaValues() { - // Arrange - String connectionString = VALID_CONNECTION_STRING + - ";AdditionallyAllowedTenants=tenant1,tenant2,tenant3"; - - // Act - DurableTaskSchedulerConnectionString parsedString = - new DurableTaskSchedulerConnectionString(connectionString); - List tenants = parsedString.getAdditionallyAllowedTenants(); - - // Assert - assertNotNull(tenants); - assertEquals(3, tenants.size()); - assertEquals("tenant1", tenants.get(0)); - assertEquals("tenant2", tenants.get(1)); - assertEquals("tenant3", tenants.get(2)); - } - - @Test - @DisplayName("getAdditionallyAllowedTenants should return null when property not present") - public void getAdditionallyAllowedTenants_ReturnsNullWhenNotPresent() { - // Arrange & Act - DurableTaskSchedulerConnectionString connectionString = - new DurableTaskSchedulerConnectionString(VALID_CONNECTION_STRING); - - // Assert - assertNull(connectionString.getAdditionallyAllowedTenants()); - } - - @Test - @DisplayName("getClientId should return correct value when present") - public void getClientId_ReturnsValueWhenPresent() { - // Arrange - String connectionString = VALID_CONNECTION_STRING + ";ClientID=my-client-id"; - - // Act - DurableTaskSchedulerConnectionString parsedString = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - assertEquals("my-client-id", parsedString.getClientId()); - } - - @Test - @DisplayName("getTenantId should return correct value when present") - public void getTenantId_ReturnsValueWhenPresent() { - // Arrange - String connectionString = VALID_CONNECTION_STRING + ";TenantId=my-tenant-id"; - - // Act - DurableTaskSchedulerConnectionString parsedString = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - assertEquals("my-tenant-id", parsedString.getTenantId()); - } - - @Test - @DisplayName("getTokenFilePath should return correct value when present") - public void getTokenFilePath_ReturnsValueWhenPresent() { - // Arrange - String connectionString = VALID_CONNECTION_STRING + ";TokenFilePath=/path/to/token"; - - // Act - DurableTaskSchedulerConnectionString parsedString = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - assertEquals("/path/to/token", parsedString.getTokenFilePath()); - } - - @Test - @DisplayName("getCredential should handle supported authentication types") - public void getCredential_HandlesSupportedAuthTypes() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s", - "https://example.com", "ManagedIdentity", "myTaskHub"); - - // Act - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - TokenCredential credential = result.getCredential(); - assertNotNull(credential); - - // Verify the correct credential type is returned - assertTrue(credential instanceof ManagedIdentityCredential); - } - - @Test - @DisplayName("getCredential should throw for unsupported authentication type") - public void getCredential_ThrowsForUnsupportedAuthType() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s", - "https://example.com", "UnsupportedType", "myTaskHub"); - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - - // Act & Assert - assertThrows(IllegalArgumentException.class, result::getCredential); - } - - @Test - @DisplayName("getCredential should configure WorkloadIdentity with all properties") - public void getCredential_ConfiguresWorkloadIdentityWithAllProperties() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s;ClientID=%s;TenantId=%s;TokenFilePath=%s;AdditionallyAllowedTenants=%s", - "https://example.com", "WorkloadIdentity", "myTaskHub", "client-id-123", "tenant-id-123", - "/path/to/token", "tenant1,tenant2,tenant3"); - - // Act - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - TokenCredential credential = result.getCredential(); - - // Assert - assertNotNull(credential); - assertTrue(credential instanceof WorkloadIdentityCredential); - } - - @Test - @DisplayName("getCredential should return VisualStudioCodeCredential for VisualStudioCode authentication type") - public void getCredential_ReturnsVisualStudioCodeCredential() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s", - "https://example.com", "VisualStudioCode", "myTaskHub"); - - // Act - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - TokenCredential credential = result.getCredential(); - assertNotNull(credential); - - // Verify the correct credential type is returned - assertTrue(credential instanceof VisualStudioCodeCredential); - } - - @Test - @DisplayName("getCredential should return InteractiveBrowserCredential for InteractiveBrowser authentication type") - public void getCredential_ReturnsInteractiveBrowserCredential() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s", - "https://example.com", "InteractiveBrowser", "myTaskHub"); - - // Act - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - TokenCredential credential = result.getCredential(); - assertNotNull(credential); - - // Verify the correct credential type is returned - assertTrue(credential instanceof InteractiveBrowserCredential); - } - - @Test - @DisplayName("getCredential should return IntelliJCredential for IntelliJ authentication type") - public void getCredential_ReturnsIntelliJCredential() { - // Arrange - String connectionString = String.format( - "Endpoint=%s;Authentication=%s;TaskHub=%s", - "https://example.com", "IntelliJ", "myTaskHub"); - - // Act - DurableTaskSchedulerConnectionString result = - new DurableTaskSchedulerConnectionString(connectionString); - - // Assert - TokenCredential credential = result.getCredential(); - assertNotNull(credential); - - // Verify the correct credential type is returned - assertTrue(credential instanceof IntelliJCredential); - } -} \ No newline at end of file diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensionsTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensionsTest.java deleted file mode 100644 index 9ad18281..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerExtensionsTest.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import com.microsoft.durabletask.DurableTaskGrpcWorkerBuilder; -import io.grpc.Channel; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * Unit tests for {@link DurableTaskSchedulerWorkerExtensions}. - */ -@ExtendWith(MockitoExtension.class) -public class DurableTaskSchedulerWorkerExtensionsTest { - - private static final String VALID_CONNECTION_STRING = - "Endpoint=https://example.com;Authentication=ManagedIdentity;TaskHub=myTaskHub"; - private static final String VALID_ENDPOINT = "https://example.com"; - private static final String VALID_TASKHUB = "myTaskHub"; - - @Mock - private DurableTaskGrpcWorkerBuilder mockBuilder; - - @Mock - private TokenCredential mockCredential; - - @Test - @DisplayName("useDurableTaskScheduler with connection string should configure builder correctly") - public void useDurableTaskScheduler_WithConnectionString_ConfiguresBuilder() { - // Arrange - when(mockBuilder.grpcChannel(any(Channel.class))).thenReturn(mockBuilder); - - // Act - DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - mockBuilder, VALID_CONNECTION_STRING); - - // Assert - verify(mockBuilder).grpcChannel(any(Channel.class)); - } - - @Test - @DisplayName("useDurableTaskScheduler with connection string should throw for null builder") - public void useDurableTaskScheduler_WithConnectionString_ThrowsForNullBuilder() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - null, VALID_CONNECTION_STRING)); - } - - @Test - @DisplayName("useDurableTaskScheduler with connection string should throw for null connection string") - public void useDurableTaskScheduler_WithConnectionString_ThrowsForNullConnectionString() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - mockBuilder, null)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should configure builder correctly") - public void useDurableTaskScheduler_WithExplicitParameters_ConfiguresBuilder() { - // Arrange - when(mockBuilder.grpcChannel(any(Channel.class))).thenReturn(mockBuilder); - - // Act - DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - mockBuilder, VALID_ENDPOINT, VALID_TASKHUB, mockCredential); - - // Assert - verify(mockBuilder).grpcChannel(any(Channel.class)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null builder") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullBuilder() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - null, VALID_ENDPOINT, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null endpoint") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullEndpoint() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - mockBuilder, null, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("useDurableTaskScheduler with explicit parameters should throw for null task hub name") - public void useDurableTaskScheduler_WithExplicitParameters_ThrowsForNullTaskHubName() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.useDurableTaskScheduler( - mockBuilder, VALID_ENDPOINT, null, mockCredential)); - } - - @Test - @DisplayName("createWorkerBuilder with connection string should create valid builder") - public void createWorkerBuilder_WithConnectionString_CreatesValidBuilder() { - // Act - DurableTaskGrpcWorkerBuilder result = - DurableTaskSchedulerWorkerExtensions.createWorkerBuilder(VALID_CONNECTION_STRING); - - // Assert - assertNotNull(result); - } - - @Test - @DisplayName("createWorkerBuilder with connection string should throw for null connection string") - public void createWorkerBuilder_WithConnectionString_ThrowsForNullConnectionString() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.createWorkerBuilder(null)); - } - - @Test - @DisplayName("createWorkerBuilder with explicit parameters should create valid builder") - public void createWorkerBuilder_WithExplicitParameters_CreatesValidBuilder() { - // Act - DurableTaskGrpcWorkerBuilder result = - DurableTaskSchedulerWorkerExtensions.createWorkerBuilder( - VALID_ENDPOINT, VALID_TASKHUB, mockCredential); - - // Assert - assertNotNull(result); - } - - @Test - @DisplayName("createWorkerBuilder with explicit parameters should throw for null endpoint") - public void createWorkerBuilder_WithExplicitParameters_ThrowsForNullEndpoint() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.createWorkerBuilder( - null, VALID_TASKHUB, mockCredential)); - } - - @Test - @DisplayName("createWorkerBuilder with explicit parameters should throw for null task hub name") - public void createWorkerBuilder_WithExplicitParameters_ThrowsForNullTaskHubName() { - // Act & Assert - assertThrows(NullPointerException.class, - () -> DurableTaskSchedulerWorkerExtensions.createWorkerBuilder( - VALID_ENDPOINT, null, mockCredential)); - } -} \ No newline at end of file diff --git a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptionsTest.java b/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptionsTest.java deleted file mode 100644 index 5fab6912..00000000 --- a/azuremanaged/src/test/java/com/microsoft/durabletask/azuremanaged/DurableTaskSchedulerWorkerOptionsTest.java +++ /dev/null @@ -1,198 +0,0 @@ -package com.microsoft.durabletask.azuremanaged; - -import com.azure.core.credential.TokenCredential; -import io.grpc.Channel; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.time.Duration; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Unit tests for {@link DurableTaskSchedulerWorkerOptions}. - */ -@ExtendWith(MockitoExtension.class) -public class DurableTaskSchedulerWorkerOptionsTest { - - private static final String VALID_ENDPOINT = "https://example.com"; - private static final String VALID_TASKHUB = "myTaskHub"; - private static final String VALID_CONNECTION_STRING = - "Endpoint=https://example.com;Authentication=ManagedIdentity;TaskHub=myTaskHub"; - - @Mock - private TokenCredential mockCredential; - - @Test - @DisplayName("Default constructor should set default values") - public void defaultConstructor_SetsDefaultValues() { - // Act - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - - // Assert - assertEquals("", options.getEndpointAddress()); - assertEquals("", options.getTaskHubName()); - assertNull(options.getCredential()); - assertEquals("https://durabletask.io", options.getResourceId()); - assertFalse(options.isAllowInsecureCredentials()); - assertEquals(Duration.ofMinutes(5), options.getTokenRefreshMargin()); - } - - @Test - @DisplayName("fromConnectionString should parse connection string correctly") - public void fromConnectionString_ParsesConnectionStringCorrectly() { - // Act - DurableTaskSchedulerWorkerOptions options = - DurableTaskSchedulerWorkerOptions.fromConnectionString(VALID_CONNECTION_STRING); - - // Assert - assertEquals(VALID_ENDPOINT, options.getEndpointAddress()); - assertEquals(VALID_TASKHUB, options.getTaskHubName()); - assertNotNull(options.getCredential()); - assertTrue(options.getCredential() instanceof com.azure.identity.ManagedIdentityCredential); - assertFalse(options.isAllowInsecureCredentials()); - } - - @Test - @DisplayName("fromConnectionString should allow insecure credentials when authentication is None") - public void fromConnectionString_AllowsInsecureCredentialsWhenAuthenticationIsNone() { - // Arrange - String connectionString = "Endpoint=https://example.com;Authentication=None;TaskHub=myTaskHub"; - - // Act - DurableTaskSchedulerWorkerOptions options = - DurableTaskSchedulerWorkerOptions.fromConnectionString(connectionString); - - // Assert - assertTrue(options.isAllowInsecureCredentials()); - assertNull(options.getCredential()); - } - - @Test - @DisplayName("setEndpointAddress should update endpoint address") - public void setEndpointAddress_UpdatesEndpointAddress() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - - // Act - DurableTaskSchedulerWorkerOptions result = options.setEndpointAddress(VALID_ENDPOINT); - - // Assert - assertEquals(VALID_ENDPOINT, options.getEndpointAddress()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("setTaskHubName should update task hub name") - public void setTaskHubName_UpdatesTaskHubName() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - - // Act - DurableTaskSchedulerWorkerOptions result = options.setTaskHubName(VALID_TASKHUB); - - // Assert - assertEquals(VALID_TASKHUB, options.getTaskHubName()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("setCredential should update credential") - public void setCredential_UpdatesCredential() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - - // Act - DurableTaskSchedulerWorkerOptions result = options.setCredential(mockCredential); - - // Assert - assertSame(mockCredential, options.getCredential()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("setResourceId should update resource ID") - public void setResourceId_UpdatesResourceId() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - String customResourceId = "https://custom-resource.example.com"; - - // Act - DurableTaskSchedulerWorkerOptions result = options.setResourceId(customResourceId); - - // Assert - assertEquals(customResourceId, options.getResourceId()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("setAllowInsecureCredentials should update allowInsecureCredentials") - public void setAllowInsecureCredentials_UpdatesAllowInsecureCredentials() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - - // Act - DurableTaskSchedulerWorkerOptions result = options.setAllowInsecureCredentials(true); - - // Assert - assertTrue(options.isAllowInsecureCredentials()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("setTokenRefreshMargin should update token refresh margin") - public void setTokenRefreshMargin_UpdatesTokenRefreshMargin() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions(); - Duration customMargin = Duration.ofMinutes(10); - - // Act - DurableTaskSchedulerWorkerOptions result = options.setTokenRefreshMargin(customMargin); - - // Assert - assertEquals(customMargin, options.getTokenRefreshMargin()); - assertSame(options, result); // Builder pattern returns this - } - - @Test - @DisplayName("createGrpcChannel should create a channel") - public void createGrpcChannel_CreatesChannel() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions() - .setEndpointAddress(VALID_ENDPOINT) - .setTaskHubName(VALID_TASKHUB); - - // Act - Channel channel = options.createGrpcChannel(); - - // Assert - assertNotNull(channel); - } - - @Test - @DisplayName("createGrpcChannel should handle endpoints without protocol") - public void createGrpcChannel_HandlesEndpointsWithoutProtocol() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions() - .setEndpointAddress("example.com") - .setTaskHubName(VALID_TASKHUB); - - // Act & Assert - assertDoesNotThrow(() -> options.createGrpcChannel()); - } - - @Test - @DisplayName("createGrpcChannel should throw for invalid URLs") - public void createGrpcChannel_ThrowsForInvalidUrls() { - // Arrange - DurableTaskSchedulerWorkerOptions options = new DurableTaskSchedulerWorkerOptions() - .setEndpointAddress("invalid:url") - .setTaskHubName(VALID_TASKHUB); - - // Act & Assert - assertThrows(IllegalArgumentException.class, () -> options.createGrpcChannel()); - } -} \ No newline at end of file diff --git a/eng/ci/code-mirror.yml b/eng/ci/code-mirror.yml deleted file mode 100644 index f884e101..00000000 --- a/eng/ci/code-mirror.yml +++ /dev/null @@ -1,19 +0,0 @@ -trigger: - branches: - include: - # These are the branches we'll mirror to our internal ADO instance - # Keep this set limited as appropriate (don't mirror individual user branches). - - main - -resources: - repositories: - - repository: eng - type: git - name: engineering - ref: refs/tags/release - -variables: - - template: ci/variables/cfs.yml@eng - -extends: - template: ci/code-mirror.yml@eng diff --git a/eng/ci/official-build.yml b/eng/ci/official-build.yml deleted file mode 100644 index bf4354b2..00000000 --- a/eng/ci/official-build.yml +++ /dev/null @@ -1,38 +0,0 @@ -variables: - - template: ci/variables/cfs.yml@eng - -trigger: - batch: true - branches: - include: - - main - -# CI only, does not trigger on PRs. -pr: none - -resources: - repositories: - - repository: 1es - type: git - name: 1ESPipelineTemplates/1ESPipelineTemplates - ref: refs/tags/release - - repository: eng - type: git - name: engineering - ref: refs/tags/release - -extends: - template: v1/1ES.Official.PipelineTemplate.yml@1es - parameters: - pool: - name: 1es-pool-azfunc - image: 1es-windows-2022 - os: windows - sdl: - spotBugs: - enabled: false - stages: - - stage: BuildAndSign - dependsOn: [] - jobs: - - template: /eng/templates/build.yml@self \ No newline at end of file diff --git a/eng/ci/release.yml b/eng/ci/release.yml deleted file mode 100644 index 408a7404..00000000 --- a/eng/ci/release.yml +++ /dev/null @@ -1,99 +0,0 @@ -pr: none -trigger: none - -resources: - repositories: - - repository: 1ESPipelineTemplates - type: git - name: 1ESPipelineTemplates/1ESPipelineTemplates - ref: refs/tags/release - pipelines: - - pipeline: DurableTaskJavaBuildPipeline - source: durabletask-java.official - branch: main - -extends: - template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates - parameters: - pool: - name: 1es-pool-azfunc - image: 1es-ubuntu-22.04 - os: linux - - stages: - - stage: release - jobs: - - job: durabletask_azuremanaged - templateContext: - type: releaseJob - isProduction: true - inputs: - # Declare inputs to be released here to ensure they receive relevant checks - - input: pipelineArtifact - pipeline: DurableTaskJavaBuildPipeline - artifactName: drop - targetPath: $(System.DefaultWorkingDirectory)/drop - - steps: - - task: SFP.release-tasks.custom-build-release-task.EsrpRelease@9 - displayName: 'Release durabletask-azuremanaged' - inputs: - connectedservicename: 'dtfx-internal-esrp-prod' - usemanagedidentity: true - keyvaultname: 'durable-esrp-akv' - signcertname: 'dts-esrp-cert' - clientid: '0b3ed1a4-0727-4a50-b82a-02c2bd9dec89' - folderlocation: '$(System.DefaultWorkingDirectory)/drop/durabletask-azuremanaged' - owners: 'wangbill@microsoft.com' - approvers: 'kaibocai@microsoft.com' - mainpublisher: 'durabletask-java' - - - job: durabletask_client - templateContext: - type: releaseJob - isProduction: true - inputs: - # Declare inputs to be released here to ensure they receive relevant checks - - input: pipelineArtifact - pipeline: DurableTaskJavaBuildPipeline - artifactName: drop - targetPath: $(System.DefaultWorkingDirectory)/drop - - steps: - - task: SFP.release-tasks.custom-build-release-task.EsrpRelease@9 - displayName: 'Release durabletask-client' - inputs: - connectedservicename: 'dtfx-internal-esrp-prod' - usemanagedidentity: true - keyvaultname: 'durable-esrp-akv' - signcertname: 'dts-esrp-cert' - clientid: '0b3ed1a4-0727-4a50-b82a-02c2bd9dec89' - folderlocation: '$(System.DefaultWorkingDirectory)/drop/durabletask-client' - owners: 'wangbill@microsoft.com' - approvers: 'kaibocai@microsoft.com' - mainpublisher: 'durabletask-java' - - - job: durabletask_azure_functions - templateContext: - type: releaseJob - isProduction: true - inputs: - # Declare inputs to be released here to ensure they receive relevant checks - - input: pipelineArtifact - pipeline: DurableTaskJavaBuildPipeline - artifactName: drop - targetPath: $(System.DefaultWorkingDirectory)/drop - - steps: - - task: SFP.release-tasks.custom-build-release-task.EsrpRelease@9 - displayName: 'Release durabletask-azure-functions' - inputs: - connectedservicename: 'dtfx-internal-esrp-prod' - usemanagedidentity: true - keyvaultname: 'durable-esrp-akv' - signcertname: 'dts-esrp-cert' - clientid: '0b3ed1a4-0727-4a50-b82a-02c2bd9dec89' - folderlocation: '$(System.DefaultWorkingDirectory)/drop/durabletask-azure-functions' - owners: 'wangbill@microsoft.com' - approvers: 'kaibocai@microsoft.com' - mainpublisher: 'durabletask-java' diff --git a/eng/templates/build.yml b/eng/templates/build.yml deleted file mode 100644 index 42550475..00000000 --- a/eng/templates/build.yml +++ /dev/null @@ -1,53 +0,0 @@ -jobs: - - job: Build - - templateContext: - outputs: - - output: pipelineArtifact - path: $(build.artifactStagingDirectory) - artifact: drop - sbomBuildDropPath: $(System.DefaultWorkingDirectory) - sbomPackageName: 'Durable Task / Durable Functions Java SBOM' - steps: - - checkout: self - - - task: Gradle@3 - inputs: - # Specifies the working directory to run the Gradle build. The task uses the repository root directory if the working directory is not specified. - workingDirectory: '' - # Specifies the gradlew wrapper's location within the repository that will be used for the build. - gradleWrapperFile: 'gradlew' - # Sets the GRADLE_OPTS environment variable, which is used to send command-line arguments to start the JVM. The xmx flag specifies the maximum memory available to the JVM. - gradleOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: 1.11 - jdkArchitectureOption: 'x64' - publishJUnitResults: false - tasks: clean assemble - displayName: Assemble durabletask-client and durabletask-azure-functions and durabletask-azuremanaged - - # the secring.gpg file is required to sign the artifacts, it's generated from GnuPG, and it's stored in the library of the durabletaskframework ADO - - task: DownloadSecureFile@1 - name: gpgSecretFile - displayName: 'Download GPG secret file' - inputs: - secureFile: 'secring.gpg' - - - task: Gradle@3 - inputs: - workingDirectory: '' - gradleWrapperFile: 'gradlew' - gradleOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: 1.11 - jdkArchitectureOption: 'x64' - tasks: publish - options: '-Psigning.keyId=$(gpgSignKey) -Psigning.password=$(gpgSignPassword) -Psigning.secretKeyRingFile=$(gpgSecretFile.secureFilePath)' - displayName: Publish durabletask-client and durabletask-azure-functions and durabletask-azuremanaged - - - task: CopyFiles@2 - displayName: 'Copy publish file to Artifact Staging Directory' - inputs: - SourceFolder: $(System.DefaultWorkingDirectory)/repo/com/microsoft - Contents: '**/*.*' - TargetFolder: $(Build.ArtifactStagingDirectory) \ No newline at end of file diff --git a/samples-azure-functions/Dockerfile b/samples-azure-functions/Dockerfile deleted file mode 100644 index 43a57dc6..00000000 --- a/samples-azure-functions/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM mcr.microsoft.com/azure-functions/java:4-java11 - -COPY samples-azure-functions/build/azure-functions/azure-functions-sample/ /home/site/wwwroot/ -ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ - AzureFunctionsJobHost__Logging__Console__IsEnabled=true diff --git a/samples-azure-functions/build.gradle b/samples-azure-functions/build.gradle deleted file mode 100644 index 646ab7c8..00000000 --- a/samples-azure-functions/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id "com.microsoft.azure.azurefunctions" version "1.11.1" -} -apply plugin: 'java' -apply plugin: "com.microsoft.azure.azurefunctions" - -group 'com.functions' -version '0.1.0-SNAPSHOT' - -repositories { - mavenLocal() - maven { - url "https://oss.sonatype.org/content/repositories/snapshots/" - } - mavenCentral() -} - -dependencies { - implementation project(':client') - implementation project(':azurefunctions') - - implementation 'com.microsoft.azure.functions:azure-functions-java-library:3.0.0' - testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2' - testImplementation 'io.rest-assured:rest-assured:5.3.0' - testImplementation 'io.rest-assured:json-path:5.3.0' - -} - -sourceCompatibility = '1.8' -targetCompatibility = '1.8' - -compileJava.options.encoding = 'UTF-8' - -task sampleTest(type: Test) { - useJUnitPlatform { - includeTags 'sampleTest' - } - dependsOn build - testLogging.showStandardStreams = true -} - -azurefunctions { - resourceGroup = 'java-functions-group' - appName = 'azure-functions-sample' - pricingTier = 'Consumption' - region = 'westus' - runtime { - os = 'Windows' - javaVersion = 'Java 8' - } - auth { - type = 'azure_cli' - } - localDebug = "transport=dt_socket,server=y,suspend=n,address=5005" -} diff --git a/samples-azure-functions/e2e-test-setup.ps1 b/samples-azure-functions/e2e-test-setup.ps1 deleted file mode 100644 index 6e57d763..00000000 --- a/samples-azure-functions/e2e-test-setup.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -# Installing PowerShell: https://docs.microsoft.com/powershell/scripting/install/installing-powershell - -param( - [Parameter(Mandatory=$true)] - [string]$DockerfilePath, - [string]$ImageName="dfapp", - [string]$ContainerName="app", - [switch]$NoSetup=$false, - [switch]$NoValidation=$false, - [string]$AzuriteVersion="3.34.0", - [int]$Sleep=30 -) - -$ErrorActionPreference = "Stop" - -if ($NoSetup -eq $false) { - # Build the docker image first, since that's the most critical step - Write-Host "Building sample app Docker container from '$DockerfilePath'..." -ForegroundColor Yellow - docker build -f $DockerfilePath -t $ImageName --progress plain . - - # Next, download and start the Azurite emulator Docker image - Write-Host "Pulling down the mcr.microsoft.com/azure-storage/azurite:$AzuriteVersion image..." -ForegroundColor Yellow - docker pull "mcr.microsoft.com/azure-storage/azurite:${AzuriteVersion}" - - Write-Host "Starting Azurite storage emulator using default ports..." -ForegroundColor Yellow - docker run --name 'azurite' -p 10000:10000 -p 10001:10001 -p 10002:10002 -d "mcr.microsoft.com/azure-storage/azurite:${AzuriteVersion}" - - # Finally, start up the smoke test container, which will connect to the Azurite container - docker run --name $ContainerName -p 8080:80 -it --add-host=host.docker.internal:host-gateway -d ` - --env 'AzureWebJobsStorage=UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://host.docker.internal' ` - --env 'WEBSITE_HOSTNAME=localhost:8080' ` - $ImageName -} - -if ($sleep -gt 0) { - # The container needs a bit more time before it can start receiving requests - Write-Host "Sleeping for $Sleep seconds to let the container finish initializing..." -ForegroundColor Yellow - Start-Sleep -Seconds $Sleep -} - -# Check to see what containers are running -docker ps \ No newline at end of file diff --git a/samples-azure-functions/host.json b/samples-azure-functions/host.json deleted file mode 100644 index 9d59145e..00000000 --- a/samples-azure-functions/host.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "version": "2.0", - "logging": { - "logLevel": { - "DurableTask.AzureStorage": "Warning", - "DurableTask.Core": "Warning" - }, - "applicationInsights": { - "samplingSettings": { - "isEnabled": false - } - } - }, - "extensions": { - "durableTask": { - "hubName": "DFJavaSmokeTest" - } - }, - "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle", - "version": "[4.*, 5.0.0)" - } -} \ No newline at end of file diff --git a/samples-azure-functions/local.settings.json b/samples-azure-functions/local.settings.json deleted file mode 100644 index e97d6ec5..00000000 --- a/samples-azure-functions/local.settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "java" - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/AzureFunctions.java b/samples-azure-functions/src/main/java/com/functions/AzureFunctions.java deleted file mode 100644 index 755e50ac..00000000 --- a/samples-azure-functions/src/main/java/com/functions/AzureFunctions.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.functions; - -import com.functions.model.Person; -import com.microsoft.azure.functions.annotation.*; -import com.microsoft.azure.functions.*; -import java.util.*; - -import com.microsoft.durabletask.*; -import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -/** - * Azure Durable Functions with HTTP trigger. - */ -public class AzureFunctions { - /** - * This HTTP-triggered function starts the orchestration. - */ - @FunctionName("StartOrchestration") - public HttpResponseMessage startOrchestration( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("Cities"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - /** - * This is the orchestrator function, which can schedule activity functions, create durable timers, - * or wait for external events in a way that's completely fault-tolerant. - */ - @FunctionName("Cities") - public String citiesOrchestrator( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - String result = ""; - result += ctx.callActivity("Capitalize", "Tokyo", String.class).await() + ", "; - result += ctx.callActivity("Capitalize", "London", String.class).await() + ", "; - result += ctx.callActivity("Capitalize", "Seattle", String.class).await() + ", "; - result += ctx.callActivity("Capitalize", "Austin", String.class).await(); - return result; - } - - /** - * This is the activity function that gets invoked by the orchestration. - */ - @FunctionName("Capitalize") - public String capitalize( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - context.getLogger().info("Capitalizing: " + name); - return name.toUpperCase(); - } - - // Orchestration with POJO input - @FunctionName("StartOrchestrationPOJO") - public HttpResponseMessage startOrchestrationPOJO( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - Person person = new Person(); - person.setName("testname"); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("Person", person); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("Person") - public Person personOrchestrator( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - Person person = ctx.getInput(Person.class); - person.setName(ctx.callActivity("Capitalize", person.getName(), String.class).await()); - return person; - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/ContinueAsNew.java b/samples-azure-functions/src/main/java/com/functions/ContinueAsNew.java deleted file mode 100644 index a8c177e3..00000000 --- a/samples-azure-functions/src/main/java/com/functions/ContinueAsNew.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.Task; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.time.Duration; -import java.util.Optional; - -public class ContinueAsNew { - @FunctionName("ContinueAsNew") - public HttpResponseMessage continueAsNew( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("EternalOrchestrator"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("EternalOrchestrator") - public void eternalOrchestrator(@DurableOrchestrationTrigger(name = "runtimeState") TaskOrchestrationContext ctx) - { - System.out.println("Processing stuff..."); - ctx.createTimer(Duration.ofSeconds(2)).await(); - ctx.continueAsNew(null); - } - - @FunctionName("ContinueAsNewExternalEvent") - public HttpResponseMessage continueAsNewExternalEvent( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("EternalEvent"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("EternalEvent") - public void eternalEvent(@DurableOrchestrationTrigger(name = "runtimeState") TaskOrchestrationContext ctx) - { - System.out.println("Waiting external event..."); - Task event = ctx.waitForExternalEvent("event"); - Task timer = ctx.createTimer(Duration.ofSeconds(10)); - Task result = ctx.anyOf(event, timer).await(); - if (result == event) { - ctx.continueAsNew(null); - } - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/ParallelFunctions.java b/samples-azure-functions/src/main/java/com/functions/ParallelFunctions.java deleted file mode 100644 index f7797705..00000000 --- a/samples-azure-functions/src/main/java/com/functions/ParallelFunctions.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.annotation.*; -import com.microsoft.azure.functions.*; - -import java.time.Duration; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.microsoft.durabletask.*; -import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -public class ParallelFunctions { - - private static final AtomicBoolean throwException = new AtomicBoolean(true); - @FunctionName("StartParallelOrchestration") - public HttpResponseMessage startParallelOrchestration( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("Parallel"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("Parallel") - public List parallelOrchestratorSad( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - RetryPolicy retryPolicy = new RetryPolicy(2, Duration.ofSeconds(5)); - TaskOptions taskOptions = new TaskOptions(retryPolicy); - List> tasks = new ArrayList<>(); - tasks.add(ctx.callActivity("AppendSad", "Input1", taskOptions, String.class)); - tasks.add(ctx.callActivity("AppendSad", "Input2", taskOptions, String.class)); - tasks.add(ctx.callActivity("AppendHappy", "Input3", taskOptions, String.class)); - tasks.add(ctx.callActivity("AppendHappy", "Input4", taskOptions, String.class)); - tasks.add(ctx.callActivity("AppendHappy", "Input5", String.class)); - tasks.add(ctx.callActivity("AppendHappy", "Input6", String.class)); - return ctx.allOf(tasks).await(); - } - - @FunctionName("AppendHappy") - public String appendHappy( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - context.getLogger().info("AppendHappy: " + name); - return name + "-test-happy"; - } - - @FunctionName("AppendSad") - public String appendSad( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - if (throwException.get()) { - throwException.compareAndSet(true, false); - throw new RuntimeException("Test sad path for retry"); - } - context.getLogger().info("AppendSad: " + name); - return name + "-test-sad"; - } - - - @FunctionName("StartParallelAnyOf") - public HttpResponseMessage startParallelAnyOf( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("ParallelAnyOf"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("ParallelAnyOf") - public Object parallelAnyOf( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - RetryPolicy retryPolicy = new RetryPolicy(2, Duration.ofSeconds(5)); - TaskOptions taskOptions = new TaskOptions(retryPolicy); - List> tasks = new ArrayList<>(); - tasks.add(ctx.callActivity("AppendHappy", "AnyOf1", taskOptions, String.class)); - tasks.add(ctx.callActivity("AppendHappy", "AnyOf2", String.class)); - tasks.add(ctx.callActivity("AppendHappy", 1, Integer.class)); - return ctx.anyOf(tasks).await().await(); - } - - @FunctionName("StartParallelCatchException") - public HttpResponseMessage startParallelCatchException( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("ParallelCatchException"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("ParallelCatchException") - public List parallelCatchException( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx, - ExecutionContext context) { - try { - List> tasks = new ArrayList<>(); - RetryPolicy policy = new RetryPolicy(2, Duration.ofSeconds(1)); - TaskOptions options = new TaskOptions(policy); - tasks.add(ctx.callActivity("AlwaysException", "Input1", options, String.class)); - tasks.add(ctx.callActivity("AppendHappy", "Input2", options, String.class)); - return ctx.allOf(tasks).await(); - } catch (CompositeTaskFailedException e) { - // only catch this type of exception to ensure the expected type of exception is thrown out. - for (Exception exception : e.getExceptions()) { - if (exception instanceof TaskFailedException) { - TaskFailedException taskFailedException = (TaskFailedException) exception; - context.getLogger().info("Task: " + taskFailedException.getTaskName() + - " Failed for cause: " + taskFailedException.getErrorDetails().getErrorMessage()); - } - } - } - return null; - } - - @FunctionName("AlwaysException") - public String alwaysException( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - context.getLogger().info("Throw Test AlwaysException: " + name); - throw new RuntimeException("Test AlwaysException"); - } -} \ No newline at end of file diff --git a/samples-azure-functions/src/main/java/com/functions/ResumeSuspend.java b/samples-azure-functions/src/main/java/com/functions/ResumeSuspend.java deleted file mode 100644 index a715d66e..00000000 --- a/samples-azure-functions/src/main/java/com/functions/ResumeSuspend.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.util.Optional; - -public class ResumeSuspend { - - @FunctionName("StartResumeSuspendOrchestration") - public HttpResponseMessage startResumeSuspendOrchestration( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("ResumeSuspend"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("ResumeSuspend") - public void resumeSuspend( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - ctx.waitForExternalEvent("test").await(); - return; - } -} \ No newline at end of file diff --git a/samples-azure-functions/src/main/java/com/functions/RetriableTask.java b/samples-azure-functions/src/main/java/com/functions/RetriableTask.java deleted file mode 100644 index 95377f3b..00000000 --- a/samples-azure-functions/src/main/java/com/functions/RetriableTask.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.RetryPolicy; -import com.microsoft.durabletask.TaskOptions; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.time.Duration; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -public class RetriableTask { - private static final AtomicBoolean throwException = new AtomicBoolean(true); - private static final AtomicInteger failedCounter = new AtomicInteger(0); - private static final AtomicInteger successCounter = new AtomicInteger(0); - @FunctionName("RetriableOrchestration") - public HttpResponseMessage retriableOrchestration( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("RetriableTask"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("RetriableTask") - public String retriableTask( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - RetryPolicy retryPolicy = new RetryPolicy(2, Duration.ofSeconds(1)); - TaskOptions taskOptions = new TaskOptions(retryPolicy); - return ctx.callActivity("Append", "Test-Input", taskOptions, String.class).await(); - } - - @FunctionName("Append") - public String append( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - if (throwException.get()) { - throwException.compareAndSet(true, false); - throw new RuntimeException("Test for retry"); - } - context.getLogger().info("Append: " + name); - return name + "-test"; - } - - @FunctionName("RetriableOrchestrationFail") - public HttpResponseMessage retriableOrchestrationFail( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("RetriableTaskFail"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("RetriableOrchestrationSuccess") - public HttpResponseMessage retriableOrchestrationSuccess( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("RetriableTaskSuccess"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("RetriableTaskFail") - public String retriableTaskFail( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - RetryPolicy retryPolicy = new RetryPolicy(2, Duration.ofSeconds(1)); - TaskOptions taskOptions = new TaskOptions(retryPolicy); - return ctx.callActivity("AppendFail", "Test-Input", taskOptions, String.class).await(); - } - - @FunctionName("RetriableTaskSuccess") - public String retriableTaskSuccess( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - RetryPolicy retryPolicy = new RetryPolicy(3, Duration.ofSeconds(1)); - TaskOptions taskOptions = new TaskOptions(retryPolicy); - return ctx.callActivity("AppendSuccess", "Test-Input", taskOptions, String.class).await(); - } - - @FunctionName("AppendFail") - public String appendFail( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - if (failedCounter.get() < 2) { - failedCounter.incrementAndGet(); - throw new RuntimeException("Test for retry"); - } - context.getLogger().info("Append: " + name); - return name + "-test"; - } - - @FunctionName("AppendSuccess") - public String appendSuccess( - @DurableActivityTrigger(name = "name") String name, - final ExecutionContext context) { - if (successCounter.get() < 2) { - successCounter.incrementAndGet(); - throw new RuntimeException("Test for retry"); - } - context.getLogger().info("Append: " + name); - return name + "-test"; - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/SubOrchestrator.java b/samples-azure-functions/src/main/java/com/functions/SubOrchestrator.java deleted file mode 100644 index f082acc7..00000000 --- a/samples-azure-functions/src/main/java/com/functions/SubOrchestrator.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.functions; - -import com.functions.model.Person; -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.util.Optional; - -public class SubOrchestrator { - - @FunctionName("SubOrchestratorHttp") - public HttpResponseMessage subOrchestratorHttp( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("RootOrchestrator"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("RootOrchestrator") - public String rootOrchestrator( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - return ctx.callSubOrchestrator("SubOrchestrator", "Austin", String.class).await(); - } - - @FunctionName("SubOrchestrator") - public String subOrchestrator( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - String input = ctx.getInput(String.class); - return ctx.callSubOrchestrator("Capitalize", input, String.class).await(); - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/ThenChain.java b/samples-azure-functions/src/main/java/com/functions/ThenChain.java deleted file mode 100644 index ca9b1b6b..00000000 --- a/samples-azure-functions/src/main/java/com/functions/ThenChain.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.util.Optional; - -public class ThenChain { - @FunctionName("StartOrchestrationThenChain") - public HttpResponseMessage startOrchestrationThenChain( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("ThenChain"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("ThenChain") - public String thenChain( - @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) { - String result = ""; - result += ctx.callActivity("Capitalize", "Austin", String.class).thenApply(s -> s + "-test").await(); - return result; - } - -} diff --git a/samples-azure-functions/src/main/java/com/functions/WaitExternalEvent.java b/samples-azure-functions/src/main/java/com/functions/WaitExternalEvent.java deleted file mode 100644 index 88fba1ce..00000000 --- a/samples-azure-functions/src/main/java/com/functions/WaitExternalEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.functions; - -import com.microsoft.azure.functions.ExecutionContext; -import com.microsoft.azure.functions.HttpMethod; -import com.microsoft.azure.functions.HttpRequestMessage; -import com.microsoft.azure.functions.HttpResponseMessage; -import com.microsoft.azure.functions.annotation.AuthorizationLevel; -import com.microsoft.azure.functions.annotation.FunctionName; -import com.microsoft.azure.functions.annotation.HttpTrigger; -import com.microsoft.durabletask.DurableTaskClient; -import com.microsoft.durabletask.Task; -import com.microsoft.durabletask.TaskOrchestrationContext; -import com.microsoft.durabletask.azurefunctions.DurableClientContext; -import com.microsoft.durabletask.azurefunctions.DurableClientInput; -import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger; - -import java.time.Duration; -import java.util.Optional; - -public class WaitExternalEvent { - @FunctionName("ExternalEventHttp") - public HttpResponseMessage externalEventHttp( - @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, - @DurableClientInput(name = "durableContext") DurableClientContext durableContext, - final ExecutionContext context) { - context.getLogger().info("Java HTTP trigger processed a request."); - - DurableTaskClient client = durableContext.getClient(); - String instanceId = client.scheduleNewOrchestrationInstance("ExternalEventOrchestrator"); - context.getLogger().info("Created new Java orchestration with instance ID = " + instanceId); - return durableContext.createCheckStatusResponse(request, instanceId); - } - - @FunctionName("ExternalEventOrchestrator") - public void externalEventOrchestrator(@DurableOrchestrationTrigger(name = "runtimeState") TaskOrchestrationContext ctx) - { - System.out.println("Waiting external event..."); - Task event = ctx.waitForExternalEvent("event", String.class); - Task timer = ctx.createTimer(Duration.ofSeconds(10)); - Task winner = ctx.anyOf(event, timer).await(); - if (winner == event) { - String eventResult = event.await(); - ctx.complete(eventResult); - } else { - ctx.complete("time out"); - } - } -} diff --git a/samples-azure-functions/src/main/java/com/functions/model/Person.java b/samples-azure-functions/src/main/java/com/functions/model/Person.java deleted file mode 100644 index 26a3d0b9..00000000 --- a/samples-azure-functions/src/main/java/com/functions/model/Person.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.functions.model; - -public class Person { - String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/samples-azure-functions/src/test/java/com/functions/EndToEndTests.java b/samples-azure-functions/src/test/java/com/functions/EndToEndTests.java deleted file mode 100644 index a756b5db..00000000 --- a/samples-azure-functions/src/test/java/com/functions/EndToEndTests.java +++ /dev/null @@ -1,253 +0,0 @@ -package com.functions; - -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import io.restassured.path.json.JsonPath; -import io.restassured.response.Response; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.time.Duration; -import java.time.Instant; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static io.restassured.RestAssured.get; -import static io.restassured.RestAssured.post; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Tag("sampleTest") -public class EndToEndTests { - - @Order(1) - @Test - public void setupHost() { - String hostHealthPingPath = "/admin/host/ping"; - post(hostHealthPingPath).then().statusCode(200); - } - - @ParameterizedTest - @ValueSource(strings = { - "StartOrchestration", - "StartParallelOrchestration", - "StartParallelAnyOf", - "StartParallelCatchException" - }) - public void generalFunctions(String functionName) throws InterruptedException { - Set continueStates = new HashSet<>(); - continueStates.add("Pending"); - continueStates.add("Running"); - String startOrchestrationPath = "/api/" + functionName; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean pass = pollingCheck(statusQueryGetUri, "Completed", continueStates, Duration.ofSeconds(20)); - assertTrue(pass); - } - - @Test - public void retryTest() throws InterruptedException { - String startOrchestrationPath = "/api/RetriableOrchestration"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean pass = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(10)); - assertTrue(pass); - } - - @Test - public void retryTestFail() throws InterruptedException { - String startOrchestrationPath = "/api/RetriableOrchestrationFail"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean pass = pollingCheck(statusQueryGetUri, "Failed", null, Duration.ofSeconds(10)); - assertTrue(pass); - } - - @Test - public void retryTestSuccess() throws InterruptedException { - String startOrchestrationPath = "/api/RetriableOrchestrationSuccess"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean pass = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(10)); - assertTrue(pass); - } - - @Test - public void continueAsNew() throws InterruptedException { - String startOrchestrationPath = "api/ContinueAsNew"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - String runTimeStatus; - //assert that the orchestration is always running. - for (int i = 0; i < 10; i++) { - Response statusResponse = get(statusQueryGetUri); - runTimeStatus = statusResponse.jsonPath().get("runtimeStatus"); - assertTrue(runTimeStatus.equals("Running") || runTimeStatus.equals("Pending")); - Thread.sleep(1000); - } - String terminatePostUri = jsonPath.get("terminatePostUri"); - post(terminatePostUri, "Terminated the test"); - Thread.sleep(5000); - Response statusResponse = get(statusQueryGetUri); - statusResponse.jsonPath().get("runtimeStatus"); - } - - @Test - public void continueAsNewExternalEvent() throws InterruptedException { - String startOrchestrationPath = "api/ContinueAsNewExternalEvent"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - - // send external event, it will cause the continue-as-new. - String sendEventPostUri = jsonPath.get("sendEventPostUri"); - sendEventPostUri = sendEventPostUri.replace("{eventName}", "event"); - - // empty request body - RestAssured - .given() - .contentType(ContentType.JSON) // Set the request content type - .body("{}") - .post(sendEventPostUri) - .then() - .statusCode(202); - - //wait 5 seconds for the continue-as-new to start new orchestration - TimeUnit.SECONDS.sleep(5); - - response = post(startOrchestrationPath); - jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - // polling 20 seconds - // assert that the orchestration completed as expected, not enter an infinite loop - boolean completed = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(20)); - assertTrue(completed); - } - - @ParameterizedTest - @ValueSource(booleans = {true, false}) - public void restart(boolean restartWithNewInstanceId) throws InterruptedException { - String startOrchestrationPath = "/api/StartOrchestration"; - Set continueStates = new HashSet<>(); - continueStates.add("Pending"); - continueStates.add("Running"); - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean completed = pollingCheck(statusQueryGetUri, "Completed", continueStates, Duration.ofSeconds(10)); - assertTrue(completed); - Response statusResponse = get(statusQueryGetUri); - String instanceId = statusResponse.jsonPath().get("instanceId"); - - String restartPostUri = jsonPath.get("restartPostUri") + "&restartWithNewInstanceId=" + restartWithNewInstanceId; - Response restartResponse = post(restartPostUri); - JsonPath restartJsonPath = restartResponse.jsonPath(); - String restartStatusQueryGetUri = restartJsonPath.get("statusQueryGetUri"); - completed = pollingCheck(restartStatusQueryGetUri, "Completed", continueStates, Duration.ofSeconds(10)); - assertTrue(completed); - Response restartStatusResponse = get(restartStatusQueryGetUri); - String newInstanceId = restartStatusResponse.jsonPath().get("instanceId"); - if (restartWithNewInstanceId) { - assertNotEquals(instanceId, newInstanceId); - } else { - assertEquals(instanceId, newInstanceId); - } - } - - @Test - public void thenChain() throws InterruptedException { - Set continueStates = new HashSet<>(); - continueStates.add("Pending"); - continueStates.add("Running"); - final String expect = "\"AUSTIN\"-test"; - String startOrchestrationPath = "/api/StartOrchestrationThenChain"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean completed = pollingCheck(statusQueryGetUri, "Completed", continueStates, Duration.ofSeconds(20)); - assertTrue(completed); - String output = get(statusQueryGetUri).jsonPath().get("output"); - assertEquals(expect, output); - } - - @Test - public void suspendResume() throws InterruptedException { - String startOrchestrationPath = "api/StartResumeSuspendOrchestration"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean running = pollingCheck(statusQueryGetUri, "Running", null, Duration.ofSeconds(5)); - assertTrue(running); - - String suspendPostUri = jsonPath.get("suspendPostUri"); - post(suspendPostUri, "SuspendOrchestration"); - boolean suspend = pollingCheck(statusQueryGetUri, "Suspended", null, Duration.ofSeconds(5)); - assertTrue(suspend); - - String sendEventPostUri = jsonPath.get("sendEventPostUri"); - sendEventPostUri = sendEventPostUri.replace("{eventName}", "test"); - - String requestBody = "{\"value\":\"Test\"}"; - RestAssured - .given() - .contentType(ContentType.JSON) // Set the request content type - .body(requestBody) // Set the request body - .post(sendEventPostUri) - .then() - .statusCode(202); - - boolean suspendAfterEventSent = pollingCheck(statusQueryGetUri, "Suspended", null, Duration.ofSeconds(5)); - assertTrue(suspendAfterEventSent); - - String resumePostUri = jsonPath.get("resumePostUri"); - post(resumePostUri, "ResumeOrchestration"); - - boolean completed = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(5)); - assertTrue(completed); - } - - @Test - public void orchestrationPOJO() throws InterruptedException { - Set continueStates = new HashSet<>(); - String startOrchestrationPath = "/api/StartOrchestrationPOJO"; - Response response = post(startOrchestrationPath); - JsonPath jsonPath = response.jsonPath(); - String statusQueryGetUri = jsonPath.get("statusQueryGetUri"); - boolean pass = pollingCheck(statusQueryGetUri, "Completed", null, Duration.ofSeconds(20)); - assertTrue(pass); - Response statusResponse = get(statusQueryGetUri); - String outputName = statusResponse.jsonPath().get("output.name"); - assertEquals("\"TESTNAME\"", outputName); - } - - private boolean pollingCheck(String statusQueryGetUri, - String expectedState, - Set continueStates, - Duration timeout) throws InterruptedException { - String runTimeStatus = null; - Instant begin = Instant.now(); - Instant runningTime = Instant.now(); - while (Duration.between(begin, runningTime).compareTo(timeout) <= 0) { - Response statusResponse = get(statusQueryGetUri); - runTimeStatus = statusResponse.jsonPath().get("runtimeStatus"); - if (expectedState.equals(runTimeStatus)) { - return true; - } - if (continueStates != null && !continueStates.contains(runTimeStatus)) { - return false; - } - Thread.sleep(1000); - runningTime = Instant.now(); - } - return false; - } -} \ No newline at end of file