diff --git a/temporal-sdk/src/main/java/io/temporal/client/WorkflowExecutionDescription.java b/temporal-sdk/src/main/java/io/temporal/client/WorkflowExecutionDescription.java new file mode 100644 index 000000000..4aa1f146f --- /dev/null +++ b/temporal-sdk/src/main/java/io/temporal/client/WorkflowExecutionDescription.java @@ -0,0 +1,41 @@ +/* + * 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.client; + +import io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse; +import io.temporal.common.converter.DataConverter; +import javax.annotation.Nonnull; + +/** Contains information about a workflow execution. */ +public class WorkflowExecutionDescription extends WorkflowExecutionMetadata { + private final @Nonnull DescribeWorkflowExecutionResponse response; + + public WorkflowExecutionDescription( + @Nonnull DescribeWorkflowExecutionResponse response, @Nonnull DataConverter dataConverter) { + super(response.getWorkflowExecutionInfo(), dataConverter); + this.response = response; + } + + /** Returns the raw response from the Temporal service. */ + public DescribeWorkflowExecutionResponse getRawDescription() { + return response; + } +} diff --git a/temporal-sdk/src/main/java/io/temporal/client/WorkflowStub.java b/temporal-sdk/src/main/java/io/temporal/client/WorkflowStub.java index b99da3e45..896c5b9d4 100644 --- a/temporal-sdk/src/main/java/io/temporal/client/WorkflowStub.java +++ b/temporal-sdk/src/main/java/io/temporal/client/WorkflowStub.java @@ -382,6 +382,16 @@ CompletableFuture getResultAsync( */ void terminate(@Nullable String reason, Object... details); + /** + * Get the current description of this workflow. + * + * @throws WorkflowNotFoundException if the workflow execution doesn't exist + * @throws WorkflowServiceException for all other failures including networking and service + * availability issues + * @return the current description of this workflow + */ + WorkflowExecutionDescription describe(); + Optional getOptions(); /** diff --git a/temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java b/temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java index 87344331e..58519e0ec 100644 --- a/temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java +++ b/temporal-sdk/src/main/java/io/temporal/client/WorkflowStubImpl.java @@ -446,6 +446,21 @@ public void terminate(@Nullable String reason, Object... details) { } } + @Override + public WorkflowExecutionDescription describe() { + checkStarted(); + WorkflowExecution targetExecution = execution.get(); + try { + WorkflowClientCallsInterceptor.DescribeWorkflowOutput result = + workflowClientInvoker.describe( + new WorkflowClientCallsInterceptor.DescribeWorkflowInput(targetExecution)); + return result.getDescription(); + } catch (Exception e) { + Throwable failure = throwAsWorkflowFailureException(e, targetExecution); + throw new WorkflowServiceException(targetExecution, workflowType.orElse(null), failure); + } + } + @Override public Optional getOptions() { return Optional.ofNullable(options); diff --git a/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptor.java b/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptor.java index ce3ef0216..1360d63c5 100644 --- a/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptor.java +++ b/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptor.java @@ -94,6 +94,8 @@ public interface WorkflowClientCallsInterceptor { TerminateOutput terminate(TerminateInput input); + DescribeWorkflowOutput describe(DescribeWorkflowInput input); + final class WorkflowStartInput { private final String workflowId; private final String workflowType; @@ -601,4 +603,28 @@ public Object[] getDetails() { } final class TerminateOutput {} + + final class DescribeWorkflowInput { + private final WorkflowExecution workflowExecution; + + public DescribeWorkflowInput(WorkflowExecution workflowExecution) { + this.workflowExecution = workflowExecution; + } + + public WorkflowExecution getWorkflowExecution() { + return workflowExecution; + } + } + + final class DescribeWorkflowOutput { + private final WorkflowExecutionDescription description; + + public DescribeWorkflowOutput(WorkflowExecutionDescription description) { + this.description = description; + } + + public WorkflowExecutionDescription getDescription() { + return description; + } + } } diff --git a/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptorBase.java b/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptorBase.java index 70cd948e3..78cc2e7d7 100644 --- a/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptorBase.java +++ b/temporal-sdk/src/main/java/io/temporal/common/interceptors/WorkflowClientCallsInterceptorBase.java @@ -87,4 +87,9 @@ public CancelOutput cancel(CancelInput input) { public TerminateOutput terminate(TerminateInput input) { return next.terminate(input); } + + @Override + public DescribeWorkflowOutput describe(DescribeWorkflowInput input) { + return next.describe(input); + } } diff --git a/temporal-sdk/src/main/java/io/temporal/internal/client/RootWorkflowClientInvoker.java b/temporal-sdk/src/main/java/io/temporal/internal/client/RootWorkflowClientInvoker.java index 517fb5466..867799b1e 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/client/RootWorkflowClientInvoker.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/client/RootWorkflowClientInvoker.java @@ -693,6 +693,26 @@ public TerminateOutput terminate(TerminateInput input) { return new TerminateOutput(); } + @Override + public DescribeWorkflowOutput describe(DescribeWorkflowInput input) { + DescribeWorkflowExecutionResponse response = + genericClient.describeWorkflowExecution( + DescribeWorkflowExecutionRequest.newBuilder() + .setNamespace(clientOptions.getNamespace()) + .setExecution(input.getWorkflowExecution()) + .build()); + + DataConverter dataConverterWithWorkflowContext = + clientOptions + .getDataConverter() + .withContext( + new WorkflowSerializationContext( + clientOptions.getNamespace(), input.getWorkflowExecution().getWorkflowId())); + + return new DescribeWorkflowOutput( + new WorkflowExecutionDescription(response, dataConverterWithWorkflowContext)); + } + private static R convertResultPayloads( Optional resultValue, Class resultClass, diff --git a/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClient.java b/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClient.java index 50d8fc510..9f5781d89 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClient.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClient.java @@ -78,6 +78,9 @@ CompletableFuture listWorkflowExecutionsAsync( DescribeScheduleResponse describeSchedule(DescribeScheduleRequest request); + DescribeWorkflowExecutionResponse describeWorkflowExecution( + DescribeWorkflowExecutionRequest request); + @Experimental UpdateWorkerBuildIdCompatibilityResponse updateWorkerBuildIdCompatability( UpdateWorkerBuildIdCompatibilityRequest request); diff --git a/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClientImpl.java b/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClientImpl.java index 44cf207eb..1ac0dd00b 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClientImpl.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/client/external/GenericWorkflowClientImpl.java @@ -303,6 +303,18 @@ public DescribeScheduleResponse describeSchedule(DescribeScheduleRequest request grpcRetryerOptions); } + @Override + public DescribeWorkflowExecutionResponse describeWorkflowExecution( + DescribeWorkflowExecutionRequest request) { + return grpcRetryer.retryWithResult( + () -> + service + .blockingStub() + .withOption(METRICS_TAGS_CALL_OPTIONS_KEY, metricsScope) + .describeWorkflowExecution(request), + grpcRetryerOptions); + } + private static CompletableFuture toCompletableFuture( ListenableFuture listenableFuture) { CompletableFuture result = new CompletableFuture<>(); diff --git a/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowDescribe.java b/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowDescribe.java new file mode 100644 index 000000000..579f394ae --- /dev/null +++ b/temporal-sdk/src/test/java/io/temporal/workflow/WorkflowDescribe.java @@ -0,0 +1,56 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; + +import io.temporal.client.WorkflowExecutionDescription; +import io.temporal.client.WorkflowStub; +import io.temporal.testing.internal.SDKTestWorkflowRule; +import io.temporal.workflow.shared.TestWorkflows.TestWorkflow1; +import org.junit.Rule; +import org.junit.Test; + +public class WorkflowDescribe { + + @Rule + public SDKTestWorkflowRule testWorkflowRule = + SDKTestWorkflowRule.newBuilder().setWorkflowTypes(TestInitWorkflow.class).build(); + + @Test + public void testWorkflowDescribe() { + TestWorkflow1 workflowStub = + testWorkflowRule.newWorkflowStubTimeoutOptions(TestWorkflow1.class); + String result = workflowStub.execute(testWorkflowRule.getTaskQueue()); + assertEquals(testWorkflowRule.getTaskQueue(), result); + WorkflowExecutionDescription description = WorkflowStub.fromTyped(workflowStub).describe(); + assertEquals(testWorkflowRule.getTaskQueue(), description.getTaskQueue()); + assertEquals("TestWorkflow1", description.getWorkflowType()); + assertEquals(WorkflowStub.fromTyped(workflowStub).getExecution(), description.getExecution()); + } + + public static class TestInitWorkflow implements TestWorkflow1 { + @Override + public String execute(String taskQueue) { + return taskQueue; + } + } +} diff --git a/temporal-test-server/src/main/java/io/temporal/internal/testservice/TestWorkflowMutableStateImpl.java b/temporal-test-server/src/main/java/io/temporal/internal/testservice/TestWorkflowMutableStateImpl.java index db5d8ccd8..84f5791fa 100644 --- a/temporal-test-server/src/main/java/io/temporal/internal/testservice/TestWorkflowMutableStateImpl.java +++ b/temporal-test-server/src/main/java/io/temporal/internal/testservice/TestWorkflowMutableStateImpl.java @@ -3024,7 +3024,8 @@ private DescribeWorkflowExecutionResponse describeWorkflowExecutionInsideLock() // No setAutoResetPoints - the test environment doesn't support that feature .setSearchAttributes(visibilityStore.getSearchAttributesForExecution(executionId)) .setStatus(this.getWorkflowExecutionStatus()) - .setHistoryLength(fullHistory.size()); + .setHistoryLength(fullHistory.size()) + .setTaskQueue(this.getStartRequest().getTaskQueue().getName()); populateWorkflowExecutionInfoFromHistory(executionInfo, fullHistory); diff --git a/temporal-testing/src/main/java/io/temporal/testing/TimeLockingInterceptor.java b/temporal-testing/src/main/java/io/temporal/testing/TimeLockingInterceptor.java index d1e907ad2..da38dbdf1 100644 --- a/temporal-testing/src/main/java/io/temporal/testing/TimeLockingInterceptor.java +++ b/temporal-testing/src/main/java/io/temporal/testing/TimeLockingInterceptor.java @@ -193,6 +193,11 @@ public WorkflowStub newInstance(WorkflowOptions options) { return new TimeLockingWorkflowStub(locker, next.newInstance(options)); } + @Override + public WorkflowExecutionDescription describe() { + return next.describe(); + } + /** Unlocks time skipping before blocking calls and locks back after completion. */ private class TimeLockingFuture extends CompletableFuture {