Add GraphQL handlers and add GraphQL schema

Remove all REST and JSON related handlers and error catchers.
This commit is contained in:
Paul van Tilburg 2019-07-09 12:24:43 +02:00
parent 5e24738bba
commit 26c86fe03e
5 changed files with 129 additions and 45 deletions

View File

@ -12,12 +12,3 @@ pub fn not_found() -> JsonValue {
"reason": "Resource was not found",
})
}
/// Catches an HTTP 422 (Unprocessable Entity) error.
#[catch(422)]
pub fn unprocessable_entity() -> JsonValue {
json!({
"status": "error",
"reason": "Could not parse JSON body or fields were missing",
})
}

88
src/graphql.rs Normal file
View File

@ -0,0 +1,88 @@
use crate::models::{
CompanyInfo, Customer, Invoice, NewCustomer, NewInvoice, NewTimeEntry, TimeEntry,
};
use crate::DbConn;
use diesel::prelude::*;
use juniper::{object, Context, FieldResult, RootNode};
impl Context for DbConn {}
pub type Schema = RootNode<'static, Query, Mutation>;
/// The query root.
pub struct Query;
#[object(Context = DbConn)]
impl Query {
/// Returns the current API version.
fn api_version() -> &'static str {
"1.0"
}
/// Returns the company info with the given ID.
fn company_info(context: &DbConn, id: i32) -> FieldResult<CompanyInfo> {
retrieve!(CompanyInfo, id, **context).map_err(Into::into)
}
/// Returns all known customers.
fn customers(context: &DbConn) -> FieldResult<Vec<Customer>> {
all!(Customer, **context).map_err(Into::into)
}
/// Returns the customer with the given ID.
fn customer(context: &DbConn, id: i32) -> FieldResult<Customer> {
retrieve!(Customer, id, **context).map_err(Into::into)
}
/// Returns all known customers.
fn customers(context: &DbConn) -> FieldResult<Vec<Customer>> {
all!(Customer, **context).map_err(Into::into)
}
/// Returns the customer with the given ID.
fn invoice(context: &DbConn, id: i32) -> FieldResult<Invoice> {
retrieve!(Invoice, id, **context).map_err(Into::into)
}
/// Returns all known invoices.
fn invoices(context: &DbConn) -> FieldResult<Vec<Invoice>> {
all!(Invoice, **context).map_err(Into::into)
}
/// Returns the time entry with the given ID.
fn time_entry(context: &DbConn, id: i32) -> FieldResult<TimeEntry> {
retrieve!(TimeEntry, id, **context).map_err(Into::into)
}
/// Returns all known invoices.
fn time_entries(context: &DbConn) -> FieldResult<Vec<TimeEntry>> {
all!(TimeEntry, **context).map_err(Into::into)
}
}
/// The mutation root.
pub struct Mutation;
#[object(Context = DbConn)]
impl Mutation {
/// Returns the current API version.
fn api_version() -> &'static str {
"1.0"
}
/// Creates a new customer.
fn create_customer(context: &DbConn, new_customer: NewCustomer) -> FieldResult<Customer> {
create!(Customer, new_customer, **context).map_err(Into::into)
}
/// Creates a new invoice.
fn create_invoice(context: &DbConn, new_invoice: NewInvoice) -> FieldResult<Invoice> {
create!(Invoice, new_invoice, **context).map_err(Into::into)
}
/// Creates a new time entry.
fn create_time_entry(context: &DbConn, new_time_entry: NewTimeEntry) -> FieldResult<TimeEntry> {
create!(TimeEntry, new_time_entry, **context).map_err(Into::into)
}
}

View File

@ -4,6 +4,7 @@ use rocket::get;
pub mod company;
pub mod customers;
pub mod graphql;
pub mod invoices;
pub mod timeline;

30
src/handlers/graphql.rs Normal file
View File

@ -0,0 +1,30 @@
//! The GraphQL handlers
//!
//! Handlers for querying and mutating models via GraphQL.
use crate::graphql::Schema;
use crate::DbConn;
use juniper_rocket::{graphiql_source, playground_source, GraphQLRequest, GraphQLResponse};
use rocket::response::content::Html;
use rocket::{get, post, State};
#[get("/graphiql")]
pub fn graphiql() -> Html<String> {
graphiql_source("/graphql")
}
#[get("/playground")]
pub fn playground() -> Html<String> {
playground_source("/graphql")
}
#[get("/?<request>")]
pub fn get(request: GraphQLRequest, conn: DbConn, schema: State<Schema>) -> GraphQLResponse {
request.execute(&schema, &conn)
}
#[post("/", data = "<request>")]
pub fn post(request: GraphQLRequest, conn: DbConn, schema: State<Schema>) -> GraphQLResponse {
request.execute(&schema, &conn)
}

View File

@ -15,7 +15,10 @@ use rocket_contrib::serve::StaticFiles;
pub mod helpers;
pub mod catchers;
pub mod graphql;
pub mod handlers;
pub mod models;
pub mod schema;
// This macro from `diesel_migrations` defines an `embedded_migrations` module containing a
// function named `run`. This allows the example to be run and tested without any outside setup of
@ -45,48 +48,19 @@ fn rocket() -> Rocket {
rocket::ignite()
.attach(DbConn::fairing())
.attach(AdHoc::on_attach("Database Migrations", run_db_migrations))
.manage(graphql::Schema::new(graphql::Query, graphql::Mutation))
.mount("/", routes![handlers::index])
.mount(
"/company",
routes![handlers::company::index, handlers::company::create],
)
.mount(
"/customers",
"/graphql",
routes![
handlers::customers::index,
handlers::customers::create,
handlers::customers::new,
handlers::customers::show,
handlers::customers::update,
handlers::customers::destroy,
handlers::customers::invoices::create,
handlers::customers::invoices::new,
handlers::customers::invoices::show,
handlers::customers::invoices::update,
handlers::customers::tasks::create,
handlers::customers::tasks::new,
handlers::customers::tasks::show,
handlers::customers::tasks::update,
handlers::customers::tasks::destroy,
handlers::graphql::playground,
handlers::graphql::graphiql,
handlers::graphql::get,
handlers::graphql::post,
],
)
.mount("/invoices", routes![handlers::invoices::index])
.mount("/static", static_files)
.mount(
"/timeline",
routes![
handlers::timeline::index,
handlers::timeline::create,
handlers::timeline::new,
handlers::timeline::show,
handlers::timeline::update,
handlers::timeline::destroy,
],
)
.register(catchers![
catchers::not_found,
catchers::unprocessable_entity
])
.register(catchers![catchers::not_found])
}
/// Runs the Rocket application.