- preserve and reuse defined impl blocks in #[graphql_object] and #[graphql_subscription] macros expansion - allow renaming `ScalarValue` type parameter in expanded code via `scalar = S: ScalarValue` syntax Additionally: - rename `rename` attribute's argument to `rename_all` - support `rename_all` in #[graphql_interface] macro
This commit is contained in:
parent
39d1e43420
commit
a3fda7363d
165 changed files with 11581 additions and 8998 deletions
|
@ -99,10 +99,11 @@ impl Into<UserPatch> for UserPatchInput {
|
||||||
struct Context {
|
struct Context {
|
||||||
session: Session,
|
session: Session,
|
||||||
}
|
}
|
||||||
|
impl juniper::Context for Context {}
|
||||||
|
|
||||||
struct Mutation;
|
struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object(Context=Context)]
|
#[juniper::graphql_object(context = Context)]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn patch_user(ctx: &Context, patch: UserPatchInput) -> FieldResult<bool> {
|
fn patch_user(ctx: &Context, patch: UserPatchInput) -> FieldResult<bool> {
|
||||||
ctx.session.patch_user(patch.into())?;
|
ctx.session.patch_user(patch.into())?;
|
||||||
|
|
|
@ -25,10 +25,6 @@ This example shows a subscription operation that returns two events, the strings
|
||||||
sequentially:
|
sequentially:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate futures;
|
|
||||||
# extern crate juniper;
|
|
||||||
# extern crate juniper_subscriptions;
|
|
||||||
# extern crate tokio;
|
|
||||||
# use juniper::{graphql_object, graphql_subscription, FieldError};
|
# use juniper::{graphql_object, graphql_subscription, FieldError};
|
||||||
# use futures::Stream;
|
# use futures::Stream;
|
||||||
# use std::pin::Pin;
|
# use std::pin::Pin;
|
||||||
|
@ -40,7 +36,7 @@ sequentially:
|
||||||
# pub struct Query;
|
# pub struct Query;
|
||||||
# #[graphql_object(context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &'static str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
|
@ -110,7 +106,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati
|
||||||
#
|
#
|
||||||
# #[graphql_object(context = Database)]
|
# #[graphql_object(context = Database)]
|
||||||
# impl Query {
|
# impl Query {
|
||||||
# fn hello_world() -> &str {
|
# fn hello_world() -> &'static str {
|
||||||
# "Hello World!"
|
# "Hello World!"
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
|
|
|
@ -6,11 +6,9 @@ Juniper follows a [code-first approach][schema_approach] to defining GraphQL sch
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
!FILENAME Cargo.toml
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
juniper = { git = "https://github.com/graphql-rust/juniper" }
|
juniper = "0.15"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Schema example
|
## Schema example
|
||||||
|
@ -89,7 +87,7 @@ struct Query;
|
||||||
context = Context,
|
context = Context,
|
||||||
)]
|
)]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn apiVersion() -> &str {
|
fn apiVersion() -> &'static str {
|
||||||
"1.0"
|
"1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,14 +112,13 @@ struct Mutation;
|
||||||
|
|
||||||
#[graphql_object(
|
#[graphql_object(
|
||||||
context = Context,
|
context = Context,
|
||||||
|
|
||||||
// If we need to use `ScalarValue` parametrization explicitly somewhere
|
// If we need to use `ScalarValue` parametrization explicitly somewhere
|
||||||
// in the object definition (like here in `FieldResult`), we should
|
// in the object definition (like here in `FieldResult`), we could
|
||||||
// declare an explicit type parameter for that, and specify it.
|
// declare an explicit type parameter for that, and specify it.
|
||||||
scalar = S,
|
scalar = S: ScalarValue + Display,
|
||||||
)]
|
)]
|
||||||
impl<S: ScalarValue + Display> Mutation {
|
impl Mutation {
|
||||||
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
|
fn createHuman<S: ScalarValue + Display>(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
|
||||||
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
|
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
|
||||||
let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
|
let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
|
||||||
Ok(human)
|
Ok(human)
|
||||||
|
|
|
@ -232,6 +232,21 @@ trait Character {
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Renaming policies for all [GraphQL interface][1] fields and arguments are supported as well:
|
||||||
|
```rust
|
||||||
|
# #![allow(deprecated)]
|
||||||
|
# extern crate juniper;
|
||||||
|
use juniper::graphql_interface;
|
||||||
|
|
||||||
|
#[graphql_interface(rename_all = "none")] // disables any renaming
|
||||||
|
trait Character {
|
||||||
|
// Now exposed as `my_id` and `my_num` in the schema
|
||||||
|
fn my_id(&self, my_num: i32) -> &str;
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Custom context
|
### Custom context
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
If you've got a struct that can't be mapped directly to GraphQL, that contains
|
If you've got a struct that can't be mapped directly to GraphQL, that contains
|
||||||
computed fields or circular structures, you have to use a more powerful tool:
|
computed fields or circular structures, you have to use a more powerful tool:
|
||||||
the `#[graphql_object]` procedural macro. This macro lets you define GraphQL object
|
the `#[graphql_object]` procedural macro. This macro lets you define GraphQL object
|
||||||
fields in a Rust `impl` block for a type. Note that only GraphQL fields
|
fields in a Rust `impl` block for a type. Note, that GraphQL fields are defined in
|
||||||
can be specified in this `impl` block. If you want to define normal methods on the struct,
|
this `impl` block by default. If you want to define normal methods on the struct,
|
||||||
you have to do so in a separate, normal `impl` block. Continuing with the
|
you have to do so either in a separate "normal" `impl` block, or mark them with
|
||||||
|
`#[graphql(ignore)]` attribute to be omitted by the macro. Continuing with the
|
||||||
example from the last chapter, this is how you would define `Person` using the
|
example from the last chapter, this is how you would define `Person` using the
|
||||||
macro:
|
macro:
|
||||||
|
|
||||||
|
@ -28,12 +29,15 @@ impl Person {
|
||||||
fn age(&self) -> i32 {
|
fn age(&self) -> i32 {
|
||||||
self.age
|
self.age
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[graphql(ignore)]
|
||||||
|
pub fn hidden_from_graphql(&self) {
|
||||||
|
// [...]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that this syntax generates an implementation of the GraphQLType trait,
|
|
||||||
// the base impl of your struct can still be written like usual:
|
|
||||||
impl Person {
|
impl Person {
|
||||||
pub fn hidden_from_graphql(&self) {
|
pub fn hidden_from_graphql2(&self) {
|
||||||
// [...]
|
// [...]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +48,6 @@ impl Person {
|
||||||
While this is a bit more verbose, it lets you write any kind of function in the
|
While this is a bit more verbose, it lets you write any kind of function in the
|
||||||
field resolver. With this syntax, fields can also take arguments:
|
field resolver. With this syntax, fields can also take arguments:
|
||||||
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::{graphql_object, GraphQLObject};
|
# use juniper::{graphql_object, GraphQLObject};
|
||||||
|
@ -61,7 +64,7 @@ struct House {
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl House {
|
impl House {
|
||||||
// Creates the field inhabitantWithName(name), returning a nullable person
|
// Creates the field `inhabitantWithName(name)`, returning a nullable `Person`.
|
||||||
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
|
||||||
self.inhabitants.iter().find(|p| p.name == name)
|
self.inhabitants.iter().find(|p| p.name == name)
|
||||||
}
|
}
|
||||||
|
@ -127,15 +130,29 @@ impl Person {
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or provide a different renaming policy on a `impl` block for all its fields:
|
||||||
|
```rust
|
||||||
|
# extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
struct Person;
|
||||||
|
|
||||||
|
#[graphql_object(rename_all = "none")] // disables any renaming
|
||||||
|
impl Person {
|
||||||
|
// Now exposed as `renamed_field` in the schema
|
||||||
|
fn renamed_field() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
## Customizing arguments
|
## Customizing arguments
|
||||||
|
|
||||||
Method field arguments can also be customized.
|
Method field arguments can also be customized.
|
||||||
|
|
||||||
They can have custom descriptions and default values.
|
They can have custom descriptions and default values.
|
||||||
|
|
||||||
**Note**: The syntax for this is currently a little awkward.
|
|
||||||
This will become better once the [Rust RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
# use juniper::graphql_object;
|
# use juniper::graphql_object;
|
||||||
|
@ -144,21 +161,22 @@ struct Person {}
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Person {
|
impl Person {
|
||||||
#[graphql(
|
fn field1(
|
||||||
arguments(
|
&self,
|
||||||
arg1(
|
#[graphql(
|
||||||
// Set a default value which will be injected if not present.
|
// Arguments can also be renamed if required.
|
||||||
// The default can be any valid Rust expression, including a function call, etc.
|
name = "arg",
|
||||||
default = true,
|
// Set a default value which will be injected if not present.
|
||||||
// Set a description.
|
// The default can be any valid Rust expression, including a function call, etc.
|
||||||
description = "The first argument..."
|
default = true,
|
||||||
),
|
// Set a description.
|
||||||
arg2(
|
description = "The first argument..."
|
||||||
default = 0,
|
)]
|
||||||
)
|
arg1: bool,
|
||||||
)
|
// If default expression is not specified then `Default::default()` value is used.
|
||||||
)]
|
#[graphql(default)]
|
||||||
fn field1(&self, arg1: bool, arg2: i32) -> String {
|
arg2: i32,
|
||||||
|
) -> String {
|
||||||
format!("{} {}", arg1, arg2)
|
format!("{} {}", arg1, arg2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,13 +184,23 @@ impl Person {
|
||||||
# fn main() { }
|
# fn main() { }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Provide a different renaming policy on a `impl` block also implies for arguments:
|
||||||
|
```rust
|
||||||
|
# extern crate juniper;
|
||||||
|
# use juniper::graphql_object;
|
||||||
|
struct Person;
|
||||||
|
|
||||||
|
#[graphql_object(rename_all = "none")] // disables any renaming
|
||||||
|
impl Person {
|
||||||
|
// Now exposed as `my_arg` in the schema
|
||||||
|
fn field(my_arg: bool) -> bool {
|
||||||
|
my_arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
## More features
|
## More features
|
||||||
|
|
||||||
GraphQL fields expose more features than Rust's standard method syntax gives us:
|
These, and more features, are described more thoroughly in [the reference documentation](https://docs.rs/juniper/latest/juniper/attr.graphql_object.html).
|
||||||
|
|
||||||
* Per-field description and deprecation messages
|
|
||||||
* Per-argument default values
|
|
||||||
* Per-argument descriptions
|
|
||||||
|
|
||||||
These, and more features, are described more thoroughly in [the reference
|
|
||||||
documentation](https://docs.rs/juniper/latest/juniper/macro.object.html).
|
|
||||||
|
|
|
@ -152,7 +152,22 @@ struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(name = "websiteURL")]
|
#[graphql(name = "websiteURL")]
|
||||||
website_url: Option<String>, // Now exposed as websiteURL in the schema
|
website_url: Option<String>, // now exposed as `websiteURL` in the schema
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# fn main() {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or provide a different renaming policy on a struct for all its fields:
|
||||||
|
```rust
|
||||||
|
# extern crate juniper;
|
||||||
|
# use juniper::GraphQLObject;
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
#[graphql(rename_all = "none")] // disables any renaming
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: i32,
|
||||||
|
website_url: Option<String>, // now exposed as `website_url` in the schema
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
|
@ -181,9 +196,9 @@ The `name`, `description`, and `deprecation` arguments can of course be
|
||||||
combined. Some restrictions from the GraphQL spec still applies though; you can
|
combined. Some restrictions from the GraphQL spec still applies though; you can
|
||||||
only deprecate object fields and enum values.
|
only deprecate object fields and enum values.
|
||||||
|
|
||||||
## Skipping fields
|
## Ignoring fields
|
||||||
|
|
||||||
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)]`:
|
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(ignore)]`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
|
@ -192,9 +207,9 @@ By default all fields in a `GraphQLObject` are included in the generated GraphQL
|
||||||
struct Person {
|
struct Person {
|
||||||
name: String,
|
name: String,
|
||||||
age: i32,
|
age: i32,
|
||||||
#[graphql(skip)]
|
#[graphql(ignore)]
|
||||||
# #[allow(dead_code)]
|
# #[allow(dead_code)]
|
||||||
password_hash: String, // This cannot be queried or modified from GraphQL
|
password_hash: String, // cannot be queried or modified from GraphQL
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
# fn main() {}
|
# fn main() {}
|
||||||
|
|
|
@ -17,8 +17,7 @@ it will bubble up to the surrounding framework and hopefully be dealt with
|
||||||
there.
|
there.
|
||||||
|
|
||||||
For recoverable errors, Juniper works well with the built-in `Result` type, you
|
For recoverable errors, Juniper works well with the built-in `Result` type, you
|
||||||
can use the `?` operator or the `try!` macro and things will generally just work
|
can use the `?` operator and things will generally just work as you expect them to:
|
||||||
as you expect them to:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate juniper;
|
# extern crate juniper;
|
||||||
|
@ -36,7 +35,7 @@ struct Example {
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn contents() -> FieldResult<String> {
|
fn contents(&self) -> FieldResult<String> {
|
||||||
let mut file = File::open(&self.filename)?;
|
let mut file = File::open(&self.filename)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents)?;
|
file.read_to_string(&mut contents)?;
|
||||||
|
@ -44,13 +43,10 @@ impl Example {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn foo() -> FieldResult<Option<String>> {
|
fn foo() -> FieldResult<Option<String>> {
|
||||||
// Some invalid bytes.
|
// Some invalid bytes.
|
||||||
let invalid = vec![128, 223];
|
let invalid = vec![128, 223];
|
||||||
|
|
||||||
match str::from_utf8(&invalid) {
|
Ok(Some(str::from_utf8(&invalid)?.to_string()))
|
||||||
Ok(s) => Ok(Some(s.to_string())),
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
|
@ -66,7 +62,6 @@ there - those errors are automatically converted into `FieldError`.
|
||||||
|
|
||||||
Juniper's error behavior conforms to the [GraphQL specification](https://spec.graphql.org/June2018/#sec-Errors-and-Non-Nullability).
|
Juniper's error behavior conforms to the [GraphQL specification](https://spec.graphql.org/June2018/#sec-Errors-and-Non-Nullability).
|
||||||
|
|
||||||
|
|
||||||
When a field returns an error, the field's result is replaced by `null`, an
|
When a field returns an error, the field's result is replaced by `null`, an
|
||||||
additional `errors` object is created at the top level of the response, and the
|
additional `errors` object is created at the top level of the response, and the
|
||||||
execution is resumed. For example, with the previous example and the following
|
execution is resumed. For example, with the previous example and the following
|
||||||
|
@ -86,12 +81,12 @@ returned:
|
||||||
|
|
||||||
!FILENAME Response for nullable field with error
|
!FILENAME Response for nullable field with error
|
||||||
|
|
||||||
```js
|
```json
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"example": {
|
"example": {
|
||||||
contents: "<Contents of the file>",
|
contents: "<Contents of the file>",
|
||||||
foo: null,
|
foo: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"errors": [
|
"errors": [
|
||||||
|
@ -120,7 +115,7 @@ following would be returned:
|
||||||
|
|
||||||
!FILENAME Response for non-null field with error and no nullable parent
|
!FILENAME Response for non-null field with error and no nullable parent
|
||||||
|
|
||||||
```js
|
```json
|
||||||
{
|
{
|
||||||
"errors": [
|
"errors": [
|
||||||
"message": "Permission denied (os error 13)",
|
"message": "Permission denied (os error 13)",
|
||||||
|
@ -162,11 +157,11 @@ struct Example {
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Example {
|
impl Example {
|
||||||
fn whatever() -> Result<bool, CustomError> {
|
fn whatever(&self) -> Result<bool, CustomError> {
|
||||||
if let Some(value) = self.whatever {
|
if let Some(value) = self.whatever {
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
Err(CustomError::WhateverNotSet)
|
Err(CustomError::WhateverNotSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
|
|
|
@ -63,8 +63,8 @@ impl User {
|
||||||
// with the context type.
|
// with the context type.
|
||||||
// Note:
|
// Note:
|
||||||
// - the type must be a reference
|
// - the type must be a reference
|
||||||
// - the name of the argument SHOULD be context
|
// - the name of the argument SHOULD be `context`
|
||||||
fn friends(&self, context: &Database) -> Vec<&User> {
|
fn friends<'db>(&self, context: &'db Database) -> Vec<&'db User> {
|
||||||
|
|
||||||
// 5. Use the database to lookup users
|
// 5. Use the database to lookup users
|
||||||
self.friend_ids.iter()
|
self.friend_ids.iter()
|
||||||
|
|
|
@ -11,7 +11,7 @@ use actix_web::{
|
||||||
use juniper::{
|
use juniper::{
|
||||||
graphql_object, graphql_subscription,
|
graphql_object, graphql_subscription,
|
||||||
tests::fixtures::starwars::schema::{Character as _, Database, Query},
|
tests::fixtures::starwars::schema::{Character as _, Database, Query},
|
||||||
DefaultScalarValue, EmptyMutation, FieldError, RootNode,
|
DefaultScalarValue, EmptyMutation, FieldError, RootNode, Value,
|
||||||
};
|
};
|
||||||
use juniper_actix::{graphql_handler, playground_handler, subscriptions::subscriptions_handler};
|
use juniper_actix::{graphql_handler, playground_handler, subscriptions::subscriptions_handler};
|
||||||
use juniper_graphql_ws::ConnectionConfig;
|
use juniper_graphql_ws::ConnectionConfig;
|
||||||
|
|
|
@ -5,8 +5,6 @@ edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
authors = ["Jordao Rosario <jordao.rosario01@gmail.com>"]
|
authors = ["Jordao Rosario <jordao.rosario01@gmail.com>"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub struct Query;
|
||||||
|
|
||||||
#[graphql_object(context = Database)]
|
#[graphql_object(context = Database)]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn hello_world() -> &str {
|
fn hello_world() -> &'static str {
|
||||||
"Hello World!"
|
"Hello World!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ serde_json = "1.0"
|
||||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||||
warp = "0.3"
|
warp = "0.3"
|
||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
|
|
||||||
juniper = { path = "../../juniper" }
|
juniper = { path = "../../juniper" }
|
||||||
juniper_graphql_ws = { path = "../../juniper_graphql_ws" }
|
juniper_graphql_ws = { path = "../../juniper_graphql_ws" }
|
||||||
juniper_warp = { path = "../../juniper_warp", features = ["subscriptions"] }
|
juniper_warp = { path = "../../juniper_warp", features = ["subscriptions"] }
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{env, pin::Pin, sync::Arc, time::Duration};
|
||||||
use futures::{FutureExt as _, Stream};
|
use futures::{FutureExt as _, Stream};
|
||||||
use juniper::{
|
use juniper::{
|
||||||
graphql_object, graphql_subscription, DefaultScalarValue, EmptyMutation, FieldError,
|
graphql_object, graphql_subscription, DefaultScalarValue, EmptyMutation, FieldError,
|
||||||
GraphQLEnum, RootNode,
|
GraphQLEnum, RootNode, Value,
|
||||||
};
|
};
|
||||||
use juniper_graphql_ws::ConnectionConfig;
|
use juniper_graphql_ws::ConnectionConfig;
|
||||||
use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws};
|
use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
error[E0277]: the trait bound `ObjA: GraphQLObjectType<__S>` is not satisfied
|
error[E0277]: the trait bound `ObjA: GraphQLObject<__S>` is not satisfied
|
||||||
--> $DIR/implementer_non_object_type.rs:15:1
|
--> $DIR/implementer_non_object_type.rs:15:1
|
||||||
|
|
|
|
||||||
15 | #[graphql_interface(for = ObjA)]
|
15 | #[graphql_interface(for = ObjA)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GraphQLObjectType<__S>` is not implemented for `ObjA`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `ObjA`
|
||||||
|
|
|
|
||||||
= note: required by `juniper::marker::GraphQLObjectType::mark`
|
= note: required by `juniper::GraphQLObject::mark`
|
||||||
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0277]: the trait bound `ObjA: IsOutputType<__S>` is not satisfied
|
error[E0277]: the trait bound `ObjA: IsOutputType<__S>` is not satisfied
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct Obj;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Obj {
|
||||||
|
fn id(&self, __num: i32) -> &str {
|
||||||
|
"funA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -1,7 +1,7 @@
|
||||||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
--> $DIR/impl_no_argument_underscore.rs:5:29
|
--> $DIR/argument_double_underscored.rs:7:18
|
||||||
|
|
|
|
||||||
5 | #[graphql(arguments(arg(name = "__arg")))]
|
7 | fn id(&self, __num: i32) -> &str {
|
||||||
| ^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -0,0 +1,17 @@
|
||||||
|
use juniper::{graphql_object, GraphQLObject};
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct ObjA {
|
||||||
|
test: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjB;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl ObjB {
|
||||||
|
fn id(&self, obj: ObjA) -> &str {
|
||||||
|
"funA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,16 @@
|
||||||
|
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
|
||||||
|
--> $DIR/argument_non_input_type.rs:10:1
|
||||||
|
|
|
||||||
|
10 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||||
|
|
|
||||||
|
= note: required by `juniper::marker::IsInputType::mark`
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
|
||||||
|
--> $DIR/argument_non_input_type.rs:10:1
|
||||||
|
|
|
||||||
|
10 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,12 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl ObjA {
|
||||||
|
fn wrong(&self, #[graphql(default = [true, false, false])] input: [bool; 2]) -> bool {
|
||||||
|
input[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0277]: the trait bound `[bool; 2]: From<[bool; 3]>` is not satisfied
|
||||||
|
--> $DIR/argument_wrong_default_array.rs:5:1
|
||||||
|
|
|
||||||
|
5 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<&'a [ascii::ascii_char::AsciiChar] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a [u8] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a mut [ascii::ascii_char::AsciiChar] as From<&'a mut ascii::ascii_str::AsciiStr>>
|
||||||
|
= note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,12 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Character for ObjA {
|
||||||
|
fn __id(&self) -> &str {
|
||||||
|
"funA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: #[graphql_object] attribute is applicable to non-trait `impl` blocks only
|
||||||
|
--> $DIR/attr_field_double_underscored.rs:5:1
|
||||||
|
|
|
||||||
|
5 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,17 @@
|
||||||
|
use juniper::{graphql_object, GraphQLInputObject};
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
struct ObjB {
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl ObjA {
|
||||||
|
fn id(&self) -> ObjB {
|
||||||
|
ObjB { id: 34 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||||
|
--> $DIR/attr_field_non_output_return_type.rs:10:1
|
||||||
|
|
|
||||||
|
10 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||||
|
|
|
||||||
|
= note: required by `juniper::marker::IsOutputType::mark`
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,17 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl ObjA {
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
"funA"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(name = "id")]
|
||||||
|
fn id2(&self) -> &str {
|
||||||
|
"funB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: GraphQL object must have a different name for each field
|
||||||
|
--> $DIR/attr_fields_duplicate.rs:6:6
|
||||||
|
|
|
||||||
|
6 | impl ObjA {
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
@ -0,0 +1,12 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct __Obj;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl __Obj {
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
"funA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -1,7 +1,7 @@
|
||||||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
--> $DIR/impl_no_underscore.rs:5:15
|
--> $DIR/attr_name_double_underscored.rs:6:6
|
||||||
|
|
|
|
||||||
5 | #[graphql(name = "__test")]
|
6 | impl __Obj {
|
||||||
| ^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -0,0 +1,8 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
struct Obj;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Obj {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: GraphQL object must have at least one field
|
||||||
|
--> $DIR/attr_no_fields.rs:6:6
|
||||||
|
|
|
||||||
|
6 | impl Obj {}
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
@ -0,0 +1,6 @@
|
||||||
|
use juniper::graphql_object;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
enum Character {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: #[graphql_object] attribute is applicable to non-trait `impl` blocks only
|
||||||
|
--> $DIR/attr_wrong_item.rs:3:1
|
||||||
|
|
|
||||||
|
3 | #[graphql_object]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,8 @@
|
||||||
|
use juniper::GraphQLObject;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct Object {
|
||||||
|
__test: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -1,7 +1,7 @@
|
||||||
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
--> $DIR/derive_no_underscore.rs:3:15
|
--> $DIR/derive_field_double_underscored.rs:5:5
|
||||||
|
|
|
|
||||||
3 | #[graphql(name = "__test")]
|
5 | __test: String,
|
||||||
| ^^^^
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Schema
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -0,0 +1,13 @@
|
||||||
|
use juniper::{GraphQLInputObject, GraphQLObject};
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
struct ObjB {
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct ObjA {
|
||||||
|
id: ObjB,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||||
|
--> $DIR/derive_field_non_output_return_type.rs:8:10
|
||||||
|
|
|
||||||
|
8 | #[derive(GraphQLObject)]
|
||||||
|
| ^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||||
|
|
|
||||||
|
= note: required by `juniper::marker::IsOutputType::mark`
|
||||||
|
= note: this error originates in the derive macro `GraphQLObject` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,10 @@
|
||||||
|
use juniper::GraphQLObject;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct ObjA {
|
||||||
|
id: String,
|
||||||
|
#[graphql(name = "id")]
|
||||||
|
id2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,11 @@
|
||||||
|
error: GraphQL object must have a different name for each field
|
||||||
|
--> $DIR/derive_fields_duplicate.rs:4:1
|
||||||
|
|
|
||||||
|
4 | / struct ObjA {
|
||||||
|
5 | | id: String,
|
||||||
|
6 | | #[graphql(name = "id")]
|
||||||
|
7 | | id2: String,
|
||||||
|
8 | | }
|
||||||
|
| |_^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
@ -1,8 +0,0 @@
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct Object {
|
|
||||||
test: String,
|
|
||||||
#[graphql(name = "test")]
|
|
||||||
test2: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,9 +0,0 @@
|
||||||
error: GraphQL object does not allow fields with the same name
|
|
||||||
--> $DIR/derive_fields_unique.rs:4:5
|
|
||||||
|
|
|
||||||
4 | / #[graphql(name = "test")]
|
|
||||||
5 | | test2: String,
|
|
||||||
| |_________________^
|
|
||||||
|
|
|
||||||
= help: There is at least one other field with the same name `test`, possibly renamed via the #[graphql] attribute
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
|
@ -1,12 +0,0 @@
|
||||||
// FIXME: enable this if interfaces are supported
|
|
||||||
#[derive(juniper::GraphQLInputObject)]
|
|
||||||
struct ObjectA {
|
|
||||||
test: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct Object {
|
|
||||||
field: ObjectA,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
use juniper::GraphQLObject;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct __Obj {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
|
--> $DIR/derive_name_double_underscored.rs:4:8
|
||||||
|
|
|
||||||
|
4 | struct __Obj {
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -1,4 +1,6 @@
|
||||||
#[derive(juniper::GraphQLObject)]
|
use juniper::GraphQLObject;
|
||||||
struct Object {}
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct Obj {}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
error: GraphQL object expects at least one field
|
error: GraphQL object must have at least one field
|
||||||
--> $DIR/derive_no_fields.rs:2:1
|
--> $DIR/derive_no_fields.rs:4:1
|
||||||
|
|
|
|
||||||
2 | struct Object {}
|
4 | struct Obj {}
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct Object {
|
|
||||||
#[graphql(name = "__test")]
|
|
||||||
test: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
use juniper::GraphQLObject;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
enum Character {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
error: GraphQL object can only be derived for structs
|
||||||
|
--> $DIR/derive_wrong_item.rs:4:1
|
||||||
|
|
|
||||||
|
4 | enum Character {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
|
@ -1,15 +0,0 @@
|
||||||
#[derive(juniper::GraphQLObject)]
|
|
||||||
struct Obj {
|
|
||||||
field: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
fn test(&self, test: Obj) -> String {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,29 +0,0 @@
|
||||||
error[E0277]: the trait bound `Obj: IsInputType<__S>` is not satisfied
|
|
||||||
--> $DIR/impl_argument_no_object.rs:8:1
|
|
||||||
|
|
|
||||||
8 | #[juniper::graphql_object]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `Obj`
|
|
||||||
|
|
|
||||||
= note: required by `juniper::marker::IsInputType::mark`
|
|
||||||
= note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `Obj: FromInputValue<__S>` is not satisfied
|
|
||||||
--> $DIR/impl_argument_no_object.rs:8:1
|
|
||||||
|
|
|
||||||
8 | #[juniper::graphql_object]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `Obj`
|
|
||||||
|
|
|
||||||
= note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `Obj: FromInputValue` is not satisfied
|
|
||||||
--> $DIR/impl_argument_no_object.rs:8:1
|
|
||||||
|
|
|
||||||
8 | #[juniper::graphql_object]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj`
|
|
||||||
|
|
|
||||||
::: $WORKSPACE/juniper/src/ast.rs
|
|
||||||
|
|
|
||||||
| pub trait FromInputValue<S = DefaultScalarValue>: Sized {
|
|
||||||
| ------------------------------------------------------- required by this bound in `FromInputValue`
|
|
||||||
|
|
|
||||||
= note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
|
@ -1,11 +0,0 @@
|
||||||
struct Object;
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
#[graphql(arguments(input(default = [true, false, false])))]
|
|
||||||
fn wrong(input: [bool; 2]) -> bool {
|
|
||||||
input[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,7 +0,0 @@
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/impl_argument_wrong_default_array.rs:3:1
|
|
||||||
|
|
|
||||||
3 | #[juniper::graphql_object]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 2 elements, found one with 3 elements
|
|
||||||
|
|
|
||||||
= note: this error originates in the attribute macro `juniper::graphql_object` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
|
@ -1,14 +0,0 @@
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
fn test(&self) -> String {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(&self) -> String {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,10 +0,0 @@
|
||||||
error: GraphQL object does not allow fields with the same name
|
|
||||||
--> $DIR/impl_fields_unique.rs:9:5
|
|
||||||
|
|
|
||||||
9 | / fn test(&self) -> String {
|
|
||||||
10 | | String::new()
|
|
||||||
11 | | }
|
|
||||||
| |_____^
|
|
||||||
|
|
|
||||||
= help: There is at least one other field with the same name `test`, possibly renamed via the #[graphql] attribute
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
|
@ -1,19 +0,0 @@
|
||||||
// FIXME: enable this if interfaces are supported
|
|
||||||
#[derive(juniper::GraphQLInputObject)]
|
|
||||||
#[graphql(scalar = juniper::DefaultScalarValue)]
|
|
||||||
struct Obj {
|
|
||||||
field: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
fn test(&self) -> Obj {
|
|
||||||
Obj {
|
|
||||||
field: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,11 +0,0 @@
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
#[graphql(arguments(arg(name = "__arg")))]
|
|
||||||
fn test(&self, arg: String) -> String {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,6 +0,0 @@
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -1,7 +0,0 @@
|
||||||
error: GraphQL object expects at least one field
|
|
||||||
--> $DIR/impl_no_fields.rs:4:1
|
|
||||||
|
|
|
||||||
4 | impl Object {}
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
|
@ -1,11 +0,0 @@
|
||||||
struct Object {}
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Object {
|
|
||||||
#[graphql(name = "__test")]
|
|
||||||
fn test(&self) -> String {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct Obj;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl Obj {
|
||||||
|
async fn id(&self, __num: i32) -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funA")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
|
--> $DIR/argument_double_underscored.rs:11:24
|
||||||
|
|
|
||||||
|
11 | async fn id(&self, __num: i32) -> Stream<'static, &'static str> {
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -0,0 +1,22 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use futures::{future, stream};
|
||||||
|
use juniper::{graphql_subscription, GraphQLObject};
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct ObjA {
|
||||||
|
test: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjB;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl ObjB {
|
||||||
|
async fn id(&self, obj: ObjA) -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funA")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,16 @@
|
||||||
|
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
|
||||||
|
--> $DIR/argument_non_input_type.rs:15:1
|
||||||
|
|
|
||||||
|
15 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
||||||
|
|
|
||||||
|
= note: required by `juniper::marker::IsInputType::mark`
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
|
||||||
|
--> $DIR/argument_non_input_type.rs:15:1
|
||||||
|
|
|
||||||
|
15 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,20 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use futures::{future, stream};
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl ObjA {
|
||||||
|
async fn wrong(
|
||||||
|
&self,
|
||||||
|
#[graphql(default = [true, false, false])] input: [bool; 2],
|
||||||
|
) -> Stream<'static, bool> {
|
||||||
|
Box::pin(stream::once(future::ready(input[0])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0277]: the trait bound `[bool; 2]: From<[bool; 3]>` is not satisfied
|
||||||
|
--> $DIR/argument_wrong_default_array.rs:10:1
|
||||||
|
|
|
||||||
|
10 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<[bool; 3]>` is not implemented for `[bool; 2]`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<&'a [ascii::ascii_char::AsciiChar] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a [u8] as From<&'a ascii::ascii_str::AsciiStr>>
|
||||||
|
<&'a mut [ascii::ascii_char::AsciiChar] as From<&'a mut ascii::ascii_str::AsciiStr>>
|
||||||
|
= note: required because of the requirements on the impl of `Into<[bool; 2]>` for `[bool; 3]`
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,16 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl Character for ObjA {
|
||||||
|
async fn __id() -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funA")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: #[graphql_subscription] attribute is applicable to non-trait `impl` blocks only
|
||||||
|
--> $DIR/field_double_underscored.rs:9:1
|
||||||
|
|
|
||||||
|
9 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,22 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use futures::{future, stream};
|
||||||
|
use juniper::{graphql_subscription, GraphQLInputObject};
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
#[derive(GraphQLInputObject)]
|
||||||
|
struct ObjB {
|
||||||
|
id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl ObjA {
|
||||||
|
async fn id(&self) -> Stream<'static, ObjB> {
|
||||||
|
Box::pin(stream::once(future::ready(ObjB { id: 34 })))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
error[E0277]: the trait bound `ObjB: IsOutputType<__S>` is not satisfied
|
||||||
|
--> $DIR/field_non_output_return_type.rs:15:1
|
||||||
|
|
|
||||||
|
15 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsOutputType<__S>` is not implemented for `ObjB`
|
||||||
|
|
|
||||||
|
= note: required by `juniper::marker::IsOutputType::mark`
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,16 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl ObjA {
|
||||||
|
fn id(&self) -> Stream<'static, bool> {
|
||||||
|
Box::pin(stream::once(future::ready(true)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
error: GraphQL object synchronous resolvers are not supported
|
||||||
|
--> $DIR/field_not_async.rs:11:5
|
||||||
|
|
|
||||||
|
11 | fn id(&self) -> Stream<'static, bool> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
||||||
|
= note: Specify that this function is async: `async fn foo()`
|
|
@ -0,0 +1,21 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct ObjA;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl ObjA {
|
||||||
|
async fn id(&self) -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funA")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(name = "id")]
|
||||||
|
async fn id2(&self) -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funB")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: GraphQL object must have a different name for each field
|
||||||
|
--> $DIR/fields_duplicate.rs:10:6
|
||||||
|
|
|
||||||
|
10 | impl ObjA {
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
@ -0,0 +1,16 @@
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
type Stream<'a, I> = Pin<Box<dyn futures::Stream<Item = I> + Send + 'a>>;
|
||||||
|
|
||||||
|
struct __Obj;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl __Obj {
|
||||||
|
fn id(&self) -> Stream<'static, &'static str> {
|
||||||
|
Box::pin(stream::once(future::ready("funA")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
|
||||||
|
--> $DIR/name_double_underscored.rs:10:6
|
||||||
|
|
|
||||||
|
10 | impl __Obj {
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Schema
|
|
@ -0,0 +1,8 @@
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
struct Obj;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl Obj {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: GraphQL object must have at least one field
|
||||||
|
--> $DIR/no_fields.rs:6:6
|
||||||
|
|
|
||||||
|
6 | impl Obj {}
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= note: https://spec.graphql.org/June2018/#sec-Objects
|
|
@ -0,0 +1,6 @@
|
||||||
|
use juniper::graphql_subscription;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
enum Character {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
error: #[graphql_subscription] attribute is applicable to non-trait `impl` blocks only
|
||||||
|
--> $DIR/wrong_item.rs:3:1
|
||||||
|
|
|
||||||
|
3 | #[graphql_subscription]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `graphql_subscription` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -1,8 +1,8 @@
|
||||||
error[E0277]: the trait bound `Test: GraphQLObjectType<__S>` is not satisfied
|
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||||
--> $DIR/enum_non_object_variant.rs:9:10
|
--> $DIR/enum_non_object_variant.rs:9:10
|
||||||
|
|
|
|
||||||
9 | #[derive(GraphQLUnion)]
|
9 | #[derive(GraphQLUnion)]
|
||||||
| ^^^^^^^^^^^^ the trait `GraphQLObjectType<__S>` is not implemented for `Test`
|
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||||
|
|
|
|
||||||
= note: required by `juniper::marker::GraphQLObjectType::mark`
|
= note: required by `juniper::GraphQLObject::mark`
|
||||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error[E0277]: the trait bound `Test: GraphQLObjectType<__S>` is not satisfied
|
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||||
--> $DIR/struct_non_object_variant.rs:9:10
|
--> $DIR/struct_non_object_variant.rs:9:10
|
||||||
|
|
|
|
||||||
9 | #[derive(GraphQLUnion)]
|
9 | #[derive(GraphQLUnion)]
|
||||||
| ^^^^^^^^^^^^ the trait `GraphQLObjectType<__S>` is not implemented for `Test`
|
| ^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||||
|
|
|
|
||||||
= note: required by `juniper::marker::GraphQLObjectType::mark`
|
= note: required by `juniper::GraphQLObject::mark`
|
||||||
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `GraphQLUnion` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error[E0277]: the trait bound `Test: GraphQLObjectType<__S>` is not satisfied
|
error[E0277]: the trait bound `Test: GraphQLObject<__S>` is not satisfied
|
||||||
--> $DIR/trait_non_object_variant.rs:9:1
|
--> $DIR/trait_non_object_variant.rs:9:1
|
||||||
|
|
|
|
||||||
9 | #[graphql_union]
|
9 | #[graphql_union]
|
||||||
| ^^^^^^^^^^^^^^^^ the trait `GraphQLObjectType<__S>` is not implemented for `Test`
|
| ^^^^^^^^^^^^^^^^ the trait `GraphQLObject<__S>` is not implemented for `Test`
|
||||||
|
|
|
|
||||||
= note: required by `juniper::marker::GraphQLObjectType::mark`
|
= note: required by `juniper::GraphQLObject::mark`
|
||||||
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `graphql_union` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -145,8 +145,7 @@ mod as_input_argument {
|
||||||
input[0]
|
input[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql(arguments(input(default = [true, false, false])))]
|
fn third(#[graphql(default = [true, false, false])] input: [bool; 3]) -> bool {
|
||||||
fn third(input: [bool; 3]) -> bool {
|
|
||||||
input[2]
|
input[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ fn test_derived_enum() {
|
||||||
let meta = SomeEnum::meta(&(), &mut registry);
|
let meta = SomeEnum::meta(&(), &mut registry);
|
||||||
|
|
||||||
assert_eq!(meta.name(), Some("Some"));
|
assert_eq!(meta.name(), Some("Some"));
|
||||||
assert_eq!(meta.description(), Some(&"enum descr".to_string()));
|
assert_eq!(meta.description(), Some("enum descr"));
|
||||||
|
|
||||||
// Test no rename variant.
|
// Test no rename variant.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -102,24 +102,21 @@ fn test_derived_enum() {
|
||||||
fn test_doc_comment() {
|
fn test_doc_comment() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = DocEnum::meta(&(), &mut registry);
|
let meta = DocEnum::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.description(), Some(&"Enum doc.".to_string()));
|
assert_eq!(meta.description(), Some("Enum doc."));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multi_doc_comment() {
|
fn test_multi_doc_comment() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = MultiDocEnum::meta(&(), &mut registry);
|
let meta = MultiDocEnum::meta(&(), &mut registry);
|
||||||
assert_eq!(
|
assert_eq!(meta.description(), Some("Doc 1. Doc 2.\n\nDoc 4."));
|
||||||
meta.description(),
|
|
||||||
Some(&"Doc 1. Doc 2.\n\nDoc 4.".to_string())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_doc_comment_override() {
|
fn test_doc_comment_override() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = OverrideDocEnum::meta(&(), &mut registry);
|
let meta = OverrideDocEnum::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.description(), Some(&"enum override".to_string()));
|
assert_eq!(meta.description(), Some("enum override"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_context<T>(_t: T)
|
fn test_context<T>(_t: T)
|
||||||
|
|
|
@ -115,7 +115,7 @@ fn test_derived_input_object() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = Input::meta(&(), &mut registry);
|
let meta = Input::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.name(), Some("MyInput"));
|
assert_eq!(meta.name(), Some("MyInput"));
|
||||||
assert_eq!(meta.description(), Some(&"input descr".to_string()));
|
assert_eq!(meta.description(), Some("input descr"));
|
||||||
|
|
||||||
// Test default value injection.
|
// Test default value injection.
|
||||||
|
|
||||||
|
@ -173,22 +173,19 @@ fn test_derived_input_object() {
|
||||||
fn test_doc_comment() {
|
fn test_doc_comment() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = DocComment::meta(&(), &mut registry);
|
let meta = DocComment::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
|
assert_eq!(meta.description(), Some("Object comment."));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multi_doc_comment() {
|
fn test_multi_doc_comment() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = MultiDocComment::meta(&(), &mut registry);
|
let meta = MultiDocComment::meta(&(), &mut registry);
|
||||||
assert_eq!(
|
assert_eq!(meta.description(), Some("Doc 1. Doc 2.\n\nDoc 4."));
|
||||||
meta.description(),
|
|
||||||
Some(&"Doc 1. Doc 2.\n\nDoc 4.".to_string())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_doc_comment_override() {
|
fn test_doc_comment_override() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = OverrideDocComment::meta(&(), &mut registry);
|
let meta = OverrideDocComment::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.description(), Some(&"obj override".to_string()));
|
assert_eq!(meta.description(), Some("obj override"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,502 +0,0 @@
|
||||||
use fnv::FnvHashMap;
|
|
||||||
use juniper::{
|
|
||||||
execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLObject,
|
|
||||||
GraphQLType, Object, Registry, RootNode, Value, Variables,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
#[graphql(name = "MyObj", description = "obj descr")]
|
|
||||||
struct Obj {
|
|
||||||
regular_field: bool,
|
|
||||||
#[graphql(
|
|
||||||
name = "renamedField",
|
|
||||||
description = "descr",
|
|
||||||
deprecated = "field deprecation"
|
|
||||||
)]
|
|
||||||
c: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
struct Nested {
|
|
||||||
obj: Obj,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Object comment.
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
struct DocComment {
|
|
||||||
/// Field comment.
|
|
||||||
regular_field: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Doc 1.\
|
|
||||||
/// Doc 2.
|
|
||||||
///
|
|
||||||
/// Doc 4.
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
struct MultiDocComment {
|
|
||||||
/// Field 1.
|
|
||||||
/// Field 2.
|
|
||||||
regular_field: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is not used as the description.
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
#[graphql(description = "obj override")]
|
|
||||||
struct OverrideDocComment {
|
|
||||||
/// This is not used as the description.
|
|
||||||
#[graphql(description = "field override")]
|
|
||||||
regular_field: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
struct WithLifetime<'a> {
|
|
||||||
regular_field: &'a i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
struct SkippedFieldObj {
|
|
||||||
regular_field: bool,
|
|
||||||
#[graphql(skip)]
|
|
||||||
skipped: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
|
||||||
#[graphql(rename = "none")]
|
|
||||||
struct NoRenameObj {
|
|
||||||
one_field: bool,
|
|
||||||
another_field: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Context;
|
|
||||||
impl juniper::Context for Context {}
|
|
||||||
|
|
||||||
#[derive(GraphQLObject, Debug)]
|
|
||||||
#[graphql(context = Context)]
|
|
||||||
struct WithCustomContext {
|
|
||||||
a: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Query;
|
|
||||||
|
|
||||||
#[graphql_object]
|
|
||||||
impl Query {
|
|
||||||
fn obj() -> Obj {
|
|
||||||
Obj {
|
|
||||||
regular_field: true,
|
|
||||||
c: 22,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nested() -> Nested {
|
|
||||||
Nested {
|
|
||||||
obj: Obj {
|
|
||||||
regular_field: false,
|
|
||||||
c: 333,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn doc() -> DocComment {
|
|
||||||
DocComment {
|
|
||||||
regular_field: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn multi_doc() -> MultiDocComment {
|
|
||||||
MultiDocComment {
|
|
||||||
regular_field: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn override_doc() -> OverrideDocComment {
|
|
||||||
OverrideDocComment {
|
|
||||||
regular_field: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skipped_field_obj() -> SkippedFieldObj {
|
|
||||||
SkippedFieldObj {
|
|
||||||
regular_field: false,
|
|
||||||
skipped: 42,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_rename_obj() -> NoRenameObj {
|
|
||||||
NoRenameObj {
|
|
||||||
one_field: true,
|
|
||||||
another_field: 146,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NoRenameQuery;
|
|
||||||
|
|
||||||
#[graphql_object(rename = "none")]
|
|
||||||
impl NoRenameQuery {
|
|
||||||
fn obj() -> Obj {
|
|
||||||
Obj {
|
|
||||||
regular_field: false,
|
|
||||||
c: 22,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn no_rename_obj() -> NoRenameObj {
|
|
||||||
NoRenameObj {
|
|
||||||
one_field: true,
|
|
||||||
another_field: 146,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_doc_comment_simple() {
|
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
|
||||||
let meta = DocComment::meta(&(), &mut registry);
|
|
||||||
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
|
|
||||||
|
|
||||||
check_descriptions(
|
|
||||||
"DocComment",
|
|
||||||
&Value::scalar("Object comment."),
|
|
||||||
"regularField",
|
|
||||||
&Value::scalar("Field comment."),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_multi_doc_comment() {
|
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
|
||||||
let meta = MultiDocComment::meta(&(), &mut registry);
|
|
||||||
assert_eq!(
|
|
||||||
meta.description(),
|
|
||||||
Some(&"Doc 1. Doc 2.\n\nDoc 4.".to_string())
|
|
||||||
);
|
|
||||||
|
|
||||||
check_descriptions(
|
|
||||||
"MultiDocComment",
|
|
||||||
&Value::scalar("Doc 1. Doc 2.\n\nDoc 4."),
|
|
||||||
"regularField",
|
|
||||||
&Value::scalar("Field 1.\nField 2."),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_doc_comment_override() {
|
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
|
||||||
let meta = OverrideDocComment::meta(&(), &mut registry);
|
|
||||||
assert_eq!(meta.description(), Some(&"obj override".to_string()));
|
|
||||||
|
|
||||||
check_descriptions(
|
|
||||||
"OverrideDocComment",
|
|
||||||
&Value::scalar("obj override"),
|
|
||||||
"regularField",
|
|
||||||
&Value::scalar("field override"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_derived_object() {
|
|
||||||
assert_eq!(
|
|
||||||
<Obj as GraphQLType<DefaultScalarValue>>::name(&()),
|
|
||||||
Some("MyObj")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify meta info.
|
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
|
||||||
let meta = Obj::meta(&(), &mut registry);
|
|
||||||
|
|
||||||
assert_eq!(meta.name(), Some("MyObj"));
|
|
||||||
assert_eq!(meta.description(), Some(&"obj descr".to_string()));
|
|
||||||
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
obj {
|
|
||||||
regularField
|
|
||||||
renamedField
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &()).await,
|
|
||||||
Ok((
|
|
||||||
Value::object(
|
|
||||||
vec![(
|
|
||||||
"obj",
|
|
||||||
Value::object(
|
|
||||||
vec![
|
|
||||||
("regularField", Value::scalar(true)),
|
|
||||||
("renamedField", Value::scalar(22)),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
vec![]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[should_panic]
|
|
||||||
async fn test_cannot_query_skipped_field() {
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
skippedFieldObj {
|
|
||||||
skippedField
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_skipped_field_siblings_unaffected() {
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
skippedFieldObj {
|
|
||||||
regularField
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_derived_object_nested() {
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
nested {
|
|
||||||
obj {
|
|
||||||
regularField
|
|
||||||
renamedField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &()).await,
|
|
||||||
Ok((
|
|
||||||
Value::object(
|
|
||||||
vec![(
|
|
||||||
"nested",
|
|
||||||
Value::object(
|
|
||||||
vec![(
|
|
||||||
"obj",
|
|
||||||
Value::object(
|
|
||||||
vec![
|
|
||||||
("regularField", Value::scalar(false)),
|
|
||||||
("renamedField", Value::scalar(333)),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
vec![]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_no_rename_root() {
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
no_rename_obj {
|
|
||||||
one_field
|
|
||||||
another_field
|
|
||||||
}
|
|
||||||
|
|
||||||
obj {
|
|
||||||
regularField
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let schema = RootNode::new(
|
|
||||||
NoRenameQuery,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &()).await,
|
|
||||||
Ok((
|
|
||||||
Value::object(
|
|
||||||
vec![
|
|
||||||
(
|
|
||||||
"no_rename_obj",
|
|
||||||
Value::object(
|
|
||||||
vec![
|
|
||||||
("one_field", Value::scalar(true)),
|
|
||||||
("another_field", Value::scalar(146)),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"obj",
|
|
||||||
Value::object(
|
|
||||||
vec![("regularField", Value::scalar(false)),]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
vec![]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_no_rename_obj() {
|
|
||||||
let doc = r#"
|
|
||||||
{
|
|
||||||
noRenameObj {
|
|
||||||
one_field
|
|
||||||
another_field
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
execute(doc, None, &schema, &Variables::new(), &()).await,
|
|
||||||
Ok((
|
|
||||||
Value::object(
|
|
||||||
vec![(
|
|
||||||
"noRenameObj",
|
|
||||||
Value::object(
|
|
||||||
vec![
|
|
||||||
("one_field", Value::scalar(true)),
|
|
||||||
("another_field", Value::scalar(146)),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
vec![]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_descriptions(
|
|
||||||
object_name: &str,
|
|
||||||
object_description: &Value,
|
|
||||||
field_name: &str,
|
|
||||||
field_value: &Value,
|
|
||||||
) {
|
|
||||||
let doc = format!(
|
|
||||||
r#"
|
|
||||||
{{
|
|
||||||
__type(name: "{}") {{
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
fields {{
|
|
||||||
name
|
|
||||||
description
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
object_name
|
|
||||||
);
|
|
||||||
let _result = run_type_info_query(&doc, |(type_info, values)| {
|
|
||||||
assert_eq!(
|
|
||||||
type_info.get_field_value("name"),
|
|
||||||
Some(&Value::scalar(object_name))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
type_info.get_field_value("description"),
|
|
||||||
Some(object_description)
|
|
||||||
);
|
|
||||||
assert!(values.contains(&Value::object(
|
|
||||||
vec![
|
|
||||||
("name", Value::scalar(field_name)),
|
|
||||||
("description", field_value.clone()),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
)));
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_type_info_query<F>(doc: &str, f: F)
|
|
||||||
where
|
|
||||||
F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (),
|
|
||||||
{
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (result, errs) = execute(doc, None, &schema, &Variables::new(), &())
|
|
||||||
.await
|
|
||||||
.expect("Execution failed");
|
|
||||||
|
|
||||||
assert_eq!(errs, []);
|
|
||||||
|
|
||||||
println!("Result: {:#?}", result);
|
|
||||||
|
|
||||||
let type_info = result
|
|
||||||
.as_object_value()
|
|
||||||
.expect("Result is not an object")
|
|
||||||
.get_field_value("__type")
|
|
||||||
.expect("__type field missing")
|
|
||||||
.as_object_value()
|
|
||||||
.expect("__type field not an object value");
|
|
||||||
|
|
||||||
let fields = type_info
|
|
||||||
.get_field_value("fields")
|
|
||||||
.expect("fields field missing")
|
|
||||||
.as_list_value()
|
|
||||||
.expect("fields not a list");
|
|
||||||
|
|
||||||
f((type_info, fields));
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
use juniper::{
|
|
||||||
execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, Object,
|
|
||||||
RootNode, Value, Variables,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct MyObject;
|
|
||||||
|
|
||||||
#[graphql_object]
|
|
||||||
impl MyObject {
|
|
||||||
#[graphql(arguments(arg(name = "test")))]
|
|
||||||
fn test(&self, arg: String) -> String {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn check_argument_rename() {
|
|
||||||
let doc = format!(
|
|
||||||
r#"
|
|
||||||
{{
|
|
||||||
__type(name: "{}") {{
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
fields {{
|
|
||||||
name
|
|
||||||
description
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
"MyObject"
|
|
||||||
);
|
|
||||||
|
|
||||||
run_type_info_query(&doc, |(_, values)| {
|
|
||||||
assert_eq!(
|
|
||||||
*values,
|
|
||||||
vec![Value::object(
|
|
||||||
vec![
|
|
||||||
("name", Value::scalar("test")),
|
|
||||||
("description", Value::null()),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
)]
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_type_info_query<F>(doc: &str, f: F)
|
|
||||||
where
|
|
||||||
F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (),
|
|
||||||
{
|
|
||||||
let schema = RootNode::new(
|
|
||||||
MyObject,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (result, errs) = execute(doc, None, &schema, &Variables::new(), &())
|
|
||||||
.await
|
|
||||||
.expect("Execution failed");
|
|
||||||
|
|
||||||
assert_eq!(errs, []);
|
|
||||||
|
|
||||||
println!("Result: {:#?}", result);
|
|
||||||
|
|
||||||
let type_info = result
|
|
||||||
.as_object_value()
|
|
||||||
.expect("Result is not an object")
|
|
||||||
.get_field_value("__type")
|
|
||||||
.expect("__type field missing")
|
|
||||||
.as_object_value()
|
|
||||||
.expect("__type field not an object value");
|
|
||||||
|
|
||||||
let fields = type_info
|
|
||||||
.get_field_value("fields")
|
|
||||||
.expect("fields field missing")
|
|
||||||
.as_list_value()
|
|
||||||
.expect("fields not a list");
|
|
||||||
|
|
||||||
f((type_info, fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
mod fallible {
|
|
||||||
use juniper::{graphql_object, FieldError};
|
|
||||||
|
|
||||||
struct Obj;
|
|
||||||
|
|
||||||
#[graphql_object]
|
|
||||||
impl Obj {
|
|
||||||
fn test(&self, arg: String) -> Result<String, FieldError> {
|
|
||||||
Ok(arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod raw_argument {
|
|
||||||
use juniper::{
|
|
||||||
graphql_object, graphql_value, EmptyMutation, EmptySubscription, RootNode, Variables,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Obj;
|
|
||||||
|
|
||||||
#[graphql_object]
|
|
||||||
impl Obj {
|
|
||||||
#[graphql(arguments(r#arg(description = "The only argument")))]
|
|
||||||
fn test(&self, arg: String) -> String {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn named_correctly() {
|
|
||||||
let doc = r#"{
|
|
||||||
__type(name: "Obj") {
|
|
||||||
fields {
|
|
||||||
args {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
let schema = RootNode::new(
|
|
||||||
Obj,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
EmptySubscription::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
juniper::execute(&doc, None, &schema, &Variables::new(), &()).await,
|
|
||||||
Ok((
|
|
||||||
graphql_value!({"__type": {"fields": [{"args": [{"name": "arg"}]}]}}),
|
|
||||||
vec![],
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,12 @@
|
||||||
mod derive_enum;
|
mod derive_enum;
|
||||||
mod derive_input_object;
|
mod derive_input_object;
|
||||||
mod derive_object;
|
|
||||||
mod derive_object_with_raw_idents;
|
mod derive_object_with_raw_idents;
|
||||||
mod derive_scalar;
|
mod derive_scalar;
|
||||||
mod impl_object;
|
|
||||||
mod impl_scalar;
|
mod impl_scalar;
|
||||||
mod interface_attr;
|
mod interface_attr;
|
||||||
|
mod object_attr;
|
||||||
|
mod object_derive;
|
||||||
mod scalar_value_transparent;
|
mod scalar_value_transparent;
|
||||||
|
mod subscription_attr;
|
||||||
mod union_attr;
|
mod union_attr;
|
||||||
mod union_derive;
|
mod union_derive;
|
||||||
|
|
2124
integration_tests/juniper_tests/src/codegen/object_attr.rs
Normal file
2124
integration_tests/juniper_tests/src/codegen/object_attr.rs
Normal file
File diff suppressed because it is too large
Load diff
1007
integration_tests/juniper_tests/src/codegen/object_derive.rs
Normal file
1007
integration_tests/juniper_tests/src/codegen/object_derive.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -63,10 +63,7 @@ fn test_scalar_value_custom() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = CustomUserId::meta(&(), &mut registry);
|
let meta = CustomUserId::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.name(), Some("MyUserId"));
|
assert_eq!(meta.name(), Some("MyUserId"));
|
||||||
assert_eq!(
|
assert_eq!(meta.description(), Some("custom description..."));
|
||||||
meta.description(),
|
|
||||||
Some(&"custom description...".to_string())
|
|
||||||
);
|
|
||||||
|
|
||||||
let input: InputValue = serde_json::from_value(serde_json::json!("userId1")).unwrap();
|
let input: InputValue = serde_json::from_value(serde_json::json!("userId1")).unwrap();
|
||||||
let output: CustomUserId = FromInputValue::from_input_value(&input).unwrap();
|
let output: CustomUserId = FromInputValue::from_input_value(&input).unwrap();
|
||||||
|
@ -81,5 +78,5 @@ fn test_scalar_value_custom() {
|
||||||
fn test_scalar_value_doc_comment() {
|
fn test_scalar_value_doc_comment() {
|
||||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||||
let meta = IdWithDocComment::meta(&(), &mut registry);
|
let meta = IdWithDocComment::meta(&(), &mut registry);
|
||||||
assert_eq!(meta.description(), Some(&"The doc comment...".to_string()));
|
assert_eq!(meta.description(), Some("The doc comment..."));
|
||||||
}
|
}
|
||||||
|
|
1822
integration_tests/juniper_tests/src/codegen/subscription_attr.rs
Normal file
1822
integration_tests/juniper_tests/src/codegen/subscription_attr.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -660,6 +660,184 @@ mod custom_scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod explicit_generic_scalar {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[graphql_union(scalar = S)]
|
||||||
|
trait Character<S: ScalarValue> {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: ScalarValue> Character<S> for Human {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: ScalarValue> Character<S> for Droid {
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynCharacter<'a, S> = dyn Character<S> + Send + Sync + 'a;
|
||||||
|
|
||||||
|
enum QueryRoot {
|
||||||
|
Human,
|
||||||
|
Droid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl QueryRoot {
|
||||||
|
fn character<__S: ScalarValue>(&self) -> Box<DynCharacter<'_, __S>> {
|
||||||
|
let ch: Box<DynCharacter<'_, _>> = match self {
|
||||||
|
Self::Human => Box::new(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
Self::Droid => Box::new(Droid {
|
||||||
|
id: "droid-99".to_string(),
|
||||||
|
primary_function: "run".to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
character {
|
||||||
|
... on Human {
|
||||||
|
humanId: id
|
||||||
|
homePlanet
|
||||||
|
}
|
||||||
|
... on Droid {
|
||||||
|
droidId: id
|
||||||
|
primaryFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_human() {
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_droid() {
|
||||||
|
let schema = schema(QueryRoot::Droid);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod bounded_generic_scalar {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[graphql_union(scalar = S: ScalarValue + Clone)]
|
||||||
|
trait Character {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character for Human {
|
||||||
|
fn as_human(&self) -> Option<&Human> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Character for Droid {
|
||||||
|
fn as_droid(&self) -> Option<&Droid> {
|
||||||
|
Some(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynCharacter<'a> = dyn Character + Send + Sync + 'a;
|
||||||
|
|
||||||
|
enum QueryRoot {
|
||||||
|
Human,
|
||||||
|
Droid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl QueryRoot {
|
||||||
|
fn character(&self) -> Box<DynCharacter<'_>> {
|
||||||
|
let ch: Box<DynCharacter<'_>> = match self {
|
||||||
|
Self::Human => Box::new(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
Self::Droid => Box::new(Droid {
|
||||||
|
id: "droid-99".to_string(),
|
||||||
|
primary_function: "run".to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
character {
|
||||||
|
... on Human {
|
||||||
|
humanId: id
|
||||||
|
homePlanet
|
||||||
|
}
|
||||||
|
... on Droid {
|
||||||
|
droidId: id
|
||||||
|
primaryFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_human() {
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_droid() {
|
||||||
|
let schema = schema(QueryRoot::Droid);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod explicit_custom_context {
|
mod explicit_custom_context {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,101 @@ mod generic_enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: make it work
|
||||||
|
mod generic_lifetime_enum {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct LifetimeHuman<'id> {
|
||||||
|
id: &'id str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLObject)]
|
||||||
|
struct GenericDroid<B = ()> {
|
||||||
|
id: String,
|
||||||
|
#[graphql(ignore)]
|
||||||
|
_t: PhantomData<B>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(GraphQLUnion)]
|
||||||
|
enum Character<'id, B = ()> {
|
||||||
|
A(LifetimeHuman<'id>),
|
||||||
|
B(GenericDroid<B>),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum QueryRoot {
|
||||||
|
Human,
|
||||||
|
Droid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl QueryRoot {
|
||||||
|
fn character(&self) -> Character<'_> {
|
||||||
|
match self {
|
||||||
|
Self::Human => Character::A(LifetimeHuman { id: "human-32" }),
|
||||||
|
Self::Droid => Character::B(GenericDroid {
|
||||||
|
id: "droid-99".to_string(),
|
||||||
|
_t: PhantomData,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
character {
|
||||||
|
... on LifetimeHuman {
|
||||||
|
humanId: id
|
||||||
|
}
|
||||||
|
... on GenericDroid {
|
||||||
|
droidId: id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_human() {
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"humanId": "human-32"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_droid() {
|
||||||
|
let schema = schema(QueryRoot::Droid);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"droidId": "droid-99"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn uses_type_name_without_type_params() {
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
__type(name: "Character") {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
mod description_from_doc_comments {
|
mod description_from_doc_comments {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -571,6 +666,150 @@ mod custom_scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod explicit_generic_scalar {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(GraphQLUnion)]
|
||||||
|
#[graphql(scalar = S)]
|
||||||
|
enum Character<S: ScalarValue> {
|
||||||
|
A(Human),
|
||||||
|
B(Droid),
|
||||||
|
#[graphql(ignore)]
|
||||||
|
_P(PhantomData<S>),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum QueryRoot {
|
||||||
|
Human,
|
||||||
|
Droid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl QueryRoot {
|
||||||
|
fn character<__S: ScalarValue>(&self) -> Character<__S> {
|
||||||
|
match self {
|
||||||
|
Self::Human => Character::A(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
Self::Droid => Character::B(Droid {
|
||||||
|
id: "droid-99".to_string(),
|
||||||
|
primary_function: "run".to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
character {
|
||||||
|
... on Human {
|
||||||
|
humanId: id
|
||||||
|
homePlanet
|
||||||
|
}
|
||||||
|
... on Droid {
|
||||||
|
droidId: id
|
||||||
|
primaryFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_human() {
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_droid() {
|
||||||
|
let schema = schema(QueryRoot::Droid);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod bounded_generic_scalar {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(GraphQLUnion)]
|
||||||
|
#[graphql(scalar = S: ScalarValue + Clone)]
|
||||||
|
enum Character {
|
||||||
|
A(Human),
|
||||||
|
B(Droid),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum QueryRoot {
|
||||||
|
Human,
|
||||||
|
Droid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl QueryRoot {
|
||||||
|
fn character(&self) -> Character {
|
||||||
|
match self {
|
||||||
|
Self::Human => Character::A(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
Self::Droid => Character::B(Droid {
|
||||||
|
id: "droid-99".to_string(),
|
||||||
|
primary_function: "run".to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
character {
|
||||||
|
... on Human {
|
||||||
|
humanId: id
|
||||||
|
homePlanet
|
||||||
|
}
|
||||||
|
... on Droid {
|
||||||
|
droidId: id
|
||||||
|
primaryFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_human() {
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"humanId": "human-32", "homePlanet": "earth"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn resolves_droid() {
|
||||||
|
let schema = schema(QueryRoot::Droid);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &()).await,
|
||||||
|
Ok((
|
||||||
|
graphql_value!({"character": {"droidId": "droid-99", "primaryFunction": "run"}}),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod custom_context {
|
mod custom_context {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -1124,8 +1363,8 @@ mod trivial_struct {
|
||||||
#[derive(GraphQLUnion)]
|
#[derive(GraphQLUnion)]
|
||||||
#[graphql(context = Database)]
|
#[graphql(context = Database)]
|
||||||
#[graphql(
|
#[graphql(
|
||||||
on Human = Character::as_human,
|
on Human = Character::as_human,
|
||||||
on Droid = Character::as_droid,
|
on Droid = Character::as_droid,
|
||||||
)]
|
)]
|
||||||
struct Character {
|
struct Character {
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -1227,6 +1466,75 @@ mod trivial_struct {
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn is_graphql_union() {
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
__type(name: "Character") {
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
let db = Database {
|
||||||
|
human: Some(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
droid: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &db).await,
|
||||||
|
Ok((graphql_value!({"__type": {"kind": "UNION"}}), vec![])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn uses_type_name() {
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
__type(name: "Character") {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
let db = Database {
|
||||||
|
human: Some(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
droid: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &db).await,
|
||||||
|
Ok((graphql_value!({"__type": {"name": "Character"}}), vec![])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn has_no_description() {
|
||||||
|
const DOC: &str = r#"{
|
||||||
|
__type(name: "Character") {
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let schema = schema(QueryRoot::Human);
|
||||||
|
let db = Database {
|
||||||
|
human: Some(Human {
|
||||||
|
id: "human-32".to_string(),
|
||||||
|
home_planet: "earth".to_string(),
|
||||||
|
}),
|
||||||
|
droid: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
execute(DOC, None, &schema, &Variables::new(), &db).await,
|
||||||
|
Ok((graphql_value!({"__type": {"description": None}}), vec![])),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod generic_struct {
|
mod generic_struct {
|
||||||
|
@ -1473,6 +1781,8 @@ mod full_featured_struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks that union with boxed variants resolves okay.
|
||||||
|
/// See [#845](https://github.com/graphql-rust/juniper/issues/845) for details.
|
||||||
mod issue_845 {
|
mod issue_845 {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use juniper::*;
|
use juniper::{
|
||||||
|
graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject,
|
||||||
|
InputValue, Nullable,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Context;
|
pub struct Context;
|
||||||
|
|
||||||
|
@ -6,12 +9,12 @@ impl juniper::Context for Context {}
|
||||||
|
|
||||||
pub struct Query;
|
pub struct Query;
|
||||||
|
|
||||||
#[derive(juniper::GraphQLInputObject)]
|
#[derive(GraphQLInputObject)]
|
||||||
struct ObjectInput {
|
struct ObjectInput {
|
||||||
field: Nullable<i32>,
|
field: Nullable<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object(Context=Context)]
|
#[graphql_object(context = Context)]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn is_explicit_null(arg: Nullable<i32>) -> bool {
|
fn is_explicit_null(arg: Nullable<i32>) -> bool {
|
||||||
arg.is_explicit_null()
|
arg.is_explicit_null()
|
||||||
|
@ -26,40 +29,38 @@ type Schema = juniper::RootNode<'static, Query, EmptyMutation<Context>, EmptySub
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn explicit_null() {
|
async fn explicit_null() {
|
||||||
let ctx = Context;
|
|
||||||
|
|
||||||
let query = r#"
|
let query = r#"
|
||||||
query Foo($emptyObj: ObjectInput!, $literalNullObj: ObjectInput!) {
|
query Foo($emptyObj: ObjectInput!, $literalNullObj: ObjectInput!) {
|
||||||
literalOneIsExplicitNull: isExplicitNull(arg: 1)
|
literalOneIsExplicitNull: isExplicitNull(arg: 1)
|
||||||
literalNullIsExplicitNull: isExplicitNull(arg: null)
|
literalNullIsExplicitNull: isExplicitNull(arg: null)
|
||||||
noArgIsExplicitNull: isExplicitNull
|
noArgIsExplicitNull: isExplicitNull
|
||||||
literalOneFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: 1})
|
literalOneFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: 1})
|
||||||
literalNullFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: null})
|
literalNullFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: null})
|
||||||
noFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {})
|
noFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {})
|
||||||
emptyVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $emptyObj)
|
emptyVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $emptyObj)
|
||||||
literalNullVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $literalNullObj)
|
literalNullVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $literalNullObj)
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
let schema = &Schema::new(
|
||||||
|
Query,
|
||||||
|
EmptyMutation::<Context>::new(),
|
||||||
|
EmptySubscription::<Context>::new(),
|
||||||
|
);
|
||||||
|
let vars = [
|
||||||
|
("emptyObj".to_string(), InputValue::Object(vec![])),
|
||||||
|
(
|
||||||
|
"literalNullObj".to_string(),
|
||||||
|
InputValue::object(vec![("field", InputValue::null())].into_iter().collect()),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
let (data, errors) = juniper::execute(
|
let (data, errors) = juniper::execute(
|
||||||
query,
|
query,
|
||||||
None,
|
None,
|
||||||
&Schema::new(
|
&schema,
|
||||||
Query,
|
&vars.iter().cloned().collect(),
|
||||||
EmptyMutation::<Context>::new(),
|
&Context,
|
||||||
EmptySubscription::<Context>::new(),
|
|
||||||
),
|
|
||||||
&[
|
|
||||||
("emptyObj".to_string(), InputValue::Object(vec![])),
|
|
||||||
(
|
|
||||||
"literalNullObj".to_string(),
|
|
||||||
InputValue::object(vec![("field", InputValue::null())].into_iter().collect()),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect(),
|
|
||||||
&ctx,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
// Original author of this test is <https://github.com/davidpdrsn>.
|
//! Checks that `executor.look_ahead().field_name()` is correct in presence of
|
||||||
|
//! multiple query fields.
|
||||||
|
//! See [#371](https://github.com/graphql-rust/juniper/issues/371) for details.
|
||||||
|
//!
|
||||||
|
//! Original author of this test is [@davidpdrsn](https://github.com/davidpdrsn).
|
||||||
|
|
||||||
use juniper::{
|
use juniper::{
|
||||||
graphql_object, EmptyMutation, EmptySubscription, LookAheadMethods as _, RootNode, Variables,
|
graphql_object, EmptyMutation, EmptySubscription, Executor, LookAheadMethods as _, RootNode,
|
||||||
|
ScalarValue, Variables,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Context;
|
pub struct Context;
|
||||||
|
@ -12,14 +17,14 @@ pub struct Query;
|
||||||
|
|
||||||
#[graphql_object(context = Context)]
|
#[graphql_object(context = Context)]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn users(exec: &Executor) -> Vec<User> {
|
fn users<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<User> {
|
||||||
let lh = exec.look_ahead();
|
let lh = executor.look_ahead();
|
||||||
assert_eq!(lh.field_name(), "users");
|
assert_eq!(lh.field_name(), "users");
|
||||||
vec![User]
|
vec![User]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn countries(exec: &Executor) -> Vec<Country> {
|
fn countries<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<Country> {
|
||||||
let lh = exec.look_ahead();
|
let lh = executor.look_ahead();
|
||||||
assert_eq!(lh.field_name(), "countries");
|
assert_eq!(lh.field_name(), "countries");
|
||||||
vec![Country]
|
vec![Country]
|
||||||
}
|
}
|
||||||
|
@ -49,98 +54,54 @@ type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn users() {
|
async fn users() {
|
||||||
let ctx = Context;
|
let query = "{ users { id } }";
|
||||||
|
|
||||||
let query = r#"{ users { id } }"#;
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||||
let (_, errors) = juniper::execute(
|
.await
|
||||||
query,
|
.unwrap();
|
||||||
None,
|
|
||||||
&Schema::new(
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<Context>::new(),
|
|
||||||
EmptySubscription::<Context>::new(),
|
|
||||||
),
|
|
||||||
&juniper::Variables::new(),
|
|
||||||
&ctx,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn countries() {
|
async fn countries() {
|
||||||
let ctx = Context;
|
let query = "{ countries { id } }";
|
||||||
|
|
||||||
let query = r#"{ countries { id } }"#;
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||||
let (_, errors) = juniper::execute(
|
.await
|
||||||
query,
|
.unwrap();
|
||||||
None,
|
|
||||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
|
||||||
&juniper::Variables::new(),
|
|
||||||
&ctx,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn both() {
|
async fn both() {
|
||||||
let ctx = Context;
|
let query = "{
|
||||||
|
|
||||||
let query = r#"
|
|
||||||
{
|
|
||||||
countries { id }
|
countries { id }
|
||||||
users { id }
|
users { id }
|
||||||
}
|
}";
|
||||||
"#;
|
|
||||||
|
|
||||||
let (_, errors) = juniper::execute(
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
query,
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||||
None,
|
.await
|
||||||
&Schema::new(
|
.unwrap();
|
||||||
Query,
|
|
||||||
EmptyMutation::<Context>::new(),
|
|
||||||
EmptySubscription::<Context>::new(),
|
|
||||||
),
|
|
||||||
&Variables::new(),
|
|
||||||
&ctx,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn both_in_different_order() {
|
async fn both_in_different_order() {
|
||||||
let ctx = Context;
|
let query = "{
|
||||||
|
|
||||||
let query = r#"
|
|
||||||
{
|
|
||||||
users { id }
|
users { id }
|
||||||
countries { id }
|
countries { id }
|
||||||
}
|
}";
|
||||||
"#;
|
|
||||||
|
|
||||||
let (_, errors) = juniper::execute(
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
query,
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||||
None,
|
.await
|
||||||
&Schema::new(
|
.unwrap();
|
||||||
Query,
|
|
||||||
EmptyMutation::<Context>::new(),
|
|
||||||
EmptySubscription::<Context>::new(),
|
|
||||||
),
|
|
||||||
&Variables::new(),
|
|
||||||
&ctx,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! Checks that `__typename` field queries okay on root types.
|
||||||
|
//! See [#372](https://github.com/graphql-rust/juniper/issues/372) for details.
|
||||||
|
|
||||||
use futures::{stream, StreamExt as _};
|
use futures::{stream, StreamExt as _};
|
||||||
use juniper::{graphql_object, graphql_subscription, graphql_value, RootNode, Value, Variables};
|
use juniper::{graphql_object, graphql_subscription, graphql_value, RootNode, Value, Variables};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
// Original author of this test is <https://github.com/davidpdrsn>.
|
//! Checks that `executor.look_ahead()` on a fragment with nested type works okay.
|
||||||
|
//! See [#398](https://github.com/graphql-rust/juniper/issues/398) for details.
|
||||||
|
//!
|
||||||
|
//! Original author of this test is [@davidpdrsn](https://github.com/davidpdrsn).
|
||||||
|
|
||||||
use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode, Variables};
|
use juniper::{
|
||||||
|
graphql_object, EmptyMutation, EmptySubscription, Executor, RootNode, ScalarValue, Variables,
|
||||||
|
};
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Query {
|
impl Query {
|
||||||
fn users(executor: &Executor) -> Vec<User> {
|
fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> {
|
||||||
// This doesn't cause a panic
|
// This doesn't cause a panic.
|
||||||
executor.look_ahead();
|
executor.look_ahead();
|
||||||
|
|
||||||
vec![User {
|
vec![User {
|
||||||
|
@ -22,7 +27,7 @@ struct User {
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl User {
|
impl User {
|
||||||
fn country(&self, executor: &Executor) -> &Country {
|
fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country {
|
||||||
// This panics!
|
// This panics!
|
||||||
executor.look_ahead();
|
executor.look_ahead();
|
||||||
|
|
||||||
|
@ -44,7 +49,7 @@ impl Country {
|
||||||
type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
|
type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_lookahead_from_fragment_with_nested_type() {
|
async fn lookahead_from_fragment_with_nested_type() {
|
||||||
let _ = juniper::execute(
|
let _ = juniper::execute(
|
||||||
r#"
|
r#"
|
||||||
query Query {
|
query Query {
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use juniper::*;
|
//! Checks that using a fragment of an implementation in an interface works okay.
|
||||||
|
//! See [#407](https://github.com/graphql-rust/juniper/issues/407) for details.
|
||||||
|
|
||||||
|
use juniper::{
|
||||||
|
graphql_interface, graphql_object, EmptyMutation, EmptySubscription, GraphQLObject, Variables,
|
||||||
|
};
|
||||||
|
|
||||||
struct Query;
|
struct Query;
|
||||||
|
|
||||||
|
@ -53,7 +58,7 @@ impl Query {
|
||||||
type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_fragments_in_interface() {
|
async fn fragments_in_interface() {
|
||||||
let query = r#"
|
let query = r#"
|
||||||
query Query {
|
query Query {
|
||||||
characters {
|
characters {
|
||||||
|
@ -71,30 +76,19 @@ async fn test_fragments_in_interface() {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let (_, errors) = juniper::execute(
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
query,
|
|
||||||
None,
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
.await
|
||||||
&Variables::new(),
|
.unwrap();
|
||||||
&(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
|
|
||||||
let (_, errors) = juniper::execute_sync(
|
let (_, errors) = juniper::execute_sync(query, None, &schema, &Variables::new(), &()).unwrap();
|
||||||
query,
|
|
||||||
None,
|
|
||||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
|
||||||
&Variables::new(),
|
|
||||||
&(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_inline_fragments_in_interface() {
|
async fn inline_fragments_in_interface() {
|
||||||
let query = r#"
|
let query = r#"
|
||||||
query Query {
|
query Query {
|
||||||
characters {
|
characters {
|
||||||
|
@ -116,24 +110,13 @@ async fn test_inline_fragments_in_interface() {
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let (_, errors) = juniper::execute(
|
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||||
query,
|
|
||||||
None,
|
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
.await
|
||||||
&Variables::new(),
|
.unwrap();
|
||||||
&(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
|
|
||||||
let (_, errors) = juniper::execute_sync(
|
let (_, errors) = juniper::execute_sync(query, None, &schema, &Variables::new(), &()).unwrap();
|
||||||
query,
|
|
||||||
None,
|
|
||||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
|
||||||
&Variables::new(),
|
|
||||||
&(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(errors.len(), 0);
|
assert_eq!(errors.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue