From f9d90277bf597773363969e0fb4de74df1e5fc26 Mon Sep 17 00:00:00 2001 From: Kai Ren Date: Wed, 13 Sep 2023 17:34:44 +0200 Subject: [PATCH] Upgrade GraphiQL to 3.0.5 version (#1188, #1069) - track GraphiQL new version via @dependabot - automate GraphiQL integration glue adapting for new versions - rework `example/warp_subscriptions` to support subscriptions in new GraphiQL --- .github/dependabot.yml | 5 + Makefile | 18 ++++ examples/warp_subscriptions/Cargo.toml | 1 + examples/warp_subscriptions/src/main.rs | 82 ++++++++++------ juniper/.gitignore | 3 + juniper/CHANGELOG.md | 4 + juniper/Cargo.toml | 2 +- juniper/Makefile | 56 +++++++++++ juniper/package.json | 9 ++ juniper/src/http/graphiql.html | 75 +++++++++++++++ juniper/src/http/graphiql.js | 14 +++ juniper/src/http/graphiql.rs | 122 ++++-------------------- juniper_actix/src/lib.rs | 6 +- juniper_warp/src/lib.rs | 2 +- 14 files changed, 259 insertions(+), 140 deletions(-) create mode 100644 juniper/.gitignore create mode 100644 juniper/Makefile create mode 100644 juniper/package.json create mode 100644 juniper/src/http/graphiql.html create mode 100644 juniper/src/http/graphiql.js diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1cf1a19bb..b90aa62a6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,8 @@ updates: directory: / schedule: interval: daily + + - package-ecosystem: npm + directory: /juniper/ + schedule: + interval: daily diff --git a/Makefile b/Makefile index 8c1db098a..69d57839c 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,23 @@ book.serve: +###################### +# Forwarded commands # +###################### + +# Download and prepare actual version of GraphiQL static files, used for +# integrating it. +# +# Usage: +# make graphiql + +graphiql: + @cd juniper/ && \ + make graphiql + + + + ################## # .PHONY section # ################## @@ -155,4 +172,5 @@ book.serve: .PHONY: book fmt lint release test \ book.build book.serve \ cargo.fmt cargo.lint cargo.release cargo.test \ + graphiql \ test.book test.cargo diff --git a/examples/warp_subscriptions/Cargo.toml b/examples/warp_subscriptions/Cargo.toml index b03381233..f708c99e3 100644 --- a/examples/warp_subscriptions/Cargo.toml +++ b/examples/warp_subscriptions/Cargo.toml @@ -11,6 +11,7 @@ env_logger = "0.10" futures = "0.3" juniper = { path = "../../juniper" } juniper_graphql_ws = { path = "../../juniper_graphql_ws" } +juniper_graphql_transport_ws = { path = "../../juniper_graphql_transport_ws" } juniper_warp = { path = "../../juniper_warp", features = ["subscriptions"] } log = "0.4.8" serde = { version = "1.0", features = ["derive"] } diff --git a/examples/warp_subscriptions/src/main.rs b/examples/warp_subscriptions/src/main.rs index ded890623..1f6800a6d 100644 --- a/examples/warp_subscriptions/src/main.rs +++ b/examples/warp_subscriptions/src/main.rs @@ -7,8 +7,12 @@ use juniper::{ graphql_object, graphql_subscription, graphql_value, EmptyMutation, FieldError, GraphQLEnum, RootNode, }; -use juniper_graphql_ws::ConnectionConfig; -use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws}; +use juniper_graphql_transport_ws::ConnectionConfig; +use juniper_graphql_ws::ConnectionConfig as LegacyConnectionConfig; +use juniper_warp::{ + graphiql_filter, playground_filter, + subscriptions::{serve_graphql_transport_ws, serve_graphql_ws}, +}; use warp::{http::Response, Filter}; #[derive(Clone)] @@ -108,13 +112,13 @@ struct Subscription; #[graphql_subscription(context = Context)] impl Subscription { async fn users() -> UsersStream { - let mut counter = 0; let mut interval = tokio::time::interval(Duration::from_secs(5)); let stream = async_stream::stream! { - counter += 1; + let mut counter = 0; loop { + counter += 1; interval.tick().await; - if counter == 2 { + if counter == 5 { yield Err(FieldError::new( "some field error from handler", graphql_value!("some additional string"), @@ -156,36 +160,58 @@ async fn main() { let qm_state = warp::any().map(|| Context); let qm_graphql_filter = juniper_warp::make_graphql_filter(qm_schema, qm_state.boxed()); - let root_node = Arc::new(schema()); + let ws_schema = Arc::new(schema()); + let transport_ws_schema = ws_schema.clone(); log::info!("Listening on 127.0.0.1:8080"); - let routes = (warp::path("subscriptions") + let routes = warp::path("subscriptions") .and(warp::ws()) .map(move |ws: warp::ws::Ws| { - let root_node = root_node.clone(); + let transport_ws_schema = transport_ws_schema.clone(); ws.on_upgrade(move |websocket| async move { - serve_graphql_ws(websocket, root_node, ConnectionConfig::new(Context)) - .map(|r| { - if let Err(e) = r { - println!("Websocket error: {e}"); - } - }) - .await + serve_graphql_transport_ws( + websocket, + transport_ws_schema, + ConnectionConfig::new(Context), + ) + .map(|r| { + if let Err(e) = r { + println!("Websocket error: {e}"); + } + }) + .await + }) + }) + .or(warp::path("legacy-subscriptions") + .and(warp::ws()) + .map(move |ws: warp::ws::Ws| { + let ws_schema = ws_schema.clone(); + ws.on_upgrade(move |websocket| async move { + serve_graphql_ws(websocket, ws_schema, LegacyConnectionConfig::new(Context)) + .map(|r| { + if let Err(e) = r { + println!("Websocket error: {e}"); + } + }) + .await + }) }) - })) - .map(|reply| { - // TODO#584: remove this workaround - warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws") - }) - .or(warp::post() - .and(warp::path("graphql")) - .and(qm_graphql_filter)) - .or(warp::get() - .and(warp::path("playground")) - .and(playground_filter("/graphql", Some("/subscriptions")))) - .or(homepage) - .with(log); + .map(|reply| { + // TODO#584: remove this workaround + warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws") + })) + .or(warp::post() + .and(warp::path("graphql")) + .and(qm_graphql_filter)) + .or(warp::get() + .and(warp::path("playground")) + .and(playground_filter("/graphql", Some("/legacy-subscriptions")))) + .or(warp::get() + .and(warp::path("graphiql")) + .and(graphiql_filter("/graphql", Some("/subscriptions")))) + .or(homepage) + .with(log); warp::serve(routes).run(([127, 0, 0, 1], 8080)).await; } diff --git a/juniper/.gitignore b/juniper/.gitignore new file mode 100644 index 000000000..b16fdf455 --- /dev/null +++ b/juniper/.gitignore @@ -0,0 +1,3 @@ +/node_modules/ +/package-lock.json +/yarn.lock diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 1d2228db4..e0e6975c0 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -51,6 +51,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi - Disabled `chrono` [Cargo feature] by default. - Removed `scalar-naivetime` [Cargo feature]. - Removed lifetime parameter from `ParseError`, `GraphlQLError`, `GraphQLBatchRequest` and `GraphQLRequest`. ([#1081], [#528]) +- Upgraded [GraphiQL] to 3.0.5 version (requires new [`graphql-ws` GraphQL over WebSocket Protocol] integration on server, see `examples/warp_subscriptions`). ([#1188]) ### Added @@ -121,6 +122,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi [#1145]: /../../pull/1145 [#1147]: /../../pull/1147 [#1176]: /../../pull/1176 +[#1188]: /../../pull/1188 [ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083 [CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j @@ -140,6 +142,8 @@ See [old CHANGELOG](/../../blob/juniper-v0.15.9/juniper/CHANGELOG.md). [`chrono-tz` crate]: https://docs.rs/chrono-tz [`time` crate]: https://docs.rs/time [Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html +[`graphql-ws` GraphQL over WebSocket Protocol]: https://github.com/graphql/graphiql +[GraphiQL]: https://github.com/enisdenjo/graphql-ws/master/PROTOCOL.md [graphql-scalars.dev]: https://graphql-scalars.dev [October 2021]: https://spec.graphql.org/October2021 [object safety]: https://doc.rust-lang.org/reference/items/traits.html#object-safety diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index 4c1a403c2..bc9b3d8bd 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -18,7 +18,7 @@ repository = "https://github.com/graphql-rust/juniper" readme = "README.md" categories = ["asynchronous", "web-programming", "web-programming::http-server"] keywords = ["apollo", "graphql", "server", "web"] -exclude = ["/release.toml"] +include = ["/src/", "/CHANGELOG.md", "/LICENSE", "/README.md"] [package.metadata.docs.rs] all-features = true diff --git a/juniper/Makefile b/juniper/Makefile new file mode 100644 index 000000000..fb43bc27d --- /dev/null +++ b/juniper/Makefile @@ -0,0 +1,56 @@ +############################### +# Common defaults/definitions # +############################### + +# Checks two given strings for equality. +eq = $(if $(or $(1),$(2)),$(and $(findstring $(1),$(2)),\ + $(findstring $(2),$(1))),1) + +# Multiplatform prefix of `sed -i` commands. +sed-i = sed -i$(if $(call eq,$(shell uname -s),Darwin), '',) + + + + +###################### +# Project parameters # +###################### + +GRAPHIQL_VER ?= $(strip \ + $(shell grep -m1 '"graphiql": "' package.json | cut -d '"' -f4)) + + + + +############ +# Commands # +############ + +# Download and prepare actual version of GraphiQL static files, used for +# integrating it. +# +# Usage: +# make graphiql + +graphiql: + curl -fL -o src/http/graphiql.html \ + https://raw.githubusercontent.com/graphql/graphiql/graphiql%40$(GRAPHIQL_VER)/examples/graphiql-cdn/index.html + $(sed-i) 's|https://unpkg.com/graphiql/|https://unpkg.com/graphiql@$(GRAPHIQL_VER)/|g' \ + src/http/graphiql.html + $(sed-i) "s|'https://swapi-graphql.netlify.app/.netlify/functions/index'|GRAPHQL_URL|g" \ + src/http/graphiql.html + $(sed-i) "s|url: GRAPHQL_URL,|url: GRAPHQL_URL,\n subscriptionUrl: normalizeSubscriptionEndpoint(GRAPHQL_URL, GRAPHQL_SUBSCRIPTIONS_URL)|" \ + src/http/graphiql.html + $(sed-i) 's| + + + + + + + + +
Loading...
+ + + diff --git a/juniper/src/http/graphiql.js b/juniper/src/http/graphiql.js new file mode 100644 index 000000000..b914cc097 --- /dev/null +++ b/juniper/src/http/graphiql.js @@ -0,0 +1,14 @@ +function normalizeSubscriptionEndpoint(endpoint, subscriptionEndpoint) { + if (subscriptionEndpoint) { + if (subscriptionEndpoint.startsWith('/')) { + const secure = + endpoint.includes('https') || location.href.includes('https') + ? 's' + : '' + return `ws${secure}://${location.host}${subscriptionEndpoint}` + } else { + return subscriptionEndpoint.replace(/^http/, 'ws') + } + } + return null +} diff --git a/juniper/src/http/graphiql.rs b/juniper/src/http/graphiql.rs index a3ac57218..588bc90e3 100644 --- a/juniper/src/http/graphiql.rs +++ b/juniper/src/http/graphiql.rs @@ -6,7 +6,7 @@ /// /// ``` /// # use juniper::http::graphiql::graphiql_source; -/// let graphiql = graphiql_source("/graphql", Some("ws://localhost:8080/subscriptions")); +/// let graphiql = graphiql_source("/graphql", Some("/subscriptions")); /// ``` pub fn graphiql_source( graphql_endpoint_url: &str, @@ -18,110 +18,20 @@ pub fn graphiql_source( "" }; - let stylesheet_source = r#" - - "#; - let fetcher_source = r#" - - "#; - - format!( - r#" - - - - GraphQL - {stylesheet_source} - - - - - -
Loading...
- - - - - {fetcher_source} - - -"#, - graphql_url = graphql_endpoint_url, - stylesheet_source = stylesheet_source, - fetcher_source = fetcher_source, - graphql_subscriptions_url = subscriptions_endpoint, - using_subscriptions = subscriptions_endpoint_url.is_some(), + include_str!("graphiql.html").replace( + "", + // Language: JavaScript + &format!( + " + var GRAPHQL_URL = '{graphql_url}'; + var GRAPHQL_SUBSCRIPTIONS_URL = '{graphql_subscriptions_url}'; + +{grahiql_js} + + ", + graphql_url = graphql_endpoint_url, + graphql_subscriptions_url = subscriptions_endpoint, + grahiql_js = include_str!("graphiql.js"), + ), ) } diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index 7a2e5b4be..c18eeee1b 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -515,10 +515,8 @@ mod tests { "text/html; charset=utf-8" ); let body = take_response_body_string(resp).await; - assert!(body.contains("")); - assert!(body.contains( - "" - )) + assert!(body.contains("var GRAPHQL_URL = '/dogs-api/graphql';")); + assert!(body.contains("var GRAPHQL_SUBSCRIPTIONS_URL = '/dogs-api/subscriptions';")) } #[actix_web::rt::test] diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index 88ee8cb0f..d6ccc2c67 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -556,7 +556,7 @@ mod tests { ); let body = String::from_utf8(response.body().to_vec()).unwrap(); - assert!(body.contains("")); + assert!(body.contains("var GRAPHQL_URL = '/dogs-api/graphql';")); } #[tokio::test]