- 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 {
|
||||
session: Session,
|
||||
}
|
||||
impl juniper::Context for Context {}
|
||||
|
||||
struct Mutation;
|
||||
|
||||
#[juniper::graphql_object(Context=Context)]
|
||||
#[juniper::graphql_object(context = Context)]
|
||||
impl Mutation {
|
||||
fn patch_user(ctx: &Context, patch: UserPatchInput) -> FieldResult<bool> {
|
||||
ctx.session.patch_user(patch.into())?;
|
||||
|
|
|
@ -25,10 +25,6 @@ This example shows a subscription operation that returns two events, the strings
|
|||
sequentially:
|
||||
|
||||
```rust
|
||||
# extern crate futures;
|
||||
# extern crate juniper;
|
||||
# extern crate juniper_subscriptions;
|
||||
# extern crate tokio;
|
||||
# use juniper::{graphql_object, graphql_subscription, FieldError};
|
||||
# use futures::Stream;
|
||||
# use std::pin::Pin;
|
||||
|
@ -40,7 +36,7 @@ sequentially:
|
|||
# pub struct Query;
|
||||
# #[graphql_object(context = Database)]
|
||||
# impl Query {
|
||||
# fn hello_world() -> &str {
|
||||
# fn hello_world() -> &'static str {
|
||||
# "Hello World!"
|
||||
# }
|
||||
# }
|
||||
|
@ -110,7 +106,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati
|
|||
#
|
||||
# #[graphql_object(context = Database)]
|
||||
# impl Query {
|
||||
# fn hello_world() -> &str {
|
||||
# fn hello_world() -> &'static str {
|
||||
# "Hello World!"
|
||||
# }
|
||||
# }
|
||||
|
|
|
@ -6,11 +6,9 @@ Juniper follows a [code-first approach][schema_approach] to defining GraphQL sch
|
|||
|
||||
## Installation
|
||||
|
||||
!FILENAME Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
juniper = { git = "https://github.com/graphql-rust/juniper" }
|
||||
juniper = "0.15"
|
||||
```
|
||||
|
||||
## Schema example
|
||||
|
@ -89,7 +87,7 @@ struct Query;
|
|||
context = Context,
|
||||
)]
|
||||
impl Query {
|
||||
fn apiVersion() -> &str {
|
||||
fn apiVersion() -> &'static str {
|
||||
"1.0"
|
||||
}
|
||||
|
||||
|
@ -114,14 +112,13 @@ struct Mutation;
|
|||
|
||||
#[graphql_object(
|
||||
context = Context,
|
||||
|
||||
// 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.
|
||||
scalar = S,
|
||||
scalar = S: ScalarValue + Display,
|
||||
)]
|
||||
impl<S: ScalarValue + Display> Mutation {
|
||||
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
|
||||
impl Mutation {
|
||||
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 human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
|
||||
Ok(human)
|
||||
|
|
|
@ -232,6 +232,21 @@ trait Character {
|
|||
# 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
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
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:
|
||||
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
|
||||
can be specified in this `impl` block. If you want to define normal methods on the struct,
|
||||
you have to do so in a separate, normal `impl` block. Continuing with the
|
||||
fields in a Rust `impl` block for a type. Note, that GraphQL fields are defined in
|
||||
this `impl` block by default. If you want to define normal methods on the struct,
|
||||
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
|
||||
macro:
|
||||
|
||||
|
@ -28,12 +29,15 @@ impl Person {
|
|||
fn age(&self) -> i32 {
|
||||
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 {
|
||||
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
|
||||
field resolver. With this syntax, fields can also take arguments:
|
||||
|
||||
|
||||
```rust
|
||||
# extern crate juniper;
|
||||
# use juniper::{graphql_object, GraphQLObject};
|
||||
|
@ -61,7 +64,7 @@ struct House {
|
|||
|
||||
#[graphql_object]
|
||||
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> {
|
||||
self.inhabitants.iter().find(|p| p.name == name)
|
||||
}
|
||||
|
@ -127,15 +130,29 @@ impl Person {
|
|||
# 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
|
||||
|
||||
Method field arguments can also be customized.
|
||||
|
||||
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
|
||||
# extern crate juniper;
|
||||
# use juniper::graphql_object;
|
||||
|
@ -144,21 +161,22 @@ struct Person {}
|
|||
|
||||
#[graphql_object]
|
||||
impl Person {
|
||||
#[graphql(
|
||||
arguments(
|
||||
arg1(
|
||||
// Set a default value which will be injected if not present.
|
||||
// The default can be any valid Rust expression, including a function call, etc.
|
||||
default = true,
|
||||
// Set a description.
|
||||
description = "The first argument..."
|
||||
),
|
||||
arg2(
|
||||
default = 0,
|
||||
)
|
||||
)
|
||||
)]
|
||||
fn field1(&self, arg1: bool, arg2: i32) -> String {
|
||||
fn field1(
|
||||
&self,
|
||||
#[graphql(
|
||||
// Arguments can also be renamed if required.
|
||||
name = "arg",
|
||||
// Set a default value which will be injected if not present.
|
||||
// The default can be any valid Rust expression, including a function call, etc.
|
||||
default = true,
|
||||
// Set a description.
|
||||
description = "The first argument..."
|
||||
)]
|
||||
arg1: bool,
|
||||
// If default expression is not specified then `Default::default()` value is used.
|
||||
#[graphql(default)]
|
||||
arg2: i32,
|
||||
) -> String {
|
||||
format!("{} {}", arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
@ -166,13 +184,23 @@ impl Person {
|
|||
# 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
|
||||
|
||||
GraphQL fields expose more features than Rust's standard method syntax gives us:
|
||||
|
||||
* 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).
|
||||
These, and more features, are described more thoroughly in [the reference documentation](https://docs.rs/juniper/latest/juniper/attr.graphql_object.html).
|
||||
|
|
|
@ -152,7 +152,22 @@ struct Person {
|
|||
name: String,
|
||||
age: i32,
|
||||
#[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() {}
|
||||
|
@ -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
|
||||
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
|
||||
# extern crate juniper;
|
||||
|
@ -192,9 +207,9 @@ By default all fields in a `GraphQLObject` are included in the generated GraphQL
|
|||
struct Person {
|
||||
name: String,
|
||||
age: i32,
|
||||
#[graphql(skip)]
|
||||
#[graphql(ignore)]
|
||||
# #[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() {}
|
||||
|
|
|
@ -17,8 +17,7 @@ it will bubble up to the surrounding framework and hopefully be dealt with
|
|||
there.
|
||||
|
||||
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
|
||||
as you expect them to:
|
||||
can use the `?` operator and things will generally just work as you expect them to:
|
||||
|
||||
```rust
|
||||
# extern crate juniper;
|
||||
|
@ -36,7 +35,7 @@ struct Example {
|
|||
|
||||
#[graphql_object]
|
||||
impl Example {
|
||||
fn contents() -> FieldResult<String> {
|
||||
fn contents(&self) -> FieldResult<String> {
|
||||
let mut file = File::open(&self.filename)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
|
@ -44,13 +43,10 @@ impl Example {
|
|||
}
|
||||
|
||||
fn foo() -> FieldResult<Option<String>> {
|
||||
// Some invalid bytes.
|
||||
let invalid = vec![128, 223];
|
||||
// Some invalid bytes.
|
||||
let invalid = vec![128, 223];
|
||||
|
||||
match str::from_utf8(&invalid) {
|
||||
Ok(s) => Ok(Some(s.to_string())),
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
Ok(Some(str::from_utf8(&invalid)?.to_string()))
|
||||
}
|
||||
}
|
||||
#
|
||||
|
@ -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).
|
||||
|
||||
|
||||
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
|
||||
execution is resumed. For example, with the previous example and the following
|
||||
|
@ -86,12 +81,12 @@ returned:
|
|||
|
||||
!FILENAME Response for nullable field with error
|
||||
|
||||
```js
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"example": {
|
||||
contents: "<Contents of the file>",
|
||||
foo: null,
|
||||
foo: null
|
||||
}
|
||||
},
|
||||
"errors": [
|
||||
|
@ -120,7 +115,7 @@ following would be returned:
|
|||
|
||||
!FILENAME Response for non-null field with error and no nullable parent
|
||||
|
||||
```js
|
||||
```json
|
||||
{
|
||||
"errors": [
|
||||
"message": "Permission denied (os error 13)",
|
||||
|
@ -162,11 +157,11 @@ struct Example {
|
|||
|
||||
#[graphql_object]
|
||||
impl Example {
|
||||
fn whatever() -> Result<bool, CustomError> {
|
||||
if let Some(value) = self.whatever {
|
||||
return Ok(value);
|
||||
}
|
||||
Err(CustomError::WhateverNotSet)
|
||||
fn whatever(&self) -> Result<bool, CustomError> {
|
||||
if let Some(value) = self.whatever {
|
||||
return Ok(value);
|
||||
}
|
||||
Err(CustomError::WhateverNotSet)
|
||||
}
|
||||
}
|
||||
#
|
||||
|
|
|
@ -63,8 +63,8 @@ impl User {
|
|||
// with the context type.
|
||||
// Note:
|
||||
// - the type must be a reference
|
||||
// - the name of the argument SHOULD be context
|
||||
fn friends(&self, context: &Database) -> Vec<&User> {
|
||||
// - the name of the argument SHOULD be `context`
|
||||
fn friends<'db>(&self, context: &'db Database) -> Vec<&'db User> {
|
||||
|
||||
// 5. Use the database to lookup users
|
||||
self.friend_ids.iter()
|
||||
|
|
|
@ -11,7 +11,7 @@ use actix_web::{
|
|||
use juniper::{
|
||||
graphql_object, graphql_subscription,
|
||||
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_graphql_ws::ConnectionConfig;
|
||||
|
|
|
@ -5,8 +5,6 @@ edition = "2018"
|
|||
publish = false
|
||||
authors = ["Jordao Rosario <jordao.rosario01@gmail.com>"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct Query;
|
|||
|
||||
#[graphql_object(context = Database)]
|
||||
impl Query {
|
||||
fn hello_world() -> &str {
|
||||
fn hello_world() -> &'static str {
|
||||
"Hello World!"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ serde_json = "1.0"
|
|||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
warp = "0.3"
|
||||
async-stream = "0.3"
|
||||
|
||||
juniper = { path = "../../juniper" }
|
||||
juniper_graphql_ws = { path = "../../juniper_graphql_ws" }
|
||||
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 juniper::{
|
||||
graphql_object, graphql_subscription, DefaultScalarValue, EmptyMutation, FieldError,
|
||||
GraphQLEnum, RootNode,
|
||||
GraphQLEnum, RootNode, Value,
|
||||
};
|
||||
use juniper_graphql_ws::ConnectionConfig;
|
||||
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
|
||||
|
|
||||
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)
|
||||
|
||||
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.
|
||||
--> $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
|
|
@ -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.
|
||||
--> $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
|
|
@ -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.
|
||||
--> $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
|
|
@ -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)]
|
||||
struct Object {}
|
||||
use juniper::GraphQLObject;
|
||||
|
||||
#[derive(GraphQLObject)]
|
||||
struct Obj {}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
error: GraphQL object expects at least one field
|
||||
--> $DIR/derive_no_fields.rs:2:1
|
||||
error: GraphQL object must have at least one field
|
||||
--> $DIR/derive_no_fields.rs:4:1
|
||||
|
|
||||
2 | struct Object {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
4 | struct Obj {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= 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
|
||||
|
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
||||
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)
|
||||
|
|
|
@ -145,8 +145,7 @@ mod as_input_argument {
|
|||
input[0]
|
||||
}
|
||||
|
||||
#[graphql(arguments(input(default = [true, false, false])))]
|
||||
fn third(input: [bool; 3]) -> bool {
|
||||
fn third(#[graphql(default = [true, false, false])] input: [bool; 3]) -> bool {
|
||||
input[2]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ fn test_derived_enum() {
|
|||
let meta = SomeEnum::meta(&(), &mut registry);
|
||||
|
||||
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.
|
||||
assert_eq!(
|
||||
|
@ -102,24 +102,21 @@ fn test_derived_enum() {
|
|||
fn test_doc_comment() {
|
||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
let meta = DocEnum::meta(&(), &mut registry);
|
||||
assert_eq!(meta.description(), Some(&"Enum doc.".to_string()));
|
||||
assert_eq!(meta.description(), Some("Enum doc."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_doc_comment() {
|
||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
let meta = MultiDocEnum::meta(&(), &mut registry);
|
||||
assert_eq!(
|
||||
meta.description(),
|
||||
Some(&"Doc 1. Doc 2.\n\nDoc 4.".to_string())
|
||||
);
|
||||
assert_eq!(meta.description(), Some("Doc 1. Doc 2.\n\nDoc 4."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_comment_override() {
|
||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
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)
|
||||
|
|
|
@ -115,7 +115,7 @@ fn test_derived_input_object() {
|
|||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
let meta = Input::meta(&(), &mut registry);
|
||||
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.
|
||||
|
||||
|
@ -173,22 +173,19 @@ fn test_derived_input_object() {
|
|||
fn test_doc_comment() {
|
||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
let meta = DocComment::meta(&(), &mut registry);
|
||||
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
|
||||
assert_eq!(meta.description(), Some("Object comment."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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())
|
||||
);
|
||||
assert_eq!(meta.description(), Some("Doc 1. Doc 2.\n\nDoc 4."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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()));
|
||||
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_input_object;
|
||||
mod derive_object;
|
||||
mod derive_object_with_raw_idents;
|
||||
mod derive_scalar;
|
||||
mod impl_object;
|
||||
mod impl_scalar;
|
||||
mod interface_attr;
|
||||
mod object_attr;
|
||||
mod object_derive;
|
||||
mod scalar_value_transparent;
|
||||
mod subscription_attr;
|
||||
mod union_attr;
|
||||
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 meta = CustomUserId::meta(&(), &mut registry);
|
||||
assert_eq!(meta.name(), Some("MyUserId"));
|
||||
assert_eq!(
|
||||
meta.description(),
|
||||
Some(&"custom description...".to_string())
|
||||
);
|
||||
assert_eq!(meta.description(), Some("custom description..."));
|
||||
|
||||
let input: InputValue = serde_json::from_value(serde_json::json!("userId1")).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() {
|
||||
let mut registry: Registry = Registry::new(FnvHashMap::default());
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
use super::*;
|
||||
|
||||
|
@ -1124,8 +1363,8 @@ mod trivial_struct {
|
|||
#[derive(GraphQLUnion)]
|
||||
#[graphql(context = Database)]
|
||||
#[graphql(
|
||||
on Human = Character::as_human,
|
||||
on Droid = Character::as_droid,
|
||||
on Human = Character::as_human,
|
||||
on Droid = Character::as_droid,
|
||||
)]
|
||||
struct Character {
|
||||
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 {
|
||||
|
@ -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 {
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use juniper::*;
|
||||
use juniper::{
|
||||
graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject,
|
||||
InputValue, Nullable,
|
||||
};
|
||||
|
||||
pub struct Context;
|
||||
|
||||
|
@ -6,12 +9,12 @@ impl juniper::Context for Context {}
|
|||
|
||||
pub struct Query;
|
||||
|
||||
#[derive(juniper::GraphQLInputObject)]
|
||||
#[derive(GraphQLInputObject)]
|
||||
struct ObjectInput {
|
||||
field: Nullable<i32>,
|
||||
}
|
||||
|
||||
#[graphql_object(Context=Context)]
|
||||
#[graphql_object(context = Context)]
|
||||
impl Query {
|
||||
fn is_explicit_null(arg: Nullable<i32>) -> bool {
|
||||
arg.is_explicit_null()
|
||||
|
@ -26,40 +29,38 @@ type Schema = juniper::RootNode<'static, Query, EmptyMutation<Context>, EmptySub
|
|||
|
||||
#[tokio::test]
|
||||
async fn explicit_null() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"
|
||||
query Foo($emptyObj: ObjectInput!, $literalNullObj: ObjectInput!) {
|
||||
literalOneIsExplicitNull: isExplicitNull(arg: 1)
|
||||
literalNullIsExplicitNull: isExplicitNull(arg: null)
|
||||
noArgIsExplicitNull: isExplicitNull
|
||||
literalOneFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: 1})
|
||||
literalNullFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: null})
|
||||
noFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {})
|
||||
emptyVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $emptyObj)
|
||||
literalNullVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $literalNullObj)
|
||||
}
|
||||
query Foo($emptyObj: ObjectInput!, $literalNullObj: ObjectInput!) {
|
||||
literalOneIsExplicitNull: isExplicitNull(arg: 1)
|
||||
literalNullIsExplicitNull: isExplicitNull(arg: null)
|
||||
noArgIsExplicitNull: isExplicitNull
|
||||
literalOneFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: 1})
|
||||
literalNullFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {field: null})
|
||||
noFieldIsExplicitNull: objectFieldIsExplicitNull(obj: {})
|
||||
emptyVariableObjectFieldIsExplicitNull: objectFieldIsExplicitNull(obj: $emptyObj)
|
||||
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(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(
|
||||
Query,
|
||||
EmptyMutation::<Context>::new(),
|
||||
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,
|
||||
&schema,
|
||||
&vars.iter().cloned().collect(),
|
||||
&Context,
|
||||
)
|
||||
.await
|
||||
.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::{
|
||||
graphql_object, EmptyMutation, EmptySubscription, LookAheadMethods as _, RootNode, Variables,
|
||||
graphql_object, EmptyMutation, EmptySubscription, Executor, LookAheadMethods as _, RootNode,
|
||||
ScalarValue, Variables,
|
||||
};
|
||||
|
||||
pub struct Context;
|
||||
|
@ -12,14 +17,14 @@ pub struct Query;
|
|||
|
||||
#[graphql_object(context = Context)]
|
||||
impl Query {
|
||||
fn users(exec: &Executor) -> Vec<User> {
|
||||
let lh = exec.look_ahead();
|
||||
fn users<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<User> {
|
||||
let lh = executor.look_ahead();
|
||||
assert_eq!(lh.field_name(), "users");
|
||||
vec![User]
|
||||
}
|
||||
|
||||
fn countries(exec: &Executor) -> Vec<Country> {
|
||||
let lh = exec.look_ahead();
|
||||
fn countries<__S: ScalarValue>(executor: &Executor<'_, '_, Context, __S>) -> Vec<Country> {
|
||||
let lh = executor.look_ahead();
|
||||
assert_eq!(lh.field_name(), "countries");
|
||||
vec![Country]
|
||||
}
|
||||
|
@ -49,98 +54,54 @@ type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription
|
|||
|
||||
#[tokio::test]
|
||||
async fn users() {
|
||||
let ctx = Context;
|
||||
let query = "{ users { id } }";
|
||||
|
||||
let query = r#"{ users { id } }"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(
|
||||
Query,
|
||||
EmptyMutation::<Context>::new(),
|
||||
EmptySubscription::<Context>::new(),
|
||||
),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn countries() {
|
||||
let ctx = Context;
|
||||
let query = "{ countries { id } }";
|
||||
|
||||
let query = r#"{ countries { id } }"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
||||
&juniper::Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn both() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"
|
||||
{
|
||||
let query = "{
|
||||
countries { id }
|
||||
users { id }
|
||||
}
|
||||
"#;
|
||||
}";
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(
|
||||
Query,
|
||||
EmptyMutation::<Context>::new(),
|
||||
EmptySubscription::<Context>::new(),
|
||||
),
|
||||
&Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn both_in_different_order() {
|
||||
let ctx = Context;
|
||||
|
||||
let query = r#"
|
||||
{
|
||||
let query = "{
|
||||
users { id }
|
||||
countries { id }
|
||||
}
|
||||
"#;
|
||||
}";
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(
|
||||
Query,
|
||||
EmptyMutation::<Context>::new(),
|
||||
EmptySubscription::<Context>::new(),
|
||||
),
|
||||
&Variables::new(),
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &Context)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
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 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;
|
||||
|
||||
#[graphql_object]
|
||||
impl Query {
|
||||
fn users(executor: &Executor) -> Vec<User> {
|
||||
// This doesn't cause a panic
|
||||
fn users<S: ScalarValue>(executor: &Executor<'_, '_, (), S>) -> Vec<User> {
|
||||
// This doesn't cause a panic.
|
||||
executor.look_ahead();
|
||||
|
||||
vec![User {
|
||||
|
@ -22,7 +27,7 @@ struct User {
|
|||
|
||||
#[graphql_object]
|
||||
impl User {
|
||||
fn country(&self, executor: &Executor) -> &Country {
|
||||
fn country<S: ScalarValue>(&self, executor: &Executor<'_, '_, (), S>) -> &Country {
|
||||
// This panics!
|
||||
executor.look_ahead();
|
||||
|
||||
|
@ -44,7 +49,7 @@ impl Country {
|
|||
type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_lookahead_from_fragment_with_nested_type() {
|
||||
async fn lookahead_from_fragment_with_nested_type() {
|
||||
let _ = juniper::execute(
|
||||
r#"
|
||||
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;
|
||||
|
||||
|
@ -53,7 +58,7 @@ impl Query {
|
|||
type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fragments_in_interface() {
|
||||
async fn fragments_in_interface() {
|
||||
let query = r#"
|
||||
query Query {
|
||||
characters {
|
||||
|
@ -71,30 +76,19 @@ async fn test_fragments_in_interface() {
|
|||
}
|
||||
"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
||||
&Variables::new(),
|
||||
&(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(errors.len(), 0);
|
||||
|
||||
let (_, errors) = juniper::execute_sync(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
||||
&Variables::new(),
|
||||
&(),
|
||||
)
|
||||
.unwrap();
|
||||
let (_, errors) = juniper::execute_sync(query, None, &schema, &Variables::new(), &()).unwrap();
|
||||
assert_eq!(errors.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_inline_fragments_in_interface() {
|
||||
async fn inline_fragments_in_interface() {
|
||||
let query = r#"
|
||||
query Query {
|
||||
characters {
|
||||
|
@ -116,24 +110,13 @@ async fn test_inline_fragments_in_interface() {
|
|||
}
|
||||
"#;
|
||||
|
||||
let (_, errors) = juniper::execute(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
||||
&Variables::new(),
|
||||
&(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
|
||||
let (_, errors) = juniper::execute(query, None, &schema, &Variables::new(), &())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(errors.len(), 0);
|
||||
|
||||
let (_, errors) = juniper::execute_sync(
|
||||
query,
|
||||
None,
|
||||
&Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
|
||||
&Variables::new(),
|
||||
&(),
|
||||
)
|
||||
.unwrap();
|
||||
let (_, errors) = juniper::execute_sync(query, None, &schema, &Variables::new(), &()).unwrap();
|
||||
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