spikard 0.6.2 → 0.7.2
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/README.md +90 -508
- data/ext/spikard_rb/Cargo.lock +3287 -0
- data/ext/spikard_rb/Cargo.toml +1 -1
- data/ext/spikard_rb/extconf.rb +3 -3
- data/lib/spikard/app.rb +72 -49
- data/lib/spikard/background.rb +38 -7
- data/lib/spikard/testing.rb +42 -4
- data/lib/spikard/version.rb +1 -1
- data/sig/spikard.rbs +4 -0
- data/vendor/crates/spikard-bindings-shared/Cargo.toml +2 -2
- data/vendor/crates/spikard-bindings-shared/tests/config_extractor_behavior.rs +191 -0
- data/vendor/crates/spikard-core/Cargo.toml +1 -1
- data/vendor/crates/spikard-core/src/http.rs +1 -0
- data/vendor/crates/spikard-core/src/lifecycle.rs +63 -0
- data/vendor/crates/spikard-core/tests/bindings_response_tests.rs +136 -0
- data/vendor/crates/spikard-core/tests/di_dependency_defaults.rs +37 -0
- data/vendor/crates/spikard-core/tests/error_mapper.rs +761 -0
- data/vendor/crates/spikard-core/tests/parameters_edge_cases.rs +106 -0
- data/vendor/crates/spikard-core/tests/parameters_full.rs +701 -0
- data/vendor/crates/spikard-core/tests/parameters_schema_and_formats.rs +301 -0
- data/vendor/crates/spikard-core/tests/request_data_roundtrip.rs +67 -0
- data/vendor/crates/spikard-core/tests/validation_coverage.rs +250 -0
- data/vendor/crates/spikard-core/tests/validation_error_paths.rs +45 -0
- data/vendor/crates/spikard-http/Cargo.toml +1 -1
- data/vendor/crates/spikard-http/src/jsonrpc/http_handler.rs +502 -0
- data/vendor/crates/spikard-http/src/jsonrpc/method_registry.rs +648 -0
- data/vendor/crates/spikard-http/src/jsonrpc/mod.rs +58 -0
- data/vendor/crates/spikard-http/src/jsonrpc/protocol.rs +1207 -0
- data/vendor/crates/spikard-http/src/jsonrpc/router.rs +2262 -0
- data/vendor/crates/spikard-http/src/testing/test_client.rs +155 -2
- data/vendor/crates/spikard-http/src/testing.rs +171 -0
- data/vendor/crates/spikard-http/src/websocket.rs +79 -6
- data/vendor/crates/spikard-http/tests/auth_integration.rs +647 -0
- data/vendor/crates/spikard-http/tests/common/test_builders.rs +633 -0
- data/vendor/crates/spikard-http/tests/di_handler_error_responses.rs +162 -0
- data/vendor/crates/spikard-http/tests/middleware_stack_integration.rs +389 -0
- data/vendor/crates/spikard-http/tests/request_extraction_full.rs +513 -0
- data/vendor/crates/spikard-http/tests/server_auth_middleware_behavior.rs +244 -0
- data/vendor/crates/spikard-http/tests/server_configured_router_behavior.rs +200 -0
- data/vendor/crates/spikard-http/tests/server_cors_preflight.rs +82 -0
- data/vendor/crates/spikard-http/tests/server_handler_wrappers.rs +464 -0
- data/vendor/crates/spikard-http/tests/server_method_router_additional_behavior.rs +286 -0
- data/vendor/crates/spikard-http/tests/server_method_router_coverage.rs +118 -0
- data/vendor/crates/spikard-http/tests/server_middleware_behavior.rs +99 -0
- data/vendor/crates/spikard-http/tests/server_middleware_branches.rs +206 -0
- data/vendor/crates/spikard-http/tests/server_openapi_jsonrpc_static.rs +281 -0
- data/vendor/crates/spikard-http/tests/server_router_behavior.rs +121 -0
- data/vendor/crates/spikard-http/tests/sse_full_behavior.rs +584 -0
- data/vendor/crates/spikard-http/tests/sse_handler_behavior.rs +130 -0
- data/vendor/crates/spikard-http/tests/test_client_requests.rs +167 -0
- data/vendor/crates/spikard-http/tests/testing_helpers.rs +87 -0
- data/vendor/crates/spikard-http/tests/testing_module_coverage.rs +156 -0
- data/vendor/crates/spikard-http/tests/urlencoded_content_type.rs +82 -0
- data/vendor/crates/spikard-http/tests/websocket_full_behavior.rs +440 -0
- data/vendor/crates/spikard-http/tests/websocket_integration.rs +152 -0
- data/vendor/crates/spikard-rb/Cargo.toml +1 -1
- data/vendor/crates/spikard-rb/src/gvl.rs +80 -0
- data/vendor/crates/spikard-rb/src/handler.rs +12 -9
- data/vendor/crates/spikard-rb/src/lib.rs +137 -124
- data/vendor/crates/spikard-rb/src/request.rs +342 -0
- data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +1 -8
- data/vendor/crates/spikard-rb/src/server.rs +1 -8
- data/vendor/crates/spikard-rb/src/testing/client.rs +168 -9
- data/vendor/crates/spikard-rb/src/websocket.rs +119 -30
- data/vendor/crates/spikard-rb-macros/Cargo.toml +14 -0
- data/vendor/crates/spikard-rb-macros/src/lib.rs +52 -0
- metadata +44 -1
|
@@ -25,6 +25,7 @@ use std::sync::Arc;
|
|
|
25
25
|
use crate::conversion::{
|
|
26
26
|
json_to_ruby, json_to_ruby_with_uploads, map_to_ruby_hash, multimap_to_ruby_hash, ruby_value_to_json,
|
|
27
27
|
};
|
|
28
|
+
use crate::gvl::with_gvl;
|
|
28
29
|
|
|
29
30
|
static KEY_METHOD: LazyId = LazyId::new("method");
|
|
30
31
|
static KEY_PATH: LazyId = LazyId::new("path");
|
|
@@ -241,15 +242,17 @@ impl RubyHandler {
|
|
|
241
242
|
|
|
242
243
|
/// Handle a request synchronously.
|
|
243
244
|
pub fn handle(&self, request_data: RequestData) -> HandlerResult {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
with_gvl(|| {
|
|
246
|
+
let result = std::panic::catch_unwind(AssertUnwindSafe(|| self.handle_inner(request_data)));
|
|
247
|
+
match result {
|
|
248
|
+
Ok(res) => res,
|
|
249
|
+
Err(_) => Err(ErrorResponseBuilder::structured_error(
|
|
250
|
+
StatusCode::INTERNAL_SERVER_ERROR,
|
|
251
|
+
"panic",
|
|
252
|
+
"Unexpected panic while executing Ruby handler",
|
|
253
|
+
)),
|
|
254
|
+
}
|
|
255
|
+
})
|
|
253
256
|
}
|
|
254
257
|
|
|
255
258
|
fn handle_inner(&self, request_data: RequestData) -> HandlerResult {
|
|
@@ -40,7 +40,6 @@ use axum::body::Body;
|
|
|
40
40
|
use axum::http::{HeaderName, HeaderValue, Method, StatusCode};
|
|
41
41
|
use axum_test::{TestServer, TestServerConfig, Transport};
|
|
42
42
|
use bytes::Bytes;
|
|
43
|
-
use cookie::Cookie;
|
|
44
43
|
use magnus::prelude::*;
|
|
45
44
|
use magnus::value::{InnerValue, Opaque};
|
|
46
45
|
use magnus::{
|
|
@@ -48,9 +47,7 @@ use magnus::{
|
|
|
48
47
|
};
|
|
49
48
|
use serde_json::Value as JsonValue;
|
|
50
49
|
use spikard_http::ProblemDetails;
|
|
51
|
-
use spikard_http::testing::
|
|
52
|
-
MultipartFilePart, ResponseSnapshot, SnapshotError, build_multipart_body, encode_urlencoded_body, snapshot_response,
|
|
53
|
-
};
|
|
50
|
+
use spikard_http::testing::ResponseSnapshot;
|
|
54
51
|
use spikard_http::{Handler, HandlerResponse, HandlerResult, RequestData};
|
|
55
52
|
use spikard_http::{Route, RouteMetadata, SchemaValidator};
|
|
56
53
|
use std::cell::RefCell;
|
|
@@ -82,22 +79,8 @@ struct ClientInner {
|
|
|
82
79
|
_handlers: Vec<RubyHandler>,
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
headers: HashMap<String, String>,
|
|
88
|
-
cookies: HashMap<String, String>,
|
|
89
|
-
body: Option<RequestBody>,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
enum RequestBody {
|
|
93
|
-
Json(JsonValue),
|
|
94
|
-
Form(JsonValue),
|
|
95
|
-
Raw(String),
|
|
96
|
-
Multipart {
|
|
97
|
-
form_data: Vec<(String, String)>,
|
|
98
|
-
files: Vec<MultipartFilePart>,
|
|
99
|
-
},
|
|
100
|
-
}
|
|
82
|
+
// Re-export from testing::client to avoid duplication
|
|
83
|
+
use testing::client::{RequestBody, RequestConfig};
|
|
101
84
|
|
|
102
85
|
#[derive(Clone)]
|
|
103
86
|
struct RubyHandler {
|
|
@@ -451,15 +434,6 @@ fn ruby_value_to_bytes(value: Value) -> Result<Bytes, io::Error> {
|
|
|
451
434
|
Err(io::Error::other("Streaming chunks must be Strings or Arrays of bytes"))
|
|
452
435
|
}
|
|
453
436
|
|
|
454
|
-
struct TestResponseData {
|
|
455
|
-
status: u16,
|
|
456
|
-
headers: HashMap<String, String>,
|
|
457
|
-
body_text: Option<String>,
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
#[derive(Debug)]
|
|
461
|
-
struct NativeRequestError(String);
|
|
462
|
-
|
|
463
437
|
impl NativeTestClient {
|
|
464
438
|
#[allow(clippy::too_many_arguments)]
|
|
465
439
|
fn initialize(
|
|
@@ -537,14 +511,7 @@ impl NativeTestClient {
|
|
|
537
511
|
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "WebSocket handlers must be a Hash"))?;
|
|
538
512
|
|
|
539
513
|
ws_hash.foreach(|path: String, factory: Value| -> Result<ForEach, Error> {
|
|
540
|
-
let
|
|
541
|
-
Error::new(
|
|
542
|
-
ruby.exception_runtime_error(),
|
|
543
|
-
format!("Failed to create WebSocket handler: {}", e),
|
|
544
|
-
)
|
|
545
|
-
})?;
|
|
546
|
-
|
|
547
|
-
let ws_state = crate::websocket::create_websocket_state(ruby, handler_instance)?;
|
|
514
|
+
let ws_state = crate::websocket::create_websocket_state(ruby, factory)?;
|
|
548
515
|
|
|
549
516
|
ws_endpoints.push((path, ws_state));
|
|
550
517
|
|
|
@@ -636,19 +603,25 @@ impl NativeTestClient {
|
|
|
636
603
|
let request_config = parse_request_config(ruby, options)?;
|
|
637
604
|
|
|
638
605
|
let runtime = crate::server::global_runtime(ruby)?;
|
|
639
|
-
let
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
606
|
+
let server = inner.http_server.clone();
|
|
607
|
+
let path_value = path.clone();
|
|
608
|
+
let response = crate::call_without_gvl!(
|
|
609
|
+
testing::client::block_on_request,
|
|
610
|
+
args: (
|
|
611
|
+
runtime, &tokio::runtime::Runtime,
|
|
612
|
+
server, Arc<TestServer>,
|
|
613
|
+
http_method, Method,
|
|
614
|
+
path_value, String,
|
|
615
|
+
request_config, testing::client::RequestConfig
|
|
616
|
+
),
|
|
617
|
+
return_type: Result<testing::client::TestResponseData, testing::client::NativeRequestError>
|
|
618
|
+
)
|
|
619
|
+
.map_err(|err| {
|
|
620
|
+
Error::new(
|
|
621
|
+
ruby.exception_runtime_error(),
|
|
622
|
+
format!("Request failed for {method_upper} {path}: {}", err.0),
|
|
623
|
+
)
|
|
624
|
+
})?;
|
|
652
625
|
|
|
653
626
|
response_to_ruby(ruby, response)
|
|
654
627
|
}
|
|
@@ -700,20 +673,26 @@ impl NativeTestClient {
|
|
|
700
673
|
.ok_or_else(|| Error::new(ruby.exception_runtime_error(), "TestClient not initialised"))?;
|
|
701
674
|
|
|
702
675
|
let runtime = crate::server::global_runtime(ruby)?;
|
|
676
|
+
let server = inner.http_server.clone();
|
|
677
|
+
let http_method = Method::GET;
|
|
703
678
|
let request_config = RequestConfig {
|
|
704
679
|
query: None,
|
|
705
680
|
headers: HashMap::new(),
|
|
706
681
|
cookies: HashMap::new(),
|
|
707
682
|
body: None,
|
|
708
683
|
};
|
|
709
|
-
let response =
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
684
|
+
let response = crate::call_without_gvl!(
|
|
685
|
+
testing::client::block_on_request,
|
|
686
|
+
args: (
|
|
687
|
+
runtime, &tokio::runtime::Runtime,
|
|
688
|
+
server, Arc<TestServer>,
|
|
689
|
+
http_method, Method,
|
|
690
|
+
path, String,
|
|
691
|
+
request_config, RequestConfig
|
|
692
|
+
),
|
|
693
|
+
return_type: Result<testing::client::TestResponseData, testing::client::NativeRequestError>
|
|
694
|
+
)
|
|
695
|
+
.map_err(|err| Error::new(ruby.exception_runtime_error(), format!("SSE request failed: {}", err.0)))?;
|
|
717
696
|
|
|
718
697
|
let body = response.body_text.unwrap_or_default().into_bytes();
|
|
719
698
|
let snapshot = ResponseSnapshot {
|
|
@@ -724,6 +703,88 @@ impl NativeTestClient {
|
|
|
724
703
|
|
|
725
704
|
testing::sse::sse_stream_from_response(ruby, &snapshot)
|
|
726
705
|
}
|
|
706
|
+
|
|
707
|
+
fn graphql(
|
|
708
|
+
ruby: &Ruby,
|
|
709
|
+
this: &Self,
|
|
710
|
+
query: String,
|
|
711
|
+
variables: Value,
|
|
712
|
+
operation_name: Value,
|
|
713
|
+
) -> Result<Value, Error> {
|
|
714
|
+
let (_status, response) = Self::execute_graphql_impl(ruby, this, query, variables, operation_name)?;
|
|
715
|
+
Ok(response)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
fn graphql_with_status(
|
|
719
|
+
ruby: &Ruby,
|
|
720
|
+
this: &Self,
|
|
721
|
+
query: String,
|
|
722
|
+
variables: Value,
|
|
723
|
+
operation_name: Value,
|
|
724
|
+
) -> Result<Value, Error> {
|
|
725
|
+
let (status, response) = Self::execute_graphql_impl(ruby, this, query, variables, operation_name)?;
|
|
726
|
+
|
|
727
|
+
let array = ruby.ary_new_capa(2);
|
|
728
|
+
array.push(ruby.integer_from_i64(status as i64))?;
|
|
729
|
+
array.push(response)?;
|
|
730
|
+
Ok(array.as_value())
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
fn execute_graphql_impl(
|
|
734
|
+
ruby: &Ruby,
|
|
735
|
+
this: &Self,
|
|
736
|
+
query: String,
|
|
737
|
+
variables: Value,
|
|
738
|
+
operation_name: Value,
|
|
739
|
+
) -> Result<(u16, Value), Error> {
|
|
740
|
+
let inner_borrow = this.inner.borrow();
|
|
741
|
+
let inner = inner_borrow
|
|
742
|
+
.as_ref()
|
|
743
|
+
.ok_or_else(|| Error::new(ruby.exception_runtime_error(), "TestClient not initialised"))?;
|
|
744
|
+
|
|
745
|
+
let json_module = ruby
|
|
746
|
+
.class_object()
|
|
747
|
+
.const_get("JSON")
|
|
748
|
+
.map_err(|_| Error::new(ruby.exception_runtime_error(), "JSON module not available"))?;
|
|
749
|
+
|
|
750
|
+
let variables_json = if variables.is_nil() {
|
|
751
|
+
None
|
|
752
|
+
} else {
|
|
753
|
+
Some(ruby_value_to_json(ruby, json_module, variables)?)
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
let operation_name_str = if operation_name.is_nil() {
|
|
757
|
+
None
|
|
758
|
+
} else {
|
|
759
|
+
Some(String::try_convert(operation_name)?)
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
let runtime = crate::server::global_runtime(ruby)?;
|
|
763
|
+
let server = inner.http_server.clone();
|
|
764
|
+
let query_value = query.clone();
|
|
765
|
+
|
|
766
|
+
let snapshot = crate::call_without_gvl!(
|
|
767
|
+
testing::client::block_on_graphql,
|
|
768
|
+
args: (
|
|
769
|
+
runtime, &tokio::runtime::Runtime,
|
|
770
|
+
server, Arc<TestServer>,
|
|
771
|
+
query_value, String,
|
|
772
|
+
variables_json, Option<JsonValue>,
|
|
773
|
+
operation_name_str, Option<String>
|
|
774
|
+
),
|
|
775
|
+
return_type: Result<ResponseSnapshot, testing::client::NativeRequestError>
|
|
776
|
+
)
|
|
777
|
+
.map_err(|err| {
|
|
778
|
+
Error::new(
|
|
779
|
+
ruby.exception_runtime_error(),
|
|
780
|
+
format!("GraphQL request failed: {}", err.0),
|
|
781
|
+
)
|
|
782
|
+
})?;
|
|
783
|
+
|
|
784
|
+
let status = snapshot.status;
|
|
785
|
+
let response = response_snapshot_to_ruby(ruby, snapshot)?;
|
|
786
|
+
Ok((status, response))
|
|
787
|
+
}
|
|
727
788
|
}
|
|
728
789
|
|
|
729
790
|
fn websocket_timeout() -> Duration {
|
|
@@ -1026,77 +1087,27 @@ impl Handler for RubyHandler {
|
|
|
1026
1087
|
}
|
|
1027
1088
|
}
|
|
1028
1089
|
|
|
1029
|
-
|
|
1030
|
-
server: Arc<TestServer>,
|
|
1031
|
-
method: Method,
|
|
1032
|
-
path: String,
|
|
1033
|
-
config: RequestConfig,
|
|
1034
|
-
) -> Result<TestResponseData, NativeRequestError> {
|
|
1035
|
-
let mut request = match method {
|
|
1036
|
-
Method::GET => server.get(&path),
|
|
1037
|
-
Method::POST => server.post(&path),
|
|
1038
|
-
Method::PUT => server.put(&path),
|
|
1039
|
-
Method::PATCH => server.patch(&path),
|
|
1040
|
-
Method::DELETE => server.delete(&path),
|
|
1041
|
-
Method::HEAD => server.method(Method::HEAD, &path),
|
|
1042
|
-
Method::OPTIONS => server.method(Method::OPTIONS, &path),
|
|
1043
|
-
Method::TRACE => server.method(Method::TRACE, &path),
|
|
1044
|
-
other => return Err(NativeRequestError(format!("Unsupported HTTP method {other}"))),
|
|
1045
|
-
};
|
|
1046
|
-
|
|
1047
|
-
if let Some(query) = config.query {
|
|
1048
|
-
request = request.add_query_params(&query);
|
|
1049
|
-
}
|
|
1090
|
+
// These functions are now in testing::client module - use call_without_gvl! macro to call them
|
|
1050
1091
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
}
|
|
1092
|
+
fn response_snapshot_to_ruby(ruby: &Ruby, snapshot: ResponseSnapshot) -> Result<Value, Error> {
|
|
1093
|
+
let hash = ruby.hash_new();
|
|
1054
1094
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1095
|
+
hash.aset(
|
|
1096
|
+
ruby.intern("status_code"),
|
|
1097
|
+
ruby.integer_from_i64(snapshot.status as i64),
|
|
1098
|
+
)?;
|
|
1058
1099
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
request = request.json(&json_value);
|
|
1063
|
-
}
|
|
1064
|
-
RequestBody::Form(form_value) => {
|
|
1065
|
-
let encoded = encode_urlencoded_body(&form_value)
|
|
1066
|
-
.map_err(|err| NativeRequestError(format!("Failed to encode form body: {err}")))?;
|
|
1067
|
-
request = request
|
|
1068
|
-
.content_type("application/x-www-form-urlencoded")
|
|
1069
|
-
.bytes(Bytes::from(encoded));
|
|
1070
|
-
}
|
|
1071
|
-
RequestBody::Raw(raw) => {
|
|
1072
|
-
request = request.bytes(Bytes::from(raw));
|
|
1073
|
-
}
|
|
1074
|
-
RequestBody::Multipart { form_data, files } => {
|
|
1075
|
-
let (multipart_body, boundary) = build_multipart_body(&form_data, &files);
|
|
1076
|
-
request = request
|
|
1077
|
-
.content_type(&format!("multipart/form-data; boundary={}", boundary))
|
|
1078
|
-
.bytes(Bytes::from(multipart_body));
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1100
|
+
let headers_hash = ruby.hash_new();
|
|
1101
|
+
for (key, value) in snapshot.headers {
|
|
1102
|
+
headers_hash.aset(ruby.str_new(&key), ruby.str_new(&value))?;
|
|
1081
1103
|
}
|
|
1104
|
+
hash.aset(ruby.intern("headers"), headers_hash)?;
|
|
1082
1105
|
|
|
1083
|
-
let
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
None
|
|
1087
|
-
} else {
|
|
1088
|
-
Some(String::from_utf8_lossy(&snapshot.body).into_owned())
|
|
1089
|
-
};
|
|
1090
|
-
|
|
1091
|
-
Ok(TestResponseData {
|
|
1092
|
-
status: snapshot.status,
|
|
1093
|
-
headers: snapshot.headers,
|
|
1094
|
-
body_text,
|
|
1095
|
-
})
|
|
1096
|
-
}
|
|
1106
|
+
let body_value = ruby.str_new(&String::from_utf8_lossy(&snapshot.body));
|
|
1107
|
+
hash.aset(ruby.intern("body"), body_value)?;
|
|
1108
|
+
hash.aset(ruby.intern("body_text"), body_value)?;
|
|
1097
1109
|
|
|
1098
|
-
|
|
1099
|
-
NativeRequestError(err.to_string())
|
|
1110
|
+
Ok(hash.as_value())
|
|
1100
1111
|
}
|
|
1101
1112
|
|
|
1102
1113
|
fn parse_request_config(ruby: &Ruby, options: Value) -> Result<RequestConfig, Error> {
|
|
@@ -1377,7 +1388,7 @@ fn is_streaming_response(ruby: &Ruby, value: Value) -> Result<bool, Error> {
|
|
|
1377
1388
|
Ok(value.respond_to(stream_sym, false)? && value.respond_to(status_sym, false)?)
|
|
1378
1389
|
}
|
|
1379
1390
|
|
|
1380
|
-
fn response_to_ruby(ruby: &Ruby, response: TestResponseData) -> Result<Value, Error> {
|
|
1391
|
+
fn response_to_ruby(ruby: &Ruby, response: testing::client::TestResponseData) -> Result<Value, Error> {
|
|
1381
1392
|
let hash = ruby.hash_new();
|
|
1382
1393
|
|
|
1383
1394
|
hash.aset(
|
|
@@ -1573,6 +1584,8 @@ pub fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
1573
1584
|
class.define_method("request", method!(NativeTestClient::request, 3))?;
|
|
1574
1585
|
class.define_method("websocket", method!(NativeTestClient::websocket, 1))?;
|
|
1575
1586
|
class.define_method("sse", method!(NativeTestClient::sse, 1))?;
|
|
1587
|
+
class.define_method("graphql", method!(NativeTestClient::graphql, 3))?;
|
|
1588
|
+
class.define_method("graphql_with_status", method!(NativeTestClient::graphql_with_status, 3))?;
|
|
1576
1589
|
class.define_method("close", method!(NativeTestClient::close, 0))?;
|
|
1577
1590
|
|
|
1578
1591
|
let built_response_class = native.define_class("BuiltResponse", ruby.class_object())?;
|