@temporalio/core-bridge 1.1.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/Cargo.lock +786 -54
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +110 -3
  5. package/index.js +2 -6
  6. package/package.json +3 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/ARCHITECTURE.md +2 -2
  15. package/sdk-core/README.md +12 -0
  16. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  17. package/sdk-core/client/Cargo.toml +6 -4
  18. package/sdk-core/client/src/lib.rs +338 -215
  19. package/sdk-core/client/src/raw.rs +352 -106
  20. package/sdk-core/client/src/retry.rs +159 -133
  21. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  22. package/sdk-core/core/Cargo.toml +18 -9
  23. package/sdk-core/core/src/core_tests/activity_tasks.rs +63 -23
  24. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  25. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  26. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  27. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  28. package/sdk-core/core/src/ephemeral_server/mod.rs +499 -0
  29. package/sdk-core/core/src/lib.rs +60 -26
  30. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  31. package/sdk-core/core/src/replay/mod.rs +3 -3
  32. package/sdk-core/core/src/retry_logic.rs +10 -9
  33. package/sdk-core/core/src/telemetry/mod.rs +10 -7
  34. package/sdk-core/core/src/test_help/mod.rs +18 -8
  35. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  36. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  37. package/sdk-core/core/src/worker/activities.rs +6 -12
  38. package/sdk-core/core/src/worker/client.rs +193 -64
  39. package/sdk-core/core/src/worker/mod.rs +14 -19
  40. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  41. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  42. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  43. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  44. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  45. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  46. package/sdk-core/core/src/worker/workflow/mod.rs +59 -58
  47. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  48. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  49. package/sdk-core/core-api/Cargo.toml +2 -2
  50. package/sdk-core/core-api/src/errors.rs +3 -11
  51. package/sdk-core/core-api/src/worker.rs +7 -0
  52. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  53. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  54. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  55. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  56. package/sdk-core/protos/api_upstream/Makefile +2 -2
  57. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  58. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  59. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  60. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  61. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  62. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  63. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  64. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  65. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  66. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  67. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  68. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  74. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  75. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  76. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  77. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  78. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  79. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  80. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  81. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  82. package/sdk-core/sdk/Cargo.toml +2 -2
  83. package/sdk-core/sdk/src/lib.rs +2 -2
  84. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  85. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  86. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  87. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  88. package/sdk-core/sdk-core-protos/build.rs +9 -1
  89. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  90. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  91. package/sdk-core/test-utils/Cargo.toml +3 -3
  92. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  93. package/sdk-core/test-utils/src/lib.rs +14 -10
  94. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +141 -0
  95. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  96. package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
  97. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
  98. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  99. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  100. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  101. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  102. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +1 -1
  103. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  104. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  105. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  106. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  107. package/sdk-core/tests/load_tests.rs +2 -1
  108. package/sdk-core/tests/main.rs +10 -0
  109. package/src/conversions.rs +138 -91
  110. package/src/helpers.rs +190 -0
  111. package/src/lib.rs +10 -912
  112. package/src/runtime.rs +436 -0
  113. package/src/testing.rs +67 -0
  114. package/src/worker.rs +465 -0
@@ -13,17 +13,25 @@ mod retry;
13
13
  mod workflow_handle;
14
14
 
15
15
  pub use crate::retry::{CallType, RetryClient, RETRYABLE_ERROR_CODES};
16
- pub use raw::WorkflowService;
16
+ pub use raw::{HealthService, OperatorService, TestService, WorkflowService};
17
+ pub use temporal_sdk_core_protos::temporal::api::{
18
+ filter::v1::{StartTimeFilter, StatusFilter, WorkflowExecutionFilter, WorkflowTypeFilter},
19
+ workflowservice::v1::{
20
+ list_closed_workflow_executions_request::Filters as ListClosedFilters,
21
+ list_open_workflow_executions_request::Filters as ListOpenFilters,
22
+ },
23
+ };
17
24
  pub use workflow_handle::{WorkflowExecutionInfo, WorkflowExecutionResult};
18
25
 
19
26
  use crate::{
20
27
  metrics::{GrpcMetricSvc, MetricsContext},
21
28
  raw::{sealed::RawClientLike, AttachMetricLabels},
22
- sealed::{RawClientLikeUser, WfHandleClient},
29
+ sealed::WfHandleClient,
23
30
  workflow_handle::UntypedWorkflowHandle,
24
31
  };
25
32
  use backoff::{ExponentialBackoff, SystemClock};
26
33
  use http::uri::InvalidUri;
34
+ use once_cell::sync::OnceCell;
27
35
  use opentelemetry::metrics::Meter;
28
36
  use parking_lot::RwLock;
