@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
@@ -0,0 +1,141 @@
1
+ use std::time::{SystemTime, UNIX_EPOCH};
2
+ use temporal_client::{TestService, WorkflowService};
3
+ use temporal_sdk_core::{
4
+ ephemeral_server::{
5
+ EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
6
+ TestServerConfigBuilder,
7
+ },
8
+ ClientOptions, ClientOptionsBuilder,
9
+ };
10
+ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
11
+ use temporal_sdk_core_test_utils::NAMESPACE;
12
+ use url::Url;
13
+
14
+ #[tokio::test]
15
+ async fn temporalite_default() {
16
+ let config = TemporaliteConfigBuilder::default()
17
+ .exe(default_cached_download())
18
+ .build()
19
+ .unwrap();
20
+ let server = config.start_server().await.unwrap();
21
+ assert_ephemeral_server(&server).await;
22
+ }
23
+
24
+ #[tokio::test]
25
+ async fn temporalite_fixed() {
26
+ let config = TemporaliteConfigBuilder::default()
27
+ .exe(fixed_cached_download("v0.1.1"))
28
+ .build()
29
+ .unwrap();
30
+ let server = config.start_server().await.unwrap();
31
+ assert_ephemeral_server(&server).await;
32
+ }
33
+
34
+ #[tokio::test]
35
+ async fn temporalite_shutdown_port_reuse() {
36
+ // Start, test shutdown, do again immediately on same port to ensure we can
37
+ // reuse after shutdown
38
+ let config = TemporaliteConfigBuilder::default()
39
+ .exe(default_cached_download())
40
+ .port(Some(10123))
41
+ .build()
42
+ .unwrap();
43
+ let mut server = config.start_server().await.unwrap();
44
+ assert_ephemeral_server(&server).await;
45
+ server.shutdown().await.unwrap();
46
+ let mut server = config.start_server().await.unwrap();
47
+ assert_ephemeral_server(&server).await;
48
+ server.shutdown().await.unwrap();
49
+ }
50
+
51
+ #[tokio::test]
52
+ async fn test_server_default() {
53
+ let config = TestServerConfigBuilder::default()
54
+ .exe(default_cached_download())
55
+ .build()
56
+ .unwrap();
57
+ let server = config.start_server().await.unwrap();
58
+ assert_ephemeral_server(&server).await;
59
+ }
60
+
61
+ #[tokio::test]
62
+ async fn test_server_fixed() {
63
+ let config = TestServerConfigBuilder::default()
64
+ .exe(fixed_cached_download("v1.16.0"))
65
+ .build()
66
+ .unwrap();
67
+ let server = config.start_server().await.unwrap();
68
+ assert_ephemeral_server(&server).await;
69
+ }
70
+
71
+ #[tokio::test]
72
+ async fn test_server_shutdown_port_reuse() {
73
+ // Start, test shutdown, do again immediately on same port to ensure we can
74
+ // reuse after shutdown
75
+ let config = TestServerConfigBuilder::default()
76
+ .exe(default_cached_download())
77
+ .port(Some(10124))
78
+ .build()
79
+ .unwrap();
80
+ let mut server = config.start_server().await.unwrap();
81
+ assert_ephemeral_server(&server).await;
82
+ server.shutdown().await.unwrap();
83
+ let mut server = config.start_server().await.unwrap();
84
+ assert_ephemeral_server(&server).await;
85
+ server.shutdown().await.unwrap();
86
+ }
87
+
88
+ async fn assert_ephemeral_server(server: &EphemeralServer) {
89
+ // Connect and describe namespace
90
+ let mut client = client_options(&server.target)
91
+ .connect_no_namespace(None, None)
92
+ .await
93
+ .unwrap();
94
+ let resp = client
95
+ .describe_namespace(DescribeNamespaceRequest {
96
+ namespace: NAMESPACE.to_string(),
97
+ ..Default::default()
98
+ })
99
+ .await
100
+ .unwrap();
101
+ assert!(resp.into_inner().namespace_info.unwrap().name == "default");
102
+
103
+ // If it has test service, make sure we can use it too
104
+ if server.has_test_service {
105
+ let resp = client.get_current_time(()).await.unwrap();
106
+ // Make sure it's within 5 mins of now
107
+ let resp_seconds = resp.get_ref().time.as_ref().unwrap().seconds as u64;
108
+ let curr_seconds = SystemTime::now()
109
+ .duration_since(UNIX_EPOCH)
110
+ .unwrap()
111
+ .as_secs();
112
+ assert!(curr_seconds - 300 < resp_seconds && curr_seconds + 300 > resp_seconds);
113
+ }
114
+ }
115
+
116
+ fn default_cached_download() -> EphemeralExe {
117
+ EphemeralExe::CachedDownload {
118
+ version: EphemeralExeVersion::Default {
119
+ sdk_name: "sdk-rust".to_string(),
120
+ sdk_version: "0.1.0".to_string(),
121
+ },
122
+ dest_dir: None,
123
+ }
124
+ }
125
+
126
+ fn fixed_cached_download(version: &str) -> EphemeralExe {
127
+ EphemeralExe::CachedDownload {
128
+ version: EphemeralExeVersion::Fixed(version.to_string()),
129
+ dest_dir: None,
130
+ }
131
+ }
132
+
133
+ fn client_options(target: &str) -> ClientOptions {
134
+ return ClientOptionsBuilder::default()
135
+ .identity("integ_tester".to_string())
136
+ .target_url(Url::try_from(&*format!("http://{}", target)).unwrap())
137
+ .client_name("temporal-core".to_string())
138
+ .client_version("0.1.0".to_string())
139
+ .build()
140
+ .unwrap();
141
+ }
@@ -1,5 +1,7 @@
1
1
  use assert_matches::assert_matches;
