Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions juniper/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ See [#419](https://github.com/graphql-rust/juniper/pull/419).
- `SchemaType` is now public
- This is helpful when using `context.getSchema()` inside of your field resolvers

- Support subscriptions in GraphiQL

See [#569](https://github.com/graphql-rust/juniper/pull/569).

## Breaking Changes

- `juniper::graphiql` has moved to `juniper::http::graphiql`
- `juniper::http::graphiql::graphiql_source` now requies a second parameter for subscriptions

- remove old `graphql_object!` macro, rename `object` proc macro to `graphql_object`

Expand Down
35 changes: 32 additions & 3 deletions juniper/src/http/graphiql.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
//! Utility module to generate a GraphiQL interface

/// Generate the HTML source to show a GraphiQL interface
pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
///
/// The subscriptions endpoint URL can optionally be provided. For example:
///
/// ```
/// # use juniper::http::graphiql::graphiql_source;
/// let graphiql = graphiql_source("/graphql", Some("ws://localhost:8080/subscriptions"));
/// ```
pub fn graphiql_source(
graphql_endpoint_url: &str,
subscriptions_endpoint_url: Option<&str>,
) -> String {
let subscriptions_endpoint = if let Some(sub_url) = subscriptions_endpoint_url {
sub_url
} else {
""
};

let stylesheet_source = r#"
<style>
html, body, #app {
Expand All @@ -14,6 +30,10 @@ pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
"#;
let fetcher_source = r#"
<script>
if (usingSubscriptions) {
var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient(GRAPHQL_SUBSCRIPTIONS_URL, { reconnect: true });
}

function graphQLFetcher(params) {
return fetch(GRAPHQL_URL, {
method: 'post',
Expand All @@ -33,9 +53,12 @@ pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
}
});
}

var fetcher = usingSubscriptions ? window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLFetcher) : graphQLFetcher;

ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: graphQLFetcher,
fetcher,
}),
document.querySelector('#app'));
</script>
Expand All @@ -53,16 +76,22 @@ pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
<body>
<div id="app"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.js"></script>
<script src="//unpkg.com/subscriptions-transport-ws@0.8.3/browser/client.js"></script>
<script src="//unpkg.com/graphiql-subscriptions-fetcher@0.0.2/browser/client.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/graphiql@0.17.5/graphiql.min.js"></script>
<script>var GRAPHQL_URL = '{graphql_url}';</script>
<script>var usingSubscriptions = {using_subscriptions};</script>
<script>var GRAPHQL_SUBSCRIPTIONS_URL = '{graphql_subscriptions_url}';</script>
{fetcher_source}
</body>
</html>
"#,
graphql_url = graphql_endpoint_url,
stylesheet_source = stylesheet_source,
fetcher_source = fetcher_source
fetcher_source = fetcher_source,
graphql_subscriptions_url = subscriptions_endpoint,
using_subscriptions = subscriptions_endpoint_url.is_some(),
)
}
4 changes: 4 additions & 0 deletions juniper_hyper/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

- Compatibility with the latest `juniper`.

## Breaking Changes

- `juniper_hyper::graphiql` now requires a second parameter for subscriptions

# [[0.5.2] 2019-12-16](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.2)

