@temporalio/core-bridge 1.12.2 → 1.12.3

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 (57) hide show
  1. package/package.json +3 -3
  2. package/releases/aarch64-apple-darwin/index.node +0 -0
  3. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  4. package/releases/x86_64-apple-darwin/index.node +0 -0
  5. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  6. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  7. package/sdk-core/.cargo/config.toml +1 -1
  8. package/sdk-core/client/src/callback_based.rs +123 -0
  9. package/sdk-core/client/src/lib.rs +96 -28
  10. package/sdk-core/client/src/metrics.rs +33 -5
  11. package/sdk-core/client/src/raw.rs +40 -1
  12. package/sdk-core/client/src/retry.rs +12 -3
  13. package/sdk-core/core/src/lib.rs +4 -2
  14. package/sdk-core/core/src/pollers/poll_buffer.rs +62 -14
  15. package/sdk-core/core/src/worker/client.rs +9 -5
  16. package/sdk-core/core/src/worker/heartbeat.rs +3 -1
  17. package/sdk-core/core-api/src/worker.rs +2 -2
  18. package/sdk-core/core-c-bridge/Cargo.toml +2 -0
  19. package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +105 -0
  20. package/sdk-core/core-c-bridge/src/client.rs +265 -8
  21. package/sdk-core/core-c-bridge/src/tests/context.rs +11 -0
  22. package/sdk-core/core-c-bridge/src/tests/mod.rs +179 -3
  23. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/CODEOWNERS +1 -1
  24. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +1 -1
  25. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -1
  26. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +1 -0
  27. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +83 -0
  28. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +37 -0
  29. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/connectivityrule/v1/message.proto +64 -0
  30. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +3 -1
  31. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +10 -0
  32. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +1 -0
  33. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +644 -9
  34. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +635 -21
  35. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +60 -2
  36. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +84 -15
  37. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -0
  38. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +11 -0
  39. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +5 -0
  40. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +1 -1
  41. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/worker_config.proto +36 -0
  42. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +29 -0
  43. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +11 -1
  44. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +122 -4
  45. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +41 -0
  46. package/sdk-core/sdk-core-protos/src/lib.rs +5 -1
  47. package/sdk-core/test-utils/Cargo.toml +1 -0
  48. package/sdk-core/test-utils/src/lib.rs +90 -3
  49. package/sdk-core/tests/cloud_tests.rs +11 -74
  50. package/sdk-core/tests/integ_tests/client_tests.rs +14 -10
  51. package/sdk-core/tests/integ_tests/worker_tests.rs +8 -2
  52. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +13 -0
  53. package/sdk-core/tests/integ_tests/workflow_tests/priority.rs +2 -108
  54. package/sdk-core/tests/main.rs +3 -0
  55. package/sdk-core/tests/shared_tests/mod.rs +43 -0
  56. package/sdk-core/tests/shared_tests/priority.rs +155 -0
  57. package/src/client.rs +5 -0
@@ -121,7 +121,9 @@ impl WorkerHeartbeatData {
121
121
  start_time: SystemTime::now(),
122
122
  heartbeat_time: None,
123
123
  worker_instance_key: Uuid::new_v4().to_string(),
124
- heartbeat_interval: worker_config.heartbeat_interval,
124
+ heartbeat_interval: worker_config
125
+ .heartbeat_interval
126
+ .expect("WorkerHeartbeatData is only called when heartbeat_interval is Some"),
125
127
  reset_notify,
126
128
  }
127
129
  }
@@ -165,8 +165,8 @@ pub struct WorkerConfig {
165
165
  /// The interval within which the worker will send a heartbeat.
166
166
  /// The timer is reset on each existing RPC call that also happens to send this data, like
167
167
  /// `PollWorkflowTaskQueueRequest`.
168
- #[builder(default = "Duration::from_secs(60)")]
169
- pub heartbeat_interval: Duration,
168
+ #[builder(default)]
169
+ pub heartbeat_interval: Option<Duration>,
170
170
  }
171
171
 
