Skip to content

Commit e619002

Browse files
committed
[web] Replace custom HTTP client with external crate
This simplifies things a lot, and we benefit from other people's work on HTTP in the browser.
1 parent 32cd3c7 commit e619002

File tree

5 files changed

+54
-179
lines changed

5 files changed

+54
-179
lines changed

examples/web/Cargo.toml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ edition = "2018"
88
#[profile.release]
99
#lto = "thin"
1010

11-
[dev-dependencies]
12-
graphql_client = { path = "../../graphql_client" }
13-
graphql_client_web = { path = "../../graphql_client_web" }
11+
[lib]
12+
crate-type = ["cdylib", "rlib"]
13+
14+
[dependencies]
15+
graphql_client = { path = "../../graphql_client", features = ["web"] }
1416
wasm-bindgen = "^0.2"
1517
serde = { version = "1.0.67", features = ["derive"] }
1618
lazy_static = "1.0.1"
1719
js-sys = "0.3.6"
18-
futures = "0.1.25"
19-
wasm-bindgen-futures = "0.3.6"
20+
futures = "0.3.5"
21+
wasm-bindgen-futures = "0.4.12"
2022

21-
[dev-dependencies.web-sys]
23+
[dependencies.web-sys]
2224
version = "0.3.6"
2325
features = [
2426
"console",
@@ -31,6 +33,6 @@ features = [
3133
"HtmlElement",
3234
]
3335

34-
[[example]]
35-
name = "web"
36-
crate-type = ["cdylib"]
36+
# [[example]]
37+
# name = "web"
38+
# crate-type = ["cdylib"]

examples/web/examples/web.rs renamed to examples/web/src/lib.rs

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
use futures::Future;
2-
use graphql_client::GraphQLQuery;
1+
use futures::{Future, FutureExt};
2+
use graphql_client::{web, GraphQLQuery, Response};
33
use lazy_static::*;
4-
use std::cell::RefCell;
5-
use std::sync::Mutex;
6-
use wasm_bindgen::prelude::*;
7-
use wasm_bindgen::JsCast;
8-
use wasm_bindgen_futures::future_to_promise;
4+
use std::{cell::RefCell, sync::Mutex};
5+
use wasm_bindgen::{prelude::*, JsCast};
96

107
#[derive(GraphQLQuery)]
118
#[graphql(
129
schema_path = "schema.json",
13-
query_path = "examples/puppy_smiles.graphql",
10+
query_path = "src/puppy_smiles.graphql",
1411
response_derives = "Debug"
1512
)]
1613
struct PuppySmiles;
@@ -23,28 +20,24 @@ lazy_static! {
2320
static ref LAST_ENTRY: Mutex<RefCell<Option<String>>> = Mutex::new(RefCell::new(None));
2421
}
2522