- Compatibility with the latest `juniper`.
Expand Down
2 changes: 1 addition & 1 deletion juniper_hyper/examples/hyper_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn main() {
let ctx = ctx.clone();
async move {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") => juniper_hyper::graphiql("/graphql").await,
(&Method::GET, "/") => juniper_hyper::graphiql("/graphql", None).await,
(&Method::GET, "/graphql") | (&Method::POST, "/graphql") => {
juniper_hyper::graphql(root_node, ctx, req).await
}
Expand Down
10 changes: 8 additions & 2 deletions juniper_hyper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,16 @@ async fn parse_post_req<S: ScalarValue>(
.map_err(GraphQLRequestError::BodyJSONError)
}

pub async fn graphiql(graphql_endpoint: &str) -> Result<Response<Body>, hyper::Error> {
pub async fn graphiql(
graphql_endpoint: &str,
subscriptions_endpoint: Option<&str>,
) -> Result<Response<Body>, hyper::Error> {
let mut resp = new_html_response(StatusCode::OK);
// XXX: is the call to graphiql_source blocking?
*resp.body_mut() = Body::from(juniper::http::graphiql::graphiql_source(graphql_endpoint));
*resp.body_mut() = Body::from(juniper::http::graphiql::graphiql_source(
graphql_endpoint,
subscriptions_endpoint,
));
Ok(resp)
}

Expand Down
4 changes: 4 additions & 0 deletions juniper_iron/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

- Compatibility with the latest `juniper`.

## Breaking Changes

- `juniper_iron::GraphiQLHandler::new` now requires a second parameter for subscriptions

# [[0.6.2] 2019-12-16](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.2)

- Compatibility with the latest `juniper`.
Expand Down
2 changes: 1 addition & 1 deletion juniper_iron/examples/iron_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn main() {
EmptyMutation::<Database>::new(),
EmptySubscription::<Database>::new(),
);
let graphiql_endpoint = GraphiQLHandler::new("/graphql");
let graphiql_endpoint = GraphiQLHandler::new("/graphql", None);

mount.mount("/", graphiql_endpoint);
mount.mount("/graphql", graphql_endpoint);
Expand Down
9 changes: 7 additions & 2 deletions juniper_iron/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ pub struct GraphQLHandler<
/// Handler that renders `GraphiQL` - a graphical query editor interface
pub struct GraphiQLHandler {
graphql_url: String,
subscription_url: Option<String>,
}

/// Handler that renders `GraphQL Playground` - a graphical query editor interface
Expand Down Expand Up @@ -275,9 +276,10 @@ impl GraphiQLHandler {
///
/// The provided URL should point to the URL of the attached `GraphQLHandler`. It can be
/// relative, so a common value could be `"/graphql"`.
pub fn new(graphql_url: &str) -> GraphiQLHandler {
pub fn new(graphql_url: &str, subscription_url: Option<&str>) -> GraphiQLHandler {
GraphiQLHandler {
graphql_url: graphql_url.to_owned(),
subscription_url: subscription_url.map(|s| s.to_owned()),
}
}
}
Expand Down Expand Up @@ -326,7 +328,10 @@ impl Handler for GraphiQLHandler {
Ok(Response::with((
content_type,
status::Ok,
juniper::http::graphiql::graphiql_source(&self.graphql_url),
juniper::http::graphiql::graphiql_source(
&self.graphql_url,
self.subscription_url.as_deref(),
),
)))
}
}
Expand Down
4 changes: 4 additions & 0 deletions juniper_rocket/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
- Compatibility with the latest `juniper`.
- Rocket integration does not require default features.

## Breaking Changes

- `juniper_rocket::graphiql_source` now requires a second parameter for subscriptions

# [[0.5.2] 2019-12-16](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.2)

- Compatibility with the latest `juniper`.
Expand Down
2 changes: 1 addition & 1 deletion juniper_rocket/examples/rocket_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Schema = RootNode<'static, Query, EmptyMutation<Database>, EmptySubscriptio

#[rocket::get("/")]
fn graphiql() -> content::Html<String> {
juniper_rocket::graphiql_source("/graphql")
juniper_rocket::graphiql_source("/graphql", None)
}

#[rocket::get("/graphql?<request>")]
Expand Down
6 changes: 5 additions & 1 deletion juniper_rocket/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,13 @@ where
pub struct GraphQLResponse(pub Status, pub String);

/// Generate an HTML page containing GraphiQL
pub fn graphiql_source(graphql_endpoint_url: &str) -> content::Html<String> {
pub fn graphiql_source(
graphql_endpoint_url: &str,
subscriptions_endpoint: Option<&str>,
) -> content::Html<String> {
content::Html(juniper::http::graphiql::graphiql_source(
graphql_endpoint_url,
subscriptions_endpoint,
))
}

Expand Down
1 change: 1 addition & 0 deletions juniper_rocket_async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub struct GraphQLResponse(pub Status, pub String);
pub fn graphiql_source(graphql_endpoint_url: &str) -> content::Html<String> {
content::Html(juniper::http::graphiql::graphiql_source(
graphql_endpoint_url,
None,
))
}

Expand Down
1 change: 1 addition & 0 deletions juniper_warp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ to `juniper` to be reused in other http integrations, since this implementation
- Update `playground_filter` to support subscription endpoint URLs
- Update `warp` to 0.2
- Rename synchronous `execute` to `execute_sync`, add asynchronous `execute`
- `juniper_warp::graphiql_filter` now requires a second parameter for subscriptions

# [[0.5.2] 2019-12-16](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.2)

Expand Down
2 changes: 1 addition & 1 deletion juniper_warp/examples/warp_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn main() {
warp::serve(
warp::get()
.and(warp::path("graphiql"))
.and(juniper_warp::graphiql_filter("/graphql"))
.and(juniper_warp::graphiql_filter("/graphql", None))
.or(homepage)
.or(warp::path("graphql").and(graphql_filter))
.with(log),
Expand Down
51 changes: 44 additions & 7 deletions juniper_warp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,20 +299,41 @@ type Response = Pin<
/// # use warp::Filter;
/// # use juniper_warp::graphiql_filter;
/// #
/// let graphiql_route = warp::path("graphiql").and(graphiql_filter("/graphql"));
/// let graphiql_route = warp::path("graphiql").and(graphiql_filter("/graphql",
/// None));
/// ```
///
/// Or with subscriptions support, provide the subscriptions endpoint URL:
///
/// ```
/// # extern crate warp;
/// # extern crate juniper_warp;
/// #
/// # use warp::Filter;
/// # use juniper_warp::graphiql_filter;
/// #
/// let graphiql_route = warp::path("graphiql").and(graphiql_filter("/graphql",
/// Some("ws://localhost:8080/subscriptions")));
/// ```
pub fn graphiql_filter(
graphql_endpoint_url: &'static str,
subscriptions_endpoint: Option<&'static str>,
) -> warp::filters::BoxedFilter<(warp::http::Response<Vec<u8>>,)> {
warp::any()
.map(move || graphiql_response(graphql_endpoint_url))
.map(move || graphiql_response(graphql_endpoint_url, subscriptions_endpoint))
.boxed()
}

fn graphiql_response(graphql_endpoint_url: &'static str) -> warp::http::Response<Vec<u8>> {
fn graphiql_response(
graphql_endpoint_url: &'static str,
subscriptions_endpoint: Option<&'static str>,
) -> warp::http::Response<Vec<u8>> {
warp::http::Response::builder()
.header("content-type", "text/html;charset=utf-8")
.body(juniper::http::graphiql::graphiql_source(graphql_endpoint_url).into_bytes())
.body(
juniper::http::graphiql::graphiql_source(graphql_endpoint_url, subscriptions_endpoint)
.into_bytes(),
)
.expect("response is valid")
}

Expand Down Expand Up @@ -568,14 +589,14 @@ mod tests {

#[test]
fn graphiql_response_does_not_panic() {
graphiql_response("/abcd");
graphiql_response("/abcd", None);
}

#[tokio::test]
async fn graphiql_endpoint_matches() {
let filter = warp::get()
.and(warp::path("graphiql"))
.and(graphiql_filter("/graphql"));
.and(graphiql_filter("/graphql", None));
let result = request()
.method("GET")
.path("/graphiql")
Expand All @@ -591,7 +612,7 @@ mod tests {
let filter = warp::get()
.and(warp::path("dogs-api"))
.and(warp::path("graphiql"))
.and(graphiql_filter("/dogs-api/graphql"));
.and(graphiql_filter("/dogs-api/graphql", None));
let response = request()
.method("GET")
.path("/dogs-api/graphiql")
Expand All @@ -609,6 +630,22 @@ mod tests {
assert!(body.contains("<script>var GRAPHQL_URL = '/dogs-api/graphql';</script>"));
}

#[tokio::test]
async fn graphiql_endpoint_with_subscription_matches() {
let filter = warp::get().and(warp::path("graphiql")).and(graphiql_filter(
"/graphql",
Some("ws:://localhost:8080/subscriptions"),
));
let result = request()
.method("GET")
.path("/graphiql")
.header("accept", "text/html")
.filter(&filter)
.await;

assert!(result.is_ok());
}

#[tokio::test]
async fn playground_endpoint_matches() {
let filter = warp::get()
Expand Down