@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.
- package/Cargo.lock +786 -54
- package/Cargo.toml +2 -2
- package/common.js +7 -3
- package/index.d.ts +110 -3
- package/index.js +2 -6
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/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/scripts/build.js +4 -3
- package/sdk-core/.buildkite/docker/Dockerfile +2 -1
- package/sdk-core/ARCHITECTURE.md +2 -2
- package/sdk-core/README.md +12 -0
- package/sdk-core/bridge-ffi/Cargo.toml +2 -2
- package/sdk-core/client/Cargo.toml +6 -4
- package/sdk-core/client/src/lib.rs +338 -215
- package/sdk-core/client/src/raw.rs +352 -106
- package/sdk-core/client/src/retry.rs +159 -133
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +18 -9
- package/sdk-core/core/src/core_tests/activity_tasks.rs +63 -23
- package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
- package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
- package/sdk-core/core/src/core_tests/workers.rs +3 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
- package/sdk-core/core/src/ephemeral_server/mod.rs +499 -0
- package/sdk-core/core/src/lib.rs +60 -26
- package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/retry_logic.rs +10 -9
- package/sdk-core/core/src/telemetry/mod.rs +10 -7
- package/sdk-core/core/src/test_help/mod.rs +18 -8
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
- package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
- package/sdk-core/core/src/worker/activities.rs +6 -12
- package/sdk-core/core/src/worker/client.rs +193 -64
- package/sdk-core/core/src/worker/mod.rs +14 -19
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
- package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
- package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +59 -58
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
- package/sdk-core/core-api/Cargo.toml +2 -2
- package/sdk-core/core-api/src/errors.rs +3 -11
- package/sdk-core/core-api/src/worker.rs +7 -0
- package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
- package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
- package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
- package/sdk-core/protos/api_upstream/Makefile +2 -2
- package/sdk-core/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
- package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
- package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
- package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
- package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
- package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
- package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
- package/sdk-core/sdk/Cargo.toml +2 -2
- package/sdk-core/sdk/src/lib.rs +2 -2
- package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
- package/sdk-core/sdk/src/workflow_context.rs +30 -6
- package/sdk-core/sdk/src/workflow_future.rs +4 -4
- package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
- package/sdk-core/sdk-core-protos/build.rs +9 -1
- package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
- package/sdk-core/test-utils/Cargo.toml +3 -3
- package/sdk-core/test-utils/src/canned_histories.rs +58 -0
- package/sdk-core/test-utils/src/lib.rs +14 -10
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +141 -0
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
- package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
- package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
- package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
- package/sdk-core/tests/load_tests.rs +2 -1
- package/sdk-core/tests/main.rs +10 -0
- package/src/conversions.rs +138 -91
- package/src/helpers.rs +190 -0
- package/src/lib.rs +10 -912
- package/src/runtime.rs +436 -0
- package/src/testing.rs +67 -0
- package/src/worker.rs +465 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
1
3
|
use futures::StreamExt;
|
|
2
4
|
use temporal_client::{WorkflowClientTrait, WorkflowExecutionInfo, WorkflowOptions};
|
|
3
5
|
use temporal_sdk::{
|
|
4
6
|
ChildWorkflowOptions, Signal, SignalWorkflowOptions, WfContext, WorkflowResult,
|
|
5
7
|
};
|
|
6
|
-
use temporal_sdk_core_protos::coresdk::IntoPayloadsExt;
|
|
8
|
+
use temporal_sdk_core_protos::{coresdk::IntoPayloadsExt, temporal::api::common::v1::Payload};
|
|
7
9
|
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
8
10
|
use uuid::Uuid;
|
|
9
11
|
|
|
@@ -57,7 +59,12 @@ async fn signal_receiver(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
57
59
|
|
|
58
60
|
async fn signal_with_create_wf_receiver(ctx: WfContext) -> WorkflowResult<()> {
|
|
59
61
|
let res = ctx.make_signal_channel(SIGNAME).next().await.unwrap();
|
|
62
|
+
println!("HEADER: {:?}", res.headers);
|
|
60
63
|
assert_eq!(&res.input, &[b"tada".into()]);
|
|
64
|
+
assert_eq!(
|
|
65
|
+
*res.headers.get("tupac").expect("tupac header exists"),
|
|
66
|
+
b"shakur".into()
|
|
67
|
+
);
|
|
61
68
|
Ok(().into())
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -96,15 +103,19 @@ async fn sends_signal_with_create_wf() {
|
|
|
96
103
|
worker.register_wf("receiversignal", signal_with_create_wf_receiver);
|
|
97
104
|
|
|
98
105
|
let client = starter.get_client().await;
|
|
106
|
+
let mut header: HashMap<String, Payload> = HashMap::new();
|
|
107
|
+
header.insert("tupac".into(), "shakur".into());
|
|
99
108
|
let res = client
|
|
100
109
|
.signal_with_start_workflow_execution(
|
|
101
110
|
None,
|
|
102
111
|
worker.inner_mut().task_queue().to_owned(),
|
|
103
112
|
"sends_signal_with_create_wf".to_owned(),
|
|
104
113
|
"receiversignal".to_owned(),
|
|
114
|
+
None,
|
|
105
115
|
WorkflowOptions::default(),
|
|
106
116
|
SIGNAME.to_owned(),
|
|
107
117
|
vec![b"tada".into()].into_payloads(),
|
|
118
|
+
Some(header.into()),
|
|
108
119
|
)
|
|
109
120
|
.await
|
|
110
121
|
.expect("request succeeds.qed");
|
|
@@ -42,7 +42,7 @@ async fn timer_workflow_manual() {
|
|
|
42
42
|
task.run_id,
|
|
43
43
|
vec![StartTimer {
|
|
44
44
|
seq: 0,
|
|
45
|
-
start_to_fire_timeout: Some(
|
|
45
|
+
start_to_fire_timeout: Some(prost_dur!(from_secs(1))),
|
|
46
46
|
}
|
|
47
47
|
.into()],
|
|
48
48
|
))
|
|
@@ -63,12 +63,12 @@ async fn timer_cancel_workflow() {
|
|
|
63
63
|
vec![
|
|
64
64
|
StartTimer {
|
|
65
65
|
seq: 0,
|
|
66
|
-
start_to_fire_timeout: Some(
|
|
66
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
|
|
67
67
|
}
|
|
68
68
|
.into(),
|
|
69
69
|
StartTimer {
|
|
70
70
|
seq: 1,
|
|
71
|
-
start_to_fire_timeout: Some(
|
|
71
|
+
start_to_fire_timeout: Some(prost_dur!(from_secs(10))),
|
|
72
72
|
}
|
|
73
73
|
.into(),
|
|
74
74
|
],
|
|
@@ -223,7 +223,7 @@ async fn fail_wf_task(#[values(true, false)] replay: bool) {
|
|
|
223
223
|
task.run_id,
|
|
224
224
|
vec![StartTimer {
|
|
225
225
|
seq: 0,
|
|
226
|
-
start_to_fire_timeout: Some(
|
|
226
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(200))),
|
|
227
227
|
}
|
|
228
228
|
.into()],
|
|
229
229
|
))
|
|
@@ -279,6 +279,7 @@ async fn signal_workflow() {
|
|
|
279
279
|
res.run_id.to_string(),
|
|
280
280
|
signal_id_1.to_string(),
|
|
281
281
|
None,
|
|
282
|
+
None,
|
|
282
283
|
)
|
|
283
284
|
.await
|
|
284
285
|
.unwrap();
|
|
@@ -288,6 +289,7 @@ async fn signal_workflow() {
|
|
|
288
289
|
res.run_id.to_string(),
|
|
289
290
|
signal_id_2.to_string(),
|
|
290
291
|
None,
|
|
292
|
+
None,
|
|
291
293
|
)
|
|
292
294
|
.await
|
|
293
295
|
.unwrap();
|
|
@@ -345,7 +347,7 @@ async fn signal_workflow_signal_not_handled_on_workflow_completion() {
|
|
|
345
347
|
res.run_id,
|
|
346
348
|
vec![StartTimer {
|
|
347
349
|
seq: 0,
|
|
348
|
-
start_to_fire_timeout: Some(
|
|
350
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(10))),
|
|
349
351
|
}
|
|
350
352
|
.into()],
|
|
351
353
|
))
|
|
@@ -374,6 +376,7 @@ async fn signal_workflow_signal_not_handled_on_workflow_completion() {
|
|
|
374
376
|
res.run_id.to_string(),
|
|
375
377
|
signal_id_1.to_string(),
|
|
376
378
|
None,
|
|
379
|
+
None,
|
|
377
380
|
)
|
|
378
381
|
.await
|
|
379
382
|
.unwrap();
|
|
@@ -458,7 +461,13 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
|
|
|
458
461
|
// Send the signals to the server & resolve activity -- sometimes this happens too fast
|
|
459
462
|
sleep(Duration::from_millis(200)).await;
|
|
460
463
|
client
|
|
461
|
-
.signal_workflow_execution(
|
|
464
|
+
.signal_workflow_execution(
|
|
465
|
+
wf_id.to_string(),
|
|
466
|
+
rid,
|
|
467
|
+
signal_at_start.to_string(),
|
|
468
|
+
None,
|
|
469
|
+
None,
|
|
470
|
+
)
|
|
462
471
|
.await
|
|
463
472
|
.unwrap();
|
|
464
473
|
// Complete activity successfully.
|
|
@@ -470,7 +479,13 @@ async fn wft_timeout_doesnt_create_unsolvable_autocomplete() {
|
|
|
470
479
|
.unwrap();
|
|
471
480
|
let rid = wf_task.run_id.clone();
|
|
472
481
|
client
|
|
473
|
-
.signal_workflow_execution(
|
|
482
|
+
.signal_workflow_execution(
|
|
483
|
+
wf_id.to_string(),
|
|
484
|
+
rid,
|
|
485
|
+
signal_at_complete.to_string(),
|
|
486
|
+
None,
|
|
487
|
+
None,
|
|
488
|
+
)
|
|
474
489
|
.await
|
|
475
490
|
.unwrap();
|
|
476
491
|
// Now poll again, it will be an eviction b/c non-sticky mode.
|
|
@@ -35,13 +35,13 @@ async fn activity_load() {
|
|
|
35
35
|
let activity = ActivityOptions {
|
|
36
36
|
activity_id: Some(activity_id.to_string()),
|
|
37
37
|
activity_type: "test_activity".to_string(),
|
|
38
|
-
input: Default::default(),
|
|
39
38
|
task_queue,
|
|
40
39
|
schedule_to_start_timeout: Some(activity_timeout),
|
|
41
40
|
start_to_close_timeout: Some(activity_timeout),
|
|
42
41
|
schedule_to_close_timeout: Some(activity_timeout),
|
|
43
42
|
heartbeat_timeout: Some(activity_timeout),
|
|
44
43
|
cancellation_type: ActivityCancellationType::TryCancel,
|
|
44
|
+
..Default::default()
|
|
45
45
|
};
|
|
46
46
|
let res = ctx.activity(activity).await.unwrap_ok_payload();
|
|
47
47
|
assert_eq!(res.data, payload_dat);
|
|
@@ -175,6 +175,7 @@ async fn workflow_load() {
|
|
|
175
175
|
"".to_string(),
|
|
176
176
|
SIGNAME.to_string(),
|
|
177
177
|
None,
|
|
178
|
+
None,
|
|
178
179
|
)
|
|
179
180
|
})
|
|
180
181
|
.collect();
|
package/sdk-core/tests/main.rs
CHANGED
|
@@ -2,10 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
#[cfg(test)]
|
|
4
4
|
mod integ_tests {
|
|
5
|
+
#[macro_export]
|
|
6
|
+
macro_rules! prost_dur {
|
|
7
|
+
($dur_call:ident $args:tt) => {
|
|
8
|
+
std::time::Duration::$dur_call$args
|
|
9
|
+
.try_into()
|
|
10
|
+
.expect("test duration fits")
|
|
11
|
+
};
|
|
12
|
+
}
|
|
5
13
|
mod client_tests;
|
|
14
|
+
mod ephemeral_server_tests;
|
|
6
15
|
mod heartbeat_tests;
|
|
7
16
|
mod polling_tests;
|
|
8
17
|
mod queries_tests;
|
|
18
|
+
mod visibility_tests;
|
|
9
19
|
mod workflow_tests;
|
|
10
20
|
|
|
11
21
|
use std::str::FromStr;
|
package/src/conversions.rs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
use crate::helpers::*;
|
|
1
2
|
use log::LevelFilter;
|
|
2
|
-
use neon::types::buffer::TypedArray;
|
|
3
3
|
use neon::{
|
|
4
4
|
context::Context,
|
|
5
5
|
handle::Handle,
|
|
@@ -7,107 +7,50 @@ use neon::{
|
|
|
7
7
|
types::{JsBoolean, JsNumber, JsString},
|
|
8
8
|
};
|
|
9
9
|
use opentelemetry::trace::{SpanContext, SpanId, TraceFlags, TraceId, TraceState};
|
|
10
|
-
use std::{collections::HashMap,
|
|
10
|
+
use std::{collections::HashMap, net::SocketAddr, str::FromStr, time::Duration};
|
|
11
11
|
use temporal_sdk_core::{
|
|
12
12
|
api::worker::{WorkerConfig, WorkerConfigBuilder},
|
|
13
|
+
ephemeral_server::{
|
|
14
|
+
TemporaliteConfig, TemporaliteConfigBuilder, TestServerConfig, TestServerConfigBuilder,
|
|
15
|
+
},
|
|
13
16
|
ClientOptions, ClientOptionsBuilder, ClientTlsConfig, Logger, MetricsExporter,
|
|
14
17
|
OtelCollectorOptions, RetryConfig, TelemetryOptions, TelemetryOptionsBuilder, TlsConfig,
|
|
15
18
|
TraceExporter, Url,
|
|
16
19
|
};
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(
|
|
21
|
-
match get_optional($js_cx, $js_obj, $prop_name) {
|
|
22
|
-
None => None,
|
|
23
|
-
Some(val) => {
|
|
24
|
-
if val.is_a::<$js_type, _>($js_cx) {
|
|
25
|
-
Some(val.downcast_or_throw::<$js_type, _>($js_cx)?)
|
|
26
|
-
} else {
|
|
27
|
-
Some($js_cx.throw_type_error(format!("Invalid {}", $prop_name))?)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
};
|
|
21
|
+
pub enum EphemeralServerConfig {
|
|
22
|
+
TestServer(TestServerConfig),
|
|
23
|
+
Temporalite(TemporaliteConfig),
|
|
32
24
|
}
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
(
|
|
36
|
-
match js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type) {
|
|
37
|
-
Some(val) => val.value($js_cx),
|
|
38
|
-
None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
|
|
39
|
-
}
|
|
40
|
-
};
|
|
26
|
+
pub trait ArrayHandleConversionsExt {
|
|
27
|
+
fn to_vec_of_string(&self, cx: &mut FunctionContext) -> NeonResult<Vec<String>>;
|
|
41
28
|
}
|
|
42
29
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
attr: K,
|
|
49
|
-
) -> Option<Handle<'a, JsValue>>
|
|
50
|
-
where
|
|
51
|
-
K: neon::object::PropertyKey,
|
|
52
|
-
C: Context<'a>,
|
|
53
|
-
{
|
|
54
|
-
match obj.get_value(cx, attr) {
|
|
55
|
-
Err(_) => None,
|
|
56
|
-
Ok(val) => match val.is_a::<JsUndefined, _>(cx) {
|
|
57
|
-
true => None,
|
|
58
|
-
false => Some(val),
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
}
|
|
30
|
+
impl ArrayHandleConversionsExt for Handle<'_, JsArray> {
|
|
31
|
+
fn to_vec_of_string(&self, cx: &mut FunctionContext) -> NeonResult<Vec<String>> {
|
|
32
|
+
let js_vec = self.to_vec(cx)?;
|
|
33
|
+
let len = js_vec.len();
|
|
34
|
+
let mut ret_vec = Vec::<String>::with_capacity(len);
|
|
62
35
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
attr: K,
|
|
68
|
-
) -> Result<Option<Vec<u8>>, neon::result::Throw>
|
|
69
|
-
where
|
|
70
|
-
K: neon::object::PropertyKey + Display + Clone,
|
|
71
|
-
C: Context<'a>,
|
|
72
|
-
{
|
|
73
|
-
if let Some(val) = get_optional(cx, obj, attr.clone()) {
|
|
74
|
-
let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
|
|
75
|
-
cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
|
|
76
|
-
.unwrap_err()
|
|
77
|
-
})?;
|
|
78
|
-
Ok(Some(buf.as_slice(cx).to_vec()))
|
|
79
|
-
} else {
|
|
80
|
-
Ok(None)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
|
|
85
|
-
fn get_vec<'a, C, K>(
|
|
86
|
-
cx: &mut C,
|
|
87
|
-
obj: &Handle<JsObject>,
|
|
88
|
-
attr: K,
|
|
89
|
-
full_attr_path: &str,
|
|
90
|
-
) -> Result<Vec<u8>, neon::result::Throw>
|
|
91
|
-
where
|
|
92
|
-
K: neon::object::PropertyKey + Display + Clone,
|
|
93
|
-
C: Context<'a>,
|
|
94
|
-
{
|
|
95
|
-
if let Some(val) = get_optional(cx, obj, attr.clone()) {
|
|
96
|
-
let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
|
|
97
|
-
cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
|
|
98
|
-
.unwrap_err()
|
|
99
|
-
})?;
|
|
100
|
-
Ok(buf.as_slice(cx).to_vec())
|
|
101
|
-
} else {
|
|
102
|
-
cx.throw_type_error::<_, Vec<u8>>(format!("Invalid or missing {}", full_attr_path))
|
|
36
|
+
for i in 0..len - 1 {
|
|
37
|
+
ret_vec[i] = js_vec[i].downcast_or_throw::<JsString, _>(cx)?.value(cx);
|
|
38
|
+
}
|
|
39
|
+
Ok(ret_vec)
|
|
103
40
|
}
|
|
104
41
|
}
|
|
105
42
|
|
|
106
|
-
pub
|
|
43
|
+
pub trait ObjectHandleConversionsExt {
|
|
44
|
+
fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()>;
|
|
107
45
|
fn as_otel_span_context(&self, ctx: &mut FunctionContext) -> NeonResult<SpanContext>;
|
|
108
46
|
fn as_client_options(&self, ctx: &mut FunctionContext) -> NeonResult<ClientOptions>;
|
|
109
47
|
fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions>;
|
|
110
48
|
fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig>;
|
|
49
|
+
fn as_ephemeral_server_config(
|
|
50
|
+
&self,
|
|
51
|
+
cx: &mut FunctionContext,
|
|
52
|
+
sdk_version: String,
|
|
53
|
+
) -> NeonResult<EphemeralServerConfig>;
|
|
111
54
|
fn as_hash_map_of_string_to_string(
|
|
112
55
|
&self,
|
|
113
56
|
cx: &mut FunctionContext,
|
|
@@ -154,8 +97,7 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
154
97
|
let tls_cfg = match js_optional_getter!(cx, self, "tls", JsObject) {
|
|
155
98
|
None => None,
|
|
156
99
|
Some(tls) => {
|
|
157
|
-
let domain =
|
|
158
|
-
.map(|h| h.value(cx));
|
|
100
|
+
let domain = js_optional_value_getter!(cx, &tls, "serverNameOverride", JsString);
|
|
159
101
|
|
|
160
102
|
let server_root_ca_cert = get_optional_vec(cx, &tls, "serverRootCACertificate")?;
|
|
161
103
|
let client_tls_config =
|
|
@@ -207,13 +149,13 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
207
149
|
"maxInterval",
|
|
208
150
|
JsNumber
|
|
209
151
|
) as u64),
|
|
210
|
-
max_elapsed_time:
|
|
152
|
+
max_elapsed_time: js_optional_value_getter!(
|
|
211
153
|
cx,
|
|
212
154
|
&retry_config,
|
|
213
155
|
"maxElapsedTime",
|
|
214
156
|
JsNumber
|
|
215
157
|
)
|
|
216
|
-
.map(|val| Duration::from_millis(val
|
|
158
|
+
.map(|val| Duration::from_millis(val as u64)),
|
|
217
159
|
max_retries: js_value_getter!(cx, retry_config, "maxRetries", JsNumber) as usize,
|
|
218
160
|
},
|
|
219
161
|
};
|
|
@@ -235,12 +177,11 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
235
177
|
fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions> {
|
|
236
178
|
let mut telemetry_opts = TelemetryOptionsBuilder::default();
|
|
237
179
|
|
|
238
|
-
if let Some(tf) =
|
|
239
|
-
telemetry_opts.tracing_filter(tf
|
|
180
|
+
if let Some(tf) = js_optional_value_getter!(cx, self, "tracingFilter", JsString) {
|
|
181
|
+
telemetry_opts.tracing_filter(tf);
|
|
240
182
|
}
|
|
241
183
|
telemetry_opts.no_temporal_prefix_for_metrics(
|
|
242
|
-
|
|
243
|
-
.map(|b| b.value(cx))
|
|
184
|
+
js_optional_value_getter!(cx, self, "noTemporalPrefixForMetrics", JsBoolean)
|
|
244
185
|
.unwrap_or_default(),
|
|
245
186
|
);
|
|
246
187
|
if let Some(ref logging) = js_optional_getter!(cx, self, "logging", JsObject) {
|
|
@@ -382,4 +323,110 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
|
|
|
382
323
|
Err(e) => cx.throw_error(format!("Invalid worker config: {:?}", e)),
|
|
383
324
|
}
|
|
384
325
|
}
|
|
326
|
+
|
|
327
|
+
fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()> {
|
|
328
|
+
let key = cx.string(key);
|
|
329
|
+
let existing: Option<Handle<JsString>> = self.get_opt(cx, key)?;
|
|
330
|
+
if existing.is_none() {
|
|
331
|
+
let value = cx.string(value);
|
|
332
|
+
self.set(cx, key, value)?;
|
|
333
|
+
}
|
|
334
|
+
Ok(())
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
fn as_ephemeral_server_config(
|
|
338
|
+
&self,
|
|
339
|
+
cx: &mut FunctionContext,
|
|
340
|
+
sdk_version: String,
|
|
341
|
+
) -> NeonResult<EphemeralServerConfig> {
|
|
342
|
+
let js_executable = js_optional_getter!(cx, self, "executable", JsObject)
|
|
343
|
+
.unwrap_or_else(|| cx.empty_object());
|
|
344
|
+
js_executable.set_default(cx, "type", "cached-download")?;
|
|
345
|
+
|
|
346
|
+
let exec_type = js_value_getter!(cx, &js_executable, "type", JsString);
|
|
347
|
+
let executable = match exec_type.as_str() {
|
|
348
|
+
"cached-download" => {
|
|
349
|
+
let version = js_optional_value_getter!(cx, &js_executable, "version", JsString)
|
|
350
|
+
.unwrap_or("default".to_owned());
|
|
351
|
+
let dest_dir =
|
|
352
|
+
js_optional_value_getter!(cx, &js_executable, "downloadDir", JsString);
|
|
353
|
+
|
|
354
|
+
let exec_version = match version.as_str() {
|
|
355
|
+
"default" => {
|
|
356
|
+
temporal_sdk_core::ephemeral_server::EphemeralExeVersion::Default {
|
|
357
|
+
sdk_name: "sdk-typescript".to_owned(),
|
|
358
|
+
sdk_version,
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
_ => temporal_sdk_core::ephemeral_server::EphemeralExeVersion::Fixed(version),
|
|
362
|
+
};
|
|
363
|
+
temporal_sdk_core::ephemeral_server::EphemeralExe::CachedDownload {
|
|
364
|
+
version: exec_version,
|
|
365
|
+
dest_dir,
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
"existing-path" => {
|
|
369
|
+
let path = js_value_getter!(cx, &js_executable, "path", JsString);
|
|
370
|
+
temporal_sdk_core::ephemeral_server::EphemeralExe::ExistingPath(path)
|
|
371
|
+
}
|
|
372
|
+
_ => {
|
|
373
|
+
return cx.throw_type_error(format!("Invalid executable type: {}", exec_type))?;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
let port = js_optional_getter!(cx, self, "port", JsNumber).map(|s| s.value(cx) as u16);
|
|
377
|
+
|
|
378
|
+
let server_type = js_value_getter!(cx, self, "type", JsString);
|
|
379
|
+
match server_type.as_str() {
|
|
380
|
+
"temporalite" => {
|
|
381
|
+
let mut config = TemporaliteConfigBuilder::default();
|
|
382
|
+
config.exe(executable).port(port);
|
|
383
|
+
|
|
384
|
+
if let Some(extra_args) = js_optional_getter!(cx, self, "extraArgs", JsArray) {
|
|
385
|
+
config.extra_args(extra_args.to_vec_of_string(cx)?);
|
|
386
|
+
};
|
|
387
|
+
if let Some(namespace) = js_optional_value_getter!(cx, self, "namespace", JsString)
|
|
388
|
+
{
|
|
389
|
+
config.namespace(namespace);
|
|
390
|
+
}
|
|
391
|
+
if let Some(ip) = js_optional_value_getter!(cx, self, "ip", JsString) {
|
|
392
|
+
config.ip(ip);
|
|
393
|
+
}
|
|
394
|
+
config.db_filename(js_optional_value_getter!(cx, self, "dbFilename", JsString));
|
|
395
|
+
config.ui(js_optional_value_getter!(cx, self, "ui", JsBoolean).unwrap_or_default());
|
|
396
|
+
|
|
397
|
+
if let Some(log) = js_optional_getter!(cx, self, "log", JsObject) {
|
|
398
|
+
let format = js_value_getter!(cx, &log, "format", JsString);
|
|
399
|
+
let level = js_value_getter!(cx, &log, "level", JsString);
|
|
400
|
+
config.log((format, level));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
match config.build() {
|
|
404
|
+
Ok(config) => Ok(EphemeralServerConfig::Temporalite(config)),
|
|
405
|
+
Err(err) => {
|
|
406
|
+
cx.throw_type_error(format!("Invalid temporalite config: {:?}", err))
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
"time-skipping" => {
|
|
411
|
+
let mut config = TestServerConfigBuilder::default();
|
|
412
|
+
config.exe(executable).port(port);
|
|
413
|
+
|
|
414
|
+
if let Some(extra_args_js) = js_optional_getter!(cx, self, "extraArgs", JsArray) {
|
|
415
|
+
let extra_args = extra_args_js.to_vec_of_string(cx)?;
|
|
416
|
+
config.extra_args(extra_args);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
match config.build() {
|
|
420
|
+
Ok(config) => Ok(EphemeralServerConfig::TestServer(config)),
|
|
421
|
+
Err(err) => {
|
|
422
|
+
cx.throw_type_error(format!("Invalid test server config: {:?}", err))
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
s => cx.throw_type_error(format!(
|
|
427
|
+
"Invalid ephemeral server type: {}, expected 'temporalite' or 'time-skipping'",
|
|
428
|
+
s
|
|
429
|
+
)),
|
|
430
|
+
}
|
|
431
|
+
}
|
|
385
432
|
}
|
package/src/helpers.rs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
use crate::errors::*;
|
|
2
|
+
use neon::prelude::*;
|
|
3
|
+
use neon::types::buffer::TypedArray;
|
|
4
|
+
use std::{fmt::Display, future::Future, sync::Arc};
|
|
5
|
+
|
|
6
|
+
/// Send a result to JS via callback using a [Channel]
|
|
7
|
+
pub fn send_result<F, T>(channel: Arc<Channel>, callback: Root<JsFunction>, res_fn: F)
|
|
8
|
+
where
|
|
9
|
+
F: for<'a> FnOnce(&mut TaskContext<'a>) -> NeonResult<Handle<'a, T>> + Send + 'static,
|
|
10
|
+
T: Value,
|
|
11
|
+
{
|
|
12
|
+
channel.send(move |mut cx| {
|
|
13
|
+
let callback = callback.into_inner(&mut cx);
|
|
14
|
+
let this = cx.undefined();
|
|
15
|
+
let error = cx.undefined();
|
|
16
|
+
let result = res_fn(&mut cx)?;
|
|
17
|
+
let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
|
|
18
|
+
callback.call(&mut cx, this, args)?;
|
|
19
|
+
Ok(())
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Send an error to JS via callback using a [Channel]
|
|
24
|
+
pub fn send_error<E, F>(channel: Arc<Channel>, callback: Root<JsFunction>, error_ctor: F)
|
|
25
|
+
where
|
|
26
|
+
E: Object,
|
|
27
|
+
F: for<'a> FnOnce(&mut TaskContext<'a>) -> JsResult<'a, E> + Send + 'static,
|
|
28
|
+
{
|
|
29
|
+
channel.send(move |mut cx| {
|
|
30
|
+
let callback = callback.into_inner(&mut cx);
|
|
31
|
+
callback_with_error(&mut cx, callback, error_ctor)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Call `callback` with given error
|
|
36
|
+
pub fn callback_with_error<'a, C, E, F>(
|
|
37
|
+
cx: &mut C,
|
|
38
|
+
callback: Handle<JsFunction>,
|
|
39
|
+
error_ctor: F,
|
|
40
|
+
) -> NeonResult<()>
|
|
41
|
+
where
|
|
42
|
+
C: Context<'a>,
|
|
43
|
+
E: Object,
|
|
44
|
+
F: FnOnce(&mut C) -> JsResult<'a, E> + Send + 'static,
|
|
45
|
+
{
|
|
46
|
+
let this = cx.undefined();
|
|
47
|
+
let error = error_ctor(cx)?;
|
|
48
|
+
let result = cx.undefined();
|
|
49
|
+
let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
|
|
50
|
+
callback.call(cx, this, args)?;
|
|
51
|
+
Ok(())
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Call `callback` with an UnexpectedError created from `err`
|
|
55
|
+
pub fn callback_with_unexpected_error<'a, C, E>(
|
|
56
|
+
cx: &mut C,
|
|
57
|
+
callback: Handle<JsFunction>,
|
|
58
|
+
err: E,
|
|
59
|
+
) -> NeonResult<()>
|
|
60
|
+
where
|
|
61
|
+
C: Context<'a>,
|
|
62
|
+
E: std::fmt::Display,
|
|
63
|
+
{
|
|
64
|
+
let err_str = format!("{}", err);
|
|
65
|
+
callback_with_error(cx, callback, move |cx| {
|
|
66
|
+
UNEXPECTED_ERROR.from_string(cx, err_str)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// When Future completes, call given JS callback using a neon::Channel with either error or
|
|
71
|
+
/// undefined
|
|
72
|
+
pub async fn void_future_to_js<E, F, ER, EF>(
|
|
73
|
+
channel: Arc<Channel>,
|
|
74
|
+
callback: Root<JsFunction>,
|
|
75
|
+
f: F,
|
|
76
|
+
error_function: EF,
|
|
77
|
+
) where
|
|
78
|
+
E: Display + Send + 'static,
|
|
79
|
+
F: Future<Output = Result<(), E>> + Send,
|
|
80
|
+
ER: Object,
|
|
81
|
+
EF: for<'a> FnOnce(&mut TaskContext<'a>, E) -> JsResult<'a, ER> + Send + 'static,
|
|
82
|
+
{
|
|
83
|
+
match f.await {
|
|
84
|
+
Ok(()) => {
|
|
85
|
+
send_result(channel, callback, |cx| Ok(cx.undefined()));
|
|
86
|
+
}
|
|
87
|
+
Err(err) => {
|
|
88
|
+
send_error(channel, callback, |cx| error_function(cx, err));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
macro_rules! js_optional_getter {
|
|
94
|
+
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
95
|
+
match get_optional($js_cx, $js_obj, $prop_name) {
|
|
96
|
+
None => None,
|
|
97
|
+
Some(val) => {
|
|
98
|
+
if val.is_a::<$js_type, _>($js_cx) {
|
|
99
|
+
Some(val.downcast_or_throw::<$js_type, _>($js_cx)?)
|
|
100
|
+
} else {
|
|
101
|
+
Some($js_cx.throw_type_error(format!("Invalid {}", $prop_name))?)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub(crate) use js_optional_getter;
|
|
109
|
+
|
|
110
|
+
macro_rules! js_optional_value_getter {
|
|
111
|
+
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
112
|
+
js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type).map(|v| v.value($js_cx))
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pub(crate) use js_optional_value_getter;
|
|
117
|
+
|
|
118
|
+
macro_rules! js_value_getter {
|
|
119
|
+
($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
|
|
120
|
+
match js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type) {
|
|
121
|
+
Some(val) => val.value($js_cx),
|
|
122
|
+
None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
pub(crate) use js_value_getter;
|
|
128
|
+
|
|
129
|
+
/// Helper for extracting an optional attribute from [obj].
|
|
130
|
+
/// If [obj].[attr] is undefined or not present, None is returned
|
|
131
|
+
pub fn get_optional<'a, C, K>(
|
|
132
|
+
cx: &mut C,
|
|
133
|
+
obj: &Handle<JsObject>,
|
|
134
|
+
attr: K,
|
|
135
|
+
) -> Option<Handle<'a, JsValue>>
|
|
136
|
+
where
|
|
137
|
+
K: neon::object::PropertyKey,
|
|
138
|
+
C: Context<'a>,
|
|
139
|
+
{
|
|
140
|
+
match obj.get_value(cx, attr) {
|
|
141
|
+
Err(_) => None,
|
|
142
|
+
Ok(val) => match val.is_a::<JsUndefined, _>(cx) {
|
|
143
|
+
true => None,
|
|
144
|
+
false => Some(val),
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
|
|
150
|
+
pub fn get_optional_vec<'a, C, K>(
|
|
151
|
+
cx: &mut C,
|
|
152
|
+
obj: &Handle<JsObject>,
|
|
153
|
+
attr: K,
|
|
154
|
+
) -> Result<Option<Vec<u8>>, neon::result::Throw>
|
|
155
|
+
where
|
|
156
|
+
K: neon::object::PropertyKey + Display + Clone,
|
|
157
|
+
C: Context<'a>,
|
|
158
|
+
{
|
|
159
|
+
if let Some(val) = get_optional(cx, obj, attr.clone()) {
|
|
160
|
+
let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
|
|
161
|
+
cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
|
|
162
|
+
.unwrap_err()
|
|
163
|
+
})?;
|
|
164
|
+
Ok(Some(buf.as_slice(cx).to_vec()))
|
|
165
|
+
} else {
|
|
166
|
+
Ok(None)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
|
|
171
|
+
pub fn get_vec<'a, C, K>(
|
|
172
|
+
cx: &mut C,
|
|
173
|
+
obj: &Handle<JsObject>,
|
|
174
|
+
attr: K,
|
|
175
|
+
full_attr_path: &str,
|
|
176
|
+
) -> Result<Vec<u8>, neon::result::Throw>
|
|
177
|
+
where
|
|
178
|
+
K: neon::object::PropertyKey + Display + Clone,
|
|
179
|
+
C: Context<'a>,
|
|
180
|
+
{
|
|
181
|
+
if let Some(val) = get_optional(cx, obj, attr.clone()) {
|
|
182
|
+
let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
|
|
183
|
+
cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
|
|
184
|
+
.unwrap_err()
|
|
185
|
+
})?;
|
|
186
|
+
Ok(buf.as_slice(cx).to_vec())
|
|
187
|
+
} else {
|
|
188
|
+
cx.throw_type_error::<_, Vec<u8>>(format!("Invalid or missing {}", full_attr_path))
|
|
189
|
+
}
|
|
190
|
+
}
|