@temporalio/core-bridge 0.22.0 → 0.23.0
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.
- package/Cargo.lock +2 -0
- package/Cargo.toml +2 -1
- package/index.d.ts +90 -15
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/bridge-ffi/src/lib.rs +1 -1
- package/sdk-core/bridge-ffi/src/wrappers.rs +60 -34
- package/sdk-core/client/Cargo.toml +1 -0
- package/sdk-core/client/src/lib.rs +24 -7
- package/sdk-core/client/src/raw.rs +1 -1
- package/sdk-core/core/benches/workflow_replay.rs +1 -7
- package/sdk-core/core/src/lib.rs +2 -1
- package/sdk-core/core/src/protosext/mod.rs +1 -3
- package/sdk-core/core/src/retry_logic.rs +73 -16
- package/sdk-core/core/src/telemetry/mod.rs +167 -109
- package/sdk-core/core/src/worker/activities/local_activities.rs +6 -12
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +27 -5
- package/sdk-core/sdk/src/lib.rs +1 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +13 -0
- package/sdk-core/test-utils/src/histfetch.rs +1 -1
- package/sdk-core/test-utils/src/lib.rs +10 -7
- package/sdk-core/tests/integ_tests/client_tests.rs +2 -2
- package/sdk-core/tests/main.rs +5 -2
- package/src/conversions.rs +104 -33
- package/src/lib.rs +70 -6
package/src/conversions.rs
CHANGED
|
@@ -6,11 +6,12 @@ use neon::{
|
|
|
6
6
|
types::{JsNumber, JsString},
|
|
7
7
|
};
|
|
8
8
|
use opentelemetry::trace::{SpanContext, SpanId, TraceFlags, TraceId, TraceState};
|
|
9
|
-
use std::{fmt::Display, net::SocketAddr, str::FromStr, time::Duration};
|
|
9
|
+
use std::{collections::HashMap, fmt::Display, net::SocketAddr, str::FromStr, time::Duration};
|
|
10
10
|
use temporal_sdk_core::{
|
|
11
11
|
api::worker::{WorkerConfig, WorkerConfigBuilder},
|
|
12
|
-
ClientOptions, ClientOptionsBuilder, ClientTlsConfig,
|
|
13
|
-
TelemetryOptionsBuilder, TlsConfig,
|
|
12
|
+
ClientOptions, ClientOptionsBuilder, ClientTlsConfig, Logger, MetricsExporter,
|
|
13
|
+
OtelCollectorOptions, RetryConfig, TelemetryOptions, TelemetryOptionsBuilder, TlsConfig,
|
|
14
|
+
TraceExporter, Url,
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
macro_rules! js_value_getter {
|
|
@@ -27,6 +28,7 @@ macro_rules! js_value_getter {
|
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
#[macro_export]
|
|
30
32
|
macro_rules! js_optional_getter {
|
|
31
33
|
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
32
34
|
match get_optional($js_cx, $js_obj, $prop_name) {
|
|
@@ -42,7 +44,7 @@ macro_rules! js_optional_getter {
|
|
|
42
44
|
|
|
43
45
|
/// Helper for extracting an optional attribute from [obj].
|
|
44
46
|
/// If [obj].[attr] is undefined or not present, None is returned
|
|
45
|
-
fn get_optional<'a, C, K>(
|
|
47
|
+
pub fn get_optional<'a, C, K>(
|
|
46
48
|
cx: &mut C,
|
|
47
49
|
obj: &Handle<JsObject>,
|
|
48
50
|
attr: K,
|
|
@@ -108,6 +110,10 @@ pub(crate) trait ObjectHandleConversionsExt {
|
|
|
108
110
|
fn as_client_options(&self, ctx: &mut FunctionContext) -> NeonResult<ClientOptions>;
|
|
109
111
|
fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions>;
|
|
110
112
|
fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig>;
|
|
113
|
+
fn as_hash_map_of_string_to_string(
|
|
114
|
+
&self,
|
|
115
|
+
cx: &mut FunctionContext,
|
|
116
|
+
) -> NeonResult<HashMap<String, String>>;
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
@@ -124,6 +130,22 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
124
130
|
))
|
|
125
131
|
}
|
|
126
132
|
|
|
133
|
+
fn as_hash_map_of_string_to_string(
|
|
134
|
+
&self,
|
|
135
|
+
cx: &mut FunctionContext,
|
|
136
|
+
) -> NeonResult<HashMap<String, String>> {
|
|
137
|
+
let props = self.get_own_property_names(cx)?;
|
|
138
|
+
let props = props.to_vec(cx)?;
|
|
139
|
+
let mut map = HashMap::new();
|
|
140
|
+
for k in props {
|
|
141
|
+
let k = k.to_string(cx)?;
|
|
142
|
+
let v = self.get(cx, k)?.to_string(cx)?.value(cx);
|
|
143
|
+
let k = k.value(cx);
|
|
144
|
+
map.insert(k, v);
|
|
145
|
+
}
|
|
146
|
+
Ok(map)
|
|
147
|
+
}
|
|
148
|
+
|
|
127
149
|
fn as_client_options(&self, cx: &mut FunctionContext) -> NeonResult<ClientOptions> {
|
|
128
150
|
let url = match Url::parse(&js_value_getter!(cx, self, "url", JsString)) {
|
|
129
151
|
Ok(url) => url,
|
|
@@ -198,11 +220,12 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
198
220
|
},
|
|
199
221
|
};
|
|
200
222
|
|
|
201
|
-
let mut
|
|
223
|
+
let mut client_options = ClientOptionsBuilder::default();
|
|
202
224
|
if let Some(tls_cfg) = tls_cfg {
|
|
203
|
-
|
|
225
|
+
client_options.tls_cfg(tls_cfg);
|
|
204
226
|
}
|
|
205
|
-
|
|
227
|
+
|
|
228
|
+
Ok(client_options
|
|
206
229
|
.client_name("temporal-typescript".to_string())
|
|
207
230
|
.client_version(js_value_getter!(cx, self, "sdkVersion", JsString))
|
|
208
231
|
.target_url(url)
|
|
@@ -214,37 +237,85 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
214
237
|
}
|
|
215
238
|
|
|
216
239
|
fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions> {
|
|
217
|
-
let log_forwarding_level_str =
|
|
218
|
-
js_value_getter!(cx, self, "logForwardingLevel", JsString).replace("WARNING", "WARN");
|
|
219
|
-
let log_forwarding_level =
|
|
220
|
-
LevelFilter::from_str(&log_forwarding_level_str).unwrap_or(LevelFilter::Off);
|
|
221
240
|
let mut telemetry_opts = TelemetryOptionsBuilder::default();
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Err(_) => cx.throw_type_error("Invalid telemetryOptions.oTelCollectorUrl")?,
|
|
226
|
-
};
|
|
227
|
-
telemetry_opts.otel_collector_url(url);
|
|
241
|
+
|
|
242
|
+
if let Some(tf) = js_optional_getter!(cx, self, "tracingFilter", JsString) {
|
|
243
|
+
telemetry_opts.tracing_filter(tf.value(cx));
|
|
228
244
|
}
|
|
229
|
-
if let Some(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
if let Some(ref logging) = js_optional_getter!(cx, self, "logging", JsObject) {
|
|
246
|
+
if let Some(_) = get_optional(cx, logging, "console") {
|
|
247
|
+
telemetry_opts.logging(Logger::Console);
|
|
248
|
+
} else if let Some(forward) = js_optional_getter!(cx, logging, "forward", JsObject) {
|
|
249
|
+
let level = js_value_getter!(cx, forward, "level", JsString);
|
|
250
|
+
match LevelFilter::from_str(&level) {
|
|
251
|
+
Ok(level) => {
|
|
252
|
+
telemetry_opts.logging(Logger::Forward(level));
|
|
253
|
+
}
|
|
254
|
+
Err(err) => cx.throw_type_error(format!(
|
|
255
|
+
"Invalid telemetryOptions.logging.forward.level: {}",
|
|
256
|
+
err
|
|
257
|
+
))?,
|
|
235
258
|
}
|
|
236
|
-
}
|
|
259
|
+
} else {
|
|
260
|
+
cx.throw_type_error(format!(
|
|
261
|
+
"Invalid telemetryOptions.logging, missing `console` or `forward` option"
|
|
262
|
+
))?
|
|
263
|
+
}
|
|
237
264
|
}
|
|
238
|
-
if let Some(
|
|
239
|
-
|
|
265
|
+
if let Some(metrics) = js_optional_getter!(cx, self, "metrics", JsObject) {
|
|
266
|
+
if let Some(ref prom) = js_optional_getter!(cx, &metrics, "prometheus", JsObject) {
|
|
267
|
+
let addr = js_value_getter!(cx, prom, "bindAddress", JsString);
|
|
268
|
+
match addr.parse::<SocketAddr>() {
|
|
269
|
+
Ok(address) => telemetry_opts.metrics(MetricsExporter::Prometheus(address)),
|
|
270
|
+
Err(_) => cx.throw_type_error(
|
|
271
|
+
"Invalid telemetryOptions.metrics.prometheus.bindAddress",
|
|
272
|
+
)?,
|
|
273
|
+
};
|
|
274
|
+
} else if let Some(ref otel) = js_optional_getter!(cx, &metrics, "otel", JsObject) {
|
|
275
|
+
let url = js_value_getter!(cx, otel, "url", JsString);
|
|
276
|
+
let url = match Url::parse(&url) {
|
|
277
|
+
Ok(url) => url,
|
|
278
|
+
Err(_) => cx.throw_type_error("Invalid telemetryOptions.metrics.otel.url")?,
|
|
279
|
+
};
|
|
280
|
+
let headers =
|
|
281
|
+
if let Some(headers) = js_optional_getter!(cx, otel, "headers", JsObject) {
|
|
282
|
+
headers.as_hash_map_of_string_to_string(cx)?
|
|
283
|
+
} else {
|
|
284
|
+
Default::default()
|
|
285
|
+
};
|
|
286
|
+
telemetry_opts
|
|
287
|
+
.metrics(MetricsExporter::Otel(OtelCollectorOptions { url, headers }));
|
|
288
|
+
} else {
|
|
289
|
+
cx.throw_type_error(format!(
|
|
290
|
+
"Invalid telemetryOptions.metrics, missing `prometheus` or `otel` option"
|
|
291
|
+
))?
|
|
292
|
+
}
|
|
240
293
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
294
|
+
|
|
295
|
+
if let Some(tracing) = js_optional_getter!(cx, self, "tracing", JsObject) {
|
|
296
|
+
if let Some(ref otel) = js_optional_getter!(cx, &tracing, "otel", JsObject) {
|
|
297
|
+
let url = js_value_getter!(cx, otel, "url", JsString);
|
|
298
|
+
let url = match Url::parse(&url) {
|
|
299
|
+
Ok(url) => url,
|
|
300
|
+
Err(_) => cx.throw_type_error("Invalid telemetryOptions.tracing.otel.url")?,
|
|
301
|
+
};
|
|
302
|
+
let headers =
|
|
303
|
+
if let Some(headers) = js_optional_getter!(cx, otel, "headers", JsObject) {
|
|
304
|
+
headers.as_hash_map_of_string_to_string(cx)?
|
|
305
|
+
} else {
|
|
306
|
+
Default::default()
|
|
307
|
+
};
|
|
308
|
+
telemetry_opts.tracing(TraceExporter::Otel(OtelCollectorOptions { url, headers }));
|
|
309
|
+
} else {
|
|
310
|
+
cx.throw_type_error(format!(
|
|
311
|
+
"Invalid telemetryOptions.tracing, missing `otel` option"
|
|
312
|
+
))?
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
telemetry_opts.build().map_err(|reason| {
|
|
316
|
+
cx.throw_type_error::<_, TelemetryOptions>(format!("{}", reason))
|
|
317
|
+
.unwrap_err()
|
|
318
|
+
})
|
|
248
319
|
}
|
|
249
320
|
|
|
250
321
|
fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig> {
|
package/src/lib.rs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
mod conversions;
|
|
2
2
|
mod errors;
|
|
3
3
|
|
|
4
|
-
use crate::conversions::ObjectHandleConversionsExt;
|
|
4
|
+
use crate::conversions::{get_optional, ObjectHandleConversionsExt};
|
|
5
5
|
use errors::*;
|
|
6
6
|
use neon::prelude::*;
|
|
7
7
|
use once_cell::sync::OnceCell;
|
|
8
8
|
use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
|
|
9
|
+
use parking_lot::RwLock;
|
|
9
10
|
use prost::Message;
|
|
11
|
+
use std::collections::HashMap;
|
|
10
12
|
use std::{
|
|
11
13
|
cell::RefCell,
|
|
12
14
|
fmt::Display,
|
|
@@ -50,6 +52,14 @@ enum Request {
|
|
|
50
52
|
CreateClient {
|
|
51
53
|
runtime: Arc<RuntimeHandle>,
|
|
52
54
|
options: ClientOptions,
|
|
55
|
+
headers: Option<HashMap<String, String>>,
|
|
56
|
+
/// Used to send the result back into JS
|
|
57
|
+
callback: Root<JsFunction>,
|
|
58
|
+
},
|
|
59
|
+
/// A request to update a client's HTTP request headers
|
|
60
|
+
UpdateClientHeaders {
|
|
61
|
+
client: Arc<RawClient>,
|
|
62
|
+
headers: HashMap<String, String>,
|
|
53
63
|
/// Used to send the result back into JS
|
|
54
64
|
callback: Root<JsFunction>,
|
|
55
65
|
},
|
|
@@ -271,13 +281,17 @@ fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedRecei
|
|
|
271
281
|
Request::CreateClient {
|
|
272
282
|
runtime,
|
|
273
283
|
options,
|
|
284
|
+
headers,
|
|
274
285
|
callback,
|
|
275
286
|
} => {
|
|
276
287
|
// `metrics_meter` (second arg) can be None here since we don't use the
|
|
277
288
|
// returned client directly at the moment, when we repurpose the client to be
|
|
278
289
|
// used by a Worker, `init_worker` will attach the correct metrics meter for
|
|
279
290
|
// us.
|
|
280
|
-
match options
|
|
291
|
+
match options
|
|
292
|
+
.connect_no_namespace(None, headers.map(|h| Arc::new(RwLock::new(h))))
|
|
293
|
+
.await
|
|
294
|
+
{
|
|
281
295
|
Err(err) => {
|
|
282
296
|
send_error(event_queue.clone(), callback, |cx| match err {
|
|
283
297
|
ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
|
|
@@ -300,6 +314,14 @@ fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedRecei
|
|
|
300
314
|
}
|
|
301
315
|
}
|
|
302
316
|
}
|
|
317
|
+
Request::UpdateClientHeaders {
|
|
318
|
+
client,
|
|
319
|
+
headers,
|
|
320
|
+
callback,
|
|
321
|
+
} => {
|
|
322
|
+
client.get_client().set_headers(headers);
|
|
323
|
+
send_result(event_queue.clone(), callback, |cx| Ok(cx.undefined()));
|
|
324
|
+
}
|
|
303
325
|
Request::PollLogs { callback } => {
|
|
304
326
|
let logs = fetch_global_buffered_logs();
|
|
305
327
|
send_result(event_queue.clone(), callback, |cx| {
|
|
@@ -568,12 +590,28 @@ fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
|
|
|
568
590
|
/// Client will be returned in the supplied `callback`.
|
|
569
591
|
fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
570
592
|
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
571
|
-
let
|
|
593
|
+
let opts = cx.argument::<JsObject>(1)?;
|
|
572
594
|
let callback = cx.argument::<JsFunction>(2)?;
|
|
573
595
|
|
|
596
|
+
let client_options = opts.as_client_options(&mut cx)?;
|
|
597
|
+
let headers = match js_optional_getter!(&mut cx, &opts, "headers", JsObject) {
|
|
598
|
+
None => None,
|
|
599
|
+
Some(h) => Some(
|
|
600
|
+
h.as_hash_map_of_string_to_string(&mut cx)
|
|
601
|
+
.map_err(|reason| {
|
|
602
|
+
cx.throw_type_error::<_, HashMap<String, String>>(format!(
|
|
603
|
+
"Invalid headers: {}",
|
|
604
|
+
reason
|
|
605
|
+
))
|
|
606
|
+
.unwrap_err()
|
|
607
|
+
})?,
|
|
608
|
+
),
|
|
609
|
+
};
|
|
610
|
+
|
|
574
611
|
let request = Request::CreateClient {
|
|
575
612
|
runtime: (**runtime).clone(),
|
|
576
613
|
options: client_options,
|
|
614
|
+
headers,
|
|
577
615
|
callback: callback.root(&mut cx),
|
|
578
616
|
};
|
|
579
617
|
if let Err(err) = runtime.sender.send(request) {
|
|
@@ -583,6 +621,33 @@ fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
|
583
621
|
Ok(cx.undefined())
|
|
584
622
|
}
|
|
585
623
|
|
|
624
|
+
/// Update a Client's HTTP request headers
|
|
625
|
+
fn client_update_headers(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
626
|
+
let client = cx.argument::<BoxedClient>(0)?;
|
|
627
|
+
let headers = cx
|
|
628
|
+
.argument::<JsObject>(1)?
|
|
629
|
+
.as_hash_map_of_string_to_string(&mut cx)?;
|
|
630
|
+
let callback = cx.argument::<JsFunction>(2)?;
|
|
631
|
+
|
|
632
|
+
match &*client.borrow() {
|
|
633
|
+
None => {
|
|
634
|
+
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
635
|
+
}
|
|
636
|
+
Some(client) => {
|
|
637
|
+
let request = Request::UpdateClientHeaders {
|
|
638
|
+
client: client.core_client.clone(),
|
|
639
|
+
headers,
|
|
640
|
+
callback: callback.root(&mut cx),
|
|
641
|
+
};
|
|
642
|
+
if let Err(err) = client.runtime.sender.send(request) {
|
|
643
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
Ok(cx.undefined())
|
|
649
|
+
}
|
|
650
|
+
|
|
586
651
|
/// Create a new worker asynchronously.
|
|
587
652
|
/// Worker uses the provided connection and returned to JS using supplied `callback`.
|
|
588
653
|
fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
@@ -593,9 +658,7 @@ fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
|
593
658
|
let config = worker_options.as_worker_config(&mut cx)?;
|
|
594
659
|
match &*client.borrow() {
|
|
595
660
|
None => {
|
|
596
|
-
|
|
597
|
-
UNEXPECTED_ERROR.from_string(cx, "Tried to use closed Client".to_string())
|
|
598
|
-
})?;
|
|
661
|
+
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
599
662
|
}
|
|
600
663
|
Some(client) => {
|
|
601
664
|
let request = Request::InitWorker {
|
|
@@ -843,6 +906,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
|
|
843
906
|
cx.export_function("initTelemetry", init_telemetry)?;
|
|
844
907
|
cx.export_function("newRuntime", runtime_new)?;
|
|
845
908
|
cx.export_function("newClient", client_new)?;
|
|
909
|
+
cx.export_function("clientUpdateHeaders", client_update_headers)?;
|
|
846
910
|
cx.export_function("newWorker", worker_new)?;
|
|
847
911
|
cx.export_function("newReplayWorker", replay_worker_new)?;
|
|
848
912
|
cx.export_function("workerShutdown", worker_shutdown)?;
|