Skip to content

Commit

Permalink
refactor: JsResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
amitksingh1490 committed May 4, 2024
1 parent 5521f6a commit 8642104
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 64 deletions.
2 changes: 1 addition & 1 deletion src/cli/javascript/js_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl JsRequest {

pub fn body(&self) -> Option<String> {
if let Some(body) = self.0.body() {
let bytes = body.as_bytes().unwrap();
let bytes = body.as_bytes()?;
Some(String::from_utf8_lossy(bytes).to_string())
} else {
None
Expand Down
122 changes: 65 additions & 57 deletions src/cli/javascript/js_response.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
use std::collections::BTreeMap;

use hyper::body::Bytes;
use nom::AsBytes;
use rquickjs::{FromJs, IntoJs};
use serde::{Deserialize, Serialize};

use super::create_header_map;
use crate::http::Response;
use crate::is_default;

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct JsResponse {
pub status: u16,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub headers: BTreeMap<String, String>,
#[serde(default, skip_serializing_if = "is_default")]
pub body: Option<String>,

#[derive(Debug)]

Check warning on line 9 in src/cli/javascript/js_response.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/javascript/js_response.rs#L9

Added line #L9 was not covered by tests
pub struct JsResponse(Response<String>);

impl JsResponse {
pub fn status(&self) -> u16 {
self.0.status.as_u16()
}

pub fn headers(&self) -> BTreeMap<String, String> {
let mut headers = BTreeMap::new();
for (key, value) in self.0.headers.iter() {
headers.insert(key.to_string(), value.to_str().unwrap().to_string());
}
headers
}

pub fn body(&self) -> Option<String> {
let b = self.0.body.as_bytes();
Some(String::from_utf8_lossy(b).to_string())
}
}

impl<'js> IntoJs<'js> for JsResponse {
fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result<rquickjs::Value<'js>> {
let object = rquickjs::Object::new(ctx.clone())?;
object.set("status", self.status)?;
object.set("headers", self.headers)?;
object.set("body", self.body)?;
object.set("status", self.status())?;
object.set("headers", self.headers())?;
object.set("body", self.body())?;
Ok(object.into_value())
}
}
Expand All @@ -39,36 +48,46 @@ impl<'js> FromJs<'js> for JsResponse {
let status = object.get::<&str, u16>("status")?;
let headers = object.get::<&str, BTreeMap<String, String>>("headers")?;
let body = object.get::<&str, Option<String>>("body")?;

Ok(JsResponse { status, headers, body })
let response = Response {
status: reqwest::StatusCode::from_u16(status).map_err(|_| rquickjs::Error::FromJs {
from: "u16",
to: "reqwest::StatusCode",
message: Some("invalid status code".to_string()),

Check warning on line 55 in src/cli/javascript/js_response.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/javascript/js_response.rs#L53-L55

Added lines #L53 - L55 were not covered by tests
})?,
headers: create_header_map(headers).map_err(|e| rquickjs::Error::FromJs {
from: "BTreeMap<String, String>",
to: "reqwest::header::HeaderMap",
message: Some(e.to_string()),

Check warning on line 60 in src/cli/javascript/js_response.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/javascript/js_response.rs#L58-L60

Added lines #L58 - L60 were not covered by tests
})?,
body: body.unwrap_or_default(),
};
Ok(JsResponse(response))
}
}

impl TryFrom<JsResponse> for Response<Bytes> {
type Error = anyhow::Error;

fn try_from(res: JsResponse) -> Result<Self, Self::Error> {
let status = reqwest::StatusCode::from_u16(res.status)?;
let headers = create_header_map(res.headers)?;
let body = res.body.unwrap_or_default();
Ok(Response { status, headers, body: Bytes::from(body) })
let res = res.0;
Ok(Response {
status: res.status,
headers: res.headers,
body: Bytes::from(res.body.as_bytes().to_vec()),
})
}
}

impl TryFrom<Response<Bytes>> for JsResponse {
type Error = anyhow::Error;

fn try_from(res: Response<Bytes>) -> Result<Self, Self::Error> {
let status = res.status.as_u16();
let mut headers = BTreeMap::new();
for (key, value) in res.headers.iter() {
let key = key.to_string();
let value = value.to_str()?.to_string();
headers.insert(key, value);
}

let body = Some(std::str::from_utf8(res.body.as_bytes())?.to_owned());
Ok(JsResponse { status, headers, body })
let body = String::from_utf8_lossy(res.body.as_ref()).to_string();
Ok(JsResponse(Response {
status: res.status,
headers: res.headers,
body,
}))
}
}

Expand All @@ -77,6 +96,7 @@ mod test {
use std::collections::BTreeMap;

use anyhow::Result;
use headers::{HeaderName, HeaderValue};
use hyper::body::Bytes;
use pretty_assertions::assert_eq;
use reqwest::header::HeaderMap;
Expand All @@ -101,12 +121,12 @@ mod test {
let js_response = create_test_response();
assert!(js_response.is_ok());
let js_response = js_response.unwrap();
assert_eq!(js_response.status, 200);
assert_eq!(js_response.status(), 200);
assert_eq!(
js_response.headers.get("content-type").unwrap(),
js_response.headers().get("content-type").unwrap(),
"application/json"
);
assert_eq!(js_response.body, Some("Hello, World!".into()));
assert_eq!(js_response.body(), Some("Hello, World!".into()));
}

#[test]
Expand All @@ -122,30 +142,18 @@ mod test {
);
assert_eq!(response.body, Bytes::from("Hello, World!"));
}
#[test]
fn test_js_response_with_defaults() {
let js_response = JsResponse {
status: 200,
headers: BTreeMap::new(), // Empty headers
body: None, // No body
};

let response: Result<crate::http::Response<Bytes>, _> = js_response.try_into();
assert!(response.is_ok());
let response = response.unwrap();
assert!(response.headers.is_empty());
assert_eq!(response.body, Bytes::new()); // Assuming `Bytes::new()` is
// the expected result for no
// body
}

#[test]
fn test_unusual_headers() {
let body = "a";
let mut headers = BTreeMap::new();
headers.insert("x-unusual-header".to_string(), "🚀".to_string());

let js_response = JsResponse { status: 200, headers, body: Some(body.into()) };
let mut headers = HeaderMap::new();
headers.insert(
HeaderName::from_static("x-unusual-header"),
HeaderValue::from_str("🚀").unwrap(),
);
let response =
crate::http::Response { status: reqwest::StatusCode::OK, headers, body: body.into() };
let js_response = JsResponse(response);

let response: Result<crate::http::Response<Bytes>, _> = js_response.try_into();
assert!(response.is_ok());
Expand Down Expand Up @@ -186,10 +194,10 @@ mod test {
let js_response = create_test_response().unwrap().into_js(&ctx).unwrap();
let response = JsResponse::from_js(&ctx, js_response).unwrap();

assert_eq!(response.status, reqwest::StatusCode::OK.as_u16());
assert_eq!(response.body, Some("Hello, World!".to_owned()));
assert_eq!(response.status(), reqwest::StatusCode::OK.as_u16());
assert_eq!(response.body(), Some("Hello, World!".to_owned()));
assert_eq!(
response.headers.get("content-type"),
response.headers().get("content-type"),
Some(&"application/json".to_owned())
);
});
Expand Down
17 changes: 11 additions & 6 deletions src/cli/javascript/request_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ impl RequestFilter {
}
Command::Response(js_response) => {
// Check if the response is a redirect
if (js_response.status == 301 || js_response.status == 302)
&& js_response.headers.contains_key("location")
if (js_response.status() == 301 || js_response.status() == 302)
&& js_response.headers().contains_key("location")

Check warning on line 76 in src/cli/javascript/request_filter.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/javascript/request_filter.rs#L75-L76

Added lines #L75 - L76 were not covered by tests
{
request
.url_mut()
.set_path(js_response.headers["location"].as_str());
.set_path(js_response.headers()["location"].as_str());

Check warning on line 80 in src/cli/javascript/request_filter.rs

View check run for this annotation

Codecov / codecov/patch

src/cli/javascript/request_filter.rs#L80

Added line #L80 was not covered by tests
self.on_request(request).await
} else {
Ok(js_response.try_into()?)
Expand All @@ -101,12 +101,12 @@ impl HttpIO for RequestFilter {

#[cfg(test)]
mod tests {
use std::collections::BTreeMap;

use hyper::body::Bytes;
use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString};

use crate::cli::javascript::request_filter::Command;
use crate::cli::javascript::{JsRequest, JsResponse};
use crate::http::Response;

#[test]
fn test_command_from_invalid_object() {
Expand Down Expand Up @@ -139,7 +139,12 @@ mod tests {
let runtime = Runtime::new().unwrap();
let context = Context::base(&runtime).unwrap();
context.with(|ctx| {
let js_response = JsResponse { status: 200, headers: BTreeMap::new(), body: None };
let js_response = JsResponse::try_from(Response {
status: reqwest::StatusCode::OK,
headers: reqwest::header::HeaderMap::default(),
body: Bytes::new(),
})
.unwrap();
let value = Object::new(ctx.clone()).unwrap();
value.set("response", js_response).unwrap();
assert!(Command::from_js(&ctx, value.into_value()).is_ok());
Expand Down

0 comments on commit 8642104

Please sign in to comment.