Skip to content

Commit 3404747

Browse files
committed
Implement GraphQL API
1 parent 391da84 commit 3404747

File tree

7 files changed

+354
-17
lines changed

7 files changed

+354
-17
lines changed

Cargo.lock

+116
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ diesel_migrations = "1"
1515
structopt = "0.3"
1616
env_logger = "0.7"
1717
chrono = {version = "0.4", features = ["serde"]}
18-
failure = "0.1"
18+
failure = "0.1"
19+
juniper = "0.14"
20+
wundergraph = {version = "0.1", features = ["postgres", "chrono"]}

src/graphql/mod.rs

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use crate::model::comments::NewComment;
2+
use crate::model::posts::PostState;
3+
use crate::model::users::NewUser;
4+
use crate::schema::*;
5+
use chrono::{DateTime, Utc};
6+
use diesel::pg::Pg;
7+
use diesel::prelude::*;
8+
use juniper::{ExecutionResult, Executor, GraphQLInputObject, Selection, Value};
9+
use wundergraph::prelude::*;
10+
use wundergraph::query_builder::mutations::{HandleBatchInsert, HandleInsert};
11+
use wundergraph::query_builder::selection::LoadingHandler;
12+
use wundergraph::scalar::WundergraphScalarValue;
13+
14+
#[derive(WundergraphEntity, Identifiable, Debug)]
15+
#[table_name = "users"]
16+
pub struct User {
17+
id: i32,
18+
name: String,
19+
joined_at: DateTime<Utc>,
20+
posts: HasMany<Post, posts::author>,
21+
comments: HasMany<Comment, comments::author>,
22+
}
23+
24+
#[derive(WundergraphEntity, Identifiable, Debug)]
25+
#[table_name = "posts"]
26+
pub struct Post {
27+
id: i32,
28+
title: String,
29+
content: Option<String>,
30+
published_at: DateTime<Utc>,
31+
author: HasOne<i32, User>,
32+
comments: HasMany<Comment, comments::post>,
33+
post_state: PostState,
34+
}
35+
36+
#[derive(WundergraphEntity, Identifiable, Debug)]
37+
#[table_name = "comments"]
38+
pub struct Comment {
39+
id: i32,
40+
comment: String,
41+
published_at: DateTime<Utc>,
42+
author: HasOne<i32, User>,
43+
post: HasOne<i32, Post>,
44+
}
45+
46+
wundergraph::query_object! {
47+
Query {
48+
User,
49+
Post,
50+
Comment,
51+
}
52+
}
53+
54+
#[derive(GraphQLInputObject, Identifiable, AsChangeset)]
55+
#[table_name = "users"]
56+
pub struct UserChangeset {
57+
id: i32,
58+
name: String,
59+
}
60+
61+
#[derive(GraphQLInputObject, Identifiable, AsChangeset)]
62+
#[table_name = "posts"]
63+
pub struct PostChangeset {
64+
id: i32,
65+
title: String,
66+
content: Option<String>,
67+
author: i32,
68+
post_state: PostState,
69+
}
70+
71+
#[derive(GraphQLInputObject, Identifiable, AsChangeset)]
72+
#[table_name = "comments"]
73+
pub struct CommentChangeset {
74+
id: i32,
75+
comment: String,
76+
author: i32,
77+
post: i32,
78+
}
79+
80+
#[derive(Debug, GraphQLInputObject)]
81+
pub struct NewPost {
82+
title: String,
83+
content: Option<String>,
84+
author: i32,
85+
}
86+
87+
impl HandleInsert<Post, NewPost, Pg, PgConnection> for posts::table {
88+
fn handle_insert(
89+
selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,
90+
executor: &Executor<'_, PgConnection, WundergraphScalarValue>,
91+
insertable: NewPost,
92+
) -> ExecutionResult<WundergraphScalarValue> {
93+
let ctx = executor.context();
94+
let conn = ctx.get_connection();
95+
conn.transaction(|| {
96+
let look_ahead = executor.look_ahead();
97+
let inserted = diesel::insert_into(posts::table)
98+
.values((
99+
posts::title.eq(insertable.title),
100+
posts::content.eq(insertable.content),
101+
posts::author.eq(insertable.author),
102+
posts::post_state.eq(PostState::Draft),
103+
))
104+
.returning(posts::id)
105+
.get_result::<i32>(conn)?;
106+
107+
let query = <Post as LoadingHandler<_, PgConnection>>::build_query(&[], &look_ahead)?
108+
.filter(posts::id.eq(inserted));
109+
let items = Post::load(&look_ahead, selection, executor, query)?;
110+
Ok(items.into_iter().next().unwrap_or(Value::Null))
111+
})
112+
}
113+
}
114+
115+
impl HandleBatchInsert<Post, NewPost, Pg, PgConnection> for posts::table {
116+
fn handle_batch_insert(
117+
selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>,
118+
executor: &Executor<'_, PgConnection, WundergraphScalarValue>,
119+
insertable: Vec<NewPost>,
120+
) -> ExecutionResult<WundergraphScalarValue> {
121+
let ctx = executor.context();
122+
let conn = ctx.get_connection();
123+
let insert = insertable
124+
.into_iter()
125+
.map(
126+
|NewPost {
127+
title,
128+
content,
129+
author,
130+
}| {
131+
(
132+
posts::title.eq(title),
133+
posts::content.eq(content),
134+
posts::author.eq(author),
135+
posts::post_state.eq(PostState::Draft),
136+
)
137+
},
138+
)
139+
.collect::<Vec<_>>();
140+
conn.transaction(|| {
141+
let look_ahead = executor.look_ahead();
142+
let inserted = diesel::insert_into(posts::table)
143+
.values(insert)
144+
.returning(posts::id)
145+
.get_results::<i32>(conn)?;
146+
147+
let query = <Post as LoadingHandler<_, PgConnection>>::build_query(&[], &look_ahead)?
148+
.filter(posts::id.eq_any(inserted));
149+
let items = Post::load(&look_ahead, selection, executor, query)?;
150+
Ok(Value::list(items))
151+
})
152+
}
153+
}
154+
155+
wundergraph::mutation_object! {
156+
Mutation {
157+
User(insert = NewUser, update = UserChangeset, delete = true),
158+
Post(insert = NewPost, update = PostChangeset, delete = true),
159+
Comment(insert = NewComment, update = CommentChangeset, delete = true),
160+
}
161+
}

0 commit comments

Comments
 (0)