Skip to content

Commit

Permalink
add support of response status_code in rhai (#5045)
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Co-authored-by: Jesse Rosenberger <git@jro.cc>
Co-authored-by: Gary Pennington <gary@apollographql.com>
  • Loading branch information
3 people committed May 14, 2024
1 parent f24ae3a commit 3aa271d
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 3 deletions.
21 changes: 21 additions & 0 deletions .changesets/feat_bnjjj_fix_5042.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
### Add support for response status_code to rhai ([Issue #5042](https://github.com/apollographql/router/issues/5042))

Added support for `response.status_code` on `Response` interface in rhai.

Convert response status code to a string.

```rhai
if response.status_code.to_string() == "200" {
print(`ok`);
}
```

Also useful if you want to convert response status code to a number

```rhai
if parse_int(response.status_code.to_string()) == 200 {
print(`ok`);
}
```

By [@bnjjj](https://github.com/bnjjj) in https://github.com/apollographql/router/pull/5045
50 changes: 50 additions & 0 deletions apollo-router/src/plugins/rhai/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use http::uri::Parts;
use http::uri::PathAndQuery;
use http::HeaderMap;
use http::Method;
use http::StatusCode;
use http::Uri;
use rhai::module_resolvers::FileModuleResolver;
use rhai::plugin::*;
Expand Down Expand Up @@ -218,6 +219,40 @@ mod router_method {
}
}

#[export_module]
mod status_code {
use rhai::INT;

pub(crate) type StatusCode = http::StatusCode;

#[rhai_fn(return_raw)]
pub(crate) fn status_code_from_int(number: INT) -> Result<StatusCode, Box<EvalAltResult>> {
let code = StatusCode::from_u16(number as u16).map_err(|e| e.to_string())?;
Ok(code)
}

#[rhai_fn(name = "to_string", pure)]
pub(crate) fn status_code_to_string(status_code: &mut StatusCode) -> String {
status_code.as_str().to_string()
}

#[rhai_fn(name = "==", pure)]
pub(crate) fn status_code_equal_comparator(
status_code: &mut StatusCode,
other: StatusCode,
) -> bool {
status_code == &other
}

#[rhai_fn(name = "!=", pure)]
pub(crate) fn status_code_not_equal_comparator(
status_code: &mut StatusCode,
other: StatusCode,
) -> bool {
status_code != &other
}
}

#[export_module]
mod router_header_map {
pub(crate) type HeaderMap = http::HeaderMap;
Expand Down Expand Up @@ -1425,6 +1460,13 @@ macro_rules! register_rhai_router_interface {
}
);

$engine.register_get(
"status_code",
|obj: &mut SharedMut<$base::Response>| -> Result<StatusCode, Box<EvalAltResult>> {
Ok(obj.with_mut(|response| response.response.status()))
}
);

$engine.register_set(
"uri",
|obj: &mut SharedMut<$base::Request>, uri: Uri| {
Expand Down Expand Up @@ -1460,6 +1502,13 @@ macro_rules! register_rhai_interface {
}
);

$engine.register_get(
"status_code",
|obj: &mut SharedMut<$base::Response>| -> Result<StatusCode, Box<EvalAltResult>> {
Ok(obj.with_mut(|response| response.response.status()))
}
);

$engine.register_set(
"context",
|obj: &mut SharedMut<$base::Request>, context: Context| {
Expand Down Expand Up @@ -1632,6 +1681,7 @@ impl Rhai {
let mut module = exported_module!(router_plugin);
combine_with_exported_module!(&mut module, "header", router_header_map);
combine_with_exported_module!(&mut module, "method", router_method);
combine_with_exported_module!(&mut module, "status_code", status_code);
combine_with_exported_module!(&mut module, "context", router_context);

let base64_module = exported_module!(router_base64);
Expand Down
10 changes: 7 additions & 3 deletions apollo-router/src/plugins/rhai/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,11 @@ async fn it_can_process_subgraph_response() {

// We must wrap our canned response in Arc<Mutex<Option<>>> to keep the rhai runtime
// happy
let response = Arc::new(Mutex::new(Some(subgraph::Response::fake_builder().build())));
let response = Arc::new(Mutex::new(Some(
subgraph::Response::fake_builder()
.status_code(StatusCode::OK)
.build(),
)));

// Call our rhai test function. If it return an error, the test failed.
let result: Result<(), Box<rhai::EvalAltResult>> = block.engine.call_fn(
Expand Down Expand Up @@ -725,7 +729,7 @@ async fn it_can_process_string_subgraph_forbidden() {
if let Err(error) = base_process_function("process_subgraph_response_string").await {
let processed_error = process_error(error);
assert_eq!(processed_error.status, StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(processed_error.message, Some("rhai execution error: 'Runtime error: I have raised an error (line 161, position 5)\nin call to function 'process_subgraph_response_string''".to_string()));
assert_eq!(processed_error.message, Some("rhai execution error: 'Runtime error: I have raised an error (line 170, position 5)\nin call to function 'process_subgraph_response_string''".to_string()));
} else {
// Test failed
panic!("error processed incorrectly");
Expand All @@ -751,7 +755,7 @@ async fn it_cannot_process_om_subgraph_missing_message_and_body() {
{
let processed_error = process_error(error);
assert_eq!(processed_error.status, StatusCode::BAD_REQUEST);
assert_eq!(processed_error.message, Some("rhai execution error: 'Runtime error: #{\"status\": 400} (line 172, position 5)\nin call to function 'process_subgraph_response_om_missing_message''".to_string()));
assert_eq!(processed_error.message, Some("rhai execution error: 'Runtime error: #{\"status\": 400} (line 181, position 5)\nin call to function 'process_subgraph_response_om_missing_message''".to_string()));
} else {
// Test failed
panic!("error processed incorrectly");
Expand Down
9 changes: 9 additions & 0 deletions apollo-router/tests/fixtures/request_response_test.rhai
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ fn process_execution_response(response) {

fn process_subgraph_response(response) {
process_common_response(response);
if type_of(response.status_code) != "http::status::StatusCode" {
throw(`status_code: expected: "http::status::StatusCode", actual: ${type_of(response.status_code)}`);
}
if parse_int(response.status_code.to_string()) != 200 {
throw(`status_code: expected: 200, actual: ${response.status_code}`);
}
if response.status_code != status_code_from_int(200) {
throw(`status_code: expected: 200, actual: ${response.status_code}`);
}
}

fn process_subgraph_response_om_forbidden(response) {
Expand Down
27 changes: 27 additions & 0 deletions docs/source/customizations/rhai-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ The `response` object includes the following fields:
```
response.context
response.id
response.status_code
response.headers
response.body.label
response.body.data
Expand Down Expand Up @@ -618,3 +619,29 @@ let error_to_add = #{
response.body.errors += error_to_add;
print(`${response.body.errors}`); // logs the response errors
```

### `response.status_code.to_string()`

Convert response status code to a string.

```rhai
if response.status_code.to_string() == "200" {
print(`ok`);
}
```

Also useful if you want to convert response status code to a number

```rhai
if parse_int(response.status_code.to_string()) == 200 {
print(`ok`);
}
```

You can also create your own status code from an integer:

```rhai
if response.status_code == status_code_from_int(200) {
print(`ok`);
}
```

0 comments on commit 3aa271d

Please sign in to comment.