2
2
  use std::time::Duration;
3
+ use temporal_client::{WfClientExt, WorkflowOptions};
4
+ use temporal_sdk::{ActContext, ActivityOptions, WfContext};
3
5
  use temporal_sdk_core_protos::{
4
6
  coresdk::{
5
7
  activity_result::{
@@ -9,12 +11,12 @@ use temporal_sdk_core_protos::{
9
11
  workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
10
12
  workflow_commands::{ActivityCancellationType, ScheduleActivity},
11
13
  workflow_completion::WorkflowActivationCompletion,
12
- ActivityHeartbeat, ActivityTaskCompletion, IntoCompletion,
14
+ ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
13
15
  },
14
16
  temporal::api::common::v1::{Payload, RetryPolicy},
15
17
  };
16
18
  use temporal_sdk_core_test_utils::{
17
- init_core_and_create_wf, schedule_activity_cmd, WorkerTestHelpers,
19
+ init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
18
20
  };
19
21
  use tokio::time::sleep;
20
22
 
@@ -103,14 +105,14 @@ async fn many_act_fails_with_heartbeats() {
103
105
  activity_id: activity_id.to_string(),
104
106
  activity_type: "test_act".to_string(),
105
107
  task_queue: starter.get_task_queue().to_string(),
106
- start_to_close_timeout: Some(Duration::from_secs(30).into()),
108
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
107
109
  retry_policy: Some(RetryPolicy {
108
- initial_interval: Some(Duration::from_millis(10).into()),
110
+ initial_interval: Some(prost_dur!(from_millis(10))),
109
111
  backoff_coefficient: 1.0,
110
112
  maximum_attempts: 4,
111
113
  ..Default::default()
112
114
  }),
113
- heartbeat_timeout: Some(Duration::from_secs(1).into()),
115
+ heartbeat_timeout: Some(prost_dur!(from_secs(1))),
114
116
  ..Default::default()
115
117
  }
116
118
  .into(),
@@ -166,3 +168,51 @@ async fn many_act_fails_with_heartbeats() {
166
168
  core.complete_execution(&task.run_id).await;
167
169
  core.shutdown().await;
168
170
  }
171
+
172
+ #[tokio::test]
173
+ async fn activity_doesnt_heartbeat_hits_timeout_then_completes() {
174
+ let wf_name = "activity_doesnt_heartbeat_hits_timeout_then_completes";
175
+ let mut starter = CoreWfStarter::new(wf_name);
176
+ let mut worker = starter.worker().await;
177
+ let client = starter.get_client().await;
178
+ worker.register_activity(
179
+ "echo_activity",
180
+ |_ctx: ActContext, echo_me: String| async move {
181
+ sleep(Duration::from_secs(4)).await;
182
+ Ok(echo_me)
183
+ },
184
+ );
185
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
186
+ let res = ctx
187
+ .activity(ActivityOptions {
188
+ activity_type: "echo_activity".to_string(),
189
+ input: "hi!".as_json_payload().expect("serializes fine"),
190
+ start_to_close_timeout: Some(Duration::from_secs(10)),
191
+ heartbeat_timeout: Some(Duration::from_secs(2)),
192
+ retry_policy: Some(RetryPolicy {
193
+ maximum_attempts: 1,
194
+ ..Default::default()
195
+ }),
196
+ ..Default::default()
197
+ })
198
+ .await;
199
+ assert!(res.timed_out());
200
+ Ok(().into())
201
+ });
202
+
203
+ let run_id = worker
204
+ .submit_wf(
205
+ wf_name.to_owned(),
206
+ wf_name.to_owned(),
207
+ vec![],
208
+ WorkflowOptions::default(),
209
+ )
210
+ .await
211
+ .unwrap();
212
+ worker.run_until_done().await.unwrap();
213
+ let handle = client.get_untyped_workflow_handle(wf_name, run_id);
214
+ handle
215
+ .get_workflow_result(Default::default())
216
+ .await
217
+ .unwrap();
218
+ }
@@ -36,7 +36,7 @@ async fn out_of_order_completion_doesnt_hang() {
36
36
  ),
