@temporalio/core-bridge 1.1.0 → 1.4.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 +765 -128
- package/Cargo.toml +2 -2
- package/common.js +7 -3
- package/index.d.ts +118 -5
- package/index.js +2 -6
- package/package.json +2 -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/.buildkite/pipeline.yml +2 -0
- package/sdk-core/.cargo/config.toml +1 -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/bridge-ffi/src/lib.rs +2 -2
- package/sdk-core/client/Cargo.toml +7 -5
- package/sdk-core/client/src/lib.rs +354 -226
- package/sdk-core/client/src/metrics.rs +13 -11
- package/sdk-core/client/src/raw.rs +352 -107
- package/sdk-core/client/src/retry.rs +188 -147
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +28 -15
- package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
- 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 +515 -0
- package/sdk-core/core/src/lib.rs +62 -28
- package/sdk-core/core/src/pollers/mod.rs +2 -0
- 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/metrics.rs +48 -39
- package/sdk-core/core/src/telemetry/mod.rs +46 -12
- package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
- 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/mocks.rs +1 -0
- 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 +62 -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 +3 -3
- 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 +35 -12
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
- package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
- 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 +2 -6
- 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/upsert_search_attrs.rs +8 -2
- 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 +17 -0
- package/sdk-core/tests/runner.rs +93 -0
- package/src/conversions.rs +157 -94
- 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
package/src/runtime.rs
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
use crate::conversions::*;
|
|
2
|
+
use crate::errors::*;
|
|
3
|
+
use crate::helpers::*;
|
|
4
|
+
use crate::worker::*;
|
|
5
|
+
use neon::prelude::*;
|
|
6
|
+
use parking_lot::RwLock;
|
|
7
|
+
use std::{
|
|
8
|
+
cell::RefCell,
|
|
9
|
+
collections::HashMap,
|
|
10
|
+
ops::Deref,
|
|
11
|
+
sync::Arc,
|
|
12
|
+
time::{Duration, SystemTime, UNIX_EPOCH},
|
|
13
|
+
};
|
|
14
|
+
use temporal_client::{ClientInitError, ConfiguredClient, TemporalServiceClientWithMetrics};
|
|
15
|
+
use temporal_sdk_core::ephemeral_server::EphemeralServer as CoreEphemeralServer;
|
|
16
|
+
use temporal_sdk_core::{
|
|
17
|
+
fetch_global_buffered_logs, init_replay_worker, init_worker,
|
|
18
|
+
protos::temporal::api::history::v1::History, telemetry_init, ClientOptions, RetryClient,
|
|
19
|
+
WorkerConfig,
|
|
20
|
+
};
|
|
21
|
+
use tokio::{
|
|
22
|
+
runtime::Runtime,
|
|
23
|
+
sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
|
24
|
+
sync::Mutex,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
pub type RawClient = RetryClient<ConfiguredClient<TemporalServiceClientWithMetrics>>;
|
|
28
|
+
|
|
29
|
+
#[derive(Clone)]
|
|
30
|
+
pub struct EphemeralServer {
|
|
31
|
+
pub(crate) runtime: Arc<RuntimeHandle>,
|
|
32
|
+
pub(crate) core_server: Arc<Mutex<CoreEphemeralServer>>,
|
|
33
|
+
}
|
|
34
|
+
pub type BoxedEphemeralServer = JsBox<RefCell<Option<EphemeralServer>>>;
|
|
35
|
+
impl Finalize for EphemeralServer {}
|
|
36
|
+
|
|
37
|
+
pub struct RuntimeHandle {
|
|
38
|
+
pub(crate) sender: UnboundedSender<RuntimeRequest>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Box it so we can use the runtime from JS
|
|
42
|
+
pub type BoxedRuntime = JsBox<Arc<RuntimeHandle>>;
|
|
43
|
+
impl Finalize for RuntimeHandle {}
|
|
44
|
+
|
|
45
|
+
#[derive(Clone)]
|
|
46
|
+
pub struct Client {
|
|
47
|
+
pub(crate) runtime: Arc<RuntimeHandle>,
|
|
48
|
+
pub(crate) core_client: Arc<RawClient>,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub type BoxedClient = JsBox<RefCell<Option<Client>>>;
|
|
52
|
+
impl Finalize for Client {}
|
|
53
|
+
|
|
54
|
+
/// A request from JS to bridge to core
|
|
55
|
+
pub enum RuntimeRequest {
|
|
56
|
+
/// A request to shutdown the runtime, breaks from the thread loop.
|
|
57
|
+
Shutdown {
|
|
58
|
+
/// Used to send the result back into JS
|
|
59
|
+
callback: Root<JsFunction>,
|
|
60
|
+
},
|
|
61
|
+
/// A request to create a client in a runtime
|
|
62
|
+
CreateClient {
|
|
63
|
+
runtime: Arc<RuntimeHandle>,
|
|
64
|
+
options: ClientOptions,
|
|
65
|
+
headers: Option<HashMap<String, String>>,
|
|
66
|
+
/// Used to send the result back into JS
|
|
67
|
+
callback: Root<JsFunction>,
|
|
68
|
+
},
|
|
69
|
+
/// A request to update a client's HTTP request headers
|
|
70
|
+
UpdateClientHeaders {
|
|
71
|
+
client: Arc<RawClient>,
|
|
72
|
+
headers: HashMap<String, String>,
|
|
73
|
+
/// Used to send the result back into JS
|
|
74
|
+
callback: Root<JsFunction>,
|
|
75
|
+
},
|
|
76
|
+
/// A request to create a new Worker using a connected client
|
|
77
|
+
InitWorker {
|
|
78
|
+
/// Worker configuration e.g. limits and task queue
|
|
79
|
+
config: WorkerConfig,
|
|
80
|
+
/// A client created with a [CreateClient] request
|
|
81
|
+
client: Arc<RawClient>,
|
|
82
|
+
/// Used to send the result back into JS
|
|
83
|
+
callback: Root<JsFunction>,
|
|
84
|
+
},
|
|
85
|
+
/// A request to register a replay worker
|
|
86
|
+
InitReplayWorker {
|
|
87
|
+
/// Worker configuration. Must have unique task queue name.
|
|
88
|
+
config: WorkerConfig,
|
|
89
|
+
/// The history this worker should replay
|
|
90
|
+
history: History,
|
|
91
|
+
/// Used to send the result back into JS
|
|
92
|
+
callback: Root<JsFunction>,
|
|
93
|
+
},
|
|
94
|
+
/// A request to drain logs from core so they can be emitted in node
|
|
95
|
+
PollLogs {
|
|
96
|
+
/// Logs are sent to this function
|
|
97
|
+
callback: Root<JsFunction>,
|
|
98
|
+
},
|
|
99
|
+
StartEphemeralServer {
|
|
100
|
+
runtime: Arc<RuntimeHandle>,
|
|
101
|
+
config: EphemeralServerConfig,
|
|
102
|
+
callback: Root<JsFunction>,
|
|
103
|
+
},
|
|
104
|
+
ShutdownEphemeralServer {
|
|
105
|
+
server: Arc<Mutex<CoreEphemeralServer>>,
|
|
106
|
+
callback: Root<JsFunction>,
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Inits a multi-threaded tokio runtime used to interact with sdk-core APIs
|
|
111
|
+
pub fn tokio_runtime() -> Runtime {
|
|
112
|
+
tokio::runtime::Builder::new_multi_thread()
|
|
113
|
+
.enable_all()
|
|
114
|
+
.thread_name("core")
|
|
115
|
+
.build()
|
|
116
|
+
.expect("Tokio runtime must construct properly")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Builds a tokio runtime and starts polling on [RuntimeRequest]s via an internal channel.
|
|
120
|
+
/// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
|
|
121
|
+
/// Blocks current thread until a [Shutdown] request is received in channel.
|
|
122
|
+
pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver<RuntimeRequest>) {
|
|
123
|
+
tokio_runtime().block_on(async {
|
|
124
|
+
loop {
|
|
125
|
+
let request_option = receiver.recv().await;
|
|
126
|
+
let request = match request_option {
|
|
127
|
+
None => break,
|
|
128
|
+
Some(request) => request,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let channel = channel.clone();
|
|
132
|
+
|
|
133
|
+
match request {
|
|
134
|
+
RuntimeRequest::Shutdown { callback } => {
|
|
135
|
+
send_result(channel, callback, |cx| Ok(cx.undefined()));
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
RuntimeRequest::CreateClient {
|
|
139
|
+
runtime,
|
|
140
|
+
options,
|
|
141
|
+
headers,
|
|
142
|
+
callback,
|
|
143
|
+
} => {
|
|
144
|
+
// `metrics_meter` (second arg) can be None here since we don't use the
|
|
145
|
+
// returned client directly at the moment, when we repurpose the client to be
|
|
146
|
+
// used by a Worker, `init_worker` will attach the correct metrics meter for
|
|
147
|
+
// us.
|
|
148
|
+
tokio::spawn(async move {
|
|
149
|
+
match options
|
|
150
|
+
.connect_no_namespace(None, headers.map(|h| Arc::new(RwLock::new(h))))
|
|
151
|
+
.await
|
|
152
|
+
{
|
|
153
|
+
Err(err) => {
|
|
154
|
+
send_error(channel.clone(), callback, |cx| match err {
|
|
155
|
+
ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
|
|
156
|
+
.from_string(
|
|
157
|
+
cx,
|
|
158
|
+
format!("Failed to call GetSystemInfo: {}", e),
|
|
159
|
+
),
|
|
160
|
+
ClientInitError::TonicTransportError(e) => {
|
|
161
|
+
TRANSPORT_ERROR.from_error(cx, e)
|
|
162
|
+
}
|
|
163
|
+
ClientInitError::InvalidUri(e) => {
|
|
164
|
+
Ok(JsError::type_error(cx, format!("{}", e))?.upcast())
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
Ok(client) => {
|
|
169
|
+
send_result(channel.clone(), callback, |cx| {
|
|
170
|
+
Ok(cx.boxed(RefCell::new(Some(Client {
|
|
171
|
+
runtime,
|
|
172
|
+
core_client: Arc::new(client),
|
|
173
|
+
}))))
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
RuntimeRequest::UpdateClientHeaders {
|
|
180
|
+
client,
|
|
181
|
+
headers,
|
|
182
|
+
callback,
|
|
183
|
+
} => {
|
|
184
|
+
client.get_client().set_headers(headers);
|
|
185
|
+
send_result(channel.clone(), callback, |cx| Ok(cx.undefined()));
|
|
186
|
+
}
|
|
187
|
+
RuntimeRequest::PollLogs { callback } => {
|
|
188
|
+
let logs = fetch_global_buffered_logs();
|
|
189
|
+
send_result(channel.clone(), callback, |cx| {
|
|
190
|
+
let logarr = cx.empty_array();
|
|
191
|
+
for (i, cl) in logs.into_iter().enumerate() {
|
|
192
|
+
// Not much to do here except for panic when there's an
|
|
193
|
+
// error here.
|
|
194
|
+
let logobj = cx.empty_object();
|
|
195
|
+
let level = cx.string(cl.level.to_string());
|
|
196
|
+
logobj.set(cx, "level", level).unwrap();
|
|
197
|
+
let ts = system_time_to_js(cx, cl.timestamp).unwrap();
|
|
198
|
+
logobj.set(cx, "timestamp", ts).unwrap();
|
|
199
|
+
let msg = cx.string(cl.message);
|
|
200
|
+
logobj.set(cx, "message", msg).unwrap();
|
|
201
|
+
logarr.set(cx, i as u32, logobj).unwrap();
|
|
202
|
+
}
|
|
203
|
+
Ok(logarr)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
RuntimeRequest::InitWorker {
|
|
207
|
+
config,
|
|
208
|
+
client,
|
|
209
|
+
callback,
|
|
210
|
+
} => {
|
|
211
|
+
let client = (*client).clone();
|
|
212
|
+
let worker = init_worker(config, client.into_inner());
|
|
213
|
+
let (tx, rx) = unbounded_channel();
|
|
214
|
+
tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
|
|
215
|
+
send_result(channel.clone(), callback, |cx| {
|
|
216
|
+
Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
RuntimeRequest::InitReplayWorker {
|
|
220
|
+
config,
|
|
221
|
+
history,
|
|
222
|
+
callback,
|
|
223
|
+
} => {
|
|
224
|
+
match init_replay_worker(config, &history) {
|
|
225
|
+
Ok(worker) => {
|
|
226
|
+
let (tx, rx) = unbounded_channel();
|
|
227
|
+
tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
|
|
228
|
+
send_result(channel.clone(), callback, |cx| {
|
|
229
|
+
Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
Err(err) => send_error(channel.clone(), callback, move |cx| {
|
|
233
|
+
UNEXPECTED_ERROR.from_error(cx, err.deref())
|
|
234
|
+
}),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
RuntimeRequest::StartEphemeralServer {
|
|
238
|
+
runtime,
|
|
239
|
+
config,
|
|
240
|
+
callback,
|
|
241
|
+
} => {
|
|
242
|
+
tokio::spawn(async move {
|
|
243
|
+
let result = match config {
|
|
244
|
+
EphemeralServerConfig::TestServer(config) => {
|
|
245
|
+
config.start_server().await
|
|
246
|
+
}
|
|
247
|
+
EphemeralServerConfig::Temporalite(config) => {
|
|
248
|
+
config.start_server().await
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
match result {
|
|
252
|
+
Err(err) => {
|
|
253
|
+
let err_str = format!("Failed to start ephemeral server: {}", err);
|
|
254
|
+
send_error(channel.clone(), callback, |cx| {
|
|
255
|
+
UNEXPECTED_ERROR.from_string(cx, err_str)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
Ok(server) => {
|
|
259
|
+
send_result(channel.clone(), callback, |cx| {
|
|
260
|
+
Ok(cx.boxed(RefCell::new(Some(EphemeralServer {
|
|
261
|
+
runtime,
|
|
262
|
+
core_server: Arc::new(Mutex::new(server)),
|
|
263
|
+
}))))
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
RuntimeRequest::ShutdownEphemeralServer { server, callback } => {
|
|
270
|
+
tokio::spawn(async move {
|
|
271
|
+
void_future_to_js(
|
|
272
|
+
channel,
|
|
273
|
+
callback,
|
|
274
|
+
async move {
|
|
275
|
+
let mut guard = server.lock().await;
|
|
276
|
+
guard.shutdown().await
|
|
277
|
+
},
|
|
278
|
+
|cx, err| {
|
|
279
|
+
UNEXPECTED_ERROR.from_string(
|
|
280
|
+
cx,
|
|
281
|
+
format!("Failed to start test server: {}", err),
|
|
282
|
+
)
|
|
283
|
+
},
|
|
284
|
+
)
|
|
285
|
+
.await
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Below are functions exported to JS
|
|
294
|
+
|
|
295
|
+
/// Convert Rust SystemTime into a JS array with 2 numbers (seconds, nanos)
|
|
296
|
+
pub fn system_time_to_js<'a, C>(cx: &mut C, time: SystemTime) -> NeonResult<Handle<'a, JsArray>>
|
|
297
|
+
where
|
|
298
|
+
C: Context<'a>,
|
|
299
|
+
{
|
|
300
|
+
let nanos = time
|
|
301
|
+
.duration_since(UNIX_EPOCH)
|
|
302
|
+
.unwrap_or(Duration::ZERO)
|
|
303
|
+
.as_nanos();
|
|
304
|
+
let only_nanos = cx.number((nanos % 1_000_000_000) as f64);
|
|
305
|
+
let ts_seconds = cx.number((nanos / 1_000_000_000) as f64);
|
|
306
|
+
let ts = cx.empty_array();
|
|
307
|
+
ts.set(cx, 0, ts_seconds).unwrap();
|
|
308
|
+
ts.set(cx, 1, only_nanos).unwrap();
|
|
309
|
+
Ok(ts)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// Helper to get the current time in nanosecond resolution.
|
|
313
|
+
pub fn get_time_of_day(mut cx: FunctionContext) -> JsResult<JsArray> {
|
|
314
|
+
system_time_to_js(&mut cx, SystemTime::now())
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// Initialize Core global telemetry.
|
|
318
|
+
/// This should typically be called once on process startup.
|
|
319
|
+
pub fn init_telemetry(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
320
|
+
let telemetry_options = cx.argument::<JsObject>(0)?.as_telemetry_options(&mut cx)?;
|
|
321
|
+
telemetry_init(&telemetry_options).map_err(|err| {
|
|
322
|
+
cx.throw_type_error::<String, ()>(format!("{}", err))
|
|
323
|
+
.unwrap_err()
|
|
324
|
+
})?;
|
|
325
|
+
Ok(cx.undefined())
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/// Create the tokio runtime required to run Core.
|
|
329
|
+
/// Immediately spawns a poller thread that will block on [RuntimeRequest]s
|
|
330
|
+
pub fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
|
|
331
|
+
let channel = Arc::new(cx.channel());
|
|
332
|
+
let (sender, mut receiver) = unbounded_channel::<RuntimeRequest>();
|
|
333
|
+
|
|
334
|
+
std::thread::spawn(move || start_bridge_loop(channel, &mut receiver));
|
|
335
|
+
|
|
336
|
+
Ok(cx.boxed(Arc::new(RuntimeHandle { sender })))
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/// Shutdown the Core instance and break out of the thread loop
|
|
340
|
+
pub fn runtime_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
341
|
+
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
342
|
+
let callback = cx.argument::<JsFunction>(1)?;
|
|
343
|
+
let request = RuntimeRequest::Shutdown {
|
|
344
|
+
callback: callback.root(&mut cx),
|
|
345
|
+
};
|
|
346
|
+
if let Err(err) = runtime.sender.send(request) {
|
|
347
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
348
|
+
};
|
|
349
|
+
Ok(cx.undefined())
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/// Request to drain forwarded logs from core
|
|
353
|
+
pub fn poll_logs(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
354
|
+
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
355
|
+
let callback = cx.argument::<JsFunction>(1)?;
|
|
356
|
+
let request = RuntimeRequest::PollLogs {
|
|
357
|
+
callback: callback.root(&mut cx),
|
|
358
|
+
};
|
|
359
|
+
if let Err(err) = runtime.sender.send(request) {
|
|
360
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
361
|
+
}
|
|
362
|
+
Ok(cx.undefined())
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/// Create a connected gRPC client which can be used to initialize workers.
|
|
366
|
+
/// Client will be returned in the supplied `callback`.
|
|
367
|
+
pub fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
368
|
+
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
369
|
+
let opts = cx.argument::<JsObject>(1)?;
|
|
370
|
+
let callback = cx.argument::<JsFunction>(2)?;
|
|
371
|
+
|
|
372
|
+
let client_options = opts.as_client_options(&mut cx)?;
|
|
373
|
+
let headers = match js_optional_getter!(&mut cx, &opts, "metadata", JsObject) {
|
|
374
|
+
None => None,
|
|
375
|
+
Some(h) => Some(
|
|
376
|
+
h.as_hash_map_of_string_to_string(&mut cx)
|
|
377
|
+
.map_err(|reason| {
|
|
378
|
+
cx.throw_type_error::<_, HashMap<String, String>>(format!(
|
|
379
|
+
"Invalid metadata: {}",
|
|
380
|
+
reason
|
|
381
|
+
))
|
|
382
|
+
.unwrap_err()
|
|
383
|
+
})?,
|
|
384
|
+
),
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
let request = RuntimeRequest::CreateClient {
|
|
388
|
+
runtime: (**runtime).clone(),
|
|
389
|
+
options: client_options,
|
|
390
|
+
headers,
|
|
391
|
+
callback: callback.root(&mut cx),
|
|
392
|
+
};
|
|
393
|
+
if let Err(err) = runtime.sender.send(request) {
|
|
394
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
Ok(cx.undefined())
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/// Drop a reference to a Client, once all references are dropped, the Client will be closed.
|
|
401
|
+
pub fn client_close(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
402
|
+
let client = cx.argument::<BoxedClient>(0)?;
|
|
403
|
+
if client.replace(None).is_none() {
|
|
404
|
+
ILLEGAL_STATE_ERROR
|
|
405
|
+
.from_string(&mut cx, "Client already closed")
|
|
406
|
+
.and_then(|err| cx.throw(err))?;
|
|
407
|
+
};
|
|
408
|
+
Ok(cx.undefined())
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/// Update a Client's HTTP request headers
|
|
412
|
+
pub fn client_update_headers(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
413
|
+
let client = cx.argument::<BoxedClient>(0)?;
|
|
414
|
+
let headers = cx
|
|
415
|
+
.argument::<JsObject>(1)?
|
|
416
|
+
.as_hash_map_of_string_to_string(&mut cx)?;
|
|
417
|
+
let callback = cx.argument::<JsFunction>(2)?;
|
|
418
|
+
|
|
419
|
+
match client.borrow().as_ref() {
|
|
420
|
+
None => {
|
|
421
|
+
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
|
|
422
|
+
}
|
|
423
|
+
Some(client) => {
|
|
424
|
+
let request = RuntimeRequest::UpdateClientHeaders {
|
|
425
|
+
client: client.core_client.clone(),
|
|
426
|
+
headers,
|
|
427
|
+
callback: callback.root(&mut cx),
|
|
428
|
+
};
|
|
429
|
+
if let Err(err) = client.runtime.sender.send(request) {
|
|
430
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
Ok(cx.undefined())
|
|
436
|
+
}
|
package/src/testing.rs
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
use crate::conversions::*;
|
|
2
|
+
use crate::errors::*;
|
|
3
|
+
use crate::helpers::*;
|
|
4
|
+
use crate::runtime::{BoxedEphemeralServer, BoxedRuntime, RuntimeRequest};
|
|
5
|
+
use neon::prelude::*;
|
|
6
|
+
|
|
7
|
+
// Below are functions exported to JS
|
|
8
|
+
|
|
9
|
+
/// Start an ephemeral Temporal server
|
|
10
|
+
pub fn start_ephemeral_server(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
11
|
+
let runtime = cx.argument::<BoxedRuntime>(0)?;
|
|
12
|
+
let config = cx.argument::<JsObject>(1)?;
|
|
13
|
+
let sdk_version = cx.argument::<JsString>(2)?.value(&mut cx);
|
|
14
|
+
let callback = cx.argument::<JsFunction>(3)?;
|
|
15
|
+
|
|
16
|
+
let config = config.as_ephemeral_server_config(&mut cx, sdk_version)?;
|
|
17
|
+
let request = RuntimeRequest::StartEphemeralServer {
|
|
18
|
+
runtime: (**runtime).clone(),
|
|
19
|
+
config,
|
|
20
|
+
callback: callback.root(&mut cx),
|
|
21
|
+
};
|
|
22
|
+
if let Err(err) = runtime.sender.send(request) {
|
|
23
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
Ok(cx.undefined())
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Get the ephemeral server "target" (address:port string)
|
|
30
|
+
pub fn get_ephemeral_server_target(mut cx: FunctionContext) -> JsResult<JsString> {
|
|
31
|
+
let server = cx.argument::<BoxedEphemeralServer>(0)?;
|
|
32
|
+
let target = server
|
|
33
|
+
.borrow()
|
|
34
|
+
.as_ref()
|
|
35
|
+
.map(|s| cx.string(s.core_server.blocking_lock().target.to_owned()));
|
|
36
|
+
if target.is_none() {
|
|
37
|
+
ILLEGAL_STATE_ERROR
|
|
38
|
+
.from_string(&mut cx, "Tried to use closed test server")
|
|
39
|
+
.and_then(|err| cx.throw(err))?;
|
|
40
|
+
};
|
|
41
|
+
Ok(target.unwrap())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Shutdown an ephemeral server - consumes the server
|
|
45
|
+
pub fn shutdown_ephemeral_server(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
|
46
|
+
let server = cx.argument::<BoxedEphemeralServer>(0)?;
|
|
47
|
+
let callback = cx.argument::<JsFunction>(1)?;
|
|
48
|
+
// Drop the ref
|
|
49
|
+
match server.replace(None) {
|
|
50
|
+
None => {
|
|
51
|
+
callback_with_unexpected_error(&mut cx, callback, "Tried to use closed test server")?;
|
|
52
|
+
}
|
|
53
|
+
Some(server) => {
|
|
54
|
+
if let Err(err) = server
|
|
55
|
+
.runtime
|
|
56
|
+
.sender
|
|
57
|
+
.send(RuntimeRequest::ShutdownEphemeralServer {
|
|
58
|
+
server: server.core_server.clone(),
|
|
59
|
+
callback: callback.root(&mut cx),
|
|
60
|
+
})
|
|
61
|
+
{
|
|
62
|
+
callback_with_unexpected_error(&mut cx, callback, err)?;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
Ok(cx.undefined())
|
|
67
|
+
}
|