172
172
  impl WorkerConfig {
@@ -10,6 +10,8 @@ crate-type = ["cdylib"]
10
10
  [dependencies]
11
11
  anyhow = "1.0"
12
12
  async-trait = "0.1"
13
+ futures-util = { version = "0.3", default-features = false }
14
+ http = "1.1"
13
15
  libc = "0.2"
14
16
  prost = { workspace = true }
15
17
  # We rely on Cargo semver rules not updating a 0.x to 0.y. Per the rand
@@ -58,6 +58,15 @@ typedef struct TemporalCoreCancellationToken TemporalCoreCancellationToken;
58
58
 
59
59
  typedef struct TemporalCoreClient TemporalCoreClient;
60
60
 
61
+ /**
62
+ * Representation of gRPC request for the callback.
63
+ *
64
+ * Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
65
+ * each request. Each request _must_ call that and the request can no longer be valid after that
66
+ * call.
67
+ */
68
+ typedef struct TemporalCoreClientGrpcOverrideRequest TemporalCoreClientGrpcOverrideRequest;
69
+
61
70
  typedef struct TemporalCoreEphemeralServer TemporalCoreEphemeralServer;
62
71
 
63
72
  typedef struct TemporalCoreForwardedLog TemporalCoreForwardedLog;
@@ -114,6 +123,20 @@ typedef struct TemporalCoreClientHttpConnectProxyOptions {
114
123
  struct TemporalCoreByteArrayRef password;
115
124
  } TemporalCoreClientHttpConnectProxyOptions;
116
125
 
126
+ /**
127
+ * Callback that is invoked for every gRPC call if set on the client options.
128
+ *
129
+ * Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
130
+ * each request. Each request _must_ call that and the request can no longer be valid after that
131
+ * call. However, all of that work and the respond call may be done well after this callback
132
+ * returns. No data lifetime is related to the callback invocation itself.
133
+ *
134
+ * Implementers should return as soon as possible and perform the network request in the
135
+ * background.
136
+ */
137
+ typedef void (*TemporalCoreClientGrpcOverrideCallback)(struct TemporalCoreClientGrpcOverrideRequest *request,
138
+ void *user_data);
139
+
117
140
  typedef struct TemporalCoreClientOptions {
118
141
  struct TemporalCoreByteArrayRef target_url;
119
142
  struct TemporalCoreByteArrayRef client_name;
@@ -125,6 +148,21 @@ typedef struct TemporalCoreClientOptions {
125
148
  const struct TemporalCoreClientRetryOptions *retry_options;
126
149
  const struct TemporalCoreClientKeepAliveOptions *keep_alive_options;
127
150
  const struct TemporalCoreClientHttpConnectProxyOptions *http_connect_proxy_options;
151
+ /**
152
+ * If this is set, all gRPC calls go through it and no connection is made to server. The client
153
+ * connection call usually calls this for "GetSystemInfo" before the connect is complete. See
154
+ * the callback documentation for more important information about usage and data lifetimes.
155
+ *
156
+ * When a callback is set, target_url is not used to connect, but it must be set to a valid URL
157
+ * anyways in case it is used for logging or other reasons. Similarly, other connect-specific
158
+ * fields like tls_options, keep_alive_options, and http_connect_proxy_options will be
159
+ * completely ignored if a callback is set.
160
+ */
161
+ TemporalCoreClientGrpcOverrideCallback grpc_override_callback;
162
+ /**
163
+ * Optional user data passed to each callback call.
164
+ */
165
+ void *grpc_override_callback_user_data;
128
166
  } TemporalCoreClientOptions;
129
167
 
130
168
  typedef struct TemporalCoreByteArray {
@@ -147,6 +185,36 @@ typedef void (*TemporalCoreClientConnectCallback)(void *user_data,
147
185
  struct TemporalCoreClient *success,
148
186
  const struct TemporalCoreByteArray *fail);
149
187
 
188
+ /**
189
+ * Response provided to temporal_core_client_grpc_override_request_respond. All values referenced
190
+ * inside here must live until that call returns.
191
+ */
192
+ typedef struct TemporalCoreClientGrpcOverrideResponse {
193
+ /**
194
+ * Numeric gRPC status code, see https://grpc.io/docs/guides/status-codes/. 0 is success, non-0
195
+ * is failure.
196
+ */
197
+ int32_t status_code;
198
+ /**
199
+ * Headers for the response if any. Note, this is meant for user-defined metadata/headers, and
200
+ * not the gRPC system headers (like :status or content-type).
201
+ */
202
+ TemporalCoreMetadataRef headers;
203
+ /**
204
+ * Protobuf bytes for a successful response. Ignored if status_code is non-0.
205
+ */
206
+ struct TemporalCoreByteArrayRef success_proto;
207
+ /**
208
+ * UTF-8 failure message. Ignored if status_code is 0.
209
+ */
210
+ struct TemporalCoreByteArrayRef fail_message;
211
+ /**
212
+ * Optional details for the gRPC failure. If non-empty, this should be a protobuf-serialized
213
+ * google.rpc.Status. Ignored if status_code is 0.
214
+ */
215
+ struct TemporalCoreByteArrayRef fail_details;
216
+ } TemporalCoreClientGrpcOverrideResponse;
217
+
150
218
  typedef struct TemporalCoreRpcCallOptions {
151
219
  enum TemporalCoreRpcService service;
152
220
  struct TemporalCoreByteArrayRef rpc;
@@ -656,6 +724,43 @@ void temporal_core_client_update_metadata(struct TemporalCoreClient *client,
656
724
  void temporal_core_client_update_api_key(struct TemporalCoreClient *client,
657
725
  struct TemporalCoreByteArrayRef api_key);
658
726
 
727
+ /**
728
+ * Get a reference to the service name.
729
+ *
730
+ * Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
731
+ */
732
+ struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_service(const struct TemporalCoreClientGrpcOverrideRequest *req);
733
+
734
+ /**
735
+ * Get a reference to the RPC name.
736
+ *
737
+ * Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
738
+ */
739
+ struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_rpc(const struct TemporalCoreClientGrpcOverrideRequest *req);
740
+
741
+ /**
742
+ * Get a reference to the service headers.
743
+ *
744
+ * Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
745
+ */
746
+ TemporalCoreMetadataRef temporal_core_client_grpc_override_request_headers(const struct TemporalCoreClientGrpcOverrideRequest *req);
747
+
748
+ /**
749
+ * Get a reference to the request protobuf bytes.
750
+ *
751
+ * Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
752
+ */
753
+ struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_proto(const struct TemporalCoreClientGrpcOverrideRequest *req);
754
+
755
+ /**
756
+ * Complete the request, freeing all request data.
757
+ *
758
+ * The data referenced in the response must live until this function returns. Once this call is
759
+ * made, none of the request data should be considered valid.
760
+ */
761
+ void temporal_core_client_grpc_override_request_respond(struct TemporalCoreClientGrpcOverrideRequest *req,
762
+ struct TemporalCoreClientGrpcOverrideResponse resp);
763
+
659
764
  /**
660
765
  * Client, options, and user data must live through callback.
661
766
  */
@@ -1,18 +1,22 @@
1
- use crate::ByteArray;
2
- use crate::ByteArrayRef;
3
- use crate::CancellationToken;
4
- use crate::MetadataRef;
5
- use crate::UserDataHandle;
6
- use crate::runtime::Runtime;
1
+ use crate::{
2
+ ByteArray, ByteArrayRef, CancellationToken, MetadataRef, UserDataHandle, runtime::Runtime,
3
+ };
7
4
 
5
+ use futures_util::FutureExt;
6
+ use prost::bytes::Bytes;
7
+ use std::cell::OnceCell;
8
8
  use std::str::FromStr;
9
+ use std::sync::Arc;
10
+ use std::sync::atomic::AtomicBool;
11
+ use std::sync::atomic::Ordering;
9
12
  use std::time::Duration;
10
13
  use temporal_client::{
11
14
  ClientKeepAliveConfig, ClientOptions as CoreClientOptions, ClientOptionsBuilder,
12
15
  ClientTlsConfig, CloudService, ConfiguredClient, HealthService, HttpConnectProxyOptions,
13
16
  OperatorService, RetryClient, RetryConfig, TemporalServiceClientWithMetrics, TestService,
14
- TlsConfig, WorkflowService,
17
+ TlsConfig, WorkflowService, callback_based,
15
18
  };
19
+ use tokio::sync::oneshot;
16
20
  use tonic::metadata::MetadataKey;
17
21
  use url::Url;
18
22
 
@@ -28,6 +32,17 @@ pub struct ClientOptions {
28
32
  pub retry_options: *const ClientRetryOptions,
29
33
  pub keep_alive_options: *const ClientKeepAliveOptions,
30
34
  pub http_connect_proxy_options: *const ClientHttpConnectProxyOptions,
35
+ /// If this is set, all gRPC calls go through it and no connection is made to server. The client
36
+ /// connection call usually calls this for "GetSystemInfo" before the connect is complete. See
37
+ /// the callback documentation for more important information about usage and data lifetimes.
38
+ ///
39
+ /// When a callback is set, target_url is not used to connect, but it must be set to a valid URL
40
+ /// anyways in case it is used for logging or other reasons. Similarly, other connect-specific
41
+ /// fields like tls_options, keep_alive_options, and http_connect_proxy_options will be
42
+ /// completely ignored if a callback is set.
43
+ pub grpc_override_callback: ClientGrpcOverrideCallback,
44
+ /// Optional user data passed to each callback call.
45
+ pub grpc_override_callback_user_data: *mut libc::c_void,
31
46
  }
32
47
 
33
48
  #[repr(C)]
@@ -106,12 +121,19 @@ pub extern "C" fn temporal_core_client_connect(
106
121
  return;
107
122
  }
108
123
  };
124
+ // Create override if present
125
+ let service_override = options.grpc_override_callback.map(|cb| {
126
+ create_callback_based_grpc_service(runtime, cb, options.grpc_override_callback_user_data)
127
+ });
109
128
  // Spawn async call
110
129
  let user_data = UserDataHandle(user_data);
111
130
  let core = runtime.core.clone();
112
131
  runtime.core.tokio_handle().spawn(async move {
113
132
  match core_options
114
- .connect_no_namespace(core.telemetry().get_temporal_metric_meter())
133
+ .connect_no_namespace_with_service_override(
134
+ core.telemetry().get_temporal_metric_meter(),
135
+ service_override,
136
+ )
115
137
  .await
116
138
  {
117
139
  Ok(core) => {
@@ -136,6 +158,75 @@ pub extern "C" fn temporal_core_client_connect(
136
158
  });
137
159
  }
138
160
 
161
+ fn create_callback_based_grpc_service(
162
+ runtime: &Runtime,
163
+ cb: unsafe extern "C" fn(request: *mut ClientGrpcOverrideRequest, user_data: *mut libc::c_void),
164
+ user_data: *mut libc::c_void,
165
+ ) -> callback_based::CallbackBasedGrpcService {
166
+ let runtime = runtime.clone();
167
+ let user_data = Arc::new(UserDataHandle(user_data));
168
+ callback_based::CallbackBasedGrpcService {
169
+ callback: Arc::new(move |req| {
170
+ let runtime = runtime.clone();
171
+ let user_data = user_data.clone();
172
+ async move {
173
+ // Create a oneshot sender/receiver for the result
174
+ let (sender, receiver) = oneshot::channel();
175
+
176
+ // Create boxed request that is dropped when the caller sets the response. If the
177
+ // caller does not, this will be a memory leak.
178
+ //
179
+ // We have to cast this to a literal pointer integer because we use spawn_blocking
180
+ // and Rust can't validate things in either of two approaches. The first approach,
181
+ // just moving the *mut to spawn_blocking closure, will not work because it is not
182
+ // send (even if you wrap it in a marked-send struct). The second, approach, moving
183
+ // the box to the closure and into_raw'ing it there won't work because Rust thinks
184
+ // the "req" param to spawn_blocking may outlive this closure even though we're
185
+ // confident in our oneshot use this will never happen.
186
+ let req_ptr = Box::into_raw(Box::new(ClientGrpcOverrideRequest {
187
+ core: req,
188
+ built_headers: OnceCell::new(),
189
+ response_sender: sender,
190
+ })) as usize;
191
+
192
+ // We want to make sure it reached user code. If spawn_blocking fails _and_ it
193
+ // didn't reach user code, it is on us to drop the box.
194
+ let reached_user_code = Arc::new(AtomicBool::new(false));
195
+
196
+ // Spawn the callback as blocking, failing on join failure. We use spawn_blocking
197
+ // just in case the user is doing something blocking in their closure, but we ask
198
+ // them not to.
199
+ let reached_user_code_clone = reached_user_code.clone();
200
+ let spawn_ret = runtime
201
+ .core
202
+ .tokio_handle()
203
+ .spawn_blocking(move || unsafe {
204
+ reached_user_code_clone.store(true, Ordering::Relaxed);
205
+ cb(
206
+ req_ptr as *mut ClientGrpcOverrideRequest,
207
+ user_data.clone().0,
208
+ );
209
+ })
210
+ .await;
211
+ if let Err(err) = spawn_ret {
212
+ // Re-own box so it can be dropped if never reached user code
213
+ if !reached_user_code.load(Ordering::Relaxed) {
214
+ let _ = unsafe { Box::from_raw(req_ptr as *mut ClientGrpcOverrideRequest) };
215
+ }
216
+ return Err(tonic::Status::internal(format!("{err}")));
217
+ }
218
+
219
+ // Wait result and return. The receiver failure in theory can never happen. If it
220
+ // does, it means somehow the sender was dropped, but our code ensures the sender
221
+ // is not dropped until a value is sent. That's why we're panicking here instead
222
+ // of turning this into a Tonic error.
223
+ receiver.await.expect("Unexpected receiver failure")
224
+ }
225
+ .boxed()
226
+ }),
227
+ }
228
+ }
229
+
139
230
  #[unsafe(no_mangle)]
140
231
  pub extern "C" fn temporal_core_client_free(client: *mut Client) {
141
232
  unsafe {
@@ -164,6 +255,164 @@ pub extern "C" fn temporal_core_client_update_api_key(client: *mut Client, api_k
164
255
  .set_api_key(api_key.to_option_string());
165
256
  }
166
257
 
258
+ /// Callback that is invoked for every gRPC call if set on the client options.
259
+ ///
260
+ /// Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
261
+ /// each request. Each request _must_ call that and the request can no longer be valid after that
262
+ /// call. However, all of that work and the respond call may be done well after this callback
263
+ /// returns. No data lifetime is related to the callback invocation itself.
264
+ ///
265
+ /// Implementers should return as soon as possible and perform the network request in the
266
+ /// background.
267
+ pub type ClientGrpcOverrideCallback = Option<
268
+ unsafe extern "C" fn(request: *mut ClientGrpcOverrideRequest, user_data: *mut libc::c_void),
269
+ >;
270
+
271
+ /// Representation of gRPC request for the callback.
272
+ ///
273
+ /// Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
274
+ /// each request. Each request _must_ call that and the request can no longer be valid after that
275
+ /// call.
276
+ pub struct ClientGrpcOverrideRequest {
277
+ core: callback_based::GrpcRequest,
278
+ built_headers: OnceCell<String>,
279
+ response_sender: oneshot::Sender<Result<callback_based::GrpcSuccessResponse, tonic::Status>>,
280
+ }
281
+
282
+ // Expected to be passed to user thread
283
+ unsafe impl Send for ClientGrpcOverrideRequest {}
284
+ unsafe impl Sync for ClientGrpcOverrideRequest {}
285
+
286
+ /// Response provided to temporal_core_client_grpc_override_request_respond. All values referenced
287
+ /// inside here must live until that call returns.
288
+ #[repr(C)]
289
+ pub struct ClientGrpcOverrideResponse {
290
+ /// Numeric gRPC status code, see https://grpc.io/docs/guides/status-codes/. 0 is success, non-0
291
+ /// is failure.
292
+ pub status_code: i32,
293
+
294
+ /// Headers for the response if any. Note, this is meant for user-defined metadata/headers, and
295
+ /// not the gRPC system headers (like :status or content-type).
296
+ pub headers: MetadataRef,
297
+
298
+ /// Protobuf bytes for a successful response. Ignored if status_code is non-0.
299
+ pub success_proto: ByteArrayRef,
300
+
301
+ /// UTF-8 failure message. Ignored if status_code is 0.
302
+ pub fail_message: ByteArrayRef,
303
+
304
+ /// Optional details for the gRPC failure. If non-empty, this should be a protobuf-serialized
305
+ /// google.rpc.Status. Ignored if status_code is 0.
306
+ pub fail_details: ByteArrayRef,
307
+ }
308
+
309
+ /// Get a reference to the service name.
310
+ ///
311
+ /// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
312
+ #[unsafe(no_mangle)]
313
+ pub extern "C" fn temporal_core_client_grpc_override_request_service(
314
+ req: *const ClientGrpcOverrideRequest,
315
+ ) -> ByteArrayRef {
316
+ let req = unsafe { &*req };
317
+ req.core.service.as_str().into()
318
+ }
319
+
320
+ /// Get a reference to the RPC name.
321
+ ///
322
+ /// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
323
+ #[unsafe(no_mangle)]
324
+ pub extern "C" fn temporal_core_client_grpc_override_request_rpc(
325
+ req: *const ClientGrpcOverrideRequest,
326
+ ) -> ByteArrayRef {
327
+ let req = unsafe { &*req };
328
+ req.core.rpc.as_str().into()
329
+ }
330
+
331
+ /// Get a reference to the service headers.
332
+ ///
333
+ /// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
334
+ #[unsafe(no_mangle)]
335
+ pub extern "C" fn temporal_core_client_grpc_override_request_headers(
336
+ req: *const ClientGrpcOverrideRequest,
337
+ ) -> MetadataRef {
338
+ let req = unsafe { &*req };
339
+ // Lazily create the headers on first access
340
+ let headers = req.built_headers.get_or_init(|| {
341
+ req.core
342
+ .headers
343
+ .iter()
344
+ .filter_map(|(name, value)| value.to_str().ok().map(|val| (name.as_str(), val)))
345
+ .flat_map(|(k, v)| [k, v])
346
+ .collect::<Vec<_>>()
347
+ .join("\n")
348
+ });
349
+ headers.as_str().into()
350
+ }
351
+
352
+ /// Get a reference to the request protobuf bytes.
353
+ ///
354
+ /// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
355
+ #[unsafe(no_mangle)]
356
+ pub extern "C" fn temporal_core_client_grpc_override_request_proto(
357
+ req: *const ClientGrpcOverrideRequest,
358
+ ) -> ByteArrayRef {
359
+ let req = unsafe { &*req };
360
+ (&*req.core.proto).into()
361
+ }
362
+
363
+ /// Complete the request, freeing all request data.
364
+ ///
365
+ /// The data referenced in the response must live until this function returns. Once this call is
366
+ /// made, none of the request data should be considered valid.
367
+ #[unsafe(no_mangle)]
368
+ pub extern "C" fn temporal_core_client_grpc_override_request_respond(
369
+ req: *mut ClientGrpcOverrideRequest,
370
+ resp: ClientGrpcOverrideResponse,
371
+ ) {
372
+ // This will be dropped at the end of this call
373
+ let req = unsafe { Box::from_raw(req) };
374
+ // Ignore failure if receiver no longer around (e.g. maybe a cancellation)
375
+ let _ = req
376
+ .response_sender
377
+ .send(resp.build_grpc_override_response());
378
+ }
379
+
380
+ impl ClientGrpcOverrideResponse {
381
+ #[allow(clippy::result_large_err)] // Tonic status, even though big, is reasonable as an Err
382
+ fn build_grpc_override_response(
383
+ self,
384
+ ) -> Result<callback_based::GrpcSuccessResponse, tonic::Status> {
385
+ let headers = Self::client_headers_from_metadata_ref(self.headers)
386
+ .map_err(tonic::Status::internal)?;
387
+ if self.status_code == 0 {
388
+ Ok(callback_based::GrpcSuccessResponse {
389
+ headers,
390
+ proto: self.success_proto.to_vec(),
391
+ })
392
+ } else {
393
+ Err(tonic::Status::with_details_and_metadata(
394
+ tonic::Code::from_i32(self.status_code),
395
+ self.fail_message.to_string(),
396
+ Bytes::copy_from_slice(self.fail_details.to_slice()),
397
+ tonic::metadata::MetadataMap::from_headers(headers),
398
+ ))
399
+ }
400
+ }
401
+
402
+ fn client_headers_from_metadata_ref(headers: MetadataRef) -> Result<http::HeaderMap, String> {
403
+ let key_values = headers.to_str_map_on_newlines();
404
+ let mut header_map = http::HeaderMap::with_capacity(key_values.len());
405
+ for (k, v) in key_values.into_iter() {
406
+ let name = http::HeaderName::try_from(k)
407
+ .map_err(|e| format!("Invalid header name '{k}': {e}"))?;
408
+ let value = http::HeaderValue::from_str(v)
409
+ .map_err(|e| format!("Invalid header value '{v}': {e}"))?;
410
+ header_map.insert(name, value);
411
+ }
412
+ Ok(header_map)
413
+ }
414
+ }
415
+
167
416
  #[repr(C)]
168
417
  pub struct RpcCallOptions {
169
418
  pub service: RpcService,
@@ -326,6 +575,7 @@ async fn call_workflow_service(
326
575
  "DescribeWorkflowExecution" => rpc_call!(client, call, describe_workflow_execution),
327
576
  "DescribeWorkflowRule" => rpc_call!(client, call, describe_workflow_rule),
328
577
  "ExecuteMultiOperation" => rpc_call!(client, call, execute_multi_operation),
578
+ "FetchWorkerConfig" => rpc_call!(client, call, fetch_worker_config),
329
579
  "GetClusterInfo" => rpc_call!(client, call, get_cluster_info),
330
580
  "GetCurrentDeployment" => rpc_call!(client, call, get_current_deployment),
331
581
  "GetDeploymentReachability" => rpc_call!(client, call, get_deployment_reachability),
@@ -419,6 +669,8 @@ async fn call_workflow_service(
419
669
  }
420
670
  "UpdateNamespace" => rpc_call_on_trait!(client, call, WorkflowService, update_namespace),
421
671
  "UpdateSchedule" => rpc_call!(client, call, update_schedule),
672
+ "UpdateTaskQueueConfig" => rpc_call!(client, call, update_task_queue_config),
673
+ "UpdateWorkerConfig" => rpc_call!(client, call, update_worker_config),
422
674
  "UpdateWorkerDeploymentVersionMetadata" => {
423
675
  rpc_call!(client, call, update_worker_deployment_version_metadata)
424
676
  }
@@ -525,6 +777,11 @@ async fn call_cloud_service(client: &CoreClient, call: &RpcCallOptions) -> anyho
525
777
  "UpdateUserGroup" => rpc_call!(client, call, update_user_group),
526
778
  "UpdateUser" => rpc_call!(client, call, update_user),
527
779
  "ValidateNamespaceExportSink" => rpc_call!(client, call, validate_namespace_export_sink),
780
+ "UpdateNamespaceTags" => rpc_call!(client, call, update_namespace_tags),
781
+ "CreateConnectivityRule" => rpc_call!(client, call, create_connectivity_rule),
782
+ "GetConnectivityRule" => rpc_call!(client, call, get_connectivity_rule),
783
+ "GetConnectivityRules" => rpc_call!(client, call, get_connectivity_rules),
784
+ "DeleteConnectivityRule" => rpc_call!(client, call, delete_connectivity_rule),
528
785
  rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
529
786
  }
530
787
  }
@@ -277,6 +277,15 @@ impl Context {
277
277
  }
278
278
 
279
279
  pub fn client_connect(self: &Arc<Self>, options: Box<ClientOptions>) -> anyhow::Result<()> {
280
+ Self::client_connect_with_override(self, options, None, std::ptr::null_mut())
281
+ }
282
+
283
+ pub fn client_connect_with_override(
284
+ self: &Arc<Self>,
285
+ options: Box<ClientOptions>,
286
+ grpc_override_callback: crate::client::ClientGrpcOverrideCallback,
287
+ grpc_override_callback_user_data: *mut libc::c_void,
288
+ ) -> anyhow::Result<()> {
280
289
  let metadata = options
281
290
  .headers
282
291
  .as_ref()
@@ -339,6 +348,8 @@ impl Context {
339
348
  retry_options: &*retry_options,
340
349
  keep_alive_options: pointer_or_null(keep_alive_options.as_deref()),
341
350
  http_connect_proxy_options: pointer_or_null(proxy_options.as_deref()),
351
+ grpc_override_callback,
352
+ grpc_override_callback_user_data,
342
353
  });
343
354
 
344
355
  let client_options_ptr = &*client_options as *const _;