37
37
  StartTimer {
38
38
  seq: 1,
39
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
39
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
40
40
  }
41
41
  .into(),
42
42
  ]
@@ -24,12 +24,12 @@ async fn simple_query_legacy() {
24
24
  vec![
25
25
  StartTimer {
26
26
  seq: 0,
27
- start_to_fire_timeout: Some(Duration::from_millis(500).into()),
27
+ start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
28
28
  }
29
29
  .into(),
30
30
  StartTimer {
31
31
  seq: 1,
32
- start_to_fire_timeout: Some(Duration::from_secs(3).into()),
32
+ start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
33
33
  }
34
34
  .into(),
35
35
  ],
@@ -343,12 +343,12 @@ async fn fail_legacy_query() {
343
343
  let t1_resp = vec![
344
344
  StartTimer {
345
345
  seq: 1,
346
- start_to_fire_timeout: Some(Duration::from_millis(500).into()),
346
+ start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
347
347
  }
348
348
  .into(),
349
349
  StartTimer {
350
350
  seq: 2,
351
- start_to_fire_timeout: Some(Duration::from_secs(3).into()),
351
+ start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
352
352
  }
353
353
  .into(),
354
354
  ];
@@ -0,0 +1,93 @@
1
+ use assert_matches::assert_matches;
2
+ use std::{sync::Arc, time::Duration};
3
+ use temporal_client::{
4
+ ListClosedFilters, ListOpenFilters, Namespace, StartTimeFilter, WorkflowClientTrait,
5
+ WorkflowExecutionFilter, WorkflowOptions,
6
+ };
7
+ use temporal_sdk_core_protos::coresdk::workflow_activation::{
8
+ workflow_activation_job, WorkflowActivationJob,
9
+ };
10
+ use temporal_sdk_core_test_utils::{
11
+ get_integ_server_options, CoreWfStarter, WorkerTestHelpers, NAMESPACE,
12
+ };
13
+
14
+ #[tokio::test]
15
+ async fn client_list_open_closed_workflow_executions() {
16
+ let wf_name = "client_list_open_closed_workflow_executions".to_owned();
17
+ let mut starter = CoreWfStarter::new(&wf_name);
18
+ let core = starter.get_worker().await;
19
+ let client = starter.get_client().await;
20
+
21
+ let earliest = std::time::SystemTime::now();
22
+ let latest = earliest + Duration::from_secs(60);
23
+
24
+ // start workflow
25
+ let run_id = starter
26
+ .start_wf_with_id(wf_name.to_owned(), WorkflowOptions::default())
27
+ .await;
28
+ let task = core.poll_workflow_activation().await.unwrap();
29
+ assert_matches!(
30
+ task.jobs.as_slice(),
31
+ [WorkflowActivationJob {
32
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
33
+ }]
34
+ );
35
+
36
+ // List above OPEN workflow
37
+ let start_time_filter = StartTimeFilter {
38
+ earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
39
+ latest_time: Some(latest).and_then(|t| t.try_into().ok()),
40
+ };
41
+ let filter = ListOpenFilters::ExecutionFilter(WorkflowExecutionFilter {
42
+ workflow_id: wf_name.clone(),
43
+ run_id: "".to_owned(),
44
+ });
45
+ let open_workflows = client
46
+ .list_open_workflow_executions(1, Default::default(), Some(start_time_filter), Some(filter))
47
+ .await
48
+ .unwrap();
49
+ assert_eq!(open_workflows.executions.len(), 1);
50
+ let workflow = open_workflows.executions[0].clone();
51
+ assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
52
+
53
+ // Complete workflow
54
+ core.complete_execution(&task.run_id).await;
55
+
56
+ // List above CLOSED workflow
57
+ let start_time_filter = StartTimeFilter {
58
+ earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
59
+ latest_time: Some(latest).and_then(|t| t.try_into().ok()),
60
+ };
61
+ let filter = ListClosedFilters::ExecutionFilter(WorkflowExecutionFilter {
62
+ workflow_id: wf_name.clone(),
63
+ run_id,
64
+ });
65
+ let closed_workflows = client
66
+ .list_closed_workflow_executions(
67
+ 1,
68
+ Default::default(),
69
+ Some(start_time_filter),
70
+ Some(filter),
71
+ )
72
+ .await
73
+ .unwrap();
74
+ assert_eq!(closed_workflows.executions.len(), 1);
75
+ let workflow = closed_workflows.executions[0].clone();
76
+ assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
77
+ }
78
+
79
+ #[tokio::test]
80
+ async fn client_describe_namespace() {
81
+ let client = Arc::new(
82
+ get_integ_server_options()
83
+ .connect(NAMESPACE.to_owned(), None, None)
84
+ .await
85
+ .expect("Must connect"),
86
+ );
87
+
88
+ let namespace_result = client
89
+ .describe_namespace(Namespace::Name(NAMESPACE.to_owned()))
90
+ .await
91
+ .unwrap();
92
+ assert_eq!(namespace_result.namespace_info.unwrap().name, NAMESPACE);
93
+ }
@@ -1,12 +1,13 @@
1
1
  use assert_matches::assert_matches;
2
2
  use std::time::Duration;
3
3
  use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowExecutionResult, WorkflowOptions};
4
- use temporal_sdk::{ActContext, ActivityOptions, CancellableFuture, WfContext, WorkflowResult};
4
+ use temporal_sdk::{
5
+ ActContext, ActExitValue, ActivityOptions, CancellableFuture, WfContext, WorkflowResult,
6
+ };
5
7
  use temporal_sdk_core_protos::{
6
8
  coresdk::{
7
9
  activity_result::{
8
- self, activity_resolution, activity_resolution as act_res, ActivityExecutionResult,
9
- ActivityResolution,
10
+ self, activity_resolution as act_res, ActivityExecutionResult, ActivityResolution,
10
11
  },
11
12
  activity_task::activity_task as act_task,
12
13
  workflow_activation::{
@@ -14,13 +15,15 @@ use temporal_sdk_core_protos::{
14
15
  },
15
16
  workflow_commands::{ActivityCancellationType, RequestCancelActivity, StartTimer},
16
17
  workflow_completion::WorkflowActivationCompletion,
17
- ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
18
+ ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
19
+ IntoCompletion,
18
20
  },
19
21
  temporal::api::{
20
22
  common::v1::{ActivityType, Payload, Payloads},
21
23
  enums::v1::RetryState,
22
24
  failure::v1::{failure::FailureInfo, ActivityFailureInfo, Failure},
23
25
  },
26
+ TaskToken,
24
27
  };
25
28
  use temporal_sdk_core_test_utils::{
26
29
  init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
@@ -297,7 +300,7 @@ async fn activity_cancellation_try_cancel() {
297
300
  ),
298
301
  StartTimer {
299
302
  seq: 1,
300
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
303
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
301
304
  }
302
305
  .into(),
303
306
  ]
@@ -359,7 +362,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
359
362
  ),
360
363
  StartTimer {
361
364
  seq: 1,
362
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
365
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
363
366
  }
364
367
  .into(),
365
368
  ]