26-
fn load_more() -> impl Future<Item = JsValue, Error = JsValue> {
27-
let client = graphql_client::web::Client::new("https://www.graphqlhub.com/graphql");
23+
async fn load_more() -> Result<JsValue, JsValue> {
24+
let client = web::Client::new("https://www.graphqlhub.com/graphql");
2825
let variables = puppy_smiles::Variables {
2926
after: LAST_ENTRY
3027
.lock()
3128
.ok()
3229
.and_then(|opt| opt.borrow().to_owned()),
3330
};
34-
let response = client.call(PuppySmiles, variables);
35-
36-
response
37-
.map(|response| {
38-
render_response(response);
39-
JsValue::NULL
40-
})
41-
.map_err(|err| {
42-
log(&format!(
43-
"Could not fetch puppies. graphql_client_web error: {:?}",
44-
err
45-
));
46-
JsValue::NULL
47-
})
31+
let response = client.call(PuppySmiles, variables).await.map_err(|err| {
32+
log(&format!(
33+
"Could not fetch puppies. graphql_client_web error: {:?}",
34+
err
35+
));
36+
JsValue::NULL
37+
})?;
38+
39+
render_response(response);
40+
Ok(JsValue::NULL)
4841
}
4942

5043
fn document() -> web_sys::Document {
@@ -60,7 +53,12 @@ fn add_load_more_button() {
6053
.expect_throw("could not create button");
6154
btn.set_inner_html("I WANT MORE PUPPIES");
6255
let on_click = Closure::wrap(
63-
Box::new(move || future_to_promise(load_more())) as Box<dyn FnMut() -> js_sys::Promise>
56+
Box::new(move || {
57+
wasm_bindgen_futures::spawn_local(async {
58+
let _ = load_more().await;
59+
});
60+
JsValue::NULL
61+
}) as Box<dyn FnMut() -> JsValue>, // Box::new(move || future_to_promise(load_more().boxed())) as Box<dyn FnMut() -> js_sys::Promise>
6462
);
6563
btn.add_event_listener_with_callback(
6664
"click",
@@ -78,14 +76,14 @@ fn add_load_more_button() {
7876
on_click.forget();
7977
}
8078

81-
fn render_response(response: graphql_client_web::Response<puppy_smiles::ResponseData>) {
79+
fn render_response(response: Response<puppy_smiles::ResponseData>) {
8280
use std::fmt::Write;
8381

8482
log(&format!("response body\n\n{:?}", response));
8583

8684
let parent = document().body().expect_throw("no body");
8785

88-
let json: graphql_client_web::Response<puppy_smiles::ResponseData> = response;
86+
let json: Response<puppy_smiles::ResponseData> = response;
8987
let response = document()
9088
.create_element("div")
9189
.expect_throw("could not create div");

graphql_client/Cargo.toml

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,10 @@ thiserror = { version = "1.0", optional = true }
1616
graphql_query_derive = { path = "../graphql_query_derive", version = "0.9.0" }
1717
serde_json = "1.0"
1818
serde = { version = "^1.0.78", features = ["derive"] }
19-
20-
[dependencies.futures]
21-
version = "^0.1"
22-
optional = true
23-
24-
[dependencies.js-sys]
25-
version = "^0.3"
26-
optional = true
27-
28-
[dependencies.log]
29-
version = "^0.4"
30-
optional = true
31-
32-
[dependencies.web-sys]
33-
version = "^0.3"
34-
optional = true
35-
features = [
36-
"Headers",
37-
"Request",
38-
"RequestInit",
39-
"Response",
40-
"Window",
41-
]
42-
43-
[dependencies.wasm-bindgen]
44-
version = "^0.2"
45-
optional = true
46-
47-
[dependencies.wasm-bindgen-futures]
48-
version = "^0.3"
49-
optional = true
19+
reqwest = { version = "0.10.4", optional = true }
5020

5121
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
5222
reqwest = "^0.9"
5323

54-
[dev-dependencies]
55-
# Note: If we bumpup wasm-bindge-test version, we should change CI setting.
56-
wasm-bindgen-test = "^0.2"
57-
5824
[features]
59-
web = [
60-
"anyhow",
61-
"thiserror",
62-
"futures",
63-
"js-sys",
64-
"log",
65-
"wasm-bindgen",
66-
"wasm-bindgen-futures",
67-
"web-sys",
68-
]
25+
web = ["reqwest"]

graphql_client/src/web.rs

Lines changed: 16 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22
//! [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen).
33
44
use crate::*;
5-
use futures::{Future, IntoFuture};
6-
use log::*;
75
use std::collections::HashMap;
8-
use thiserror::*;
9-
use wasm_bindgen::{JsCast, JsValue};
10-
use wasm_bindgen_futures::JsFuture;
116

127
/// The main interface to the library.
138
///
@@ -19,39 +14,7 @@ use wasm_bindgen_futures::JsFuture;
1914
pub struct Client {
2015
endpoint: String,
2116
headers: HashMap<String, String>,
22-
}
23-
24-
/// All the ways a request can go wrong.
25-
///
26-
/// not exhaustive
27-
#[derive(Debug, Error, PartialEq)]
28-
pub enum ClientError {
29-
/// The body couldn't be built
30-
#[error("Request body is not a valid string")]
31-
Body,
32-
/// An error caused by window.fetch
33-
#[error("Network error")]
34-
Network(String),
35-
/// Error in a dynamic JS cast that should have worked
36-
#[error("JS casting error")]
37-
Cast,
38-
/// No window object could be retrieved
39-
#[error(
40-
"No Window object available - the client works only in a browser (non-worker) context"
41-
)]
42-
NoWindow,
43-
/// Response shape does not match the generated code
44-
#[error("Response shape error")]
45-
ResponseShape,
46-
/// Response could not be converted to text
47-
#[error("Response conversion to text failed (Response.text threw)")]
48-
ResponseText,
49-
/// Exception thrown when building the request
50-
#[error("Error building the request")]
51-
RequestError,
52-
/// Other JS exception
53-
#[error("Unexpected JS exception")]
54-
JsException,
17+
reqwest_client: reqwest::Client,
5518
}
5619

5720
impl Client {
@@ -63,6 +26,7 @@ impl Client {
6326
Client {
6427
endpoint: endpoint.into(),
6528
headers: HashMap::new(),
29+
reqwest_client: reqwest::Client::new(),
6630
}
6731
}
6832

@@ -75,71 +39,25 @@ impl Client {
7539
///
7640
// Lint disabled: We can pass by value because it's always an empty struct.
7741
#[allow(clippy::needless_pass_by_value)]
78-
pub fn call<Q: GraphQLQuery + 'static>(
42+
pub async fn call<Q: GraphQLQuery + 'static>(
7943
&self,
8044
_query: Q,
8145
variables: Q::Variables,
82-
) -> impl Future<Item = crate::Response<Q::ResponseData>, Error = ClientError> + 'static {
83-
// this can be removed when we convert to async/await
84-
let endpoint = self.endpoint.clone();
85-
let custom_headers = self.headers.clone();
86-
87-
web_sys::window()
88-
.ok_or_else(|| ClientError::NoWindow)
89-
.into_future()
90-
.and_then(move |window| {
91-
serde_json::to_string(&Q::build_query(variables))
92-
.map_err(|_| ClientError::Body)
93-
.map(move |body| (window, body))
94-
})
95-
.and_then(move |(window, body)| {
96-
let mut request_init = web_sys::RequestInit::new();
97-
request_init
98-
.method("POST")
99-
.body(Some(&JsValue::from_str(&body)));
100-
101-
web_sys::Request::new_with_str_and_init(&endpoint, &request_init)
102-
.map_err(|_| ClientError::JsException)
103-
.map(|request| (window, request))
104-
// "Request constructor threw");
105-
})
106-
.and_then(move |(window, request)| {
107-
let headers = request.headers();
108-
headers
109-
.set("Content-Type", "application/json")
110-
.map_err(|_| ClientError::RequestError)?;
111-
headers
112-
.set("Accept", "application/json")
113-
.map_err(|_| ClientError::RequestError)?;
46+
) -> Result<crate::Response<Q::ResponseData>, reqwest::Error> {
47+
// TODO: remove the unwrap
48+
// TODO: remove tests and test harness
49+
// TODO: custom headers
50+
let reqwest_response = self
51+
.reqwest_client
52+
.post(&self.endpoint)
53+
.header("Content-Type", "application/json")
54+
.body(serde_json::to_string(&Q::build_query(variables)).unwrap())
55+
.send()
56+
.await?;
11457

115-
for (header_name, header_value) in custom_headers.iter() {
116-
headers
117-
.set(header_name, header_value)
118-
.map_err(|_| ClientError::RequestError)?;
119-
}
58+
let text_response = reqwest_response.text().await?;
12059

121-
Ok((window, request))
122-
})
123-
.and_then(move |(window, request)| {
124-
JsFuture::from(window.fetch_with_request(&request))
125-
.map_err(|err| ClientError::Network(js_sys::Error::from(err).message().into()))
126-
})
127-
.and_then(move |res| {
128-
debug!("response: {:?}", res);
129-
res.dyn_into::<web_sys::Response>()
130-
.map_err(|_| ClientError::Cast)
131-
})
132-
.and_then(move |cast_response| {
133-
cast_response.text().map_err(|_| ClientError::ResponseText)
134-
})
135-
.and_then(move |text_promise| {
136-
JsFuture::from(text_promise).map_err(|_| ClientError::ResponseText)
137-
})
138-
.and_then(|text| {
139-
let response_text = text.as_string().unwrap_or_default();
140-
debug!("response text as string: {:?}", response_text);
141-
serde_json::from_str(&response_text).map_err(|_| ClientError::ResponseShape)
142-
})
60+
Ok(serde_json::from_str(&text_response).unwrap())
14361
}
14462
}
14563

0 commit comments

Comments
 (0)