1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql::{ObjectType, SubscriptionType};
use async_graphql_warp::{GraphQLBadRequest, GraphQLResponse};
use dotenv::dotenv;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use std::convert::Infallible;
use std::str::FromStr;
use warp::{http::Response as HttpResponse, http::StatusCode, Filter, Rejection};
pub use anyhow;
pub use async_graphql::{
self, Context, EmptyMutation, EmptySubscription, Object, Result, Schema, SchemaBuilder,
SimpleObject,
};
pub use chrono;
pub use serde;
pub use serde_json;
pub use sqlx::{self, sqlite::SqlitePool};
pub use tokio;
pub use uuid;
pub struct SimpleGraphql<
Q: ObjectType + 'static,
M: ObjectType + 'static,
S: SubscriptionType + 'static,
> {
builder: SchemaBuilder<Q, M, S>,
sqlite: Option<SimpleSqlite>,
}
impl<Q: ObjectType + 'static, M: ObjectType + 'static, S: SubscriptionType + 'static>
SimpleGraphql<Q, M, S>
{
pub fn new(builder: SchemaBuilder<Q, M, S>) -> Self {
Self {
builder,
sqlite: None,
}
}
pub fn with_sqlite(self, sqlite: SimpleSqlite) -> Self {
Self {
sqlite: Some(sqlite),
..self
}
}
pub async fn run(self) {
dotenv().ok();
let schema = if let Some(sqlite) = self.sqlite {
self.builder.data(sqlite.pool()).finish()
} else {
self.builder.finish()
};
let graphql_post = warp::path!("graphql")
.and(async_graphql_warp::graphql(schema))
.and_then(
|(schema, request): (Schema<Q, M, S>, async_graphql::Request)| async move {
Ok::<_, Infallible>(GraphQLResponse::from(schema.execute(request).await))
},
);
let graphql_playground = warp::path!("playground").and(warp::get()).map(|| {
HttpResponse::builder()
.header("content-type", "text/html")
.body(playground_source(GraphQLPlaygroundConfig::new("/graphql")))
});
let routes = graphql_playground
.or(graphql_post)
.recover(|err: Rejection| async move {
if let Some(GraphQLBadRequest(err)) = err.find() {
return Ok::<_, Infallible>(warp::reply::with_status(
err.to_string(),
StatusCode::BAD_REQUEST,
));
}
Ok(warp::reply::with_status(
"INTERNAL_SERVER_ERROR".to_string(),
StatusCode::INTERNAL_SERVER_ERROR,
))
});
println!("Running at http://0.0.0.0:3030");
println!("\nRoutes:");
println!("\t/graphql");
println!("\t/playground");
warp::serve(routes).run(([0, 0, 0, 0], 3030)).await;
}
}
pub struct SimpleSqlite {
pool: SqlitePool,
}
impl SimpleSqlite {
pub fn new(filename: &str) -> Self {
let url = format!("sqlite://{}", filename);
let options = SqliteConnectOptions::from_str(&url)
.unwrap()
.create_if_missing(true);
let pool = SqlitePoolOptions::new().connect_lazy_with(options);
Self { pool }
}
pub fn pool(&self) -> SqlitePool {
self.pool.clone()
}
}