@@ -390,7 +393,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
390
393
  variant: Some(workflow_activation_job::Variant::ResolveActivity(
391
394
  ResolveActivity {
392
395
  result: Some(ActivityResolution {
393
- status: Some(activity_resolution::Status::Cancelled(_))
396
+ status: Some(act_res::Status::Cancelled(_))
394
397
  }),
395
398
  ..
396
399
  }
@@ -403,7 +406,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
403
406
  task.run_id,
404
407
  vec![StartTimer {
405
408
  seq: 2,
406
- start_to_fire_timeout: Some(Duration::from_millis(100).into()),
409
+ start_to_fire_timeout: Some(prost_dur!(from_millis(100))),
407
410
  }
408
411
  .into()],
409
412
  ))
@@ -506,7 +509,7 @@ async fn activity_cancellation_wait_cancellation_completed() {
506
509
  ),
507
510
  StartTimer {
508
511
  seq: 1,
509
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
512
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
510
513
  }
511
514
  .into(),
512
515
  ]
@@ -573,7 +576,7 @@ async fn activity_cancellation_abandon() {
573
576
  ),
574
577
  StartTimer {
575
578
  seq: 1,
576
- start_to_fire_timeout: Some(Duration::from_millis(50).into()),
579
+ start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
577
580
  }
