Merge pull request #1044 from bvanneerven/master

This commit is contained in:
Sunli 2022-08-28 20:49:38 +08:00 committed by GitHub
commit 521193f6b6
2 changed files with 343 additions and 0 deletions

View File

@ -0,0 +1,341 @@
use std::collections::HashMap;
/// A builder for constructing a GraphiQL (v2) HTML page.
///
/// # Example
///
/// ```rust
/// use async_graphql::http::*;
///
/// GraphiQLSource::build()
/// .endpoint("http://localhost:8000")
/// .subscription_endpoint("ws://localhost:8000/ws")
/// .header("Authorization", "Bearer <token>")
/// .finish();
/// ```
#[derive(Default)]
pub struct GraphiQLSource<'a> {
endpoint: &'a str,
subscription_endpoint: Option<&'a str>,
headers: Option<HashMap<&'a str, &'a str>>,
}
impl<'a> GraphiQLSource<'a> {
/// Creates a builder for constructing a GraphiQL (v2) HTML page.
pub fn build() -> GraphiQLSource<'a> {
Default::default()
}
/// Sets the endpoint of the server GraphiQL will connect to.
#[must_use]
pub fn endpoint(self, endpoint: &'a str) -> GraphiQLSource<'a> {
GraphiQLSource { endpoint, ..self }
}
/// Sets the subscription endpoint of the server GraphiQL will connect to.
pub fn subscription_endpoint(self, endpoint: &'a str) -> GraphiQLSource<'a> {
GraphiQLSource {
subscription_endpoint: Some(endpoint),
..self
}
}
/// Sets a header to be sent with requests GraphiQL will send.
pub fn header(self, name: &'a str, value: &'a str) -> GraphiQLSource<'a> {
let mut headers = match self.headers {
Some(headers) => headers,
None => HashMap::new(),
};
headers.insert(name, value);
GraphiQLSource {
headers: Some(headers),
..self
}
}
/// Returns a GraphiQL (v2) HTML page.
pub fn finish(self) -> String {
let graphiql_url = format!("'{}'", self.endpoint);
let graphiql_subscription_url = self
.subscription_endpoint
.map(|endpoint| format!("'{}'", endpoint))
.unwrap_or_else(|| "undefined".into());
let graphiql_headers = match self.headers {
Some(headers) => serde_json::to_string(&headers).unwrap(),
None => "undefined".into(),
};
r#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="origin">
<title>GraphiQL IDE</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<link rel="icon" href="https://graphql.org/favicon.ico">
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://unpkg.com/graphiql/graphiql.min.js"
type="application/javascript"
></script>
<script>
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: GraphiQL.createFetcher({
url: %GRAPHIQL_URL%,
subscriptionUrl: %GRAPHIQL_SUBSCRIPTION_URL%,
headers: %GRAPHIQL_HEADERS%,
}),
defaultEditorToolsVisibility: true,
}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
"#
.replace("%GRAPHIQL_URL%", &graphiql_url)
.replace("%GRAPHIQL_SUBSCRIPTION_URL%", &graphiql_subscription_url)
.replace("%GRAPHIQL_HEADERS%", &graphiql_headers)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_with_only_url() {
let graphiql_source = GraphiQLSource::build()
.endpoint("http://localhost:8000")
.finish();
assert_eq!(
graphiql_source,
r#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="origin">
<title>GraphiQL IDE</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<link rel="icon" href="https://graphql.org/favicon.ico">
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://unpkg.com/graphiql/graphiql.min.js"
type="application/javascript"
></script>
<script>
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: GraphiQL.createFetcher({
url: 'http://localhost:8000',
subscriptionUrl: undefined,
headers: undefined,
}),
defaultEditorToolsVisibility: true,
}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
"#
)
}
#[test]
fn test_with_both_urls() {
let graphiql_source = GraphiQLSource::build()
.endpoint("http://localhost:8000")
.subscription_endpoint("ws://localhost:8000/ws")
.finish();
assert_eq!(
graphiql_source,
r#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="origin">
<title>GraphiQL IDE</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<link rel="icon" href="https://graphql.org/favicon.ico">
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://unpkg.com/graphiql/graphiql.min.js"
type="application/javascript"
></script>
<script>
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: GraphiQL.createFetcher({
url: 'http://localhost:8000',
subscriptionUrl: 'ws://localhost:8000/ws',
headers: undefined,
}),
defaultEditorToolsVisibility: true,
}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
"#
)
}
#[test]
fn test_with_all_options() {
let graphiql_source = GraphiQLSource::build()
.endpoint("http://localhost:8000")
.subscription_endpoint("ws://localhost:8000/ws")
.header("Authorization", "Bearer <token>")
.finish();
assert_eq!(
graphiql_source,
r#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="origin">
<title>GraphiQL IDE</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<link rel="icon" href="https://graphql.org/favicon.ico">
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://unpkg.com/graphiql/graphiql.min.js"
type="application/javascript"
></script>
<script>
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: GraphiQL.createFetcher({
url: 'http://localhost:8000',
subscriptionUrl: 'ws://localhost:8000/ws',
headers: {"Authorization":"Bearer <token>"},
}),
defaultEditorToolsVisibility: true,
}),
document.getElementById("graphiql")
);
</script>
</body>
</html>
"#
)
}
}

View File

@ -1,12 +1,14 @@
//! A helper module that supports HTTP
mod graphiql_source;
mod graphiql_v2_source;
mod multipart;
mod playground_source;
mod websocket;
use futures_util::io::{AsyncRead, AsyncReadExt};
pub use graphiql_source::graphiql_source;
pub use graphiql_v2_source::GraphiQLSource;
use mime;
pub use multipart::MultipartOptions;
pub use playground_source::{playground_source, GraphQLPlaygroundConfig};