Skip to content

Commit b1874ae

Browse files
committed
update from main and solve conflicts
2 parents 2029997 + b4075c0 commit b1874ae

119 files changed

Lines changed: 17968 additions & 54 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
## Unreleased
22

3+
## v1.9.0
4+
* Fix entity locking deserialization and add Jackson support for EntityInstanceId/EntityMetadata ([#281](https://github.com/microsoft/durabletask-java/pull/281))
5+
* Durable Entities ([#268](https://github.com/microsoft/durabletask-java/pull/268))
6+
* Add work item filtering support for `DurableTaskGrpcWorker` to enable worker-side filtering of orchestration and activity work items ([#275](https://github.com/microsoft/durabletask-java/pull/275))
7+
* Add support for calls to HTTP endpoints ([#271](https://github.com/microsoft/durabletask-java/pull/271))
8+
* Add getSuspendPostUri and getResumePostUri getters to HttpManagementPayload ([#264](https://github.com/microsoft/durabletask-java/pull/264))
9+
10+
## v1.8.0
311
* Adding rewind client API ([#253](https://github.com/microsoft/durabletask-java/pull/253)). Note: orchestration processing for rewind is supported with Azure Functions but not with the standalone `GrpcDurableTaskWorker`.
12+
* Add permissions to build-validation workflow ([#265](https://github.com/microsoft/durabletask-java/pull/265))
413
* Add distributed tracing (OpenTelemetry) support with W3C Trace Context propagation ([#266](https://github.com/microsoft/durabletask-java/pull/266))
14+
* Add GitHub Copilot agents for automated PR verification, daily code review, and issue triage ([#269](https://github.com/microsoft/durabletask-java/pull/269))
515

616
## v1.7.0
717
* Add descriptive error when orchestration type is not registered ([#261](https://github.com/microsoft/durabletask-java/pull/261))

azurefunctions/build.gradle

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,26 @@ plugins {
66
}
77

88
group 'com.microsoft'
9-
version = '1.7.0'
9+
version = '1.9.0'
1010
archivesBaseName = 'durabletask-azure-functions'
1111

1212
def protocVersion = '3.25.8'
13+
// Java 11 is used to compile and run all tests. Set the JDK_11 env var to your
14+
// local JDK 11 home directory, e.g. C:/Program Files/Java/openjdk-11.0.12_7/
15+
// If unset, falls back to the current JDK running Gradle.
16+
def rawJdkPath = System.env.JDK_11 ?: System.getProperty("java.home")
17+
// Handle case where JDK_11 points to an executable (e.g., .../bin/javac.exe)
18+
// instead of the JDK home directory — walk up to the JDK root.
19+
def PATH_TO_TEST_JAVA_RUNTIME = rawJdkPath
20+
if (rawJdkPath != null) {
21+
def f = new File(rawJdkPath)
22+
if (f.isFile()) {
23+
PATH_TO_TEST_JAVA_RUNTIME = f.parentFile.parentFile.absolutePath
24+
}
25+
}
26+
// Append .exe on Windows when invoking javac/java directly
27+
def isWindows = System.getProperty("os.name").toLowerCase().contains("win")
28+
def exeSuffix = isWindows ? ".exe" : ""
1329

1430
repositories {
1531
maven {
@@ -22,11 +38,30 @@ dependencies {
2238
implementation group: 'com.microsoft.azure.functions', name: 'azure-functions-java-library', version: '3.2.3'
2339
implementation "com.google.protobuf:protobuf-java:${protocVersion}"
2440
compileOnly "com.microsoft.azure.functions:azure-functions-java-spi:1.1.0"
41+
42+
// Test dependencies
43+
testImplementation 'org.mockito:mockito-core:5.21.0'
44+
testImplementation 'org.mockito:mockito-junit-jupiter:5.21.0'
45+
testImplementation platform('org.junit:junit-bom:5.14.2')
46+
testImplementation 'org.junit.jupiter:junit-jupiter'
47+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
2548
}
2649

2750
sourceCompatibility = JavaVersion.VERSION_1_8
2851
targetCompatibility = JavaVersion.VERSION_1_8
2952

53+
compileTestJava {
54+
sourceCompatibility = JavaVersion.VERSION_11
55+
targetCompatibility = JavaVersion.VERSION_11
56+
options.fork = true
57+
options.forkOptions.executable = "${PATH_TO_TEST_JAVA_RUNTIME}/bin/javac${exeSuffix}"
58+
}
59+
60+
test {
61+
useJUnitPlatform()
62+
executable = "${PATH_TO_TEST_JAVA_RUNTIME}/bin/java${exeSuffix}"
63+
}
64+
3065
publishing {
3166
repositories {
3267
maven {

azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/DurableClientContext.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
import com.microsoft.azure.functions.HttpStatus;
1010
import com.microsoft.durabletask.DurableTaskClient;
1111
import com.microsoft.durabletask.DurableTaskGrpcClientFactory;
12+
import com.microsoft.durabletask.DurableEntityClient;
13+
import com.microsoft.durabletask.EntityInstanceId;
14+
import com.microsoft.durabletask.EntityMetadata;
15+
import com.microsoft.durabletask.EntityQuery;
16+
import com.microsoft.durabletask.EntityQueryResult;
17+
import com.microsoft.durabletask.CleanEntityStorageRequest;
18+
import com.microsoft.durabletask.CleanEntityStorageResult;
1219
import com.microsoft.durabletask.OrchestrationMetadata;
1320
import com.microsoft.durabletask.OrchestrationRuntimeStatus;
1421

@@ -133,6 +140,79 @@ public HttpManagementPayload createHttpManagementPayload(HttpRequestMessage<?> r
133140
return this.getClientResponseLinks(request, instanceId);
134141
}
135142

143+
/**
144+
* Gets the entity client for interacting with durable entities.
145+
* <p>
146+
* This mirrors the .NET SDK's {@code DurableTaskClient.Entities} property.
147+
*
148+
* @return the {@link DurableEntityClient} for this client
149+
*/
150+
public DurableEntityClient getEntities() {
151+
return getClient().getEntities();
152+
}
153+
154+
/**
155+
* Sends a fire-and-forget signal to a durable entity.
156+
*
157+
* @param entityId the target entity's instance ID
158+
* @param operationName the name of the operation to invoke on the entity
159+
* @param input the input to pass to the operation (may be {@code null})
160+
*/
161+
public void signalEntity(EntityInstanceId entityId, String operationName, Object input) {
162+
getClient().getEntities().signalEntity(entityId, operationName, input);
163+
}
164+
165+
/**
166+
* Sends a fire-and-forget signal to a durable entity with no input.
167+
*
168+
* @param entityId the target entity's instance ID
169+
* @param operationName the name of the operation to invoke on the entity
170+
*/
171+
public void signalEntity(EntityInstanceId entityId, String operationName) {
172+
getClient().getEntities().signalEntity(entityId, operationName);
173+
}
174+
175+
/**
176+
* Gets the metadata for a durable entity, including optionally its serialized state.
177+
*
178+
* @param entityId the entity's instance ID
179+
* @param includeState whether to include the entity's serialized state in the result
180+
* @return the entity metadata, or {@code null} if the entity does not exist
181+
*/
182+
public EntityMetadata getEntityMetadata(EntityInstanceId entityId, boolean includeState) {
183+
return getClient().getEntities().getEntityMetadata(entityId, includeState);
184+
}
185+
186+
/**
187+
* Gets the metadata for a durable entity without including its serialized state.
188+
*
189+
* @param entityId the entity's instance ID
190+
* @return the entity metadata, or {@code null} if the entity does not exist
191+
*/
192+
public EntityMetadata getEntityMetadata(EntityInstanceId entityId) {
193+
return getClient().getEntities().getEntityMetadata(entityId);
194+
}
195+
196+
/**
197+
* Queries the durable store for entity instances matching the specified filter criteria.
198+
*
199+
* @param query the query filter criteria
200+
* @return the query result containing matching entities and an optional continuation token
201+
*/
202+
public EntityQueryResult queryEntities(EntityQuery query) {
203+
return getClient().getEntities().queryEntities(query);
204+
}
205+
206+
/**
207+
* Cleans up entity storage by removing empty entities and/or releasing orphaned locks.
208+
*
209+
* @param request the clean storage request specifying what to clean
210+
* @return the result of the clean operation, including counts of removed entities and released locks
211+
*/
212+
public CleanEntityStorageResult cleanEntityStorage(CleanEntityStorageRequest request) {
213+
return getClient().getEntities().cleanEntityStorage(request);
214+
}
215+
136216
private HttpManagementPayload getClientResponseLinks(HttpRequestMessage<?> request, String instanceId) {
137217
String instanceStatusURL = this.getInstanceStatusURL(request, instanceId);
138218
return new HttpManagementPayload(instanceId, instanceStatusURL, this.requiredQueryStringParameters);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for
4+
* license information.
5+
*/
6+
7+
package com.microsoft.durabletask.azurefunctions;
8+
9+
import com.microsoft.azure.functions.annotation.CustomBinding;
10+
import com.microsoft.azure.functions.annotation.HasImplicitOutput;
11+
12+
import java.lang.annotation.ElementType;
13+
import java.lang.annotation.Retention;
14+
import java.lang.annotation.RetentionPolicy;
15+
import java.lang.annotation.Target;
16+
17+
/**
18+
* <p>
19+
* Azure Functions attribute for binding a function parameter to a Durable Task entity request.
20+
* </p><p>
21+
* The following is an example of an entity function that uses this trigger binding to implement
22+
* a counter entity backed by a {@code TaskEntity} subclass.
23+
* </p>
24+
* <pre>
25+
* {@literal @}FunctionName("Counter")
26+
* public String counterEntity(
27+
* {@literal @}DurableEntityTrigger(name = "req") String req) {
28+
* return EntityRunner.loadAndRun(req, () -&gt; new CounterEntity());
29+
* }
30+
* </pre>
31+
*
32+
* @since 2.0.0
33+
*/
34+
@Retention(RetentionPolicy.RUNTIME)
35+
@Target(ElementType.PARAMETER)
36+
@CustomBinding(direction = "in", name = "", type = "entityTrigger")
37+
@HasImplicitOutput
38+
public @interface DurableEntityTrigger {
39+
/**
40+
* <p>The name of the entity function.</p>
41+
* <p>If not specified, the function name is used as the name of the entity.</p>
42+
* <p>This property supports binding parameters.</p>
43+
*
44+
* @return The name of the entity function.
45+
*/
46+
String entityName() default "";
47+
48+
/**
49+
* The variable name used in function.json.
50+
*
51+
* @return The variable name used in function.json.
52+
*/
53+
String name();
54+
55+
/**
56+
* <p>
57+
* Defines how Functions runtime should treat the parameter value. Possible values are:
58+
* </p>
59+
* <ul>
60+
* <li>"": get the value as a string, and try to deserialize to actual parameter type like POJO</li>
61+
* <li>string: always get the value as a string</li>
62+
* <li>binary: get the value as a binary data, and try to deserialize to actual parameter type byte[]</li>
63+
* </ul>
64+
*
65+
* @return The dataType which will be used by the Functions runtime.
66+
*/
67+
String dataType() default "string";
68+
}

azurefunctions/src/main/java/com/microsoft/durabletask/azurefunctions/HttpManagementPayload.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,25 @@ public String getPurgeHistoryDeleteUri() {
9393
* @return The HTTP URL for posting instance restart commands.
9494
*/
9595
public String getRestartPostUri() {
96-
return restartPostUri;
96+
return this.restartPostUri;
97+
}
98+
99+
/**
100+
* Gets the HTTP POST instance suspend endpoint.
101+
*
102+
* @return The HTTP URL for posting instance suspend commands.
103+
*/
104+
public String getSuspendPostUri() {
105+
return this.suspendPostUri;
106+
}
107+
108+
/**
109+
* Gets the HTTP POST instance resume endpoint.
110+
*
111+
* @return The HTTP URL for posting instance resume commands.
112+
*/
113+
public String getResumePostUri() {
114+
return this.resumePostUri;
97115
}
98116

99117
/**
@@ -102,7 +120,7 @@ public String getRestartPostUri() {
102120
* @return The HTTP URL for posting instance rewind commands.
103121
*/
104122
public String getRewindPostUri() {
105-
return rewindPostUri;
123+
return this.rewindPostUri;
106124
}
107125

108126
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for
4+
* license information.
5+
*/
6+
7+
package com.microsoft.durabletask.azurefunctions.internal.middleware;
8+
9+
import com.microsoft.azure.functions.internal.spi.middleware.Middleware;
10+
import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareChain;
11+
import com.microsoft.azure.functions.internal.spi.middleware.MiddlewareContext;
12+
import com.microsoft.durabletask.DataConverter;
13+
14+
/**
15+
* Durable Function Entity Middleware
16+
*
17+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
18+
* at any time.
19+
*/
20+
public class EntityMiddleware implements Middleware {
21+
22+
private static final String ENTITY_TRIGGER = "DurableEntityTrigger";
23+
24+
@Override
25+
public void invoke(MiddlewareContext context, MiddlewareChain chain) throws Exception {
26+
String parameterName = context.getParameterName(ENTITY_TRIGGER);
27+
if (parameterName == null) {
28+
chain.doNext(context);
29+
return;
30+
}
31+
32+
// The entity function receives the raw base64-encoded EntityBatchRequest as a String.
33+
// The user function is expected to call EntityRunner.loadAndRun() with a TaskEntityFactory
34+
// and return the base64-encoded EntityBatchResult.
35+
//
36+
// Unlike orchestrations, entity operations are simple request/response calls with no
37+
// replay-based blocking (no OrchestratorBlockedException equivalent), so the middleware
38+
// delegates directly to the user function.
39+
try {
40+
chain.doNext(context);
41+
} catch (Exception e) {
42+
Throwable cause = e.getCause();
43+
if (cause instanceof DataConverter.DataConverterException) {
44+
throw (DataConverter.DataConverterException) cause;
45+
}
46+
throw new RuntimeException("Unexpected failure in entity function execution", e);
47+
}
48+
}
49+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
com.microsoft.durabletask.azurefunctions.internal.middleware.OrchestrationMiddleware
1+
com.microsoft.durabletask.azurefunctions.internal.middleware.OrchestrationMiddleware
2+
com.microsoft.durabletask.azurefunctions.internal.middleware.EntityMiddleware

0 commit comments

Comments
 (0)