578
581
  .into(),
579
582
  ]
@@ -793,3 +796,83 @@ async fn one_activity_abandon_cancelled_after_complete() {
793
796
  .unwrap();
794
797
  assert_matches!(res, WorkflowExecutionResult::Succeeded(_));
795
798
  }
799
+
800
+ #[tokio::test]
801
+ async fn it_can_complete_async() {
802
+ use std::sync::Arc;
803
+ use tokio::sync::Mutex;
804
+
805
+ let wf_name = "it_can_complete_async".to_owned();
806
+ let mut starter = CoreWfStarter::new(&wf_name);
807
+ let mut worker = starter.worker().await;
808
+ let client = starter.get_client().await;
809
+ let async_response = "agence";
810
+ let shared_token: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
811
+ worker.register_wf(wf_name.clone(), move |ctx: WfContext| async move {
812
+ let activity_resolution = ctx
813
+ .activity(ActivityOptions {
814
+ activity_type: "complete_async_activity".to_string(),
815
+ input: "hi".as_json_payload().expect("serializes fine"),
816
+ start_to_close_timeout: Some(Duration::from_secs(30)),
817
+ ..Default::default()
818
+ })
819
+ .await;
820
+
821
+ let res = match activity_resolution.status {
822
+ Some(act_res::Status::Completed(activity_result::Success { result })) => result
823
+ .map(|p| String::from_json_payload(&p).unwrap())
824
+ .unwrap(),
825
+ _ => panic!("activity task failed {:?}", activity_resolution),
826
+ };
827
+
828
+ assert_eq!(&res, async_response);
829
+ Ok(().into())
830
+ });
831
+
832
+ let shared_token_ref = shared_token.clone();
833
+ worker.register_activity(
834
+ "complete_async_activity",
835
+ move |ctx: ActContext, _: String| {
836
+ let shared_token_ref = shared_token_ref.clone();
837
+ async move {
838
+ // set the `activity_task_token`
839
+ let activity_info = ctx.get_info();
840
+ let task_token = &activity_info.task_token;
841
+ let mut shared = shared_token_ref.lock().await;
842
+ *shared = Some(task_token.clone());
843
+ Ok::<ActExitValue<()>, _>(ActExitValue::WillCompleteAsync)
844
+ }
845
+ },
846
+ );
847
+
848
+ let shared_token_ref2 = shared_token.clone();
849
+ tokio::spawn(async move {
850
+ loop {
851
+ let mut shared = shared_token_ref2.lock().await;
852
+ let maybe_token = shared.take();
853
+
854
+ if let Some(task_token) = maybe_token {
855
+ client
856
+ .complete_activity_task(
857
+ TaskToken(task_token),
858
+ Some(async_response.as_json_payload().unwrap().into()),
859
+ )
860
+ .await
861
+ .unwrap();
862
+ return;
863
+ }
864
+ }
865
+ });
866
+
867
+ let _run_id = worker
868
+ .submit_wf(
869
+ wf_name.to_owned(),
870
+ wf_name.to_owned(),
871
+ vec![],
872
+ WorkflowOptions::default(),
873
+ )
874
+ .await
875
+ .unwrap();
876
+
877
+ worker.run_until_done().await.unwrap();
878
+ }
@@ -39,7 +39,7 @@ async fn cancel_during_timer() {
39
39
  tokio::time::sleep(Duration::from_millis(500)).await;
40
40
  // Cancel the workflow externally
41
41
  client
42
- .cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string())
42
+ .cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string(), None)
43
43
  .await
