use crate::{
ActivityCancelOptions, ActivityDescribeOptions, ActivityExecutionDescription,
ActivityTerminateOptions, NamespacedClient,
errors::{ActivityInteractionError, ActivityResultError},
grpc::WorkflowService,
};
use std::marker::PhantomData;
use temporalio_common::{
ActivityDefinition,
data_converters::{DecodablePayloads, NoopDecodeHint, SerializationContextData},
protos::temporal::api::{
activity::v1::{ActivityExecutionOutcome, activity_execution_outcome},
failure::v1::failure::FailureInfo,
workflowservice::v1::{
DescribeActivityExecutionRequest, PollActivityExecutionRequest,
RequestCancelActivityExecutionRequest, TerminateActivityExecutionRequest,
},
},
};
use tonic::IntoRequest;
use uuid::Uuid;
pub struct ActivityHandle<ClientT, ActivityT>
where
ActivityT: ActivityDefinition,
{
client: ClientT,
activity_id: String,
run_id: Option<String>,
_phantom: PhantomData<ActivityT>,
}
impl<ClientT, ActivityT> ActivityHandle<ClientT, ActivityT>
where
ActivityT: ActivityDefinition,
{
pub(crate) fn new(client: ClientT, activity_id: String, run_id: Option<String>) -> Self {
Self {
client,
activity_id,
run_id,
_phantom: PhantomData,
}
}
pub fn activity_id(&self) -> &str {
&self.activity_id
}
pub fn run_id(&self) -> Option<&str> {
self.run_id.as_deref()
}
}
impl<ClientT, ActivityT> ActivityHandle<ClientT, ActivityT>
where
ClientT: WorkflowService + NamespacedClient + Clone,
ActivityT: ActivityDefinition,
{
pub async fn result(&self) -> Result<ActivityT::Output, ActivityResultError> {
let mut client = self.client.clone();
loop {
let resp = client
.poll_activity_execution(
PollActivityExecutionRequest {
namespace: client.namespace(),
activity_id: self.activity_id.clone(),
run_id: self.run_id.clone().unwrap_or_default(),
}
.into_request(),
)
.await?
.into_inner();
let Some(ActivityExecutionOutcome {
value: Some(outcome),
..
}) = resp.outcome
else {
continue;
};
let dc = client.data_converter();
let ctx = SerializationContextData::Activity;
return match outcome {
activity_execution_outcome::Value::Result(payloads) => {
Ok(dc.from_payloads(&ctx, payloads.payloads).await?)
}
activity_execution_outcome::Value::Failure(failure) => {
Err(match failure.failure_info {
Some(FailureInfo::CanceledFailureInfo(info)) => {
let payloads = info.details.unwrap_or_default().payloads;
let details = DecodablePayloads::new(
payloads,
dc.payload_converter().clone(),
ctx,
);
ActivityResultError::Cancelled { details }
}
Some(FailureInfo::TerminatedFailureInfo(_)) => {
ActivityResultError::Terminated
}
_ => ActivityResultError::ActivityFailed(dc.to_error(
&ctx,
failure,
NoopDecodeHint,
)?),
})
}
};
}
}
pub async fn describe(
&self,
options: ActivityDescribeOptions,
) -> Result<ActivityExecutionDescription<ActivityT>, ActivityInteractionError> {
let mut client = self.client.clone();
let resp = client
.describe_activity_execution(
DescribeActivityExecutionRequest {
namespace: client.namespace(),
activity_id: self.activity_id.clone(),
run_id: self.run_id.clone().unwrap_or_default(),
include_input: options.include_input,
include_outcome: options.include_outcome,
include_heartbeat_details: options.include_heartbeat_details,
include_last_failure: options.include_last_failure,
..Default::default()
}
.into_request(),
)
.await?
.into_inner();
Ok(ActivityExecutionDescription::new(
client.data_converter().clone(),
SerializationContextData::Activity,
resp,
)?)
}
pub async fn cancel(
&self,
options: ActivityCancelOptions,
) -> Result<(), ActivityInteractionError> {
let mut client = self.client.clone();
client
.request_cancel_activity_execution(
RequestCancelActivityExecutionRequest {
namespace: client.namespace(),
activity_id: self.activity_id.clone(),
run_id: self.run_id.clone().unwrap_or_default(),
identity: client.identity(),
request_id: Uuid::new_v4().to_string(),
reason: options.reason,
}
.into_request(),
)
.await?;
Ok(())
}
pub async fn terminate(
&self,
options: ActivityTerminateOptions,
) -> Result<(), ActivityInteractionError> {
let mut client = self.client.clone();
client
.terminate_activity_execution(
TerminateActivityExecutionRequest {
namespace: client.namespace(),
activity_id: self.activity_id.clone(),
run_id: self.run_id.clone().unwrap_or_default(),
identity: client.identity(),
request_id: Uuid::new_v4().to_string(),
reason: options.reason,
}
.into_request(),
)
.await?;
Ok(())
}
}