Skip to content

Instantly share code, notes, and snippets.

@djc
Created August 22, 2018 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djc/7555a2f23db7613e6637aaa256c4e972 to your computer and use it in GitHub Desktop.
Save djc/7555a2f23db7613e6637aaa256c4e972 to your computer and use it in GitHub Desktop.
extern crate diesel;
extern crate dotenv;
extern crate futures;
extern crate hyper;
extern crate juniper;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate serde;
extern crate serde_json;
extern crate trackpay;
extern crate url;
use diesel::sqlite::SqliteConnection as Connection;
use futures::future;
use hyper::rt::{Future, Stream};
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use juniper::graphiql::graphiql_source;
use juniper::http;
use juniper::{InputValue, RootNode};
use r2d2_diesel::ConnectionManager;
use std::env;
use url::Url;
use trackpay::{graphql, Context, Pool};
fn main() {
dotenv::dotenv().ok();
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let conn_mgr = ConnectionManager::<Connection>::new(db_url);
let pool = r2d2::Pool::builder()
.build(conn_mgr)
.expect("Failed to create pool");
let new_svc = move || {
let pool = pool.clone();
service_fn(move |req| respond(pool.clone(), req))
};
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr)
.serve(new_svc)
.map_err(|e| eprintln!("server error: {}", e));
hyper::rt::run(server);
}
fn respond(pool: Pool, req: Request<Body>) -> BoxFut {
println!("request: {:?} {:?}", req.uri().path(), req.method());
match (req.uri().path(), req.method()) {
("/", &Method::GET) => Box::new(future::ok(
Response::builder()
.header("Content-Type", "text/html; charset=utf-8")
.body(Body::from(graphiql_source("/graphql")))
.unwrap(),
)),
("/graphql", &Method::GET) => {
let url = Url::parse(&format!("{}", req.uri())).unwrap();
let (mut query, mut op, mut vars) = (None, None, None);
for (name, val) in url.query_pairs() {
if name == "query" {
query = Some(val.to_string());
} else if name == "operationName" {
op = Some(val.to_string());
} else if name == "variables" {
vars = Some(val.to_string());
}
}
let vars = vars
.as_ref()
.map(|v| serde_json::from_str::<InputValue>(v).unwrap());
Box::new(future::ok(gql_response(
&pool,
http::GraphQLRequest::new(query.unwrap(), op, vars),
)))
}
("/graphql", &Method::POST) => Box::new(req.into_body().concat2().map(move |chunk| {
gql_response(
&pool,
serde_json::from_slice::<http::GraphQLRequest>(&chunk).unwrap(),
)
})),
("/", _) | ("/graphql", _) => {
Box::new(future::ok(error_response(StatusCode::METHOD_NOT_ALLOWED)))
}
(_, _) => Box::new(future::ok(error_response(StatusCode::NOT_FOUND))),
}
}
fn gql_response(pool: &Pool, req: http::GraphQLRequest) -> Response<Body> {
Response::builder()
.header("Content-Type", "application/json")
.body(Body::from(
serde_json::to_vec(&req.execute(
&RootNode::new(graphql::Query, graphql::Mutation),
&Context {
connection: pool.get().unwrap(),
},
)).unwrap(),
))
.unwrap()
}
fn error_response(status: StatusCode) -> Response<Body> {
Response::builder()
.status(status)
.header("Content-Type", "text/plain; charset=utf-8")
.body(Body::from(status.canonical_reason().unwrap()))
.unwrap()
}
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment