diff --git a/temporal-sdk/src/main/java/io/temporal/activity/ActivityOptions.java b/temporal-sdk/src/main/java/io/temporal/activity/ActivityOptions.java
index fb498e28b..0f582a479 100644
--- a/temporal-sdk/src/main/java/io/temporal/activity/ActivityOptions.java
+++ b/temporal-sdk/src/main/java/io/temporal/activity/ActivityOptions.java
@@ -22,6 +22,7 @@
import com.google.common.base.Objects;
import io.temporal.client.WorkflowClientOptions;
+import io.temporal.common.Experimental;
import io.temporal.common.MethodRetry;
import io.temporal.common.RetryOptions;
import io.temporal.common.VersioningIntent;
@@ -64,6 +65,7 @@ public static final class Builder {
private ActivityCancellationType cancellationType;
private boolean disableEagerExecution;
private VersioningIntent versioningIntent;
+ private String summary;
private Builder() {}
@@ -81,6 +83,7 @@ private Builder(ActivityOptions options) {
this.cancellationType = options.cancellationType;
this.disableEagerExecution = options.disableEagerExecution;
this.versioningIntent = options.versioningIntent;
+ this.summary = options.summary;
}
/**
@@ -243,6 +246,18 @@ public Builder setVersioningIntent(VersioningIntent versioningIntent) {
return this;
}
+ /**
+ * Single-line fixed summary for this activity that will appear in UI/CLI. This can be in
+ * single-line Temporal Markdown format.
+ *
+ *
Default is none/empty.
+ */
+ @Experimental
+ public Builder setSummary(String summary) {
+ this.summary = summary;
+ return this;
+ }
+
public Builder mergeActivityOptions(ActivityOptions override) {
if (override == null) {
return this;
@@ -274,6 +289,7 @@ public Builder mergeActivityOptions(ActivityOptions override) {
if (override.versioningIntent != VersioningIntent.VERSIONING_INTENT_UNSPECIFIED) {
this.versioningIntent = override.versioningIntent;
}
+ this.summary = (override.summary == null) ? this.summary : override.summary;
return this;
}
@@ -296,7 +312,8 @@ public ActivityOptions build() {
contextPropagators,
cancellationType,
disableEagerExecution,
- versioningIntent);
+ versioningIntent,
+ summary);
}
public ActivityOptions validateAndBuildWithDefaults() {
@@ -312,7 +329,8 @@ public ActivityOptions validateAndBuildWithDefaults() {
disableEagerExecution,
versioningIntent == null
? VersioningIntent.VERSIONING_INTENT_UNSPECIFIED
- : versioningIntent);
+ : versioningIntent,
+ summary);
}
}
@@ -326,6 +344,7 @@ public ActivityOptions validateAndBuildWithDefaults() {
private final ActivityCancellationType cancellationType;
private final boolean disableEagerExecution;
private final VersioningIntent versioningIntent;
+ private final String summary;
private ActivityOptions(
Duration heartbeatTimeout,
@@ -337,7 +356,8 @@ private ActivityOptions(
List contextPropagators,
ActivityCancellationType cancellationType,
boolean disableEagerExecution,
- VersioningIntent versioningIntent) {
+ VersioningIntent versioningIntent,
+ String summary) {
this.heartbeatTimeout = heartbeatTimeout;
this.scheduleToStartTimeout = scheduleToStartTimeout;
this.scheduleToCloseTimeout = scheduleToCloseTimeout;
@@ -348,6 +368,7 @@ private ActivityOptions(
this.cancellationType = cancellationType;
this.disableEagerExecution = disableEagerExecution;
this.versioningIntent = versioningIntent;
+ this.summary = summary;
}
/**
@@ -417,6 +438,11 @@ public VersioningIntent getVersioningIntent() {
return versioningIntent;
}
+ @Experimental
+ public String getSummary() {
+ return summary;
+ }
+
public Builder toBuilder() {
return new Builder(this);
}
@@ -435,7 +461,8 @@ public boolean equals(Object o) {
&& Objects.equal(retryOptions, that.retryOptions)
&& Objects.equal(contextPropagators, that.contextPropagators)
&& disableEagerExecution == that.disableEagerExecution
- && versioningIntent == that.versioningIntent;
+ && versioningIntent == that.versioningIntent
+ && Objects.equal(summary, that.summary);
}
@Override
@@ -450,7 +477,8 @@ public int hashCode() {
contextPropagators,
cancellationType,
disableEagerExecution,
- versioningIntent);
+ versioningIntent,
+ summary);
}
@Override
@@ -477,6 +505,8 @@ public String toString() {
+ disableEagerExecution
+ ", versioningIntent="
+ versioningIntent
+ + ", summary="
+ + summary
+ '}';
}
}
diff --git a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ActivityStateMachine.java b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ActivityStateMachine.java
index 7ca5b49c7..8a6d695ec 100644
--- a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ActivityStateMachine.java
+++ b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ActivityStateMachine.java
@@ -35,6 +35,7 @@
import io.temporal.api.history.v1.ActivityTaskCompletedEventAttributes;
import io.temporal.api.history.v1.ActivityTaskFailedEventAttributes;
import io.temporal.api.history.v1.ActivityTaskTimedOutEventAttributes;
+import io.temporal.api.sdk.v1.UserMetadata;
import io.temporal.workflow.Functions;
import java.util.Optional;
import javax.annotation.Nonnull;
@@ -54,6 +55,7 @@ final class ActivityStateMachine
private final String activityId;
private final ActivityType activityType;
private final ActivityCancellationType cancellationType;
+ private UserMetadata userMetadata;
private final Functions.Proc2, FailureResult> completionCallback;
@@ -265,16 +267,21 @@ private ActivityStateMachine(
this.activityId = scheduleAttr.getActivityId();
this.activityType = scheduleAttr.getActivityType();
this.cancellationType = parameters.getCancellationType();
+ this.userMetadata = parameters.getMetadata();
this.completionCallback = completionCallback;
explicitEvent(ExplicitEvent.SCHEDULE);
}
public void createScheduleActivityTaskCommand() {
- addCommand(
+ Command.Builder command =
Command.newBuilder()
.setCommandType(CommandType.COMMAND_TYPE_SCHEDULE_ACTIVITY_TASK)
- .setScheduleActivityTaskCommandAttributes(parameters.getAttributes())
- .build());
+ .setScheduleActivityTaskCommandAttributes(parameters.getAttributes());
+ if (userMetadata != null) {
+ command.setUserMetadata(userMetadata);
+ userMetadata = null;
+ }
+ addCommand(command.build());
parameters = null; // avoiding retaining large input for the duration of the activity
}
diff --git a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ExecuteActivityParameters.java b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ExecuteActivityParameters.java
index b9ec661e9..3776f0ffb 100644
--- a/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ExecuteActivityParameters.java
+++ b/temporal-sdk/src/main/java/io/temporal/internal/statemachines/ExecuteActivityParameters.java
@@ -22,18 +22,22 @@
import io.temporal.activity.ActivityCancellationType;
import io.temporal.api.command.v1.ScheduleActivityTaskCommandAttributes;
+import io.temporal.api.sdk.v1.UserMetadata;
import java.util.Objects;
public class ExecuteActivityParameters {
private final ScheduleActivityTaskCommandAttributes.Builder attributes;
private final ActivityCancellationType cancellationType;
+ private final UserMetadata metadata;
public ExecuteActivityParameters(
ScheduleActivityTaskCommandAttributes.Builder attributes,
- ActivityCancellationType cancellationType) {
+ ActivityCancellationType cancellationType,
+ UserMetadata metadata) {
this.attributes = Objects.requireNonNull(attributes);
this.cancellationType = Objects.requireNonNull(cancellationType);
+ this.metadata = metadata;
}
public ScheduleActivityTaskCommandAttributes.Builder getAttributes() {
@@ -43,4 +47,8 @@ public ScheduleActivityTaskCommandAttributes.Builder getAttributes() {
public ActivityCancellationType getCancellationType() {
return cancellationType;
}
+
+ public UserMetadata getMetadata() {
+ return metadata;
+ }
}
diff --git a/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflowContext.java b/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflowContext.java
index dbc77b144..1fcbf59d0 100644
--- a/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflowContext.java
+++ b/temporal-sdk/src/main/java/io/temporal/internal/sync/SyncWorkflowContext.java
@@ -625,7 +625,11 @@ private ExecuteActivityParameters constructExecuteActivityParameters(
replayContext.getTaskQueue().equals(options.getTaskQueue())));
}
- return new ExecuteActivityParameters(attributes, options.getCancellationType());
+ @Nullable
+ UserMetadata userMetadata =
+ makeUserMetaData(options.getSummary(), null, dataConverterWithCurrentWorkflowContext);
+
+ return new ExecuteActivityParameters(attributes, options.getCancellationType(), userMetadata);
}
private ExecuteLocalActivityParameters constructExecuteLocalActivityParameters(
diff --git a/temporal-sdk/src/test/java/io/temporal/internal/statemachines/ActivityStateMachineTest.java b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/ActivityStateMachineTest.java
index b360ff691..d2b6ed46e 100644
--- a/temporal-sdk/src/test/java/io/temporal/internal/statemachines/ActivityStateMachineTest.java
+++ b/temporal-sdk/src/test/java/io/temporal/internal/statemachines/ActivityStateMachineTest.java
@@ -93,7 +93,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> stateMachines.scheduleActivityTask(parameters, c))
@@ -173,7 +173,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> stateMachines.scheduleActivityTask(parameters, c))
@@ -254,7 +254,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> stateMachines.scheduleActivityTask(parameters, c))
@@ -340,7 +340,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -388,7 +388,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -490,7 +490,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -574,7 +574,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -680,7 +680,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -798,7 +798,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -918,7 +918,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -1015,7 +1015,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -1135,7 +1135,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -1244,7 +1244,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
@@ -1352,7 +1352,7 @@ public void buildWorkflow(AsyncWorkflowBuilder builder) {
ScheduleActivityTaskCommandAttributes.newBuilder().setActivityId("id1");
ExecuteActivityParameters parameters =
new ExecuteActivityParameters(
- attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED);
+ attributes, ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, null);
builder
., Failure>add2(
(v, c) -> cancellationHandler = stateMachines.scheduleActivityTask(parameters, c))
diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityMetadataTest.java b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityMetadataTest.java
new file mode 100644
index 000000000..309d012b1
--- /dev/null
+++ b/temporal-sdk/src/test/java/io/temporal/workflow/activityTests/ActivityMetadataTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
+ *
+ * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Modifications copyright (C) 2017 Uber Technologies, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this material except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.temporal.workflow.activityTests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import io.temporal.activity.ActivityOptions;
+import io.temporal.api.common.v1.WorkflowExecution;
+import io.temporal.api.history.v1.HistoryEvent;
+import io.temporal.client.WorkflowStub;
+import io.temporal.common.WorkflowExecutionHistory;
+import io.temporal.common.converter.DefaultDataConverter;
+import io.temporal.testing.internal.SDKTestWorkflowRule;
+import io.temporal.workflow.Workflow;
+import io.temporal.workflow.shared.TestActivities;
+import io.temporal.workflow.shared.TestWorkflows.TestWorkflow1;
+import java.time.Duration;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class ActivityMetadataTest {
+
+ @Rule
+ public SDKTestWorkflowRule testWorkflowRule =
+ SDKTestWorkflowRule.newBuilder()
+ .setWorkflowTypes(TestWorkflowImpl.class)
+ .setActivityImplementations(new TestActivities.TestActivitiesImpl())
+ .build();
+
+ static final String activitySummary = "activity-summary";
+
+ @Before
+ public void checkRealServer() {
+ assumeTrue("skipping for test server", SDKTestWorkflowRule.useExternalService);
+ }
+
+ @Test
+ public void testActivityWithMetaData() {
+ TestWorkflow1 stub = testWorkflowRule.newWorkflowStubTimeoutOptions(TestWorkflow1.class);
+ stub.execute(testWorkflowRule.getTaskQueue());
+
+ WorkflowExecution exec = WorkflowStub.fromTyped(stub).getExecution();
+ WorkflowExecutionHistory workflowExecutionHistory =
+ testWorkflowRule.getWorkflowClient().fetchHistory(exec.getWorkflowId());
+ List activityScheduledEvents =
+ workflowExecutionHistory.getEvents().stream()
+ .filter(HistoryEvent::hasActivityTaskScheduledEventAttributes)
+ .collect(Collectors.toList());
+ assertEventMetadata(activityScheduledEvents.get(0), activitySummary, null);
+ }
+
+ private void assertEventMetadata(HistoryEvent event, String summary, String details) {
+ if (summary != null) {
+ String describedSummary =
+ DefaultDataConverter.STANDARD_INSTANCE.fromPayload(
+ event.getUserMetadata().getSummary(), String.class, String.class);
+ assertEquals(summary, describedSummary);
+ }
+ if (details != null) {
+ String describedDetails =
+ DefaultDataConverter.STANDARD_INSTANCE.fromPayload(
+ event.getUserMetadata().getDetails(), String.class, String.class);
+ assertEquals(details, describedDetails);
+ }
+ }
+
+ public static class TestWorkflowImpl implements TestWorkflow1 {
+
+ private final TestActivities.VariousTestActivities activities =
+ Workflow.newActivityStub(
+ TestActivities.VariousTestActivities.class,
+ ActivityOptions.newBuilder()
+ .setSummary(activitySummary)
+ .setStartToCloseTimeout(Duration.ofSeconds(5))
+ .build());
+
+ @Override
+ public String execute(String taskQueue) {
+ return activities.activity();
+ }
+ }
+}