app_bridge 4.1.0 → 5.0.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.
- checksums.yaml +4 -4
- data/.tool-versions +1 -1
- data/Cargo.lock +482 -448
- data/Cargo.toml +1 -1
- data/README.md +6 -0
- data/Rakefile +16 -13
- data/ext/app_bridge/Cargo.toml +6 -4
- data/ext/app_bridge/docs/connection-config-schema.json +500 -0
- data/ext/app_bridge/src/app_state.rs +13 -8
- data/ext/app_bridge/src/component.rs +83 -17
- data/ext/app_bridge/src/error_mapping.rs +5 -0
- data/ext/app_bridge/src/file_ops.rs +2 -1
- data/ext/app_bridge/src/lib.rs +3 -0
- data/ext/app_bridge/src/request_builder.rs +76 -15
- data/ext/app_bridge/src/wrappers/action_context.rs +8 -7
- data/ext/app_bridge/src/wrappers/app.rs +49 -37
- data/ext/app_bridge/src/wrappers/trigger_context.rs +5 -4
- data/ext/app_bridge/wit/v4_1/world.wit +2 -0
- data/ext/app_bridge/wit/v5/world.wit +363 -0
- data/lib/app_bridge/app.rb +10 -0
- data/lib/app_bridge/connection_config_validator.rb +101 -0
- data/lib/app_bridge/version.rb +1 -1
- data/lib/app_bridge.rb +2 -0
- data/tasks/fixtures.rake +25 -4
- metadata +20 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
use std::result::Result::Ok;
|
|
3
|
-
use wasmtime::component::{Component, Linker};
|
|
3
|
+
use wasmtime::component::{Component, HasSelf, Linker};
|
|
4
4
|
use wasmtime::{Engine, Result, Store};
|
|
5
|
-
use wasmtime_wasi::
|
|
5
|
+
use wasmtime_wasi::WasiCtxBuilder;
|
|
6
6
|
|
|
7
7
|
use crate::app_state::AppState;
|
|
8
8
|
use crate::types::{
|
|
@@ -42,6 +42,13 @@ pub mod v4_1 {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
pub mod v5 {
|
|
46
|
+
wasmtime::component::bindgen!({
|
|
47
|
+
path: "./wit/v5",
|
|
48
|
+
world: "bridge",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
// ============================================================================
|
|
46
53
|
// Version conversion macro - generates From impls for a version module
|
|
47
54
|
// ============================================================================
|
|
@@ -174,6 +181,15 @@ impl From<v4_1::standout::app::types::ReferenceObject> for ReferenceObject {
|
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
|
|
184
|
+
impl From<v5::standout::app::types::ReferenceObject> for ReferenceObject {
|
|
185
|
+
fn from(r: v5::standout::app::types::ReferenceObject) -> Self {
|
|
186
|
+
Self {
|
|
187
|
+
reference: r.reference,
|
|
188
|
+
status: r.status,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
177
193
|
impl From<&ReferenceObject> for v4_1::standout::app::types::ReferenceObject {
|
|
178
194
|
fn from(r: &ReferenceObject) -> Self {
|
|
179
195
|
Self {
|
|
@@ -183,6 +199,15 @@ impl From<&ReferenceObject> for v4_1::standout::app::types::ReferenceObject {
|
|
|
183
199
|
}
|
|
184
200
|
}
|
|
185
201
|
|
|
202
|
+
impl From<&ReferenceObject> for v5::standout::app::types::ReferenceObject {
|
|
203
|
+
fn from(r: &ReferenceObject) -> Self {
|
|
204
|
+
Self {
|
|
205
|
+
reference: r.reference.clone(),
|
|
206
|
+
status: r.status.clone(),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
186
211
|
// Generate conversions for all supported versions
|
|
187
212
|
impl_error_code_conversion!(v3,);
|
|
188
213
|
impl_conversions!(v3);
|
|
@@ -199,6 +224,13 @@ impl_error_code_conversion!(
|
|
|
199
224
|
impl_conversions!(v4_1);
|
|
200
225
|
impl_app_error_conversion!(v4_1);
|
|
201
226
|
impl_action_context_conversion_retry!(v4_1);
|
|
227
|
+
impl_error_code_conversion!(
|
|
228
|
+
v5,
|
|
229
|
+
V::RetryWithReference(r) => Self::RetryWithReference(r.into()),
|
|
230
|
+
);
|
|
231
|
+
impl_conversions!(v5);
|
|
232
|
+
impl_app_error_conversion!(v5);
|
|
233
|
+
impl_action_context_conversion_retry!(v5);
|
|
202
234
|
|
|
203
235
|
// ============================================================================
|
|
204
236
|
// BridgeWrapper - unified interface for all component versions
|
|
@@ -210,6 +242,7 @@ pub enum BridgeWrapper {
|
|
|
210
242
|
V3(v3::Bridge),
|
|
211
243
|
V4(v4::Bridge),
|
|
212
244
|
V4_1(v4_1::Bridge),
|
|
245
|
+
V5(v5::Bridge),
|
|
213
246
|
}
|
|
214
247
|
|
|
215
248
|
impl BridgeWrapper {
|
|
@@ -219,8 +252,13 @@ impl BridgeWrapper {
|
|
|
219
252
|
BridgeWrapper::V3(_) => "3.0.0",
|
|
220
253
|
BridgeWrapper::V4(_) => "4.0.0",
|
|
221
254
|
BridgeWrapper::V4_1(_) => "4.1.0",
|
|
255
|
+
BridgeWrapper::V5(_) => "5.0.0",
|
|
222
256
|
}
|
|
223
257
|
}
|
|
258
|
+
|
|
259
|
+
pub fn connections_supported(&self) -> bool {
|
|
260
|
+
matches!(self, BridgeWrapper::V5(_))
|
|
261
|
+
}
|
|
224
262
|
}
|
|
225
263
|
|
|
226
264
|
/// Macro to implement a bridge method that works across all versions.
|
|
@@ -242,6 +280,10 @@ macro_rules! bridge_method {
|
|
|
242
280
|
let r = b.$interface().$method(store)?;
|
|
243
281
|
Ok(r.map_err(Into::into))
|
|
244
282
|
}
|
|
283
|
+
BridgeWrapper::V5(b) => {
|
|
284
|
+
let r = b.$interface().$method(store)?;
|
|
285
|
+
Ok(r.map_err(Into::into))
|
|
286
|
+
}
|
|
245
287
|
}
|
|
246
288
|
}
|
|
247
289
|
};
|
|
@@ -261,6 +303,10 @@ macro_rules! bridge_method {
|
|
|
261
303
|
let r = b.$interface().$method(store, &ctx.into())?;
|
|
262
304
|
Ok(r.map(Into::into).map_err(Into::into))
|
|
263
305
|
}
|
|
306
|
+
BridgeWrapper::V5(b) => {
|
|
307
|
+
let r = b.$interface().$method(store, &ctx.into())?;
|
|
308
|
+
Ok(r.map(Into::into).map_err(Into::into))
|
|
309
|
+
}
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
};
|
|
@@ -280,6 +326,10 @@ macro_rules! bridge_method {
|
|
|
280
326
|
let r = b.$interface().$method(store, &ctx.into())?;
|
|
281
327
|
Ok(r.map(Into::into).map_err(Into::into))
|
|
282
328
|
}
|
|
329
|
+
BridgeWrapper::V5(b) => {
|
|
330
|
+
let r = b.$interface().$method(store, &ctx.into())?;
|
|
331
|
+
Ok(r.map(Into::into).map_err(Into::into))
|
|
332
|
+
}
|
|
283
333
|
}
|
|
284
334
|
}
|
|
285
335
|
};
|
|
@@ -297,6 +347,18 @@ impl BridgeWrapper {
|
|
|
297
347
|
bridge_method!(fn call_action_input_schema(&ActionContext) -> Result<String> via standout_app_actions . call_input_schema);
|
|
298
348
|
bridge_method!(fn call_action_output_schema(&ActionContext) -> Result<String> via standout_app_actions . call_output_schema);
|
|
299
349
|
bridge_method!(fn call_execute(&ActionContext) -> Result<ActionResponse> via standout_app_actions . call_execute);
|
|
350
|
+
|
|
351
|
+
pub fn call_connection_config(
|
|
352
|
+
&self,
|
|
353
|
+
store: &mut Store<AppState>,
|
|
354
|
+
) -> Result<String> {
|
|
355
|
+
match self {
|
|
356
|
+
BridgeWrapper::V5(b) => {
|
|
357
|
+
b.standout_app_connections().call_connection_config(store)
|
|
358
|
+
}
|
|
359
|
+
_ => unreachable!("call_connection_config requires connections_supported"),
|
|
360
|
+
}
|
|
361
|
+
}
|
|
300
362
|
}
|
|
301
363
|
|
|
302
364
|
// ============================================================================
|
|
@@ -315,24 +377,23 @@ pub fn build_linker(engine: &Engine) -> Result<Linker<AppState>> {
|
|
|
315
377
|
|
|
316
378
|
// ---- Version-specific interfaces ----
|
|
317
379
|
// v3: http + environment
|
|
318
|
-
v3::standout::app::http::add_to_linker(&mut linker, |s| s)?;
|
|
319
|
-
v3::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
|
|
380
|
+
v3::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
381
|
+
v3::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
320
382
|
|
|
321
383
|
// v4: http + environment + file
|
|
322
|
-
v4::standout::app::http::add_to_linker(&mut linker, |s| s)?;
|
|
323
|
-
v4::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
|
|
324
|
-
v4::standout::app::file::add_to_linker(&mut linker, |s| s)?;
|
|
384
|
+
v4::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
385
|
+
v4::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
386
|
+
v4::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
325
387
|
|
|
326
388
|
// v4.1: http + environment + file
|
|
327
|
-
v4_1::standout::app::http::add_to_linker(&mut linker, |s| s)?;
|
|
328
|
-
v4_1::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
|
|
329
|
-
v4_1::standout::app::file::add_to_linker(&mut linker, |s| s)?;
|
|
389
|
+
v4_1::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
390
|
+
v4_1::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
391
|
+
v4_1::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
330
392
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// v5::standout::app::new_feature::add_to_linker(&mut linker, |s| s)?;
|
|
393
|
+
// v5: http + environment + file
|
|
394
|
+
v5::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
395
|
+
v5::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
396
|
+
v5::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
|
|
336
397
|
|
|
337
398
|
Ok(linker)
|
|
338
399
|
}
|
|
@@ -360,7 +421,12 @@ pub fn app(
|
|
|
360
421
|
let component = Component::from_file(&engine, &file_path)?;
|
|
361
422
|
|
|
362
423
|
// Try versions newest-first. When adding vN, insert at the top.
|
|
363
|
-
//
|
|
424
|
+
// v5 (current - has connections interface)
|
|
425
|
+
if let Ok(instance) = v5::Bridge::instantiate(&mut *store, &component, &linker) {
|
|
426
|
+
return Ok(BridgeWrapper::V5(instance));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// v4.1 (has file interface + retry-with-reference)
|
|
364
430
|
if let Ok(instance) = v4_1::Bridge::instantiate(&mut *store, &component, &linker) {
|
|
365
431
|
return Ok(BridgeWrapper::V4_1(instance));
|
|
366
432
|
}
|
|
@@ -376,6 +442,6 @@ pub fn app(
|
|
|
376
442
|
}
|
|
377
443
|
|
|
378
444
|
Err(wasmtime::Error::msg(
|
|
379
|
-
"Failed to instantiate component: no compatible WIT version found (tried v4.1, v4, v3)",
|
|
445
|
+
"Failed to instantiate component: no compatible WIT version found (tried v5, v4.1, v4, v3)",
|
|
380
446
|
))
|
|
381
447
|
}
|
|
@@ -2,6 +2,11 @@ use crate::types::{AppError, ErrorCode};
|
|
|
2
2
|
use magnus::prelude::*;
|
|
3
3
|
use magnus::{Error, ExceptionClass, RObject, Ruby};
|
|
4
4
|
|
|
5
|
+
pub fn runtime_error(message: impl Into<String>) -> Error {
|
|
6
|
+
let ruby = Ruby::get().unwrap();
|
|
7
|
+
Error::new(ruby.exception_runtime_error(), message.into())
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
impl From<ErrorCode> for ExceptionClass {
|
|
6
11
|
fn from(value: ErrorCode) -> Self {
|
|
7
12
|
fn get_class(name: &str) -> ExceptionClass {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::app_state::AppState;
|
|
2
|
-
use crate::component::{v4, v4_1};
|
|
2
|
+
use crate::component::{v4, v4_1, v5};
|
|
3
3
|
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
|
|
4
4
|
|
|
5
5
|
/// Detects the type of input string
|
|
@@ -257,6 +257,7 @@ macro_rules! impl_file_host {
|
|
|
257
257
|
// When adding v5, add: impl_file_host!(v5);
|
|
258
258
|
impl_file_host!(v4);
|
|
259
259
|
impl_file_host!(v4_1);
|
|
260
|
+
impl_file_host!(v5);
|
|
260
261
|
|
|
261
262
|
#[cfg(test)]
|
|
262
263
|
mod tests {
|
data/ext/app_bridge/src/lib.rs
CHANGED
|
@@ -90,6 +90,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
90
90
|
let app_class = module.define_class("App", ruby.class_object())?;
|
|
91
91
|
app_class.define_alloc_func::<MutRApp>();
|
|
92
92
|
app_class.define_method("wit_version", method!(MutRApp::wit_version, 0))?;
|
|
93
|
+
app_class.define_method("connections_supported?", method!(MutRApp::connections_supported, 0))?;
|
|
93
94
|
app_class.define_method("trigger_ids", method!(MutRApp::trigger_ids, 0))?;
|
|
94
95
|
app_class.define_method("action_ids", method!(MutRApp::action_ids, 0))?;
|
|
95
96
|
app_class.define_method("action_input_schema", method!(MutRApp::action_input_schema, 1))?;
|
|
@@ -97,6 +98,8 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
97
98
|
app_class.define_method("trigger_input_schema", method!(MutRApp::trigger_input_schema, 1))?;
|
|
98
99
|
app_class.define_method("trigger_output_schema", method!(MutRApp::trigger_output_schema, 1))?;
|
|
99
100
|
app_class.define_private_method("_rust_initialize", method!(MutRApp::initialize, 2))?;
|
|
101
|
+
app_class.define_private_method("_rust_connections_supported?", method!(MutRApp::connections_supported, 0))?;
|
|
102
|
+
app_class.define_private_method("_rust_connection_config", method!(MutRApp::connection_config, 0))?;
|
|
100
103
|
app_class.define_private_method("_rust_fetch_events", method!(MutRApp::rb_fetch_events, 1))?;
|
|
101
104
|
app_class.define_private_method("_rust_execute_action", method!(MutRApp::rb_execute_action, 1))?;
|
|
102
105
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use crate::app_state::AppState;
|
|
2
|
-
use crate::component::{v3, v4, v4_1};
|
|
3
|
-
use crate::component::v4::standout::app::http::{Method, Request, RequestError
|
|
2
|
+
use crate::component::{v3, v4, v4_1, v5};
|
|
3
|
+
use crate::component::v4::standout::app::http::{Method, Request, RequestError};
|
|
4
4
|
use reqwest::Method as ReqwestMethod;
|
|
5
5
|
use std::result::Result::Ok;
|
|
6
6
|
use wasmtime::component::Resource;
|
|
@@ -212,16 +212,6 @@ macro_rules! impl_http_type_conversions {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
impl From<Response> for $v::standout::app::http::Response {
|
|
216
|
-
fn from(r: Response) -> Self {
|
|
217
|
-
Self {
|
|
218
|
-
status: r.status,
|
|
219
|
-
headers: r.headers,
|
|
220
|
-
body: r.body,
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
215
|
impl From<Request> for $v::standout::app::http::Request {
|
|
226
216
|
fn from(r: Request) -> Self {
|
|
227
217
|
Self {
|
|
@@ -243,6 +233,32 @@ macro_rules! impl_http_type_conversions {
|
|
|
243
233
|
};
|
|
244
234
|
}
|
|
245
235
|
|
|
236
|
+
macro_rules! impl_http_response_conversion {
|
|
237
|
+
($v:ident, with_bytes) => {
|
|
238
|
+
impl From<Response> for $v::standout::app::http::Response {
|
|
239
|
+
fn from(r: Response) -> Self {
|
|
240
|
+
Self {
|
|
241
|
+
status: r.status,
|
|
242
|
+
headers: r.headers,
|
|
243
|
+
body: r.body,
|
|
244
|
+
body_bytes: r.body_bytes,
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
($v:ident, no_bytes) => {
|
|
250
|
+
impl From<Response> for $v::standout::app::http::Response {
|
|
251
|
+
fn from(r: Response) -> Self {
|
|
252
|
+
Self {
|
|
253
|
+
status: r.status,
|
|
254
|
+
headers: r.headers,
|
|
255
|
+
body: r.body,
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
246
262
|
// ============================================================================
|
|
247
263
|
// Generate implementations for all supported versions
|
|
248
264
|
// When adding v5, just add:
|
|
@@ -253,15 +269,28 @@ macro_rules! impl_http_type_conversions {
|
|
|
253
269
|
impl_host_request_builder!(v3, no);
|
|
254
270
|
impl_host_request_builder!(v4, no);
|
|
255
271
|
impl_host_request_builder!(v4_1, yes);
|
|
272
|
+
impl_host_request_builder!(v5, yes);
|
|
256
273
|
|
|
257
274
|
impl_http_type_conversions!(v3);
|
|
258
|
-
// Note: v4 doesn't need conversions since we use v4 types as the canonical internal types
|
|
259
275
|
impl_http_type_conversions!(v4_1);
|
|
276
|
+
impl_http_type_conversions!(v5);
|
|
277
|
+
impl_http_response_conversion!(v3, no_bytes);
|
|
278
|
+
impl_http_response_conversion!(v4, no_bytes);
|
|
279
|
+
impl_http_response_conversion!(v4_1, with_bytes);
|
|
280
|
+
impl_http_response_conversion!(v5, with_bytes);
|
|
260
281
|
|
|
261
282
|
// ============================================================================
|
|
262
283
|
// Shared request sending logic
|
|
263
284
|
// ============================================================================
|
|
264
285
|
|
|
286
|
+
#[derive(Debug, Clone)]
|
|
287
|
+
struct Response {
|
|
288
|
+
status: u16,
|
|
289
|
+
headers: Vec<(String, String)>,
|
|
290
|
+
body: String,
|
|
291
|
+
body_bytes: Option<Vec<u8>>,
|
|
292
|
+
}
|
|
293
|
+
|
|
265
294
|
fn send_request(
|
|
266
295
|
client: &std::sync::Arc<std::sync::Mutex<reqwest::blocking::Client>>,
|
|
267
296
|
request: &Request,
|
|
@@ -281,6 +310,7 @@ fn send_request(
|
|
|
281
310
|
|
|
282
311
|
match builder.send() {
|
|
283
312
|
Ok(resp) => {
|
|
313
|
+
let status = resp.status().as_u16();
|
|
284
314
|
let headers = resp
|
|
285
315
|
.headers()
|
|
286
316
|
.iter()
|
|
@@ -292,10 +322,14 @@ fn send_request(
|
|
|
292
322
|
})
|
|
293
323
|
.collect();
|
|
294
324
|
|
|
325
|
+
let bytes = resp.bytes().unwrap_or_default().to_vec();
|
|
326
|
+
let body = String::from_utf8(bytes.clone()).unwrap_or_default();
|
|
327
|
+
|
|
295
328
|
Ok(Response {
|
|
296
|
-
status
|
|
329
|
+
status,
|
|
297
330
|
headers,
|
|
298
|
-
body
|
|
331
|
+
body,
|
|
332
|
+
body_bytes: Some(bytes),
|
|
299
333
|
})
|
|
300
334
|
}
|
|
301
335
|
Err(error) => Err(RequestError::Other(format!(
|
|
@@ -343,6 +377,7 @@ impl Default for Response {
|
|
|
343
377
|
status: 0,
|
|
344
378
|
headers: Vec::new(),
|
|
345
379
|
body: String::new(),
|
|
380
|
+
body_bytes: None,
|
|
346
381
|
}
|
|
347
382
|
}
|
|
348
383
|
}
|
|
@@ -422,4 +457,30 @@ mod tests {
|
|
|
422
457
|
assert_eq!(response.status, 200);
|
|
423
458
|
mock.assert();
|
|
424
459
|
}
|
|
460
|
+
|
|
461
|
+
#[test]
|
|
462
|
+
fn returns_binary_response_body_bytes() {
|
|
463
|
+
use v4_1::standout::app::http::HostRequestBuilder;
|
|
464
|
+
|
|
465
|
+
let server = MockServer::start();
|
|
466
|
+
let body = b"%PDF-1.3".to_vec();
|
|
467
|
+
let mock = server.mock(|when, then| {
|
|
468
|
+
when.method(POST).path("/download");
|
|
469
|
+
then.status(200)
|
|
470
|
+
.header("Content-Type", "application/pdf")
|
|
471
|
+
.body(body.clone());
|
|
472
|
+
});
|
|
473
|
+
let url = format!("{}/download", server.base_url());
|
|
474
|
+
|
|
475
|
+
let mut app_state = AppState::default();
|
|
476
|
+
let builder = app_state.new();
|
|
477
|
+
let builder = app_state.method(builder, v4_1::standout::app::http::Method::Post);
|
|
478
|
+
let builder = app_state.url(builder, url);
|
|
479
|
+
|
|
480
|
+
let response = app_state.send(builder).expect("Request failed");
|
|
481
|
+
|
|
482
|
+
assert_eq!(response.status, 200);
|
|
483
|
+
assert_eq!(response.body_bytes, Some(body));
|
|
484
|
+
mock.assert();
|
|
485
|
+
}
|
|
425
486
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
use magnus::{prelude::*, scan_args::scan_args, Error, RHash, Ruby,
|
|
1
|
+
use magnus::{prelude::*, scan_args::scan_args, Error, RHash, Ruby, TryConvert, Value};
|
|
2
|
+
use crate::error_mapping::runtime_error;
|
|
2
3
|
use crate::types::{ActionContext, ReferenceObject};
|
|
3
4
|
use super::connection::RConnection;
|
|
4
5
|
|
|
@@ -33,12 +34,12 @@ impl RActionContext {
|
|
|
33
34
|
retry: Option<Value>,
|
|
34
35
|
) -> Result<Self, Error> {
|
|
35
36
|
if connection.is_nil() {
|
|
36
|
-
return Err(
|
|
37
|
+
return Err(runtime_error("Connection is required"));
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
let wrapped_connection: RConnection = match TryConvert::try_convert(connection) {
|
|
40
41
|
Ok(conn) => conn,
|
|
41
|
-
Err(_) => return Err(
|
|
42
|
+
Err(_) => return Err(runtime_error("Connection is required")),
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
let reference_object = match retry {
|
|
@@ -99,12 +100,12 @@ impl TryConvert for RActionContext {
|
|
|
99
100
|
};
|
|
100
101
|
|
|
101
102
|
if connection_val.is_nil() {
|
|
102
|
-
return Err(
|
|
103
|
+
return Err(runtime_error("Connection is required"));
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
let wrapped_connection: RConnection = match TryConvert::try_convert(connection_val) {
|
|
106
107
|
Ok(conn) => conn,
|
|
107
|
-
Err(_) => return Err(
|
|
108
|
+
Err(_) => return Err(runtime_error("Connection is required")),
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
let inner = ActionContext {
|
|
@@ -124,8 +125,8 @@ impl TryConvert for RActionContext {
|
|
|
124
125
|
fn fetch_hash_string(hash: Value, key: &str) -> Result<String, Error> {
|
|
125
126
|
let value: Value = hash.funcall("[]", (key,))?;
|
|
126
127
|
let value = if value.is_nil() {
|
|
127
|
-
let
|
|
128
|
-
hash.funcall("[]", (
|
|
128
|
+
let ruby = Ruby::get().unwrap();
|
|
129
|
+
hash.funcall("[]", (ruby.to_symbol(key),))?
|
|
129
130
|
} else {
|
|
130
131
|
value
|
|
131
132
|
};
|
|
@@ -5,6 +5,7 @@ use wasmtime::Store;
|
|
|
5
5
|
|
|
6
6
|
use crate::app_state::AppState;
|
|
7
7
|
use crate::component::{app, build_engine, build_linker, build_store, BridgeWrapper};
|
|
8
|
+
use crate::error_mapping::runtime_error;
|
|
8
9
|
use crate::types::{ActionContext, ActionResponse, AppError, ErrorCode, TriggerContext, TriggerResponse};
|
|
9
10
|
use super::{
|
|
10
11
|
action_context::RActionContext,
|
|
@@ -33,10 +34,45 @@ impl MutRApp {
|
|
|
33
34
|
if let Some(instance) = &*instance {
|
|
34
35
|
Ok(instance.wit_version().to_string())
|
|
35
36
|
} else {
|
|
36
|
-
Err(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
Err(runtime_error("App not initialized"))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn connections_supported(&self) -> Result<bool, Error> {
|
|
42
|
+
let binding = self.0.borrow();
|
|
43
|
+
let instance = binding.instance.borrow();
|
|
44
|
+
|
|
45
|
+
if let Some(instance) = &*instance {
|
|
46
|
+
Ok(instance.connections_supported())
|
|
47
|
+
} else {
|
|
48
|
+
Err(runtime_error("App not initialized"))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn connection_config(&self) -> Result<String, Error> {
|
|
53
|
+
let binding = self.0.borrow();
|
|
54
|
+
let mut instance = binding.instance.borrow_mut();
|
|
55
|
+
let mut store = binding.store.borrow_mut();
|
|
56
|
+
|
|
57
|
+
if let (Some(instance), Some(store)) = (&mut *instance, &mut *store) {
|
|
58
|
+
if !instance.connections_supported() {
|
|
59
|
+
return Err(AppError {
|
|
60
|
+
code: ErrorCode::Unsupported,
|
|
61
|
+
message: "connections interface not supported by this component version"
|
|
62
|
+
.to_string(),
|
|
63
|
+
}
|
|
64
|
+
.into());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
instance.call_connection_config(store).map_err(|err| {
|
|
68
|
+
runtime_error(format!("Failed to read connection-config: {err}"))
|
|
69
|
+
})
|
|
70
|
+
} else {
|
|
71
|
+
Err(AppError {
|
|
72
|
+
code: ErrorCode::InternalError,
|
|
73
|
+
message: "App instance couldn't be initialized".to_string(),
|
|
74
|
+
}
|
|
75
|
+
.into())
|
|
40
76
|
}
|
|
41
77
|
}
|
|
42
78
|
|
|
@@ -44,10 +80,7 @@ impl MutRApp {
|
|
|
44
80
|
let mut this = self.0.borrow_mut();
|
|
45
81
|
let engine = build_engine();
|
|
46
82
|
let linker = build_linker(&engine).map_err(|e| {
|
|
47
|
-
|
|
48
|
-
magnus::exception::runtime_error(),
|
|
49
|
-
format!("Failed to build linker: {}", e),
|
|
50
|
-
)
|
|
83
|
+
runtime_error(format!("Failed to build linker: {}", e))
|
|
51
84
|
})?;
|
|
52
85
|
let mut store = if env_vars.is_empty() {
|
|
53
86
|
build_store(&engine, None)
|
|
@@ -57,12 +90,9 @@ impl MutRApp {
|
|
|
57
90
|
|
|
58
91
|
let app = app(component_path.clone(), engine, &mut store, linker).map_err(|e| {
|
|
59
92
|
if e.to_string().contains("Incompatible WASM file version") {
|
|
60
|
-
|
|
93
|
+
runtime_error(e.to_string())
|
|
61
94
|
} else {
|
|
62
|
-
|
|
63
|
-
magnus::exception::runtime_error(),
|
|
64
|
-
format!("Failed to initialize app: {}", e),
|
|
65
|
-
)
|
|
95
|
+
runtime_error(format!("Failed to initialize app: {}", e))
|
|
66
96
|
}
|
|
67
97
|
})?;
|
|
68
98
|
|
|
@@ -85,10 +115,7 @@ impl MutRApp {
|
|
|
85
115
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
86
116
|
Err(wit_err.clone().into())
|
|
87
117
|
} else {
|
|
88
|
-
Err(
|
|
89
|
-
magnus::exception::runtime_error(),
|
|
90
|
-
format!("Unexpected error: {:?}", err),
|
|
91
|
-
))
|
|
118
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
92
119
|
}
|
|
93
120
|
}
|
|
94
121
|
}
|
|
@@ -114,10 +141,7 @@ impl MutRApp {
|
|
|
114
141
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
115
142
|
Err(wit_err.clone().into())
|
|
116
143
|
} else {
|
|
117
|
-
Err(
|
|
118
|
-
magnus::exception::runtime_error(),
|
|
119
|
-
format!("Unexpected error: {:?}", err),
|
|
120
|
-
))
|
|
144
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
121
145
|
}
|
|
122
146
|
}
|
|
123
147
|
}
|
|
@@ -143,10 +167,7 @@ impl MutRApp {
|
|
|
143
167
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
144
168
|
Err(wit_err.clone().into())
|
|
145
169
|
} else {
|
|
146
|
-
Err(
|
|
147
|
-
magnus::exception::runtime_error(),
|
|
148
|
-
format!("Unexpected error: {:?}", err),
|
|
149
|
-
))
|
|
170
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
150
171
|
}
|
|
151
172
|
}
|
|
152
173
|
}
|
|
@@ -208,10 +229,7 @@ impl MutRApp {
|
|
|
208
229
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
209
230
|
Err(wit_err.clone().into())
|
|
210
231
|
} else {
|
|
211
|
-
Err(
|
|
212
|
-
magnus::exception::runtime_error(),
|
|
213
|
-
format!("Unexpected error: {:?}", err),
|
|
214
|
-
))
|
|
232
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
215
233
|
}
|
|
216
234
|
}
|
|
217
235
|
}
|
|
@@ -237,10 +255,7 @@ impl MutRApp {
|
|
|
237
255
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
238
256
|
Err(wit_err.clone().into())
|
|
239
257
|
} else {
|
|
240
|
-
Err(
|
|
241
|
-
magnus::exception::runtime_error(),
|
|
242
|
-
format!("Unexpected error: {:?}", err),
|
|
243
|
-
))
|
|
258
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
244
259
|
}
|
|
245
260
|
}
|
|
246
261
|
}
|
|
@@ -266,10 +281,7 @@ impl MutRApp {
|
|
|
266
281
|
if let Some(wit_err) = err.downcast_ref::<AppError>() {
|
|
267
282
|
Err(wit_err.clone().into())
|
|
268
283
|
} else {
|
|
269
|
-
Err(
|
|
270
|
-
magnus::exception::runtime_error(),
|
|
271
|
-
format!("Unexpected error: {:?}", err),
|
|
272
|
-
))
|
|
284
|
+
Err(runtime_error(format!("Unexpected error: {:?}", err)))
|
|
273
285
|
}
|
|
274
286
|
}
|
|
275
287
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
use magnus::{prelude::*, Error, TryConvert, Value};
|
|
2
|
+
use crate::error_mapping::runtime_error;
|
|
2
3
|
use crate::types::TriggerContext;
|
|
3
4
|
use super::connection::RConnection;
|
|
4
5
|
|
|
@@ -11,12 +12,12 @@ pub struct RTriggerContext {
|
|
|
11
12
|
impl RTriggerContext {
|
|
12
13
|
pub fn new(trigger_id: String, connection: Value, store: String, serialized_input: String) -> Result<Self, Error> {
|
|
13
14
|
if connection.is_nil() {
|
|
14
|
-
return Err(
|
|
15
|
+
return Err(runtime_error("Connection is required"));
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
let wrapped_connection: RConnection = match TryConvert::try_convert(connection) {
|
|
18
19
|
Ok(conn) => conn,
|
|
19
|
-
Err(_) => return Err(
|
|
20
|
+
Err(_) => return Err(runtime_error("Connection is required")),
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
let inner = TriggerContext {
|
|
@@ -56,12 +57,12 @@ impl TryConvert for RTriggerContext {
|
|
|
56
57
|
let serialized_input: String = val.funcall("serialized_input", ())?;
|
|
57
58
|
|
|
58
59
|
if connection_val.is_nil() {
|
|
59
|
-
return Err(
|
|
60
|
+
return Err(runtime_error("Connection is required"));
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
let wrapped_connection: RConnection = match TryConvert::try_convert(connection_val) {
|
|
63
64
|
Ok(conn) => conn,
|
|
64
|
-
Err(_) => return Err(
|
|
65
|
+
Err(_) => return Err(runtime_error("Connection is required")),
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
let inner = TriggerContext {
|