29
37
  use std::{
@@ -36,18 +44,23 @@ use std::{
36
44
  };
37
45
  use temporal_sdk_core_protos::{
38
46
  coresdk::{workflow_commands::QueryResult, IntoPayloadsExt},
47
+ grpc::health::v1::health_client::HealthClient,
39
48
  temporal::api::{
40
49
  command::v1::Command,
41
- common::v1::{Payload, Payloads, WorkflowExecution, WorkflowType},
42
- enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
50
+ common::v1::{Header, Payload, Payloads, WorkflowExecution, WorkflowType},
51
+ enums::v1::{TaskQueueKind, WorkflowIdReusePolicy, WorkflowTaskFailedCause},
43
52
  failure::v1::Failure,
44
- query::v1::{WorkflowQuery, WorkflowQueryResult},
45
- taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata},
53
+ operatorservice::v1::operator_service_client::OperatorServiceClient,
54
+ query::v1::WorkflowQuery,
55
+ taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
56
+ testservice::v1::test_service_client::TestServiceClient,
46
57
  workflowservice::v1::{workflow_service_client::WorkflowServiceClient, *},
47
58
  },
48
59
  TaskToken,
49
60
  };
50
61
  use tonic::{
62
+ body::BoxBody,
63
+ client::GrpcService,
51
64
  codegen::InterceptedService,
52
65
  metadata::{MetadataKey, MetadataValue},
53
66
  service::Interceptor,
@@ -58,6 +71,8 @@ use tower::ServiceBuilder;
58
71
  use url::Url;
59
72
  use uuid::Uuid;
60
73
 
74
+ static CLIENT_NAME_HEADER_KEY: &str = "client-name";
75
+ static CLIENT_VERSION_HEADER_KEY: &str = "client-version";
61
76
  static LONG_POLL_METHOD_NAMES: [&str; 2] = ["PollWorkflowTaskQueue", "PollActivityTaskQueue"];
62
77
  /// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
63
78
  const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(70);
@@ -203,51 +218,12 @@ pub enum ClientInitError {
203
218
  SystemInfoCallError(tonic::Status),
204
219
  }
205
220
 
206
- #[doc(hidden)]
207
- /// Allows passing different kinds of clients into things that want to be flexible. Motivating
208
- /// use-case was worker initialization.
209
- ///
210
- /// Needs to exist in this crate to avoid blanket impl conflicts.
211
- pub enum AnyClient {
212
- /// A high level client, like the type workers work with
213
- HighLevel(Arc<dyn WorkflowClientTrait + Send + Sync>),
214
- /// A low level gRPC client, wrapped with the typical interceptors
215
- LowLevel(Box<ConfiguredClient<WorkflowServiceClientWithMetrics>>),
216
- }
217
-
218
- impl<SGA> From<SGA> for AnyClient
219
- where
220
- SGA: WorkflowClientTrait + Send + Sync + 'static,
221
- {
222
- fn from(s: SGA) -> Self {
223
- Self::HighLevel(Arc::new(s))
224
- }
225
- }
226
- impl<SGA> From<Arc<SGA>> for AnyClient
227
- where
228
- SGA: WorkflowClientTrait + Send + Sync + 'static,
229
- {
230
- fn from(s: Arc<SGA>) -> Self {
231
- Self::HighLevel(s)
232
- }
233
- }
234
- impl From<RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>> for AnyClient {
235
- fn from(c: RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>) -> Self {
236
- Self::LowLevel(Box::new(c.into_inner()))
237
- }
238
- }
239
- impl From<ConfiguredClient<WorkflowServiceClientWithMetrics>> for AnyClient {
240
- fn from(c: ConfiguredClient<WorkflowServiceClientWithMetrics>) -> Self {
241
- Self::LowLevel(Box::new(c))
242
- }
243
- }
244
-
245
221
  /// A client with [ClientOptions] attached, which can be passed to initialize workers,
246
- /// or can be used directly.
222
+ /// or can be used directly. Is cheap to clone.
247
223
  #[derive(Clone, Debug)]
248
224
  pub struct ConfiguredClient<C> {
249
225
  client: C,
250
- options: ClientOptions,
226
+ options: Arc<ClientOptions>,
251
227
  headers: Arc<RwLock<HashMap<String, String>>>,
252
228
  /// Capabilities as read from the `get_system_info` RPC call made on client connection
253
229
  capabilities: Option<get_system_info_response::Capabilities>,
@@ -270,11 +246,6 @@ impl<C> ConfiguredClient<C> {
270
246
  pub fn capabilities(&self) -> Option<&get_system_info_response::Capabilities> {
271
247
  self.capabilities.as_ref()
272
248
  }
273
-
274
- /// De-constitute this type
275
- pub fn into_parts(self) -> (C, ClientOptions) {
276
- (self.client, self.options)
277
- }
278
249
  }
279
250
 
280
251
  // The configured client is effectively a "smart" (dumb) pointer
@@ -317,7 +288,7 @@ impl ClientOptions {
317
288
  &self,
318
289
  metrics_meter: Option<&Meter>,
319
290
  headers: Option<Arc<RwLock<HashMap<String, String>>>>,
320
- ) -> Result<RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>, ClientInitError>
291
+ ) -> Result<RetryClient<ConfiguredClient<TemporalServiceClientWithMetrics>>, ClientInitError>
321
292
  {
322
293
  let channel = Channel::from_shared(self.target_url.to_string())?;
323
294
  let channel = self.add_tls_to_channel(channel).await?;
@@ -333,11 +304,12 @@ impl ClientOptions {
333
304
  opts: self.clone(),
334
305
  headers: headers.clone(),
335
306
  };
307
+ let svc = InterceptedService::new(service, interceptor);
336
308
 
337
309
  let mut client = ConfiguredClient {
338
310
  headers,
339
- client: WorkflowServiceClient::with_interceptor(service, interceptor),
340
- options: self.clone(),
311
+ client: TemporalServiceClient::new(svc),
312
+ options: Arc::new(self.clone()),
341
313
  capabilities: None,
342
314
  };
343
315
  match client
@@ -416,27 +388,35 @@ impl Interceptor for ServiceCallInterceptor {
416
388
  /// cancel the request and have that status returned to the caller.
417
389
  fn call(&mut self, mut request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> {
418
390
  let metadata = request.metadata_mut();
419
- metadata.insert(
420
- "client-name",
421
- self.opts
422
- .client_name
423
- .parse()
424
- .unwrap_or_else(|_| MetadataValue::from_static("")),
425
- );
426
- metadata.insert(
427
- "client-version",
428
- self.opts
429
- .client_version
430
- .parse()
431
- .unwrap_or_else(|_| MetadataValue::from_static("")),
432
- );
391
+ if !metadata.contains_key(CLIENT_NAME_HEADER_KEY) {
392
+ metadata.insert(
393
+ CLIENT_NAME_HEADER_KEY,
394
+ self.opts
395
+ .client_name
396
+ .parse()
397
+ .unwrap_or_else(|_| MetadataValue::from_static("")),
398
+ );
399
+ }
400
+ if !metadata.contains_key(CLIENT_VERSION_HEADER_KEY) {
401
+ metadata.insert(
402
+ CLIENT_VERSION_HEADER_KEY,
403
+ self.opts
404
+ .client_version
405
+ .parse()
406
+ .unwrap_or_else(|_| MetadataValue::from_static("")),
407
+ );
408
+ }
433
409
  let headers = &*self.headers.read();
434
410
  for (k, v) in headers {
435
- if let (Ok(k), Ok(v)) = (MetadataKey::from_str(k), MetadataValue::from_str(v)) {
411
+ if metadata.contains_key(k) {
412
+ // Don't overwrite per-request specified headers
413
+ continue;
414
+ }
415
+ if let (Ok(k), Ok(v)) = (MetadataKey::from_str(k), v.parse()) {
436
416
  metadata.insert(k, v);
437
417
  }
438
418
  }
439
- if metadata.get("grpc-timeout").is_none() {
419
+ if !metadata.contains_key("grpc-timeout") {
440
420
  request.set_timeout(OTHER_CALL_TIMEOUT);
441
421
  }
442
422
 
@@ -444,15 +424,88 @@ impl Interceptor for ServiceCallInterceptor {
444
424
  }
445
425
  }
446
426
 
427
+ /// Aggregates various services exposed by the Temporal server
428
+ #[derive(Debug, Clone)]
429
+ pub struct TemporalServiceClient<T> {
430
+ svc: T,
431
+ workflow_svc_client: OnceCell<WorkflowServiceClient<T>>,
432
+ operator_svc_client: OnceCell<OperatorServiceClient<T>>,
433
+ test_svc_client: OnceCell<TestServiceClient<T>>,
434
+ health_svc_client: OnceCell<HealthClient<T>>,
435
+ }
436
+ impl<T> TemporalServiceClient<T>
437
+ where
438
+ T: Clone,
439
+ T: GrpcService<BoxBody> + Send + Clone + 'static,
440
+ T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
441
+ T::Error: Into<tonic::codegen::StdError>,
442
+ <T::ResponseBody as tonic::codegen::Body>::Error: Into<tonic::codegen::StdError> + Send,
443
+ {
444
+ fn new(svc: T) -> Self {
445
+ Self {
446
+ svc,
447
+ workflow_svc_client: OnceCell::new(),
448
+ operator_svc_client: OnceCell::new(),
449
+ test_svc_client: OnceCell::new(),
450
+ health_svc_client: OnceCell::new(),
451
+ }
452
+ }
453
+ /// Get the underlying workflow service client
454
+ pub fn workflow_svc(&self) -> &WorkflowServiceClient<T> {
455
+ self.workflow_svc_client
456
+ .get_or_init(|| WorkflowServiceClient::new(self.svc.clone()))
457
+ }
458
+ /// Get the underlying operator service client
459
+ pub fn operator_svc(&self) -> &OperatorServiceClient<T> {
460
+ self.operator_svc_client
461
+ .get_or_init(|| OperatorServiceClient::new(self.svc.clone()))
462
+ }
463
+ /// Get the underlying test service client
464
+ pub fn test_svc(&self) -> &TestServiceClient<T> {
465
+ self.test_svc_client
466
+ .get_or_init(|| TestServiceClient::new(self.svc.clone()))
467
+ }
468
+ /// Get the underlying health service client
469
+ pub fn health_svc(&self) -> &HealthClient<T> {
470
+ self.health_svc_client
471
+ .get_or_init(|| HealthClient::new(self.svc.clone()))
472
+ }
473
+ /// Get the underlying workflow service client mutably
474
+ pub fn workflow_svc_mut(&mut self) -> &mut WorkflowServiceClient<T> {
475
+ let _ = self.workflow_svc();
476
+ self.workflow_svc_client.get_mut().unwrap()
477
+ }
478
+ /// Get the underlying operator service client mutably
479
+ pub fn operator_svc_mut(&mut self) -> &mut OperatorServiceClient<T> {
480
+ let _ = self.operator_svc();
481
+ self.operator_svc_client.get_mut().unwrap()
482
+ }
483
+ /// Get the underlying test service client mutably
484
+ pub fn test_svc_mut(&mut self) -> &mut TestServiceClient<T> {
485
+ let _ = self.test_svc();
486
+ self.test_svc_client.get_mut().unwrap()
487
+ }
488
+ /// Get the underlying health service client mutably
489
+ pub fn health_svc_mut(&mut self) -> &mut HealthClient<T> {
490
+ let _ = self.health_svc();
491
+ self.health_svc_client.get_mut().unwrap()
492
+ }
493
+ }
447
494
  /// A [WorkflowServiceClient] with the default interceptors attached.
448
495
  pub type WorkflowServiceClientWithMetrics = WorkflowServiceClient<InterceptedMetricsSvc>;
496
+ /// An [OperatorServiceClient] with the default interceptors attached.
497
+ pub type OperatorServiceClientWithMetrics = OperatorServiceClient<InterceptedMetricsSvc>;
498
+ /// An [TestServiceClient] with the default interceptors attached.
499
+ pub type TestServiceClientWithMetrics = TestServiceClient<InterceptedMetricsSvc>;
500
+ /// A [TemporalServiceClient] with the default interceptors attached.
501
+ pub type TemporalServiceClientWithMetrics = TemporalServiceClient<InterceptedMetricsSvc>;
449
502
  type InterceptedMetricsSvc = InterceptedService<GrpcMetricSvc, ServiceCallInterceptor>;
450
503
 
451
504
  /// Contains an instance of a namespace-bound client for interacting with the Temporal server
452
505
  #[derive(Debug, Clone)]
453
506
  pub struct Client {
454
507
  /// Client for interacting with workflow service
455
- inner: ConfiguredClient<WorkflowServiceClientWithMetrics>,
508
+ inner: ConfiguredClient<TemporalServiceClientWithMetrics>,
456
509
  /// The namespace this client interacts with
457
510
  namespace: String,
458
511
  /// If set, attach as the worker build id to relevant calls
@@ -462,7 +515,7 @@ pub struct Client {
462
515
  impl Client {
463
516
  /// Create a new client from an existing configured lower level client and a namespace
464
517
  pub fn new(
465
- client: ConfiguredClient<WorkflowServiceClientWithMetrics>,
518
+ client: ConfiguredClient<TemporalServiceClientWithMetrics>,
466
519
  namespace: String,
467
520
  ) -> Self {
468
521
  Client {
@@ -489,7 +542,7 @@ impl Client {
489
542
  /// Note that it is reasonably cheap to clone the returned type if you need to own it. Such
490
543
  /// clones will keep re-using the same channel.
491
544
  pub fn raw_client(&self) -> &WorkflowServiceClientWithMetrics {
492
- self.inner.deref()
545
+ self.inner.workflow_svc()
493
546
  }
494
547
 
495
548
  /// Return the options this client was initialized with
@@ -499,7 +552,7 @@ impl Client {
499
552
 
500
553
  /// Return the options this client was initialized with mutably
501
554
  pub fn options_mut(&mut self) -> &mut ClientOptions {
502
- &mut self.inner.options
555
+ Arc::make_mut(&mut self.inner.options)
503
556
  }
504
557
 
505
558
  /// Set a worker build id to be attached to relevant requests. Unlikely to be useful outside
@@ -507,6 +560,39 @@ impl Client {
507
560
  pub fn set_worker_build_id(&mut self, id: String) {
508
561
  self.bound_worker_build_id = Some(id)
509
562
  }
563
+
564
+ /// Returns a reference to the underlying client
565
+ pub fn inner(&self) -> &ConfiguredClient<TemporalServiceClientWithMetrics> {
566
+ &self.inner
567
+ }
568
+
569
+ /// Consumes self and returns the underlying client
570
+ pub fn into_inner(self) -> ConfiguredClient<TemporalServiceClientWithMetrics> {
571
+ self.inner
572
+ }
573
+
574
+ fn wf_svc(&self) -> WorkflowServiceClientWithMetrics {
575
+ self.inner.workflow_svc().clone()
576
+ }
577
+ }
578
+
579
+ /// Enum to help reference a namespace by either the namespace name or the namespace id
580
+ #[derive(Clone)]
581
+ pub enum Namespace {
582
+ /// Namespace name
583
+ Name(String),
584
+ /// Namespace id
585
+ Id(String),
586
+ }
587
+
588
+ impl Namespace {
589
+ fn into_describe_namespace_request(self) -> DescribeNamespaceRequest {
590
+ let (namespace, id) = match self {
591
+ Namespace::Name(n) => (n, "".to_owned()),
592
+ Namespace::Id(n) => ("".to_owned(), n),
593
+ };
594
+ DescribeNamespaceRequest { namespace, id }
595
+ }
510
596
  }
511
597
 
512
598
  /// This trait provides higher-level friendlier interaction with the server.
@@ -521,25 +607,10 @@ pub trait WorkflowClientTrait {
521
607
  task_queue: String,
522
608
  workflow_id: String,
523
609
  workflow_type: String,
610
+ request_id: Option<String>,
524
611
  options: WorkflowOptions,
525
612
  ) -> Result<StartWorkflowExecutionResponse>;
526
613
 
527
- /// Fetch new workflow tasks from the provided queue. Should block indefinitely if there is no
528
- /// work.
529
- async fn poll_workflow_task(
530
- &self,
531
- task_queue: String,
532
- is_sticky: bool,
533
- ) -> Result<PollWorkflowTaskQueueResponse>;
534
-
535
- /// Fetch new activity tasks from the provided queue. Should block indefinitely if there is no
536
- /// work.
537
- async fn poll_activity_task(
538
- &self,
539
- task_queue: String,
540
- max_tasks_per_sec: Option<f64>,
541
- ) -> Result<PollActivityTaskQueueResponse>;
542
-
543
614
  /// Notifies the server that workflow tasks for a given workflow should be sent to the normal
544
615
  /// non-sticky task queue. This normally happens when workflow has been evicted from the cache.
545
616
  async fn reset_sticky_task_queue(
@@ -548,12 +619,6 @@ pub trait WorkflowClientTrait {
548
619
  run_id: String,
549
620
  ) -> Result<ResetStickyTaskQueueResponse>;
550
621
 
551
- /// Complete a workflow activation.
552
- async fn complete_workflow_task(
553
- &self,
554
- request: WorkflowTaskCompletion,
555
- ) -> Result<RespondWorkflowTaskCompletedResponse>;
556
-
557
622
  /// Complete activity task by sending response to the server. `task_token` contains activity
558
623
  /// identifier that would've been received from polling for an activity task. `result` is a blob
559
624
  /// that contains activity response.
@@ -607,6 +672,7 @@ pub trait WorkflowClientTrait {
607
672
  run_id: String,
608
673
  signal_name: String,
609
674
  payloads: Option<Payloads>,
675
+ request_id: Option<String>,
610
676
  ) -> Result<SignalWorkflowExecutionResponse>;
611
677
 
612
678
  /// Send signal and start workflow transcationally
@@ -618,9 +684,11 @@ pub trait WorkflowClientTrait {
618
684
  task_queue: String,
619
685
  workflow_id: String,
620
686
  workflow_type: String,
687
+ request_id: Option<String>,
621
688
  options: WorkflowOptions,
622
689
  signal_name: String,
623
690
  signal_input: Option<Payloads>,
691
+ signal_header: Option<Header>,
624
692
  ) -> Result<SignalWithStartWorkflowExecutionResponse>;
625
693
 
626
694
  /// Request a query of a certain workflow instance
@@ -659,6 +727,7 @@ pub trait WorkflowClientTrait {
659
727
  workflow_id: String,
660
728
  run_id: Option<String>,
661
729
  reason: String,
730
+ request_id: Option<String>,
662
731
  ) -> Result<RequestCancelWorkflowExecutionResponse>;
663
732
 
664
733
  /// Terminate a currently executing workflow
@@ -671,6 +740,35 @@ pub trait WorkflowClientTrait {
671
740
  /// Lists all available namespaces
672
741
  async fn list_namespaces(&self) -> Result<ListNamespacesResponse>;
673
742
 
743
+ /// Query namespace details
744
+ async fn describe_namespace(&self, namespace: Namespace) -> Result<DescribeNamespaceResponse>;
745
+
746
+ /// List open workflows with Standard Visibility filtering
747
+ async fn list_open_workflow_executions(
748
+ &self,
749
+ max_page_size: i32,
750
+ next_page_token: Vec<u8>,
751
+ start_time_filter: Option<StartTimeFilter>,
752
+ filters: Option<ListOpenFilters>,
753
+ ) -> Result<ListOpenWorkflowExecutionsResponse>;
754
+
755
+ /// List closed workflows Standard Visibility filtering
756
+ async fn list_closed_workflow_executions(
757
+ &self,
758
+ max_page_size: i32,
759
+ next_page_token: Vec<u8>,
760
+ start_time_filter: Option<StartTimeFilter>,
761
+ filters: Option<ListClosedFilters>,
762
+ ) -> Result<ListClosedWorkflowExecutionsResponse>;
763
+
764
+ /// List workflows with Advanced Visibility filtering
765
+ async fn list_workflow_executions(
766
+ &self,
767
+ max_page_size: i32,
768
+ next_page_token: Vec<u8>,
769
+ query: String,
770
+ ) -> Result<ListWorkflowExecutionsResponse>;
771
+
674
772
  /// Returns options that were used to initialize the client
675
773
  fn get_options(&self) -> &ClientOptions;
676
774
 
@@ -681,9 +779,22 @@ pub trait WorkflowClientTrait {
681
779
  /// Optional fields supplied at the start of workflow execution
682
780
  #[derive(Debug, Clone, Default)]
683
781
  pub struct WorkflowOptions {
684
- /// Optionally indicates the default task timeout for workflow tasks
782
+ /// Set the policy for reusing the workflow id
783
+ pub id_reuse_policy: WorkflowIdReusePolicy,
784
+
785
+ /// Optionally set the execution timeout for the workflow
786
+ /// <https://docs.temporal.io/workflows/#workflow-execution-timeout>
787
+ pub execution_timeout: Option<Duration>,
788
+
789
+ /// Optionally indicates the default run timeout for a workflow run
790
+ pub run_timeout: Option<Duration>,
791
+
792
+ /// Optionally indicates the default task timeout for a workflow run
685
793
  pub task_timeout: Option<Duration>,
686
794
 
795
+ /// Optionally set a cron schedule for the workflow
796
+ pub cron_schedule: Option<String>,
797
+
687
798
  /// Optionally associate extra search attributes with a workflow
688
799
  pub search_attributes: Option<HashMap<String, Payload>>,
689
800
  }
@@ -696,10 +807,9 @@ impl WorkflowClientTrait for Client {
696
807
  task_queue: String,
697
808
  workflow_id: String,
698
809
  workflow_type: String,
810
+ request_id: Option<String>,
699
811
  options: WorkflowOptions,
700
812
  ) -> Result<StartWorkflowExecutionResponse> {
701
- let request_id = Uuid::new_v4().to_string();
702
-
703
813
  Ok(self
704
814
  .wf_svc()
705
815
  .start_workflow_execution(StartWorkflowExecutionRequest {
@@ -713,65 +823,21 @@ impl WorkflowClientTrait for Client {
713
823
  name: task_queue,
714
824
  kind: TaskQueueKind::Unspecified as i32,
715
825
  }),
716
- request_id,
717
- workflow_task_timeout: options.task_timeout.map(Into::into),
718
- search_attributes: options.search_attributes.map(Into::into),
826
+ request_id: request_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
827
+ workflow_id_reuse_policy: options.id_reuse_policy as i32,
828
+ workflow_execution_timeout: options
829
+ .execution_timeout
830
+ .and_then(|d| d.try_into().ok()),
831
+ workflow_run_timeout: options.execution_timeout.and_then(|d| d.try_into().ok()),
832
+ workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()),
833
+ search_attributes: options.search_attributes.and_then(|d| d.try_into().ok()),
834
+ cron_schedule: options.cron_schedule.unwrap_or_default(),
719
835
  ..Default::default()
720
836
  })
721
837
  .await?
722
838
  .into_inner())
723
839
  }
724
840
 
725
- async fn poll_workflow_task(
726
- &self,
727
- task_queue: String,
728
- is_sticky: bool,
729
- ) -> Result<PollWorkflowTaskQueueResponse> {
730
- let request = PollWorkflowTaskQueueRequest {
731
- namespace: self.namespace.clone(),
732
- task_queue: Some(TaskQueue {
733
- name: task_queue,
734
- kind: if is_sticky {
735
- TaskQueueKind::Sticky
736
- } else {
737
- TaskQueueKind::Normal
738
- } as i32,
739
- }),
740
- identity: self.inner.options.identity.clone(),
741
- binary_checksum: self.bound_worker_build_id.clone().unwrap_or_default(),
742
- };
743
-
744
- Ok(self
745
- .wf_svc()
746
- .poll_workflow_task_queue(request)
747
- .await?
748
- .into_inner())
749
- }
750
-
751
- async fn poll_activity_task(
752
- &self,
753
- task_queue: String,
754
- max_tasks_per_sec: Option<f64>,
755
- ) -> Result<PollActivityTaskQueueResponse> {
756
- let request = PollActivityTaskQueueRequest {
757
- namespace: self.namespace.clone(),
758
- task_queue: Some(TaskQueue {
759
- name: task_queue,
760
- kind: TaskQueueKind::Normal as i32,
761
- }),
762
- identity: self.inner.options.identity.clone(),
763
- task_queue_metadata: max_tasks_per_sec.map(|tps| TaskQueueMetadata {
764
- max_tasks_per_second: Some(tps),
765
- }),
766
- };
767
-
768
- Ok(self
769
- .wf_svc()
770
- .poll_activity_task_queue(request)
771
- .await?
772
- .into_inner())
773
- }
774
-
775
841
  async fn reset_sticky_task_queue(
776
842
  &self,
777
843
  workflow_id: String,
@@ -791,42 +857,6 @@ impl WorkflowClientTrait for Client {
791
857
  .into_inner())
792
858
  }
793
859
 
794
- async fn complete_workflow_task(
795
- &self,
796
- request: WorkflowTaskCompletion,
797
- ) -> Result<RespondWorkflowTaskCompletedResponse> {
798
- let request = RespondWorkflowTaskCompletedRequest {
799
- task_token: request.task_token.into(),
800
- commands: request.commands,
801
- identity: self.inner.options.identity.clone(),
802
- sticky_attributes: request.sticky_attributes,
803
- return_new_workflow_task: request.return_new_workflow_task,
804
- force_create_new_workflow_task: request.force_create_new_workflow_task,
805
- binary_checksum: self.bound_worker_build_id.clone().unwrap_or_default(),
806
- query_results: request
807
- .query_responses
808
- .into_iter()
809
- .map(|qr| {
810
- let (id, completed_type, query_result, error_message) = qr.into_components();
811
- (
812
- id,
813
- WorkflowQueryResult {
814
- result_type: completed_type as i32,
815
- answer: query_result,
816
- error_message,
817
- },
818
- )
819
- })
820
- .collect(),
821
- namespace: self.namespace.clone(),
822
- };
823
- Ok(self
824
- .wf_svc()
825
- .respond_workflow_task_completed(request)
826
- .await?
827
- .into_inner())
828
- }
829
-
830
860
  async fn complete_activity_task(
831
861
  &self,
832
862
  task_token: TaskToken,
@@ -924,6 +954,7 @@ impl WorkflowClientTrait for Client {
924
954
  run_id: String,
925
955
  signal_name: String,
926
956
  payloads: Option<Payloads>,
957
+ request_id: Option<String>,
927
958
  ) -> Result<SignalWorkflowExecutionResponse> {
928
959
  Ok(self
929
960
  .wf_svc()
@@ -936,6 +967,7 @@ impl WorkflowClientTrait for Client {
936
967
  signal_name,
937
968
  input: payloads,
938
969
  identity: self.inner.options.identity.clone(),
970
+ request_id: request_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
939
971
  ..Default::default()
940
972
  })
941
973
  .await?
@@ -948,11 +980,12 @@ impl WorkflowClientTrait for Client {
948
980
  task_queue: String,
949
981
  workflow_id: String,
950
982
  workflow_type: String,
983
+ request_id: Option<String>,
951
984
  options: WorkflowOptions,
952
985
  signal_name: String,
953
986
  signal_input: Option<Payloads>,
987
+ signal_header: Option<Header>,
954
988
  ) -> Result<SignalWithStartWorkflowExecutionResponse> {
955
- let request_id = Uuid::new_v4().to_string();
956
989
  Ok(self
957
990
  .wf_svc()
958
991
  .signal_with_start_workflow_execution(SignalWithStartWorkflowExecutionRequest {
@@ -965,13 +998,20 @@ impl WorkflowClientTrait for Client {
965
998
  name: task_queue,
966
999
  kind: TaskQueueKind::Normal as i32,
967
1000
  }),
968
- request_id,
969
1001
  input,
970
1002
  signal_name,
971
1003
  signal_input,
972
1004
  identity: self.inner.options.identity.clone(),
973
- workflow_task_timeout: options.task_timeout.map(Into::into),
974
- search_attributes: options.search_attributes.map(Into::into),
1005
+ request_id: request_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
1006
+ workflow_id_reuse_policy: options.id_reuse_policy as i32,
1007
+ workflow_execution_timeout: options
1008
+ .execution_timeout
1009
+ .and_then(|d| d.try_into().ok()),
1010
+ workflow_run_timeout: options.execution_timeout.and_then(|d| d.try_into().ok()),
1011
+ workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()),
1012
+ search_attributes: options.search_attributes.and_then(|d| d.try_into().ok()),
1013
+ cron_schedule: options.cron_schedule.unwrap_or_default(),
1014
+ header: signal_header,
975
1015
  ..Default::default()
976
1016
  })
977
1017
  .await?
@@ -1062,6 +1102,7 @@ impl WorkflowClientTrait for Client {
1062
1102
  workflow_id: String,
1063
1103
  run_id: Option<String>,
1064
1104
  reason: String,
1105
+ request_id: Option<String>,
1065
1106
  ) -> Result<RequestCancelWorkflowExecutionResponse> {
1066
1107
  Ok(self
1067
1108
  .wf_svc()
@@ -1072,7 +1113,7 @@ impl WorkflowClientTrait for Client {
1072
1113
  run_id: run_id.unwrap_or_default(),
1073
1114
  }),
1074
1115
  identity: self.inner.options.identity.clone(),
1075
- request_id: "".to_string(),
1116
+ request_id: request_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
1076
1117
  first_execution_run_id: "".to_string(),
1077
1118
  reason,
1078
1119
  })
@@ -1110,6 +1151,72 @@ impl WorkflowClientTrait for Client {
1110
1151
  .into_inner())
1111
1152
  }
1112
1153
 
1154
+ async fn describe_namespace(&self, namespace: Namespace) -> Result<DescribeNamespaceResponse> {
1155
+ Ok(self
1156
+ .wf_svc()
1157
+ .describe_namespace(namespace.into_describe_namespace_request())
1158
+ .await?
1159
+ .into_inner())
1160
+ }
1161
+
1162
+ async fn list_open_workflow_executions(
1163
+ &self,
1164
+ maximum_page_size: i32,
1165
+ next_page_token: Vec<u8>,
1166
+ start_time_filter: Option<StartTimeFilter>,
1167
+ filters: Option<ListOpenFilters>,
1168
+ ) -> Result<ListOpenWorkflowExecutionsResponse> {
1169
+ Ok(self
1170
+ .wf_svc()
1171
+ .list_open_workflow_executions(ListOpenWorkflowExecutionsRequest {
1172
+ namespace: self.namespace.clone(),
1173
+ maximum_page_size,
1174
+ next_page_token,
1175
+ start_time_filter,
1176
+ filters,
1177
+ })
1178
+ .await?
1179
+ .into_inner())
1180
+ }
1181
+
1182
+ async fn list_closed_workflow_executions(
1183
+ &self,
1184
+ maximum_page_size: i32,
1185
+ next_page_token: Vec<u8>,
1186
+ start_time_filter: Option<StartTimeFilter>,
1187
+ filters: Option<ListClosedFilters>,
1188
+ ) -> Result<ListClosedWorkflowExecutionsResponse> {
1189
+ Ok(self
1190
+ .wf_svc()
1191
+ .list_closed_workflow_executions(ListClosedWorkflowExecutionsRequest {
1192
+ namespace: self.namespace.clone(),
1193
+ maximum_page_size,
1194
+ next_page_token,
1195
+ start_time_filter,
1196
+ filters,
1197
+ })
1198
+ .await?
1199
+ .into_inner())
1200
+ }
1201
+
1202
+ async fn list_workflow_executions(
1203
+ &self,
1204
+ page_size: i32,
1205
+ next_page_token: Vec<u8>,
1206
+ query: String,
1207
+ ) -> Result<ListWorkflowExecutionsResponse> {
1208
+ Ok(self
1209
+ .wf_svc()
1210
+ .list_workflow_executions(ListWorkflowExecutionsRequest {
1211
+ namespace: self.namespace.clone(),
1212
+ page_size,
1213
+ next_page_token,
1214
+ query,
1215
+ })
1216
+ .await?
1217
+ .into_inner())
1218
+ }
1219
+
1113
1220
  fn get_options(&self) -> &ClientOptions {
1114
1221
  &self.inner.options
1115
1222
  }
@@ -1122,39 +1229,28 @@ impl WorkflowClientTrait for Client {
1122
1229
  mod sealed {
1123
1230
  use crate::{InterceptedMetricsSvc, RawClientLike, WorkflowClientTrait};
1124
1231
 
1125
- pub trait RawClientLikeUser {
1126
- type RawClientT: RawClientLike<SvcType = InterceptedMetricsSvc>;
1127
- /// Used to access the client as a [WorkflowService] implementor rather than the raw struct
1128
- fn wf_svc(&self) -> Self::RawClientT;
1232
+ pub trait WfHandleClient:
1233
+ WorkflowClientTrait + RawClientLike<SvcType = InterceptedMetricsSvc>
1234
+ {
1129
1235
  }
1130
-
1131
- pub trait WfHandleClient: WorkflowClientTrait + RawClientLikeUser {}
1132
- impl<T> WfHandleClient for T where T: WorkflowClientTrait + RawClientLikeUser {}
1133
- }
1134
-
1135
- impl RawClientLikeUser for Client {
1136
- type RawClientT = WorkflowServiceClientWithMetrics;
1137
-
1138
- fn wf_svc(&self) -> Self::RawClientT {
1139
- self.raw_client().clone()
1236
+ impl<T> WfHandleClient for T where
1237
+ T: WorkflowClientTrait + RawClientLike<SvcType = InterceptedMetricsSvc>
1238
+ {
1140
1239
  }
1141
1240
  }
1142
1241
 
1143
1242
  /// Additional methods for workflow clients
1144
- pub trait WfClientExt: WfHandleClient + Sized {
1243
+ pub trait WfClientExt: WfHandleClient + Sized + Clone {
1145
1244
  /// Create an untyped handle for a workflow execution, which can be used to do things like
1146
1245
  /// wait for that workflow's result. `run_id` may be left blank to target the latest run.
1147
1246
  fn get_untyped_workflow_handle(
1148
1247
  &self,
1149
1248
  workflow_id: impl Into<String>,
1150
1249
  run_id: impl Into<String>,
1151
- ) -> UntypedWorkflowHandle<Self::RawClientT>
1152
- where
1153
- Self::RawClientT: Clone,
1154
- {
1250
+ ) -> UntypedWorkflowHandle<Self> {
1155
1251
  let rid = run_id.into();
1156
1252
  UntypedWorkflowHandle::new(
1157
- self.wf_svc(),
1253
+ self.clone(),
1158
1254
  WorkflowExecutionInfo {
1159
1255
  namespace: self.namespace().to_string(),
1160
1256
  workflow_id: workflow_id.into(),
@@ -1163,4 +1259,31 @@ pub trait WfClientExt: WfHandleClient + Sized {
1163
1259
  )
1164
1260
  }
1165
1261
  }
1166
- impl<T> WfClientExt for T where T: WfHandleClient + Sized {}
1262
+ impl<T> WfClientExt for T where T: WfHandleClient + Clone + Sized {}
1263
+
1264
+ #[cfg(test)]
1265
+ mod tests {
1266
+ use super::*;
1267
+
1268
+ #[test]
1269
+ fn respects_per_call_headers() {
1270
+ let opts = ClientOptionsBuilder::default()
1271
+ .identity("enchicat".to_string())
1272
+ .target_url(Url::parse("https://smolkitty").unwrap())
1273
+ .client_name("cute-kitty".to_string())
1274
+ .client_version("0.1.0".to_string())
1275
+ .build()
1276
+ .unwrap();
1277
+
1278
+ let mut static_headers = HashMap::new();
1279
+ static_headers.insert("enchi".to_string(), "kitty".to_string());
1280
+ let mut iceptor = ServiceCallInterceptor {
1281
+ opts,
1282
+ headers: Arc::new(RwLock::new(static_headers)),
1283
+ };
1284
+ let mut req = tonic::Request::new(());
1285
+ req.metadata_mut().insert("enchi", "cat".parse().unwrap());
1286
+ let next_req = iceptor.call(req).unwrap();
1287
+ assert_eq!(next_req.metadata().get("enchi").unwrap(), "cat");
1288
+ }
1289
+ }