Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Studio Explorer boilerplate HTML #526

Merged
merged 4 commits into from
Feb 23, 2022
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## :bug: Fixes

- **Studio Explorer Boilerplate HTML** ([PR #526](https://github.com/apollograpqhl/router/pull/526))

This replaces the behavior of redirecting directly to studio with the more complete implementation which includes a landing-page that is served locally and offers a redirect to Studio. This will match the behavior of Apollo Server and Apollo Gateway today, exactly. This offers more transparency to the user to understand what about to happen (the redirect) and allows them to optionally make the behavior sticky (on account of a browser cookie) for future requests.

- **Anonymous operation names are now empty in tracing** ([PR #525](https://github.com/apollograpqhl/router/pull/525))

When GraphQL operation names are not nececessary to execute an operation (i.e., when there is only a single operation in a GraphQL document) and the GraphQL operation is _not_ named (i.e., it is anonymous), the `operation_name` attribute on the trace spans that are associated with the request will no longer contain a single hyphen character (`-`) but will instead be an empty string. This matches the way that these operations are represented during the GraphQL operation's life-cycle as well.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { __typename }
53 changes: 53 additions & 0 deletions apollo-router/resources/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<link rel="icon" href="https://apollo-server-landing-page.cdn.apollographql.com/_latest/assets/favicon.png" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" rel="stylesheet" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Apollo Router landing page" />
<link rel="apple-touch-icon"
href="https://apollo-server-landing-page.cdn.apollographql.com/_latest/assets/favicon.png" />
<link rel="manifest" href="https://apollo-server-landing-page.cdn.apollographql.com/_latest/manifest.json" />
<title>Apollo Router</title>
</head>

<body style="margin: 0; overflow-x: hidden; overflow-y: hidden">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="react-root">
<style>
.fallback {
opacity: 0;
animation: fadeIn 1s 1s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
padding: 1em;
}

@keyframes fadeIn {
0% {
opacity: 0;
}

100% {
opacity: 1;
}
}
</style>
<div class="fallback">
<h1>Welcome to the Apollo Router</h1>
<p>It appears that you might be offline. POST to this endpoint to query your graph:</p>
<code style="white-space: pre;">
curl --request POST \\
--header 'content-type: application/json' \\
--url '<script>document.write(window.location.href)</script>' \\
--data '{"query":"query { __typename }"}'</code>
bnjjj marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
<script src="https://apollo-server-landing-page.cdn.apollographql.com/_latest/static/js/main.js"></script>
</body>

</html>
57 changes: 14 additions & 43 deletions apollo-router/src/warp_http_server_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ use tower_service::Service;
use tracing::instrument::WithSubscriber;
use tracing::{Level, Span};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use warp::host::Authority;
use warp::{
http::{header::HeaderMap, StatusCode, Uri},
http::{header::HeaderMap, StatusCode},
hyper::Body,
Filter,
};
Expand Down Expand Up @@ -246,22 +245,18 @@ where
warp::get()
.and(warp::path::end().or(warp::path("graphql")).unify())
.and(warp::header::optional::<String>("accept"))
.and(warp::host::optional())
.and(
warp::query::raw()
.or(warp::any().map(String::default))
.unify(),
)
.and(warp::header::headers_cloned())
.and_then(
move |accept: Option<String>,
host: Option<Authority>,
query: String,
header_map: HeaderMap| {
move |accept: Option<String>, query: String, header_map: HeaderMap| {
let service = service.clone();
async move {
let reply: Box<dyn Reply> = if accept.map(prefers_html).unwrap_or_default() {
redirect_to_studio(host)
display_home_page()
} else if let Ok(request) = graphql::Request::from_urlencoded_query(query) {
run_graphql_request(service, http::Method::GET, request, header_map).await
} else {
Expand All @@ -277,29 +272,9 @@ where
)
}

fn redirect_to_studio(host: Option<Authority>) -> Box<dyn Reply> {
// Try to redirect to Studio
if host.is_some() {
if let Ok(uri) = format!(
"https://studio.apollographql.com/sandbox?endpoint=http://{}",
// we made sure host.is_some() above
host.unwrap()
)
.parse::<Uri>()
{
Box::new(warp::redirect::temporary(uri))
} else {
Box::new(warp::reply::with_status(
"Invalid host to redirect to",
StatusCode::BAD_REQUEST,
))
}
} else {
Box::new(warp::reply::with_status(
"Invalid host to redirect to",
StatusCode::BAD_REQUEST,
))
}
fn display_home_page() -> Box<dyn Reply> {
let html = include_str!("../resources/index.html");
Box::new(warp::reply::html(html))
}

fn get_health_request() -> impl Filter<Extract = (Box<dyn Reply>,), Error = Rejection> + Clone {
Expand Down Expand Up @@ -465,7 +440,7 @@ mod tests {
use reqwest::header::{
ACCEPT, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS,
ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD,
LOCATION, ORIGIN,
ORIGIN,
};
use reqwest::redirect::Policy;
use reqwest::{Client, Method, StatusCode};
Expand Down Expand Up @@ -608,7 +583,7 @@ mod tests {
}

#[test(tokio::test)]
async fn redirect_to_studio() -> Result<(), FederatedServerError> {
async fn display_home_page() -> Result<(), FederatedServerError> {
let expectations = MockRouterService::new();
let (server, client) = init(expectations).await;

Expand All @@ -625,19 +600,15 @@ mod tests {
.unwrap();
assert_eq!(
response.status(),
StatusCode::TEMPORARY_REDIRECT,
StatusCode::OK,
"{}",
response.text().await.unwrap()
);
assert_header!(
&response,
LOCATION,
vec![format!(
"https://studio.apollographql.com/sandbox?endpoint={}",
server.listen_address()
)],
"Incorrect redirect url"
);
assert!(response
.text()
.await
.unwrap()
.starts_with("<!DOCTYPE html>"))
}

server.shutdown().await
Expand Down