diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/BasicWorkflowContext.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/BasicWorkflowContext.java index 0bffd6e1b..7a0d2dc05 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/BasicWorkflowContext.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/BasicWorkflowContext.java @@ -7,6 +7,7 @@ import io.temporal.api.common.v1.*; import io.temporal.api.failure.v1.Failure; import io.temporal.api.history.v1.WorkflowExecutionStartedEventAttributes; +import io.temporal.api.sdk.v1.UserMetadata; import io.temporal.common.RetryOptions; import io.temporal.internal.common.ProtobufTimeUtils; import java.time.Duration; @@ -24,6 +25,7 @@ final class BasicWorkflowContext { private final WorkflowExecutionStartedEventAttributes startedAttributes; private final String namespace; @Nonnull private final WorkflowExecution workflowExecution; + @Nonnull private final UserMetadata userMetadata; @Nullable private final Payloads lastCompletionResult; @@ -33,11 +35,13 @@ final class BasicWorkflowContext { String namespace, @Nonnull WorkflowExecution workflowExecution, WorkflowExecutionStartedEventAttributes startedAttributes, - long runStartedTimestampMillis) { + long runStartedTimestampMillis, + @Nonnull UserMetadata userMetadata) { this.namespace = namespace; this.workflowExecution = Preconditions.checkNotNull(workflowExecution); this.startedAttributes = startedAttributes; this.runStartedTimestampMillis = runStartedTimestampMillis; + this.userMetadata = userMetadata; this.lastCompletionResult = startedAttributes.hasLastCompletionResult() ? startedAttributes.getLastCompletionResult() @@ -144,4 +148,9 @@ public RetryOptions getRetryOptions() { public Priority getPriority() { return startedAttributes.getPriority(); } + + @Nonnull + public UserMetadata getUserMetadata() { + return userMetadata; + } } diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContext.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContext.java index 982737dbe..6d8f4ec5c 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContext.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContext.java @@ -127,6 +127,12 @@ public Functions.Proc1 getCancellationHandle() { Payload getMemo(String key); + @Nullable + Payload getStaticSummaryPayload(); + + @Nullable + Payload getStaticDetailsPayload(); + /** * Requests an activity execution. * diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContextImpl.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContextImpl.java index cf704544b..802fbf9b8 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContextImpl.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowContextImpl.java @@ -51,11 +51,16 @@ final class ReplayWorkflowContextImpl implements ReplayWorkflowContext { long runStartedTimestampMillis, @Nullable String fullReplayDirectQueryName, SingleWorkerOptions workerOptions, - Scope workflowMetricsScope) { + Scope workflowMetricsScope, + @Nonnull UserMetadata userMetadata) { this.workflowStateMachines = workflowStateMachines; this.basicWorkflowContext = new BasicWorkflowContext( - namespace, workflowExecution, startedAttributes, runStartedTimestampMillis); + namespace, + workflowExecution, + startedAttributes, + runStartedTimestampMillis, + userMetadata); this.mutableState = new WorkflowMutableState(startedAttributes); this.fullReplayDirectQueryName = fullReplayDirectQueryName; this.replayAwareWorkflowMetricsScope = @@ -278,6 +283,20 @@ public Priority getPriority() { return basicWorkflowContext.getPriority(); } + @Override + @Nullable + public Payload getStaticSummaryPayload() { + UserMetadata userMetadata = basicWorkflowContext.getUserMetadata(); + return userMetadata.hasSummary() ? userMetadata.getSummary() : null; + } + + @Override + @Nullable + public Payload getStaticDetailsPayload() { + UserMetadata userMetadata = basicWorkflowContext.getUserMetadata(); + return userMetadata.hasDetails() ? userMetadata.getDetails() : null; + } + @Override public Functions.Proc1 newTimer( Duration delay, UserMetadata metadata, Functions.Proc1 callback) { diff --git a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java index 1d0d09a69..4d1aad9f1 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java @@ -19,6 +19,7 @@ import io.temporal.api.protocol.v1.Message; import io.temporal.api.query.v1.WorkflowQuery; import io.temporal.api.query.v1.WorkflowQueryResult; +import io.temporal.api.sdk.v1.UserMetadata; import io.temporal.api.workflowservice.v1.GetSystemInfoResponse; import io.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponseOrBuilder; import io.temporal.internal.Config; @@ -89,6 +90,7 @@ class ReplayWorkflowRunTaskHandler implements WorkflowRunTaskHandler { "First event in the history is not WorkflowExecutionStarted"); } this.startedEvent = startedEvent.getWorkflowExecutionStartedEventAttributes(); + UserMetadata userMetadata = startedEvent.getUserMetadata(); this.metricsScope = metricsScope; this.localActivityDispatcher = localActivityDispatcher; this.workflow = workflow; @@ -111,7 +113,8 @@ class ReplayWorkflowRunTaskHandler implements WorkflowRunTaskHandler { Timestamps.toMillis(startedEvent.getEventTime()), fullReplayDirectQueryType, workerOptions, - metricsScope); + metricsScope, + userMetadata); this.replayWorkflowExecutor = new ReplayWorkflowExecutor(workflow, workflowStateMachines, context); diff --git a/temporal-sdk/src/main/java/io/temporal/internal/sync/WorkflowInternal.java b/temporal-sdk/src/main/java/io/temporal/internal/sync/WorkflowInternal.java index 79495694b..47a3d680a 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/sync/WorkflowInternal.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/sync/WorkflowInternal.java @@ -855,6 +855,26 @@ public static String getCurrentDetails() { return getRootWorkflowContext().getCurrentDetails(); } + @Nullable + public static String getStaticSummary() { + Payload payload = getRootWorkflowContext().getReplayContext().getStaticSummaryPayload(); + if (payload == null) { + return null; + } + return getDataConverterWithCurrentWorkflowContext() + .fromPayload(payload, String.class, String.class); + } + + @Nullable + public static String getStaticDetails() { + Payload payload = getRootWorkflowContext().getReplayContext().getStaticDetailsPayload(); + if (payload == null) { + return null; + } + return getDataConverterWithCurrentWorkflowContext() + .fromPayload(payload, String.class, String.class); + } + @Nullable public static Object getInstance() { return getRootWorkflowContext().getInstance(); diff --git a/temporal-sdk/src/main/java/io/temporal/workflow/Workflow.java b/temporal-sdk/src/main/java/io/temporal/workflow/Workflow.java index 61c787757..ac5ada19d 100644 --- a/temporal-sdk/src/main/java/io/temporal/workflow/Workflow.java +++ b/temporal-sdk/src/main/java/io/temporal/workflow/Workflow.java @@ -1554,6 +1554,31 @@ public static String getCurrentDetails() { return WorkflowInternal.getCurrentDetails(); } + /** + * Get the fixed summary for this workflow execution that was set at workflow start. This can be + * in single-line Temporal markdown format. + * + * @return the static summary, or null if not set + */ + @Experimental + @Nullable + public static String getStaticSummary() { + return WorkflowInternal.getStaticSummary(); + } + + /** + * Get the fixed details for this workflow execution that were set at workflow start. This can be + * in Temporal markdown format and can span multiple lines. Unlike {@link #getCurrentDetails()}, + * this value cannot be updated during workflow execution. + * + * @return the static details, or null if not set + */ + @Experimental + @Nullable + public static String getStaticDetails() { + return WorkflowInternal.getStaticDetails(); + } + /** * Get the currently running workflow instance. * diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowStaticSummaryTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowStaticSummaryTest.java new file mode 100644 index 000000000..2373a4884 --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowStaticSummaryTest.java @@ -0,0 +1,62 @@ +package io.temporal.workflow; + +import static io.temporal.testing.internal.SDKTestOptions.newWorkflowOptionsWithTimeouts; +import static org.junit.Assert.assertEquals; + +import io.temporal.client.WorkflowOptions; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import org.junit.Rule; +import org.junit.Test; + +public class WorkflowStaticSummaryTest { + + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(WorkflowReadingStaticMetadata.class) + .build(); + + static final String SUMMARY = "my-static-summary"; + static final String DETAILS = "my-static-details"; + + @Test + public void testGetStaticSummaryAndDetails() { + WorkflowOptions options = + newWorkflowOptionsWithTimeouts(testWorkflowRule.getTaskQueue()).toBuilder() + .setStaticSummary(SUMMARY) + .setStaticDetails(DETAILS) + .build(); + TestStaticMetadataWorkflow stub = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub(TestStaticMetadataWorkflow.class, options); + + String result = stub.execute(); + assertEquals(SUMMARY + "|" + DETAILS, result); + } + + @Test + public void testGetStaticSummaryAndDetailsWhenNotSet() { + WorkflowOptions options = newWorkflowOptionsWithTimeouts(testWorkflowRule.getTaskQueue()); + TestStaticMetadataWorkflow stub = + testWorkflowRule + .getWorkflowClient() + .newWorkflowStub(TestStaticMetadataWorkflow.class, options); + + String result = stub.execute(); + assertEquals("null|null", result); + } + + @WorkflowInterface + public interface TestStaticMetadataWorkflow { + @WorkflowMethod + String execute(); + } + + public static class WorkflowReadingStaticMetadata implements TestStaticMetadataWorkflow { + @Override + public String execute() { + return Workflow.getStaticSummary() + "|" + Workflow.getStaticDetails(); + } + } +} diff --git a/temporal-testing/src/main/java/io/temporal/internal/sync/DummySyncWorkflowContext.java b/temporal-testing/src/main/java/io/temporal/internal/sync/DummySyncWorkflowContext.java index f89e61c64..c607de3bc 100644 --- a/temporal-testing/src/main/java/io/temporal/internal/sync/DummySyncWorkflowContext.java +++ b/temporal-testing/src/main/java/io/temporal/internal/sync/DummySyncWorkflowContext.java @@ -165,6 +165,18 @@ public Payload getMemo(String key) { throw new UnsupportedOperationException("not implemented"); } + @Override + @Nullable + public Payload getStaticSummaryPayload() { + return null; + } + + @Override + @Nullable + public Payload getStaticDetailsPayload() { + return null; + } + @Override @Nullable public SearchAttributes getSearchAttributes() {