(book) Update and fix book compilation and tests

* Use mdbook for building the book
* Update book config
* Update book hierarchy to work properly with mdbook
    This necessitated adding place-holder index pages
    since mdbook does not suppoert stand-alon menu items
* Update tests to use 2018 edition
* Fix various compilation errors in the tests
This commit is contained in:
Christoph Herzog 2019-01-11 17:16:21 +01:00
parent 49297afab0
commit 9623e4d326
40 changed files with 1288 additions and 564 deletions

View file

@ -10,3 +10,6 @@ members = [
"juniper_rocket",
"juniper_warp",
]
exclude = [
"docs/book/tests",
]

View file

@ -1,4 +1 @@
_book
_skeptic/target
node_modules
*.swp
_rendered

View file

@ -1,38 +0,0 @@
language: rust
rust:
- stable
- beta
- nightly
env:
global:
- secure: "vaPicq7s2hHBZwtG5eZ1wSmlIYog8FBZ7OilJs6cXQ0fyP5FqGFdc+VG+FSNbEDqPct/v5ojrfbwhQWZVjzgyMZ+Ikrpd9QD0T2Ie4f5yHh2schpphiog7pAfRG1A56/JsGq7aZr76DsICYUGeU4d8BzjaeFC2ozoo5tE9NXpp5ENLFNuErYGwMcQ0vlLTrK2miyuDn18HasHeT5pmxZT1qN5KjxzqChTvEFbH9pQsVKv+dVQiWVifYt4beOfSxaZJmCyBJHv2MjUOyWmYPtqikVxz4dkTbS/Cyx9dK3u2AgrH2Trrl0RFa5VKQUA+06v9NC+oH8NJj72aw44JdryVTchfQw3VF27H/2xfeg3WJX87/1J1oWvCBBtFWU5UwWapXq4Tz7UjT75H7unmlnc11hwmgMklpqMpD52om8n/GLMY2wkS5/dPJpLbYWt6OnBCPtHdP2EO59Wxg1YJ73PZdsrC81z3t8c4SSUXCmzUCG7P8UrSjpBl0g3yXTtR1/fvvSU1qQLFIDN8ib4tl8KGEgbX1ipJkkgCExriuZ58wOPqOdioqNMfWxyGszqxALsL1qxcET8ZtVOzIRCGuVptV0cUujxUwM9LJBqWq4MqPVO9+98FtX6xZvMM5gUM2dq4gWI45KK/VcNEkgihoSKUyVR2OaW5sTs6d28OejOXs="
before_install:
# Install node.
- curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
- sudo apt-get install -y nodejs
# Install yarn.
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
- sudo apt-get update && sudo apt-get install -y yarn
script:
- yarn
- yarn build
- yarn test
- touch _book/.nojekyll
branches:
only:
- source
deploy:
provider: pages
skip_cleanup: true
github_token: $DEPLOY_TOKEN
on:
branch: source
rust: stable
target_branch: master
local_dir: _book/

View file