44
44
  .unwrap();
45
45
  };
@@ -189,10 +189,10 @@ async fn local_act_retry_timer_backoff() {
189
189
  activity_type: "echo".to_string(),
190
190
  input: "hi".as_json_payload().expect("serializes fine"),
191
191
  retry_policy: RetryPolicy {
192
- initial_interval: Some(Duration::from_micros(15).into()),
192
+ initial_interval: Some(prost_dur!(from_micros(15))),
193
193
  // We want two local backoffs that are short. Third backoff will use timer
194
194
  backoff_coefficient: 1_000.,
195
- maximum_interval: Some(Duration::from_millis(1500).into()),
195
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
196
196
  maximum_attempts: 4,
197
197
  non_retryable_error_types: vec![],
198
198
  },
@@ -318,9 +318,9 @@ async fn cancel_after_act_starts(
318
318
  activity_type: "echo".to_string(),
319
319
  input: "hi".as_json_payload().expect("serializes fine"),
320
320
  retry_policy: RetryPolicy {
321
- initial_interval: Some(bo_dur.into()),
321
+ initial_interval: Some(bo_dur.try_into().unwrap()),
322
322
  backoff_coefficient: 1.,
323
- maximum_interval: Some(bo_dur.into()),
323
+ maximum_interval: Some(bo_dur.try_into().unwrap()),
324
324
  // Retry forever until cancelled
325
325
  ..Default::default()
326
326
  },
@@ -410,9 +410,9 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
410
410
  activity_type: "echo".to_string(),
411
411
  input: "hi".as_json_payload().expect("serializes fine"),
412
412
  retry_policy: RetryPolicy {
413
- initial_interval: Some(Duration::from_micros(15).into()),
413
+ initial_interval: Some(prost_dur!(from_micros(15))),
414
414
  backoff_coefficient: 1_000.,
415
- maximum_interval: Some(Duration::from_millis(1500).into()),
415
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
416
416
  maximum_attempts: 4,
417
417
  non_retryable_error_types: vec![],
418
418
  },
@@ -467,9 +467,9 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
467
467
  activity_type: "echo".to_string(),
468
468
  input: "hi".as_json_payload().expect("serializes fine"),
469
469
  retry_policy: RetryPolicy {
470
- initial_interval: Some(Duration::from_micros(15).into()),
470
+ initial_interval: Some(prost_dur!(from_micros(15))),
471
471
  backoff_coefficient: 1_000.,
472
- maximum_interval: Some(Duration::from_millis(1500).into()),
472
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
473
473
  maximum_attempts: 40,
474
474
  non_retryable_error_types: vec![],
475
475
  },
@@ -538,9 +538,9 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
538
538
  activity_type: "echo".to_string(),
539
539
  input: "hi".as_json_payload().expect("serializes fine"),
540
540
  retry_policy: RetryPolicy {
541
- initial_interval: Some(Duration::from_micros(15).into()),
541
+ initial_interval: Some(prost_dur!(from_micros(15))),
542
542
  backoff_coefficient: 1_000.,
543
- maximum_interval: Some(Duration::from_millis(1500).into()),
543
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
544
544
  maximum_attempts: 4,
545
545
  non_retryable_error_types: vec![],
546
546
  },
@@ -551,9 +551,9 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
551
551
  activity_type: "echo".to_string(),
552
552
  input: "hi".as_json_payload().expect("serializes fine"),
553
553
  retry_policy: RetryPolicy {
554
- initial_interval: Some(Duration::from_millis(15).into()),
554
+ initial_interval: Some(prost_dur!(from_millis(15))),
555
555
  backoff_coefficient: 10.,
556
- maximum_interval: Some(Duration::from_millis(1500).into()),
556
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
557
557
  maximum_attempts: 4,
558
558
  non_retryable_error_types: vec![],
559
559
  },
@@ -593,9 +593,9 @@ async fn repro_nondeterminism_with_timer_bug() {
593
593
  activity_type: "delay".to_string(),
594
594
  input: "hi".as_json_payload().expect("serializes fine"),
595
595
  retry_policy: RetryPolicy {
596
- initial_interval: Some(Duration::from_micros(15).into()),
596
+ initial_interval: Some(prost_dur!(from_micros(15))),
597
597
  backoff_coefficient: 1_000.,
598
- maximum_interval: Some(Duration::from_millis(1500).into()),
598
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
599
599
  maximum_attempts: 4,
600
600
  non_retryable_error_types: vec![],
601
601
  },
@@ -30,7 +30,7 @@ async fn timer_workflow_replay() {
30
30
  task.run_id,
31
31
  vec![StartTimer {
32
32
  seq: 0,
33
- start_to_fire_timeout: Some(Duration::from_secs(1).into()),
33
+ start_to_fire_timeout: Some(prost_dur!(from_secs(1))),
34
34
  }
35
35
  .into()],
36
36
  ))
@@ -46,13 +46,12 @@ async fn reset_workflow() {
46
46
  .await
47
47
  .unwrap();
48
48
 
49
- let client = starter.get_client().await;
49
+ let mut client = starter.get_client().await;
50
+ let client = Arc::make_mut(&mut client);
50
51
  let resetter_fut = async {
51
52
  notify.notified().await;
52
53
  // Do the reset
53
54
  client
54
- .get_client()
55
- .raw_retry_client()
56
55
  .reset_workflow_execution(ResetWorkflowExecutionRequest {
57
56
  namespace: NAMESPACE.to_owned(),
58
57
  workflow_execution: Some(WorkflowExecution {
@@ -69,15 +68,16 @@ async fn reset_workflow() {
69
68
 
70
69
  // Unblock the workflow by sending the signal. Run ID will have changed after reset so
71
70
  // we use empty run id
72
- client
73
- .signal_workflow_execution(
74
- wf_name.to_owned(),
75
- "".to_owned(),
76
- POST_RESET_SIG.to_owned(),
77
- None,
78
- )
79
- .await
80
- .unwrap();
71
+ WorkflowClientTrait::signal_workflow_execution(
72
+ client,
73
+ wf_name.to_owned(),
74
+ "".to_owned(),
75
+ POST_RESET_SIG.to_owned(),
76
+ None,
77
+ None,
78
+ )
79
+ .await
80
+ .unwrap();
81
81
 
82
82
  // Wait for the now-reset workflow to finish
83
83
  client