@ -1,12 +1,48 @@
Work in progress with improved documentation and main website for Juniper.
# Juniper Book
## Requirements
- Node.js
- `yarn`
Book containing the Juniper documentation.
## Contributing
- Run `yarn build` to build your changes.
- Run `yarn serve` to view your changes.
- Run `yarn test` to test your changes.
### Requirements
The book is built with [mdBook](https://github.com/rust-lang-nursery/mdBook).
You can install it with:
```bash
cargo install mdbook
```
### Starting a local test server
To launch a local test server that continually re-builds the book and autoreloads the page, run:
```bash
mdbook serve
```
### Building the book
You can build the book to rendered HTML with this command:
```bash
mdbook build
```
The output will be in the `./_rendered` directory.
### Running the tests
To run the tests validating all code examples in the book, run:
```bash
cd ./tests
cargo test
```
## Test setup
All Rust code examples in the book are compiled on the CI.
This is done using the [skeptic](https://github.com/budziq/rust-skeptic) library.

View file

@ -1,11 +0,0 @@
{
"plugins": [
"anchors",
"codeblock-omit",
"codeblock-rust",
"sane-sidebar",
"-sharing"
],
"root": "./docs"
}

11
docs/book/book.toml Normal file
View file

@ -0,0 +1,11 @@
[book]
title = "Juniper - GraphQL Server for Rust"
description = "Documentation for juniper, a GraphQL server library for Rust."
src = "content"
[build]
build-dir = "_rendered"
create-missing = false
[output.html]
git_repository_url = "https://github.com/graphql-rs/juniper"

View file

@ -0,0 +1,31 @@
- [Introduction](README.md)
- [Quickstart](quickstart.md)
- [Type System](types/index.md)
- [Defining objects](types/objects/defining_objects.md)
- [Complex fields](types/objects/complex_fields.md)
- [Using contexts](types/objects/using_contexts.md)
- [Error handling](types/objects/error_handling.md)
- [Other types](types/other-index.md)
- [Enums](types/enums.md)
- [Interfaces](types/interfaces.md)
- [Input objects](types/input_objects.md)
- [Scalars](types/scalars.md)
- [Unions](types/unions.md)
- [Schemas and mutations](schema/schemas_and_mutations.md)
- [Adding A Server](servers/index.md)
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
- [Warp](servers/warp.md)
- [Rocket](servers/rocket.md)
- [Iron](servers/iron.md)
- [Hyper](servers/hyper.md)
- [Third Party Integrations](servers/third-party.md)
- [Advanced Topics](advanced/index.md)
- [Non-struct objects](advanced/non_struct_objects.md)
- [Objects and generics](advanced/objects_and_generics.md)
- [Multiple operations per request](advanced/multiple_ops_per_request.md)
# - [Context switching]
# - [Dynamic type system]

View file

@ -0,0 +1,7 @@
# Advanced Topics
The chapters below cover some more advanced scenarios.
- [Non-struct objects](advanced/non_struct_objects.md)
- [Objects and generics](advanced/objects_and_generics.md)
- [Multiple operations per request](advanced/multiple_ops_per_request.md)

View file

@ -9,11 +9,9 @@ Using `Result`-like enums can be a useful way of reporting e.g. validation
errors from a mutation:
```rust
#[macro_use] extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct ValidationError {
field: String,
message: String,
@ -25,7 +23,7 @@ enum SignUpResult {
Error(Vec<ValidationError>),
}
graphql_object!(SignUpResult: () |&self| {
juniper::graphql_object!(SignUpResult: () |&self| {
field user() -> Option<&User> {
match *self {
SignUpResult::Ok(ref user) => Some(user),

View file

@ -13,11 +13,10 @@ Let's make a slightly more compact but generic implementation of [the last
chapter](non_struct_objects.md):
```rust
#[macro_use] extern crate juniper;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(GraphQLObject)] struct ForumPost { title: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct ForumPost { title: String }
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct ValidationError {
field: String,
message: String,
@ -26,7 +25,7 @@ struct ValidationError {
# #[allow(dead_code)]
struct MutationResult<T>(Result<T, Vec<ValidationError>>);
graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
juniper::graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
field user() -> Option<&User> {
self.0.as_ref().ok()
}
@ -36,7 +35,7 @@ graphql_object!(MutationResult<User>: () as "UserResult" |&self| {
}
});
graphql_object!(MutationResult<ForumPost>: () as "ForumPostResult" |&self| {
juniper::graphql_object!(MutationResult<ForumPost>: () as "ForumPostResult" |&self| {
field forum_post() -> Option<&ForumPost> {
self.0.as_ref().ok()
}

View file

@ -12,7 +12,7 @@ for more detailed information.
```toml
[dependencies]
juniper = "0.10"
juniper = "0.11"
```
## Schema example
@ -27,11 +27,7 @@ types to a GraphQL schema. The most important one is the
[graphql_object!][jp_obj_macro] macro that is used for declaring an object with
resolvers, which you will use for the `Query` and `Mutation` roots.
!FILENAME main.rs
```rust
#[macro_use] extern crate juniper;
use juniper::{FieldResult};
# struct DatabasePool;
@ -41,14 +37,14 @@ use juniper::{FieldResult};
# fn insert_human(&self, human: &NewHuman) -> FieldResult<Human> { Err("")? }
# }
#[derive(GraphQLEnum)]
#[derive(juniper::GraphQLEnum)]
enum Episode {
NewHope,
Empire,
Jedi,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")]
struct Human {
id: String,
@ -59,7 +55,7 @@ struct Human {
// There is also a custom derive for mapping GraphQL input objects.
#[derive(GraphQLInputObject)]
#[derive(juniper::GraphQLInputObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")]
struct NewHuman {
name: String,
@ -82,7 +78,7 @@ impl juniper::Context for Context {}
struct Query;
graphql_object!(Query: Context |&self| {
juniper::graphql_object!(Query: Context |&self| {
field apiVersion() -> &str {
"1.0"
@ -105,7 +101,7 @@ graphql_object!(Query: Context |&self| {
struct Mutation;
graphql_object!(Mutation: Context |&self| {
juniper::graphql_object!(Mutation: Context |&self| {
field createHuman(&executor, new_human: NewHuman) -> FieldResult<Human> {
let db = executor.context().pool.get_connection()?;
@ -118,28 +114,25 @@ graphql_object!(Mutation: Context |&self| {
// Request queries can be executed against a RootNode.
type Schema = juniper::RootNode<'static, Query, Mutation>;
fn main() {
println!("Hello, world!");
}
# fn main() { }
```
We now have a very simple but functional schema for a GraphQL server!
To actually serve the schema, see the guides for our [Hyper], [Rocket],
[Warp], and [Iron] server integrations. Or you can invoke the executor directly:
To actually serve the schema, see the guides for our various [server integrations](./servers/index.md).
You can also invoke the executor directly to get a result for a query:
## Executor
You can invoke `juniper::execute` directly to run a GraphQL query:
!FILENAME main.rs
```rust
#[macro_use] extern crate juniper;
# // Only needed due to 2018 edition because the macro is not accessible.
# extern crate juniper;
use juniper::{FieldResult, Variables, EmptyMutation};
#[derive(GraphQLEnum, Clone, Copy)]
#[derive(juniper::GraphQLEnum, Clone, Copy)]
enum Episode {
NewHope,
Empire,
@ -148,7 +141,7 @@ enum Episode {
struct Query;
graphql_object!(Query: Ctx |&self| {
juniper::graphql_object!(Query: Ctx |&self| {
field favoriteEpisode(&executor) -> FieldResult<Episode> {
// Use the special &executor argument to fetch our fav episode.
Ok(executor.context().0)
@ -177,8 +170,10 @@ fn main() {
// Ensure the value matches.
assert_eq!(
res.as_object_value().unwrap().get_field_value("favoriteEpisode").unwrap().as_string_value().unwrap(),
"NEW_HOPE",
res,
graphql_value!({
"favoriteEpisode": "NEW_HONE",
})
);
}
```

View file

@ -23,13 +23,11 @@ The query root is just a GraphQL object. You define it like any other GraphQL
object in Juniper, most commonly using the `graphql_object!` macro:
```rust
# #[macro_use] extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
# use juniper::FieldResult;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
struct Root;
graphql_object!(Root: () |&self| {
juniper::graphql_object!(Root: () |&self| {
field userWithUsername(username: String) -> FieldResult<Option<User>> {
// Look up user in database...
# unimplemented!()
@ -45,13 +43,11 @@ Mutations are _also_ just GraphQL objects. Each mutation is a single field that
usually performs some mutating side-effect, such as updating a database.
```rust
# #[macro_use] extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
# use juniper::FieldResult;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
struct Mutations;
graphql_object!(Mutations: () |&self| {
juniper::graphql_object!(Mutations: () |&self| {
field signUpUser(name: String, email: String) -> FieldResult<User> {
// Validate inputs and save user in database...
# unimplemented!()

View file

@ -0,0 +1,17 @@
# Adding A Server
To allow using Juniper with the HTTP server of your choice,
it does **not** come with a built in HTTP server.
To actually get a server up and running, there are multiple official and
third-party integration crates that will get you there.
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
- [Warp](servers/warp.md)
- [Rocket](servers/rocket.md)
- [Iron](servers/iron.md)
- [Hyper](servers/hyper.md)
- [Third Party Integrations](servers/third-party.md)
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)

View file

@ -31,7 +31,7 @@ set up other global data that the schema might require.
In this example, we won't use any global data so we just return an empty value.
```rust,ignore
#[macro_use] extern crate juniper;
extern crate juniper;
extern crate juniper_iron;
extern crate iron;
extern crate mount;
@ -79,7 +79,7 @@ resolver, you need to pass this data using Juniper's [context
feature](context.md).
```rust,ignore
# #[macro_use] extern crate juniper;
# extern crate juniper;
# extern crate juniper_iron;
# extern crate iron;
# use iron::prelude::*;

View file

@ -0,0 +1,9 @@
# Official Server Integrations
Juniper provides official integration crates for several popular Rust server
libraries.
- [Hyper](./hyper.md)
- [Warp](./warp.md)
- [Rocket](./rocket.md)
- [Iron](./iron.md)

View file

@ -0,0 +1,8 @@
# Third-Party Integrations
There are several examples or third party integration crates that are not
officially maintained by Juniper developers.
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)

View file

@ -5,10 +5,7 @@ possible values. Simple Rust enums can be converted to GraphQL enums by using a
custom derive attribute:
```rust
#[macro_use] extern crate juniper_codegen;
extern crate juniper;
#[derive(GraphQLEnum)]
#[derive(juniper::GraphQLEnum)]
enum Episode {
NewHope,
Empire,
@ -24,10 +21,7 @@ you want to override this, you can use the `graphql` attribute, similar to how
it works when [defining objects](defining_objects.md):
```rust
# #[macro_use] extern crate juniper_codegen;
# extern crate juniper;
#[derive(GraphQLEnum)]
#[derive(juniper::GraphQLEnum)]
enum Episode {
#[graphql(name="NEW_HOPE")]
NewHope,
@ -44,10 +38,7 @@ Just like when defining objects, the type itself can be renamed and documented,
while individual enum variants can be renamed, documented, and deprecated:
```rust
# #[macro_use] extern crate juniper_codegen;
# extern crate juniper;
#[derive(GraphQLEnum)]
#[derive(juniper::GraphQLEnum)]
#[graphql(name="Episode", description="An episode of Star Wars")]
enum StarWarsEpisode {
#[graphql(deprecated="We don't really talk about this one")]

View file

@ -0,0 +1,20 @@
# Type System
Most of the work in working with juniper consists of mapping the
GraphQL type system to the Rust types your application uses.
Juniper provides some convenient abstractions that try to make this process
as painless as possible.
Find out more in the individual chapters below.
- [Defining objects](types/objects/defining_objects.md)
- [Complex fields](types/objects/complex_fields.md)
- [Using contexts](types/objects/using_contexts.md)
- [Error handling](types/objects/error_handling.md)
- [Other types](types/other-index.md)
- [Enums](types/enums.md)
- [Interfaces](types/interfaces.md)
- [Input objects](types/input_objects.md)
- [Scalars](types/scalars.md)
- [Unions](types/unions.md)

View file

@ -5,19 +5,16 @@ GraphQL fields. In Juniper, you can define input objects using a custom derive
attribute, similar to simple objects and enums:
```rust
#[macro_use] extern crate juniper_codegen;
#[macro_use] extern crate juniper;
#[derive(GraphQLInputObject)]
#[derive(juniper::GraphQLInputObject)]
struct Coordinate {
latitude: f64,
longitude: f64
}
struct Root;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
graphql_object!(Root: () |&self| {
juniper::graphql_object!(Root: () |&self| {
field users_at_location(coordinate: Coordinate, radius: f64) -> Vec<User> {
// Send coordinate to database
# unimplemented!()
@ -33,10 +30,7 @@ Just like the [other](defining_objects.md) [derives](enums.md), you can rename
and add documentation to both the type and the fields:
```rust
#[macro_use] extern crate juniper_codegen;
#[macro_use] extern crate juniper;
#[derive(GraphQLInputObject)]
#[derive(juniper::GraphQLInputObject)]
#[graphql(name="Coordinate", description="A position on the globe")]
struct WorldCoordinate {
#[graphql(name="lat", description="The latitude")]
@ -47,9 +41,9 @@ struct WorldCoordinate {
}
struct Root;
# #[derive(GraphQLObject)] struct User { name: String }
# #[derive(juniper::GraphQLObject)] struct User { name: String }
graphql_object!(Root: () |&self| {
juniper::graphql_object!(Root: () |&self| {
field users_at_location(coordinate: WorldCoordinate, radius: f64) -> Vec<User> {
// Send coordinate to database
# unimplemented!()

View file

@ -19,15 +19,13 @@ be done in a couple of different ways:
### Downcasting via accessor methods
```rust
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
@ -51,7 +49,7 @@ impl Character for Droid {
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
}
graphql_interface!(<'a> &'a Character: () as "Character" |&self| {
juniper::graphql_interface!(<'a> &'a Character: () as "Character" where Scalar = <S>|&self| {
field id() -> &str { self.id() }
instance_resolvers: |_| {
@ -78,19 +76,17 @@ If you can afford an extra database lookup when the concrete class is requested,
you can do away with the downcast methods and use the context instead. Here,
we'll use two hashmaps, but this could be two tables and some SQL calls instead:
FIXME: This example does not compile at the moment
```rust,ignore
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
```rust
# use std::collections::HashMap;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Droid {
id: String,
primary_function: String,
@ -115,7 +111,7 @@ impl Character for Droid {
fn id(&self) -> &str { self.id.as_str() }
}
graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
juniper::graphql_interface!(<'a> &'a Character: Database as "Character" where Scalar = <S> |&self| {
field id() -> &str { self.id() }
instance_resolvers: |&context| {
@ -134,19 +130,17 @@ This removes the need of downcast methods, but still requires some repetition.
Continuing on from the last example, the trait itself seems a bit unneccesary.
Maybe it can just be a struct containing the ID?
FIXME: This example does not compile at the moment
```rust,ignore
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
```rust
# use std::collections::HashMap;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Droid {
id: String,
primary_function: String,
@ -163,7 +157,7 @@ struct Character {
id: String,
}
graphql_interface!(Character: Database |&self| {
juniper::graphql_interface!(Character: Database where Scalar = <S> |&self| {
field id() -> &str { self.id.as_str() }
instance_resolvers: |&context| {
@ -185,15 +179,13 @@ placeholder objects. We don't need the extra database call in this case, so
we'll remove it.
```rust
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
@ -205,7 +197,7 @@ enum Character {
Droid(Droid),
}
graphql_interface!(Character: () |&self| {
juniper::graphql_interface!(Character: () where Scalar = <S> |&self| {
field id() -> &str {
match *self {
Character::Human(Human { ref id, .. }) |

View file

@ -8,14 +8,13 @@ example from the last chapter, this is how you would define `Person` using the
macro:
```rust
#[macro_use] extern crate juniper;
struct Person {
name: String,
age: i32,
}
graphql_object!(Person: () |&self| {
juniper::graphql_object!(Person: () |&self| {
field name() -> &str {
self.name.as_str()
}
@ -33,9 +32,7 @@ field resolver. With this syntax, fields can also take arguments:
```rust
# #[macro_use] extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
@ -45,7 +42,7 @@ struct House {
inhabitants: Vec<Person>,
}
graphql_object!(House: () |&self| {
juniper::graphql_object!(House: () |&self| {
// Creates the field inhabitantWithName(name), returning a nullable person
field inhabitant_with_name(name: String) -> Option<&Person> {
self.inhabitants.iter().find(|p| p.name == name)
@ -66,14 +63,12 @@ to `camelCase`. If you need to override the conversion, you can simply rename
the field. Also, the type name can be changed with an alias:
```rust
#[macro_use] extern crate juniper;
struct Person {
name: String,
website_url: String,
}
graphql_object!(Person: () as "PersonObject" |&self| {
juniper::graphql_object!(Person: () as "PersonObject" |&self| {
field name() -> &str {
self.name.as_str()
}

View file

@ -9,10 +9,7 @@ attribute. The other way is described in the [Complex fields](complex_fields.md)
chapter.
```rust
extern crate juniper;
#[macro_use] extern crate juniper_codegen;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
@ -34,10 +31,7 @@ descriptions:
!FILENAME GraphQL descriptions via Rust doc comments
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
/// Information about a person
struct Person {
/// The person's full name, including both first and last names
@ -55,10 +49,7 @@ via the `graphql` attribute. The following example is equivalent to the above:
!FILENAME GraphQL descriptions via attribute
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(description="Information about a person")]
struct Person {
#[graphql(description="The person's full name, including both first and last names")]
@ -75,10 +66,7 @@ doc comments. This enables internal Rust documentation and external GraphQL
documentation to differ:
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(description="This description shows up in GraphQL")]
/// This description shows up in RustDoc
struct Person {
@ -107,16 +95,13 @@ You can only use the custom derive attribute under these circumstances:
Let's see what that means for building relationships between objects:
```rust
extern crate juniper;
#[macro_use] extern crate juniper_codegen;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct House {
address: Option<String>, // Converted into String (nullable)
inhabitants: Vec<Person>, // Converted into [Person!]!
@ -135,10 +120,7 @@ By default, struct fields are converted from Rust's standard `snake_case` naming
convention into GraphQL's `camelCase` convention:
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
first_name: String, // Would be exposed as firstName in the GraphQL schema
last_name: String, // Exposed as lastName
@ -151,10 +133,7 @@ You can override the name by using the `graphql` attribute on individual struct
fields:
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
@ -171,10 +150,7 @@ To deprecate a field, you specify a deprecation reason using the `graphql`
attribute:
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
@ -194,14 +170,12 @@ only deprecate object fields and enum values.
By default all fields in a `GraphQLObject` are included in the generated GraphQL type. To prevent including a specific field, annotate the field with `#[graphql(skip)]`:
```rust
# extern crate juniper;
# #[macro_use] extern crate juniper_codegen;
#
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Person {
name: String,
age: i32,
#[graphql(skip)]
# #[allow(dead_code)]
password_hash: String, // This cannot be queried or modified from GraphQL
}

View file

@ -12,18 +12,20 @@ can use the `?` operator or the `try!` macro and things will generally just work
as you expect them to:
```rust
# #[macro_use] extern crate juniper;
# extern crate juniper;
use std::{
str,
path::PathBuf,
fs::{File},
io::{Read},
};
use juniper::FieldResult;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;
use std::str;
struct Example {
filename: PathBuf,
}
graphql_object!(Example: () |&self| {
juniper::graphql_object!(Example: () |&self| {
field contents() -> FieldResult<String> {
let mut file = File::open(&self.filename)?;
let mut contents = String::new();
@ -118,18 +120,16 @@ to clients. This can be accomplished by implementing [`IntoFieldError`](https://
```rust
# #[macro_use] extern crate juniper;
use juniper::{FieldError, IntoFieldError};
enum CustomError {
WhateverNotSet,
}
impl IntoFieldError for CustomError {
fn into_field_error(self) -> FieldError {
impl juniper::IntoFieldError for CustomError {
fn into_field_error(self) -> juniper::FieldError {
match self {
CustomError::WhateverNotSet => FieldError::new(
CustomError::WhateverNotSet => juniper::FieldError::new(
"Whatever does not exist",
graphql_value!({
juniper::graphql_value!({
"type": "NO_WHATEVER"
}),
),
@ -141,7 +141,7 @@ struct Example {
whatever: Option<bool>,
}
graphql_object!(Example: () |&self| {
juniper::graphql_object!(Example: () |&self| {
field whatever() -> Result<bool, CustomError> {
if let Some(value) = self.whatever {
return Ok(value);

View file

@ -36,7 +36,7 @@ current context object:
```rust
# use std::collections::HashMap;
#[macro_use] extern crate juniper;
extern crate juniper;
struct Database {
users: HashMap<i32, User>,
@ -52,7 +52,7 @@ struct User {
impl juniper::Context for Database {}
// 2. Assign Database as the context type for User
graphql_object!(User: Database |&self| {
juniper::graphql_object!(User: Database |&self| {
// 3. Use the special executor argument
field friends(&executor) -> Vec<&User> {
// 4. Use the executor to access the context object

View file

@ -0,0 +1,11 @@
# Other Types
The GraphQL type system provides several types in additon to objects.
Find out more about each type below:
- [Enums](./enums.md)
- [Interfaces](./interfaces.md)
- [Input objects](./input_objects.md)
- [Scalars](./scalars.md)
- [Unions](./unions.md)

View file

@ -13,23 +13,25 @@ In Juniper, you use the `graphql_scalar!` macro to create a custom scalar. In
this example, we're representing a user ID as a string wrapped in a custom type:
```rust
#[macro_use] extern crate juniper;
use juniper::Value;
struct UserID(String);
graphql_scalar!(UserID {
juniper::graphql_scalar!(UserID {
description: "An opaque identifier, represented as a string"
resolve(&self) -> Value {
Value::string(&self.0)
Value::scalar(self.0.clone())
}
from_input_value(v: &InputValue) -> Option<UserID> {
// If there's a parse error here, simply return None. Juniper will
// present an error to the client.
v.as_string_value().map(|s| UserID(s.to_owned()))
v.as_scalar_value::<String>().map(|s| UserID(s.to_owned()))
}
from_str<'a>(value: ScalarToken<'a>) -> juniper::ParseScalarResult<'a, juniper::DefaultScalarValue> {
<String as juniper::ParseScalarValue>::from_str(value)
}
});

View file

@ -16,15 +16,13 @@ similarities and the tradeoffs:
### Downcasting via accessor methods
```rust
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
@ -44,7 +42,7 @@ impl Character for Droid {
fn as_droid(&self) -> Option<&Droid> { Some(&self) }
}
graphql_union!(<'a> &'a Character: () as "Character" |&self| {
juniper::graphql_union!(<'a> &'a Character: () as "Character" where Scalar = <S> |&self| {
instance_resolvers: |_| {
// The left hand side indicates the concrete type T, the right hand
// side should be an expression returning Option<T>
@ -60,17 +58,17 @@ graphql_union!(<'a> &'a Character: () as "Character" |&self| {
FIXME: This example does not compile at the moment
```rust,ignore
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
```rust
# use std::collections::HashMap;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Droid {
id: String,
primary_function: String,
@ -95,7 +93,7 @@ impl Character for Droid {
fn id(&self) -> &str { self.id.as_str() }
}
graphql_union!(<'a> &'a Character: Database as "Character" |&self| {
juniper::graphql_union!(<'a> &'a Character: Database as "Character" where Scalar = <S> |&self| {
instance_resolvers: |&context| {
&Human => context.humans.get(self.id()),
&Droid => context.droids.get(self.id()),
@ -107,19 +105,17 @@ graphql_union!(<'a> &'a Character: Database as "Character" |&self| {
## Placeholder objects
FIXME: This example does not compile at the moment
```rust,ignore
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
```rust
# use std::collections::HashMap;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
#[graphql(Context = "Database")]
struct Droid {
id: String,
primary_function: String,
@ -136,7 +132,7 @@ struct Character {
id: String,
}
graphql_union!(Character: Database |&self| {
juniper::graphql_union!(Character: Database where Scalar = <S> |&self| {
instance_resolvers: |&context| {
&Human => context.humans.get(&self.id),
&Droid => context.droids.get(&self.id),
@ -149,15 +145,13 @@ graphql_union!(Character: Database |&self| {
## Enums
```rust
# #[macro_use] extern crate juniper_codegen;
# #[macro_use] extern crate juniper;
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Human {
id: String,
home_planet: String,
}
#[derive(GraphQLObject)]
#[derive(juniper::GraphQLObject)]
struct Droid {
id: String,
primary_function: String,
@ -169,7 +163,7 @@ enum Character {
Droid(Droid),
}
graphql_union!(Character: () |&self| {
juniper::graphql_union!(Character: () where Scalar = <S> |&self| {
instance_resolvers: |_| {
&Human => match *self { Character::Human(ref h) => Some(h), _ => None },
&Droid => match *self { Character::Droid(ref d) => Some(d), _ => None },

View file

@ -1,41 +0,0 @@
# Summary
- [Introduction](README.md)
- [Quickstart](quickstart.md)
## Type System
- [Defining objects](types/objects/defining_objects.md)
- [Complex fields](types/objects/complex_fields.md)
- [Using contexts](types/objects/using_contexts.md)
- [Error handling](types/objects/error_handling.md)
- Other types
- [Enums](types/enums.md)
- [Interfaces](types/interfaces.md)
- [Input objects](types/input_objects.md)
- [Scalars](types/scalars.md)
- [Unions](types/unions.md)
## Schema
- [Schemas and mutations](schema/schemas_and_mutations.md)
## Adding a server
- Integrations by Juniper
- [Hyper](servers/hyper.md)
- [Warp](servers/warp.md)
- [Rocket](servers/rocket.md)
- [Iron](servers/iron.md)
- Integrations by others
- [Actix-Web](https://github.com/actix/examples/tree/master/juniper)
- [Finchers](https://github.com/finchers-rs/finchers-juniper)
- [Tsukuyomi](https://github.com/tsukuyomi-rs/tsukuyomi/tree/master/examples/juniper)
## Advanced Topics
- [Non-struct objects](advanced/non_struct_objects.md)
- [Objects and generics](advanced/objects_and_generics.md)
- [Context switching]
- [Dynamic type system]
- [Multiple operations per request](advanced/multiple_ops_per_request.md)

View file

@ -1,17 +0,0 @@
{
"name": "graphql-rust.github.io",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "gitbook install && gitbook build",
"serve": "gitbook install && gitbook serve",
"test": "(cd _skeptic && cargo test)"
},
"dependencies": {
"gitbook-cli": "^2.3.2",
"gitbook-plugin-codeblock-omit": "^0.0.5",
"gitbook-plugin-codeblock-rust": "^0.0.4",
"gitbook-plugin-sane-sidebar": "^1.0.0"
}
}

View file

@ -1,12 +1,13 @@
[package]
name = "_skeptic"
name = "juniper_book_tests"
version = "0.1.0"
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
edition = "2018"
build = "build.rs"
[dependencies]
juniper = { git = "https://github.com/graphql-rust/juniper" }
juniper_iron = { git = "https://github.com/graphql-rust/juniper" }
juniper = { version = "0.11", path = "../../../juniper" }
juniper_iron = { version = "0.3", path = "../../../juniper_iron" }
iron = "^0.5.0"
mount = "^0.3.0"
@ -17,4 +18,4 @@ skeptic = "0.13"
skeptic = "0.13"
[patch.crates-io]
juniper_codegen = { git = "https://github.com/graphql-rust/juniper" }
juniper_codegen = { path = "../../../juniper_codegen" }

View file

@ -1,6 +1,6 @@
extern crate skeptic;
fn main() {
let files = skeptic::markdown_files_of_directory("../docs/");
let files = skeptic::markdown_files_of_directory("../content/types");
skeptic::generate_doc_tests(&files);
}

View file

@ -1 +1,3 @@
#![deny(warnings)]
include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs"));

File diff suppressed because it is too large Load diff