Deprecate graphql_object! and replace with impl_object
This commit deprecates the graphql_object macro and replaces all of it's uses with the new impl_object proc macro. (Except for the old macro tests). This commit also adds new integration tests for impl_object.
This commit is contained in:
parent
758f3f7d40
commit
a993c16b85
27 changed files with 1218 additions and 526 deletions
|
@ -1,10 +1,27 @@
|
|||
# master
|
||||
|
||||
- Refactored all crates to the 2018 edition. [#345](https://github.com/graphql-rust/juniper/pull/345)
|
||||
### impl_object macro
|
||||
|
||||
The `graphql_object!` macro is deprecated and will be removed in the future.
|
||||
It is replaced by the new [impl_object](https://docs.rs/juniper/latest/juniper/macro.impl_object.html) procedural macro.
|
||||
|
||||
[#333](https://github.com/graphql-rust/juniper/pull/333)
|
||||
|
||||
### 2018 Edition
|
||||
|
||||
All crates were refactored to the Rust 2018 edition.
|
||||
|
||||
This should not have any impact on your code, since juniper already was 2018 compatible.
|
||||
|
||||
[#345](https://github.com/graphql-rust/juniper/pull/345)
|
||||
|
||||
### Other changes
|
||||
|
||||
- The minimum required Rust version is now `1.31.0`.
|
||||
- The `ScalarValue` custom derive has been renamed to `GraphQLScalarValue`.
|
||||
- Added built-in support for the canonical schema introspection query via
|
||||
`juniper::introspect()`. [#307](https://github.com/graphql-rust/juniper/issues/307)
|
||||
`juniper::introspect()`.
|
||||
[#307](https://github.com/graphql-rust/juniper/issues/307)
|
||||
- Fix introspection query validity
|
||||
The DirectiveLocation::InlineFragment had an invalid literal value,
|
||||
which broke third party tools like apollo cli.
|
||||
|
|
|
@ -23,7 +23,6 @@ harness = false
|
|||
path = "benches/bench.rs"
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
expose-test-schema = []
|
||||
default = [
|
||||
"chrono",
|
||||
|
|
|
@ -5,15 +5,16 @@ use crate::value::{DefaultScalarValue, Object, Value};
|
|||
|
||||
struct TestType;
|
||||
|
||||
graphql_object!(TestType: () |&self| {
|
||||
field a() -> &str {
|
||||
#[crate::impl_object_internal]
|
||||
impl TestType {
|
||||
fn a() -> &str {
|
||||
"a"
|
||||
}
|
||||
|
||||
field b() -> &str {
|
||||
fn b() -> &str {
|
||||
"b"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
|
||||
where
|
||||
|
|
|
@ -17,15 +17,16 @@ enum Color {
|
|||
}
|
||||
struct TestType;
|
||||
|
||||
graphql_object!(TestType: () |&self| {
|
||||
field to_string(color: Color) -> String {
|
||||
#[crate::impl_object_internal]
|
||||
impl TestType {
|
||||
fn to_string(color: Color) -> String {
|
||||
format!("Color::{:?}", color)
|
||||
}
|
||||
|
||||
field a_color() -> Color {
|
||||
fn a_color() -> Color {
|
||||
Color::Red
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
|
||||
where
|
||||
|
|
|
@ -7,34 +7,57 @@ mod field_execution {
|
|||
struct DataType;
|
||||
struct DeepDataType;
|
||||
|
||||
graphql_object!(DataType: () |&self| {
|
||||
field a() -> &str { "Apple" }
|
||||
field b() -> &str { "Banana" }
|
||||
field c() -> &str { "Cookie" }
|
||||
field d() -> &str { "Donut" }
|
||||
field e() -> &str { "Egg" }
|
||||
field f() -> &str { "Fish" }
|
||||
#[crate::impl_object_internal]
|
||||
impl DataType {
|
||||
fn a() -> &str {
|
||||
"Apple"
|
||||
}
|
||||
fn b() -> &str {
|
||||
"Banana"
|
||||
}
|
||||
fn c() -> &str {
|
||||
"Cookie"
|
||||
}
|
||||
fn d() -> &str {
|
||||
"Donut"
|
||||
}
|
||||
fn e() -> &str {
|
||||
"Egg"
|
||||
}
|
||||
fn f() -> &str {
|
||||
"Fish"
|
||||
}
|
||||
|
||||
field pic(size: Option<i32>) -> String {
|
||||
fn pic(size: Option<i32>) -> String {
|
||||
format!("Pic of size: {}", size.unwrap_or(50))
|
||||
}
|
||||
|
||||
field deep() -> DeepDataType {
|
||||
fn deep() -> DeepDataType {
|
||||
DeepDataType
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(DeepDataType: () |&self| {
|
||||
field a() -> &str { "Already Been Done" }
|
||||
field b() -> &str { "Boring" }
|
||||
field c() -> Vec<Option<&str>> { vec![Some("Contrived"), None, Some("Confusing")] }
|
||||
#[crate::impl_object_internal]
|
||||
impl DeepDataType {
|
||||
fn a() -> &str {
|
||||
"Already Been Done"
|
||||
}
|
||||
fn b() -> &str {
|
||||
"Boring"
|
||||
}
|
||||
fn c() -> Vec<Option<&str>> {
|
||||
vec![Some("Contrived"), None, Some("Confusing")]
|
||||
}
|
||||
|
||||
field deeper() -> Vec<Option<DataType>> { vec![Some(DataType), None, Some(DataType) ] }
|
||||
});
|
||||
fn deeper() -> Vec<Option<DataType>> {
|
||||
vec![Some(DataType), None, Some(DataType)]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let schema = RootNode::new(DataType, EmptyMutation::<()>::new());
|
||||
let schema =
|
||||
RootNode::<_, _, crate::DefaultScalarValue>::new(DataType, EmptyMutation::<()>::new());
|
||||
let doc = r"
|
||||
query Example($size: Int) {
|
||||
a,
|
||||
|
@ -139,12 +162,21 @@ mod merge_parallel_fragments {
|
|||
|
||||
struct Type;
|
||||
|
||||
graphql_object!(Type: () |&self| {
|
||||
field a() -> &str { "Apple" }
|
||||
field b() -> &str { "Banana" }
|
||||
field c() -> &str { "Cherry" }
|
||||
field deep() -> Type { Type }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Type {
|
||||
fn a() -> &str {
|
||||
"Apple"
|
||||
}
|
||||
fn b() -> &str {
|
||||
"Banana"
|
||||
}
|
||||
fn c() -> &str {
|
||||
"Cherry"
|
||||
}
|
||||
fn deep() -> Type {
|
||||
Type
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
@ -214,21 +246,43 @@ mod merge_parallel_inline_fragments {
|
|||
struct Type;
|
||||
struct Other;
|
||||
|
||||
graphql_object!(Type: () |&self| {
|
||||
field a() -> &str { "Apple" }
|
||||
field b() -> &str { "Banana" }
|
||||
field c() -> &str { "Cherry" }
|
||||
field deep() -> Type { Type }
|
||||
field other() -> Vec<Other> { vec![Other, Other] }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Type {
|
||||
fn a() -> &str {
|
||||
"Apple"
|
||||
}
|
||||
fn b() -> &str {
|
||||
"Banana"
|
||||
}
|
||||
fn c() -> &str {
|
||||
"Cherry"
|
||||
}
|
||||
fn deep() -> Type {
|
||||
Type
|
||||
}
|
||||
fn other() -> Vec<Other> {
|
||||
vec![Other, Other]
|
||||
}
|
||||
}
|
||||
|
||||
graphql_object!(Other: () |&self| {
|
||||
field a() -> &str { "Apple" }
|
||||
field b() -> &str { "Banana" }
|
||||
field c() -> &str { "Cherry" }
|
||||
field deep() -> Type { Type }
|
||||
field other() -> Vec<Other> { vec![Other, Other] }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Other {
|
||||
fn a() -> &str {
|
||||
"Apple"
|
||||
}
|
||||
fn b() -> &str {
|
||||
"Banana"
|
||||
}
|
||||
fn c() -> &str {
|
||||
"Cherry"
|
||||
}
|
||||
fn deep() -> Type {
|
||||
Type
|
||||
}
|
||||
fn other() -> Vec<Other> {
|
||||
vec![Other, Other]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
@ -342,9 +396,14 @@ mod threads_context_correctly {
|
|||
|
||||
impl Context for TestContext {}
|
||||
|
||||
graphql_object!(Schema: TestContext |&self| {
|
||||
field a(&executor) -> String { executor.context().value.clone() }
|
||||
});
|
||||
#[crate::impl_object_internal(
|
||||
Context = TestContext,
|
||||
)]
|
||||
impl Schema {
|
||||
fn a(context: &TestContext) -> String {
|
||||
context.value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
@ -403,36 +462,42 @@ mod dynamic_context_switching {
|
|||
|
||||
struct ItemRef;
|
||||
|
||||
graphql_object!(Schema: OuterContext |&self| {
|
||||
field item_opt(&executor, key: i32) -> Option<(&InnerContext, ItemRef)> {
|
||||
#[crate::impl_object_internal(Context = OuterContext)]
|
||||
impl Schema {
|
||||
fn item_opt(context: &OuterContext, key: i32) -> Option<(&InnerContext, ItemRef)> {
|
||||
executor.context().items.get(&key).map(|c| (c, ItemRef))
|
||||
}
|
||||
|
||||
field item_res(&executor, key: i32) -> FieldResult<(&InnerContext, ItemRef)> {
|
||||
let res = executor.context().items.get(&key)
|
||||
fn item_res(context: &OuterContext, key: i32) -> FieldResult<(&InnerContext, ItemRef)> {
|
||||
let res = context
|
||||
.items
|
||||
.get(&key)
|
||||
.ok_or(format!("Could not find key {}", key))
|
||||
.map(|c| (c, ItemRef))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
field item_res_opt(&executor, key: i32) -> FieldResult<Option<(&InnerContext, ItemRef)>> {
|
||||
fn item_res_opt(
|
||||
context: &OuterContext,
|
||||
key: i32,
|
||||
) -> FieldResult<Option<(&InnerContext, ItemRef)>> {
|
||||
if key > 100 {
|
||||
Err(format!("Key too large: {}", key))?;
|
||||
}
|
||||
Ok(executor.context().items.get(&key)
|
||||
.map(|c| (c, ItemRef)))
|
||||
Ok(context.items.get(&key).map(|c| (c, ItemRef)))
|
||||
}
|
||||
|
||||
field item_always(&executor, key: i32) -> (&InnerContext, ItemRef) {
|
||||
executor.context().items.get(&key)
|
||||
.map(|c| (c, ItemRef))
|
||||
.unwrap()
|
||||
fn item_always(context: &OuterContext, key: i32) -> (&InnerContext, ItemRef) {
|
||||
context.items.get(&key).map(|c| (c, ItemRef)).unwrap()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(ItemRef: InnerContext |&self| {
|
||||
field value(&executor) -> String { executor.context().value.clone() }
|
||||
});
|
||||
#[crate::impl_object_internal(Context = InnerContext)]
|
||||
impl ItemRef {
|
||||
fn value(context: &InnerContext) -> String {
|
||||
context.value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt() {
|
||||
|
@ -736,19 +801,37 @@ mod propagates_errors_to_nullable_fields {
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(Schema: () |&self| {
|
||||
field inner() -> Inner { Inner }
|
||||
field inners() -> Vec<Inner> { (0..5).map(|_| Inner).collect() }
|
||||
field nullable_inners() -> Vec<Option<Inner>> { (0..5).map(|_| Some(Inner)).collect() }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Schema {
|
||||
fn inner() -> Inner {
|
||||
Inner
|
||||
}
|
||||
fn inners() -> Vec<Inner> {
|
||||
(0..5).map(|_| Inner).collect()
|
||||
}
|
||||
fn nullable_inners() -> Vec<Option<Inner>> {
|
||||
(0..5).map(|_| Some(Inner)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
graphql_object!(Inner: () |&self| {
|
||||
field nullable_field() -> Option<Inner> { Some(Inner) }
|
||||
field non_nullable_field() -> Inner { Inner }
|
||||
field nullable_error_field() -> FieldResult<Option<&str>> { Err("Error for nullableErrorField")? }
|
||||
field non_nullable_error_field() -> FieldResult<&str> { Err("Error for nonNullableErrorField")? }
|
||||
field custom_error_field() -> Result<&str, CustomError> { Err(CustomError::NotFound) }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Inner {
|
||||
fn nullable_field() -> Option<Inner> {
|
||||
Some(Inner)
|
||||
}
|
||||
fn non_nullable_field() -> Inner {
|
||||
Inner
|
||||
}
|
||||
fn nullable_error_field() -> FieldResult<Option<&str>> {
|
||||
Err("Error for nullableErrorField")?
|
||||
}
|
||||
fn non_nullable_error_field() -> FieldResult<&str> {
|
||||
Err("Error for nonNullableErrorField")?
|
||||
}
|
||||
fn custom_error_field() -> Result<&str, CustomError> {
|
||||
Err(CustomError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nullable_first_level() {
|
||||
|
@ -985,13 +1068,17 @@ mod named_operations {
|
|||
|
||||
struct Schema;
|
||||
|
||||
graphql_object!(Schema: () |&self| {
|
||||
field a() -> &str { "b" }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Schema {
|
||||
fn a() -> &str {
|
||||
"b"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_inline_operation_if_no_name_provided() {
|
||||
let schema = RootNode::new(Schema, EmptyMutation::<()>::new());
|
||||
let schema =
|
||||
RootNode::<_, _, crate::DefaultScalarValue>::new(Schema, EmptyMutation::<()>::new());
|
||||
let doc = r"{ a }";
|
||||
|
||||
let vars = vec![].into_iter().collect();
|
||||
|
|
|
@ -37,12 +37,17 @@ mod interface {
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(Dog: () |&self| {
|
||||
field name() -> &str { &self.name }
|
||||
field woofs() -> bool { self.woofs }
|
||||
|
||||
interfaces: [&Pet]
|
||||
});
|
||||
#[crate::impl_object_internal(
|
||||
interfaces = [&Pet]
|
||||
)]
|
||||
impl Dog {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn woofs(&self) -> bool {
|
||||
self.woofs
|
||||
}
|
||||
}
|
||||
|
||||
struct Cat {
|
||||
name: String,
|
||||
|
@ -58,22 +63,28 @@ mod interface {
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(Cat: () |&self| {
|
||||
field name() -> &str { &self.name }
|
||||
field meows() -> bool { self.meows }
|
||||
|
||||
interfaces: [&Pet]
|
||||
});
|
||||
#[crate::impl_object_internal(
|
||||
interfaces = [&Pet]
|
||||
)]
|
||||
impl Cat {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn meows(&self) -> bool {
|
||||
self.meows
|
||||
}
|
||||
}
|
||||
|
||||
struct Schema {
|
||||
pets: Vec<Box<Pet>>,
|
||||
}
|
||||
|
||||
graphql_object!(Schema: () |&self| {
|
||||
field pets() -> Vec<&Pet> {
|
||||
#[crate::impl_object_internal]
|
||||
impl Schema {
|
||||
fn pets(&self) -> Vec<&Pet> {
|
||||
self.pets.iter().map(|p| p.as_ref()).collect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
@ -177,10 +188,15 @@ mod union {
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(Dog: () |&self| {
|
||||
field name() -> &str { &self.name }
|
||||
field woofs() -> bool { self.woofs }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Dog {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn woofs(&self) -> bool {
|
||||
self.woofs
|
||||
}
|
||||
}
|
||||
|
||||
struct Cat {
|
||||
name: String,
|
||||
|
@ -193,20 +209,26 @@ mod union {
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(Cat: () |&self| {
|
||||
field name() -> &str { &self.name }
|
||||
field meows() -> bool { self.meows }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Cat {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn meows(&self) -> bool {
|
||||
self.meows
|
||||
}
|
||||
}
|
||||
|
||||
struct Schema {
|
||||
pets: Vec<Box<Pet>>,
|
||||
}
|
||||
|
||||
graphql_object!(Schema: () |&self| {
|
||||
field pets() -> Vec<&Pet> {
|
||||
#[crate::impl_object_internal]
|
||||
impl Schema {
|
||||
fn pets(&self) -> Vec<&Pet> {
|
||||
self.pets.iter().map(|p| p.as_ref()).collect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
|
|
@ -64,14 +64,27 @@ enum EnumDeprecation {
|
|||
|
||||
struct Root;
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field default_name() -> DefaultName { DefaultName::Foo }
|
||||
field named() -> Named { Named::Foo }
|
||||
field no_trailing_comma() -> NoTrailingComma { NoTrailingComma::Foo }
|
||||
field enum_description() -> EnumDescription { EnumDescription::Foo }
|
||||
field enum_value_description() -> EnumValueDescription { EnumValueDescription::Foo }
|
||||
field enum_deprecation() -> EnumDeprecation { EnumDeprecation::Foo }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Root {
|
||||
fn default_name() -> DefaultName {
|
||||
DefaultName::Foo
|
||||
}
|
||||
fn named() -> Named {
|
||||
Named::Foo
|
||||
}
|
||||
fn no_trailing_comma() -> NoTrailingComma {
|
||||
NoTrailingComma::Foo
|
||||
}
|
||||
fn enum_description() -> EnumDescription {
|
||||
EnumDescription::Foo
|
||||
}
|
||||
fn enum_value_description() -> EnumValueDescription {
|
||||
EnumValueDescription::Foo
|
||||
}
|
||||
fn enum_deprecation() -> EnumDeprecation {
|
||||
EnumDeprecation::Foo
|
||||
}
|
||||
}
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where
|
||||
|
|
|
@ -79,8 +79,9 @@ struct FieldWithDefaults {
|
|||
field_two: i32,
|
||||
}
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field test_field(
|
||||
#[crate::impl_object_internal]
|
||||
impl Root {
|
||||
fn test_field(
|
||||
a1: DefaultName,
|
||||
a2: NoTrailingComma,
|
||||
a3: Derive,
|
||||
|
@ -95,7 +96,7 @@ graphql_object!(Root: () |&self| {
|
|||
) -> i32 {
|
||||
0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where
|
||||
|
|
|
@ -51,22 +51,26 @@ graphql_interface!(Interface: () as "SampleInterface" |&self| {
|
|||
}
|
||||
});
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
description: "The root query object in the schema"
|
||||
|
||||
interfaces: [Interface]
|
||||
|
||||
field sample_enum() -> Sample {
|
||||
/// The root query object in the schema
|
||||
#[crate::impl_object_internal(
|
||||
interfaces = [&Interface]
|
||||
Scalar = crate::DefaultScalarValue,
|
||||
)]
|
||||
impl Root {
|
||||
fn sample_enum() -> Sample {
|
||||
Sample::One
|
||||
}
|
||||
|
||||
field sample_scalar(
|
||||
first: i32 as "The first number",
|
||||
second = 123: i32 as "The second number"
|
||||
) -> Scalar as "A sample scalar field on the object" {
|
||||
#[graphql(arguments(
|
||||
first(description = "The first number",),
|
||||
second(description = "The second number", default = 123,),
|
||||
))]
|
||||
|
||||
/// A sample scalar field on the object
|
||||
fn sample_scalar(first: i32, second: i32) -> Scalar {
|
||||
Scalar(first + second)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execution() {
|
||||
|
|
|
@ -62,59 +62,67 @@ struct InputWithDefaults {
|
|||
a: i32,
|
||||
}
|
||||
|
||||
graphql_object!(TestType: () |&self| {
|
||||
field field_with_object_input(input: Option<TestInputObject>) -> String {
|
||||
#[crate::impl_object_internal]
|
||||
impl TestType {
|
||||
fn field_with_object_input(input: Option<TestInputObject>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field field_with_nullable_string_input(input: Option<String>) -> String {
|
||||
fn field_with_nullable_string_input(input: Option<String>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field field_with_non_nullable_string_input(input: String) -> String {
|
||||
fn field_with_non_nullable_string_input(input: String) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field field_with_default_argument_value(input = ("Hello World".to_owned()): String) -> String {
|
||||
#[graphql(
|
||||
arguments(
|
||||
input(
|
||||
default = "Hello World".to_string(),
|
||||
)
|
||||
)
|
||||
)]
|
||||
fn field_with_default_argument_value(input: String) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field field_with_nested_object_input(input: Option<TestNestedInputObject>) -> String {
|
||||
fn field_with_nested_object_input(input: Option<TestNestedInputObject>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field list(input: Option<Vec<Option<String>>>) -> String {
|
||||
fn list(input: Option<Vec<Option<String>>>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field nn_list(input: Vec<Option<String>>) -> String {
|
||||
fn nn_list(input: Vec<Option<String>>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field list_nn(input: Option<Vec<String>>) -> String {
|
||||
fn list_nn(input: Option<Vec<String>>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field nn_list_nn(input: Vec<String>) -> String {
|
||||
fn nn_list_nn(input: Vec<String>) -> String {
|
||||
format!("{:?}", input)
|
||||
}
|
||||
|
||||
field example_input(arg: ExampleInputObject) -> String {
|
||||
fn example_input(arg: ExampleInputObject) -> String {
|
||||
format!("a: {:?}, b: {:?}", arg.a, arg.b)
|
||||
}
|
||||
|
||||
field input_with_defaults(arg: InputWithDefaults) -> String {
|
||||
fn input_with_defaults(arg: InputWithDefaults) -> String {
|
||||
format!("a: {:?}", arg.a)
|
||||
}
|
||||
|
||||
field integer_input(value: i32) -> String {
|
||||
fn integer_input(value: i32) -> String {
|
||||
format!("value: {}", value)
|
||||
}
|
||||
|
||||
field float_input(value: f64) -> String {
|
||||
fn float_input(value: f64) -> String {
|
||||
format!("value: {}", value)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
|
||||
where
|
||||
|
|
|
@ -209,20 +209,22 @@ mod integration_test {
|
|||
#[test]
|
||||
fn test_serialization() {
|
||||
struct Root;
|
||||
graphql_object!(Root: () |&self| {
|
||||
field exampleNaiveDate() -> NaiveDate {
|
||||
|
||||
#[crate::impl_object_internal]
|
||||
impl Root {
|
||||
fn exampleNaiveDate() -> NaiveDate {
|
||||
NaiveDate::from_ymd(2015, 3, 14)
|
||||
}
|
||||
field exampleNaiveDateTime() -> NaiveDateTime {
|
||||
fn exampleNaiveDateTime() -> NaiveDateTime {
|
||||
NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11)
|
||||
}
|
||||
field exampleDateTimeFixedOffset() -> DateTime<FixedOffset> {
|
||||
DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00").unwrap()
|
||||
fn exampleDateTimeFixedOffset() -> DateTime<FixedOffset> {
|
||||
DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00").unwrap()
|
||||
}
|
||||
field exampleDateTimeUtc() -> DateTime<Utc> {
|
||||
Utc.timestamp(61, 0)
|
||||
fn exampleDateTimeUtc() -> DateTime<Utc> {
|
||||
Utc.timestamp(61, 0)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let doc = r#"
|
||||
{
|
||||
|
|
|
@ -61,13 +61,18 @@ impl Character for Droid {
|
|||
fn id(&self) -> &str { &self.id }
|
||||
}
|
||||
|
||||
juniper::graphql_object!(Human: Database as "Human" |&self| {
|
||||
field id() -> &str { &self.id }
|
||||
});
|
||||
#[juniper::impl_object(Context = Database)]
|
||||
impl Human {
|
||||
fn id(&self) -> &str { &self.id }
|
||||
}
|
||||
|
||||
juniper::graphql_object!(Droid: Database as "Droid" |&self| {
|
||||
field id() -> &str { &self.id }
|
||||
});
|
||||
#[juniper::impl_object(
|
||||
name = "Droid",
|
||||
Context = Database,
|
||||
)]
|
||||
impl Droid {
|
||||
fn id(&self) -> &str { &self.id }
|
||||
}
|
||||
|
||||
// You can introduce lifetimes or generic parameters by < > before the name.
|
||||
juniper::graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
/**
|
||||
## DEPRECATION WARNING
|
||||
|
||||
The `graphql_object!` macro is deprecated and will be removed soon.
|
||||
Use the new[impl_object](https://docs.rs/juniper/latest/juniper/macro.impl_object.html) macro instead.
|
||||
|
||||
Expose GraphQL objects
|
||||
|
||||
|
||||
This is a short-hand macro that implements the `GraphQLType` trait for a given
|
||||
type. By using this macro instead of implementing it manually, you gain type
|
||||
safety and reduce repetitive declarations.
|
||||
|
@ -308,7 +314,6 @@ arg_name: ArgType
|
|||
```
|
||||
|
||||
[1]: struct.Executor.html
|
||||
|
||||
*/
|
||||
#[macro_export]
|
||||
macro_rules! graphql_object {
|
||||
|
|
|
@ -26,63 +26,111 @@ struct Point {
|
|||
x: i32,
|
||||
}
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
field exec_arg(&executor) -> i32 { 0 }
|
||||
field exec_arg_and_more(&executor, arg: i32) -> i32 { 0 }
|
||||
#[crate::impl_object_internal]
|
||||
impl Root {
|
||||
fn simple() -> i32 {
|
||||
0
|
||||
}
|
||||
fn exec_arg(executor: &Executor) -> i32 {
|
||||
0
|
||||
}
|
||||
fn exec_arg_and_more(executor: &Executor, arg: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field single_arg(arg: i32) -> i32 { 0 }
|
||||
field multi_args(
|
||||
arg1: i32,
|
||||
arg2: i32
|
||||
) -> i32 { 0 }
|
||||
field multi_args_trailing_comma(
|
||||
arg1: i32,
|
||||
arg2: i32,
|
||||
) -> i32 { 0 }
|
||||
fn single_arg(arg: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field single_arg_descr(arg: i32 as "The arg") -> i32 { 0 }
|
||||
field multi_args_descr(
|
||||
arg1: i32 as "The first arg",
|
||||
arg2: i32 as "The second arg"
|
||||
) -> i32 { 0 }
|
||||
field multi_args_descr_trailing_comma(
|
||||
arg1: i32 as "The first arg",
|
||||
arg2: i32 as "The second arg",
|
||||
) -> i32 { 0 }
|
||||
fn multi_args(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field attr_arg_descr(#[doc = "The arg"] arg: i32) -> i32 { 0 }
|
||||
field attr_arg_descr_collapse(
|
||||
#[doc = "The arg"]
|
||||
#[doc = "and more details"]
|
||||
arg: i32,
|
||||
) -> i32 { 0 }
|
||||
fn multi_args_trailing_comma(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field arg_with_default(arg = 123: i32) -> i32 { 0 }
|
||||
field multi_args_with_default(
|
||||
arg1 = 123: i32,
|
||||
arg2 = 456: i32
|
||||
) -> i32 { 0 }
|
||||
field multi_args_with_default_trailing_comma(
|
||||
arg1 = 123: i32,
|
||||
arg2 = 456: i32,
|
||||
) -> i32 { 0 }
|
||||
#[graphql(arguments(arg(description = "The arg")))]
|
||||
fn single_arg_descr(arg: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field arg_with_default_descr(arg = 123: i32 as "The arg") -> i32 { 0 }
|
||||
field multi_args_with_default_descr(
|
||||
arg1 = 123: i32 as "The first arg",
|
||||
arg2 = 456: i32 as "The second arg"
|
||||
) -> i32 { 0 }
|
||||
field multi_args_with_default_trailing_comma_descr(
|
||||
arg1 = 123: i32 as "The first arg",
|
||||
arg2 = 456: i32 as "The second arg",
|
||||
) -> i32 { 0 }
|
||||
#[graphql(arguments(
|
||||
arg1(description = "The first arg",),
|
||||
arg2(description = "The second arg")
|
||||
))]
|
||||
fn multi_args_descr(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field args_with_complex_default(
|
||||
arg1 = ("test".to_owned()): String as "A string default argument",
|
||||
arg2 = (Point { x: 1 }): Point as "An input object default argument",
|
||||
) -> i32 { 0 }
|
||||
});
|
||||
#[graphql(arguments(
|
||||
arg1(description = "The first arg",),
|
||||
arg2(description = "The second arg")
|
||||
))]
|
||||
fn multi_args_descr_trailing_comma(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented
|
||||
// fn attr_arg_descr(#[doc = "The arg"] arg: i32) -> i32 { 0 }
|
||||
// fn attr_arg_descr_collapse(
|
||||
// #[doc = "The arg"]
|
||||
// #[doc = "and more details"]
|
||||
// arg: i32,
|
||||
// ) -> i32 { 0 }
|
||||
|
||||
#[graphql(arguments(arg(default = 123,),))]
|
||||
fn arg_with_default(arg: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(arguments(arg1(default = 123,), arg2(default = 456,)))]
|
||||
fn multi_args_with_default(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(arguments(arg1(default = 123,), arg2(default = 456,),))]
|
||||
fn multi_args_with_default_trailing_comma(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(arguments(arg(default = 123, description = "The arg")))]
|
||||
fn arg_with_default_descr(arg: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(arguments(
|
||||
arg1(default = 123, description = "The first arg"),
|
||||
arg2(default = 456, description = "The second arg")
|
||||
))]
|
||||
fn multi_args_with_default_descr(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(arguments(
|
||||
arg1(default = 123, description = "The first arg",),
|
||||
arg2(default = 456, description = "The second arg",)
|
||||
))]
|
||||
fn multi_args_with_default_trailing_comma_descr(arg1: i32, arg2: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(
|
||||
arguments(
|
||||
arg1(
|
||||
default = "test".to_string(),
|
||||
description = "A string default argument",
|
||||
),
|
||||
arg2(
|
||||
default = Point{ x: 1 },
|
||||
description = "An input object default argument",
|
||||
)
|
||||
),
|
||||
)]
|
||||
fn args_with_complex_default(arg1: String, arg2: Point) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn run_args_info_query<F>(field_name: &str, f: F)
|
||||
where
|
||||
|
@ -509,71 +557,73 @@ fn introspect_field_multi_args_descr_trailing_comma() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn introspect_field_attr_arg_descr() {
|
||||
run_args_info_query("attrArgDescr", |args| {
|
||||
assert_eq!(args.len(), 1);
|
||||
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented
|
||||
// #[test]
|
||||
// fn introspect_field_attr_arg_descr() {
|
||||
// run_args_info_query("attrArgDescr", |args| {
|
||||
// assert_eq!(args.len(), 1);
|
||||
|
||||
assert!(args.contains(&Value::object(
|
||||
vec![
|
||||
("name", Value::scalar("arg")),
|
||||
("description", Value::scalar("The arg")),
|
||||
("defaultValue", Value::null()),
|
||||
(
|
||||
"type",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::null()),
|
||||
(
|
||||
"ofType",
|
||||
Value::object(
|
||||
vec![("name", Value::scalar("Int"))].into_iter().collect(),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)));
|
||||
});
|
||||
}
|
||||
// assert!(args.contains(&Value::object(
|
||||
// vec![
|
||||
// ("name", Value::scalar("arg")),
|
||||
// ("description", Value::scalar("The arg")),
|
||||
// ("defaultValue", Value::null()),
|
||||
// (
|
||||
// "type",
|
||||
// Value::object(
|
||||
// vec![
|
||||
// ("name", Value::null()),
|
||||
// (
|
||||
// "ofType",
|
||||
// Value::object(
|
||||
// vec![("name", Value::scalar("Int"))].into_iter().collect(),
|
||||
// ),
|
||||
// ),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// ),
|
||||
// ),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// )));
|
||||
// });
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn introspect_field_attr_arg_descr_collapse() {
|
||||
run_args_info_query("attrArgDescrCollapse", |args| {
|
||||
assert_eq!(args.len(), 1);
|
||||
// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented
|
||||
// #[test]
|
||||
// fn introspect_field_attr_arg_descr_collapse() {
|
||||
// run_args_info_query("attrArgDescrCollapse", |args| {
|
||||
// assert_eq!(args.len(), 1);
|
||||
|
||||
assert!(args.contains(&Value::object(
|
||||
vec![
|
||||
("name", Value::scalar("arg")),
|
||||
("description", Value::scalar("The arg\nand more details")),
|
||||
("defaultValue", Value::null()),
|
||||
(
|
||||
"type",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::null()),
|
||||
(
|
||||
"ofType",
|
||||
Value::object(
|
||||
vec![("name", Value::scalar("Int"))].into_iter().collect(),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)));
|
||||
});
|
||||
}
|
||||
// assert!(args.contains(&Value::object(
|
||||
// vec![
|
||||
// ("name", Value::scalar("arg")),
|
||||
// ("description", Value::scalar("The arg\nand more details")),
|
||||
// ("defaultValue", Value::null()),
|
||||
// (
|
||||
// "type",
|
||||
// Value::object(
|
||||
// vec![
|
||||
// ("name", Value::null()),
|
||||
// (
|
||||
// "ofType",
|
||||
// Value::object(
|
||||
// vec![("name", Value::scalar("Int"))].into_iter().collect(),
|
||||
// ),
|
||||
// ),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// ),
|
||||
// ),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .collect(),
|
||||
// )));
|
||||
// });
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn introspect_field_arg_with_default() {
|
||||
|
|
|
@ -20,49 +20,77 @@ Syntax to validate:
|
|||
|
||||
*/
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
|
||||
field description() -> i32 as "Field description" { 0 }
|
||||
|
||||
field deprecated "Deprecation reason"
|
||||
deprecated() -> i32 { 0 }
|
||||
|
||||
field deprecated "Deprecation reason"
|
||||
deprecated_descr() -> i32 as "Field description" { 0 }
|
||||
#[crate::impl_object_internal(
|
||||
interfaces = [&Interface],
|
||||
)]
|
||||
impl Root {
|
||||
fn simple() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Field description
|
||||
field attr_description() -> i32 { 0 }
|
||||
fn description() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(deprecated = "Deprecation reason")]
|
||||
fn deprecated() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[graphql(deprecated = "Deprecation reason", description = "Field description")]
|
||||
fn deprecated_descr() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Field description
|
||||
fn attr_description() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Field description
|
||||
/// with `collapse_docs` behavior
|
||||
field attr_description_collapse() -> i32 { 0 }
|
||||
fn attr_description_collapse() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Get the i32 representation of 0.
|
||||
///
|
||||
/// - This comment is longer.
|
||||
/// - These two lines are rendered as bullets by GraphiQL.
|
||||
/// - subsection
|
||||
field attr_description_long() -> i32 { 0 }
|
||||
fn attr_description_long() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
field attr_deprecated() -> i32 { 0 }
|
||||
#[graphql(deprecated)]
|
||||
fn attr_deprecated() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[deprecated(note = "Deprecation reason")]
|
||||
field attr_deprecated_reason() -> i32 { 0 }
|
||||
#[graphql(deprecated = "Deprecation reason")]
|
||||
fn attr_deprecated_reason() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Field description
|
||||
#[deprecated(note = "Deprecation reason")]
|
||||
field attr_deprecated_descr() -> i32 { 0 }
|
||||
#[graphql(deprecated = "Deprecation reason")]
|
||||
fn attr_deprecated_descr() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
field with_field_result() -> FieldResult<i32> { Ok(0) }
|
||||
fn with_field_result() -> FieldResult<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
field with_return() -> i32 { return 0; }
|
||||
fn with_return() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
field with_return_field_result() -> FieldResult<i32> { return Ok(0); }
|
||||
|
||||
interfaces: [Interface]
|
||||
});
|
||||
fn with_return_field_result() -> FieldResult<i32> {
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
graphql_interface!(Interface: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
|
|
269
juniper/src/macros/tests/impl_object.rs
Normal file
269
juniper/src/macros/tests/impl_object.rs
Normal file
|
@ -0,0 +1,269 @@
|
|||
use super::util;
|
||||
use crate::{graphql_value, EmptyMutation, RootNode};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Context {
|
||||
flag1: bool,
|
||||
}
|
||||
|
||||
impl crate::Context for Context {}
|
||||
|
||||
struct WithLifetime<'a> {
|
||||
value: &'a str,
|
||||
}
|
||||
|
||||
#[crate::impl_object_internal(Context=Context)]
|
||||
impl<'a> WithLifetime<'a> {
|
||||
fn value(&'a self) -> &'a str {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
struct WithContext;
|
||||
|
||||
#[crate::impl_object_internal(Context=Context)]
|
||||
impl WithContext {
|
||||
fn ctx(ctx: &Context) -> bool {
|
||||
ctx.flag1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Query {
|
||||
b: bool,
|
||||
}
|
||||
|
||||
#[crate::impl_object_internal(
|
||||
scalar = crate::DefaultScalarValue,
|
||||
name = "Query",
|
||||
context = Context,
|
||||
)]
|
||||
/// Query Description.
|
||||
impl<'a> Query {
|
||||
#[graphql(description = "With Self Description")]
|
||||
fn with_self(&self) -> bool {
|
||||
self.b
|
||||
}
|
||||
|
||||
fn independent() -> i32 {
|
||||
100
|
||||
}
|
||||
|
||||
fn with_executor(_exec: &Executor<Context>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn with_executor_and_self(&self, _exec: &Executor<Context>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn with_context(_context: &Context) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn with_context_and_self(&self, _context: &Context) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[graphql(name = "renamed")]
|
||||
fn has_custom_name() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[graphql(description = "attr")]
|
||||
fn has_description_attr() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Doc description
|
||||
fn has_description_doc_comment() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn has_argument(arg1: bool) -> bool {
|
||||
arg1
|
||||
}
|
||||
|
||||
#[graphql(arguments(default_arg(default = true)))]
|
||||
fn default_argument(default_arg: bool) -> bool {
|
||||
default_arg
|
||||
}
|
||||
|
||||
#[graphql(arguments(arg(description = "my argument description")))]
|
||||
fn arg_with_description(arg: bool) -> bool {
|
||||
arg
|
||||
}
|
||||
|
||||
fn with_context_child(&self) -> WithContext {
|
||||
WithContext
|
||||
}
|
||||
|
||||
fn with_lifetime_child(&self) -> WithLifetime<'a> {
|
||||
WithLifetime { value: "blub" }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Mutation;
|
||||
|
||||
#[crate::impl_object_internal(context = Context)]
|
||||
impl Mutation {
|
||||
fn empty() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_object_introspect() {
|
||||
let res = util::run_info_query::<Query, Mutation, Context>("Query");
|
||||
assert_eq!(
|
||||
res,
|
||||
crate::graphql_value!({
|
||||
"name": "Query",
|
||||
"description": "Query Description.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "withSelf",
|
||||
"description": "With Self Description",
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "independent",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "withExecutor",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "withExecutorAndSelf",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "withContext",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "withContextAndSelf",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "renamed",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "hasDescriptionAttr",
|
||||
"description": "attr",
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "hasDescriptionDocComment",
|
||||
"description": "Doc description",
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "hasArgument",
|
||||
"description": None,
|
||||
"args": [
|
||||
{
|
||||
"name": "arg1",
|
||||
"description": None,
|
||||
"type": {
|
||||
"name": None,
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "defaultArgument",
|
||||
"description": None,
|
||||
"args": [
|
||||
{
|
||||
"name": "defaultArg",
|
||||
"description": None,
|
||||
"type": {
|
||||
"name": "Boolean",
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "argWithDescription",
|
||||
"description": None,
|
||||
"args": [
|
||||
{
|
||||
"name": "arg",
|
||||
"description": "my argument description",
|
||||
"type": {
|
||||
"name": None
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "withContextChild",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
{
|
||||
"name": "withLifetimeChild",
|
||||
"description": None,
|
||||
"args": [],
|
||||
},
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_object_query() {
|
||||
let doc = r#"
|
||||
query {
|
||||
withSelf
|
||||
independent
|
||||
withExecutor
|
||||
withExecutorAndSelf
|
||||
withContext
|
||||
withContextAndSelf
|
||||
renamed
|
||||
hasArgument(arg1: true)
|
||||
defaultArgument
|
||||
argWithDescription(arg: true)
|
||||
withContextChild {
|
||||
ctx
|
||||
}
|
||||
withLifetimeChild {
|
||||
value
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let schema = RootNode::new(Query { b: true }, EmptyMutation::<Context>::new());
|
||||
let vars = std::collections::HashMap::new();
|
||||
|
||||
let (result, errs) = crate::execute(doc, None, &schema, &vars, &Context { flag1: true })
|
||||
.expect("Execution failed");
|
||||
assert_eq!(errs, []);
|
||||
assert_eq!(
|
||||
result,
|
||||
graphql_value!({
|
||||
"withSelf": true,
|
||||
"independent": 100,
|
||||
"withExecutor": true,
|
||||
"withExecutorAndSelf": true,
|
||||
"withContext": true,
|
||||
"withContextAndSelf": true,
|
||||
"renamed": true,
|
||||
"hasArgument": true,
|
||||
"defaultArgument": true,
|
||||
"argWithDescription": true,
|
||||
"withContextChild": { "ctx": true },
|
||||
"withLifetimeChild": { "value": "blub" },
|
||||
})
|
||||
);
|
||||
}
|
|
@ -42,9 +42,12 @@ struct ResolversWithTrailingComma;
|
|||
|
||||
struct Root;
|
||||
|
||||
graphql_object!(Concrete: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Concrete {
|
||||
fn simple() -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
graphql_interface!(CustomName: () as "ACustomNamedInterface" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
|
@ -108,24 +111,40 @@ graphql_interface!(ResolversWithTrailingComma: () |&self| {
|
|||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
graphql_object!(<'a> Root: () as "Root" |&self| {
|
||||
field custom_name() -> CustomName { CustomName {} }
|
||||
|
||||
field with_lifetime() -> WithLifetime<'a> { WithLifetime { data: PhantomData } }
|
||||
field with_generics() -> WithGenerics<i32> { WithGenerics { data: 123 } }
|
||||
|
||||
field description_first() -> DescriptionFirst { DescriptionFirst {} }
|
||||
field fields_first() -> FieldsFirst { FieldsFirst {} }
|
||||
field interfaces_first() -> InterfacesFirst { InterfacesFirst {} }
|
||||
|
||||
field commas_with_trailing() -> CommasWithTrailing { CommasWithTrailing {} }
|
||||
field commas_on_meta() -> CommasOnMeta { CommasOnMeta {} }
|
||||
|
||||
field resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
|
||||
ResolversWithTrailingComma {}
|
||||
#[crate::impl_object_internal]
|
||||
impl<'a> Root {
|
||||
fn custom_name() -> CustomName {
|
||||
CustomName {}
|
||||
}
|
||||
|
||||
});
|
||||
fn with_lifetime() -> WithLifetime<'a> {
|
||||
WithLifetime { data: PhantomData }
|
||||
}
|
||||
fn with_generics() -> WithGenerics<i32> {
|
||||
WithGenerics { data: 123 }
|
||||
}
|
||||
|
||||
fn description_first() -> DescriptionFirst {
|
||||
DescriptionFirst {}
|
||||
}
|
||||
fn fields_first() -> FieldsFirst {
|
||||
FieldsFirst {}
|
||||
}
|
||||
fn interfaces_first() -> InterfacesFirst {
|
||||
InterfacesFirst {}
|
||||
}
|
||||
|
||||
fn commas_with_trailing() -> CommasWithTrailing {
|
||||
CommasWithTrailing {}
|
||||
}
|
||||
fn commas_on_meta() -> CommasOnMeta {
|
||||
CommasOnMeta {}
|
||||
}
|
||||
|
||||
fn resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
|
||||
ResolversWithTrailingComma {}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_type_info_query<F>(type_name: &str, f: F)
|
||||
where
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
mod args;
|
||||
mod field;
|
||||
mod impl_object;
|
||||
mod interface;
|
||||
mod object;
|
||||
mod scalar;
|
||||
mod union;
|
||||
mod util;
|
||||
|
|
|
@ -18,41 +18,29 @@ Syntax to validate:
|
|||
|
||||
*/
|
||||
|
||||
struct Interface;
|
||||
|
||||
struct CustomName;
|
||||
graphql_object!(CustomName: () as "ACustomNamedType" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct WithLifetime<'a> {
|
||||
data: PhantomData<&'a i32>,
|
||||
}
|
||||
graphql_object!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct WithGenerics<T> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
struct DescriptionFirst;
|
||||
struct FieldsFirst;
|
||||
struct InterfacesFirst;
|
||||
|
||||
struct CommasWithTrailing;
|
||||
struct CommasOnMeta;
|
||||
|
||||
struct Root;
|
||||
|
||||
graphql_object!(CustomName: () as "ACustomNamedType" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
graphql_object!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
graphql_object!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
struct Interface;
|
||||
struct DescriptionFirst;
|
||||
graphql_interface!(Interface: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
|
||||
|
@ -60,7 +48,6 @@ graphql_interface!(Interface: () |&self| {
|
|||
DescriptionFirst => Some(DescriptionFirst {}),
|
||||
}
|
||||
});
|
||||
|
||||
graphql_object!(DescriptionFirst: () |&self| {
|
||||
description: "A description"
|
||||
|
||||
|
@ -69,6 +56,7 @@ graphql_object!(DescriptionFirst: () |&self| {
|
|||
interfaces: [Interface]
|
||||
});
|
||||
|
||||
struct FieldsFirst;
|
||||
graphql_object!(FieldsFirst: () |&self| {
|
||||
field simple() -> i32 { 0 }
|
||||
|
||||
|
@ -77,6 +65,7 @@ graphql_object!(FieldsFirst: () |&self| {
|
|||
interfaces: [Interface]
|
||||
});
|
||||
|
||||
struct InterfacesFirst;
|
||||
graphql_object!(InterfacesFirst: ()|&self| {
|
||||
interfaces: [Interface]
|
||||
|
||||
|
@ -85,6 +74,7 @@ graphql_object!(InterfacesFirst: ()|&self| {
|
|||
description: "A description"
|
||||
});
|
||||
|
||||
struct CommasWithTrailing;
|
||||
graphql_object!(CommasWithTrailing: () |&self| {
|
||||
interfaces: [Interface],
|
||||
|
||||
|
@ -93,6 +83,8 @@ graphql_object!(CommasWithTrailing: () |&self| {
|
|||
description: "A description",
|
||||
});
|
||||
|
||||
struct CommasOnMeta;
|
||||
|
||||
graphql_object!(CommasOnMeta: () |&self| {
|
||||
interfaces: [Interface],
|
||||
description: "A description",
|
||||
|
@ -100,6 +92,8 @@ graphql_object!(CommasOnMeta: () |&self| {
|
|||
field simple() -> i32 { 0 }
|
||||
});
|
||||
|
||||
struct Root;
|
||||
|
||||
struct InnerContext;
|
||||
impl Context for InnerContext {}
|
||||
|
||||
|
|
|
@ -78,12 +78,21 @@ graphql_scalar!(ScalarDescription {
|
|||
}
|
||||
});
|
||||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field default_name() -> DefaultName { DefaultName(0) }
|
||||
field other_order() -> OtherOrder { OtherOrder(0) }
|
||||
field named() -> Named { Named(0) }
|
||||
field scalar_description() -> ScalarDescription { ScalarDescription(0) }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Root {
|
||||
fn default_name() -> DefaultName {
|
||||
DefaultName(0)
|
||||
}
|
||||
fn other_order() -> OtherOrder {
|
||||
OtherOrder(0)
|
||||
}
|
||||
fn named() -> Named {
|
||||
Named(0)
|
||||
}
|
||||
fn scalar_description() -> ScalarDescription {
|
||||
ScalarDescription(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where
|
||||
|
|
|
@ -46,23 +46,26 @@ enum ResolversWithTrailingComma {
|
|||
|
||||
struct Root;
|
||||
|
||||
graphql_object!(Concrete: () |&self| {
|
||||
field simple() -> i32 { 123 }
|
||||
});
|
||||
#[crate::impl_object_internal]
|
||||
impl Concrete {
|
||||
fn simple() -> i32 {
|
||||
123
|
||||
}
|
||||
}
|
||||
|
||||
graphql_union!(CustomName: () as "ACustomNamedUnion" |&self| {
|
||||
graphql_union!(CustomName: () as "ACustomNamedUnion" |&self| {
|
||||
instance_resolvers: |&_| {
|
||||
&Concrete => match *self { CustomName::Concrete(ref c) => Some(c) }
|
||||
}
|
||||
});
|
||||
|
||||
graphql_union!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
|
||||
graphql_union!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
|
||||
instance_resolvers: |&_| {
|
||||
Concrete => match *self { WithLifetime::Int(_) => Some(Concrete) }
|
||||
}
|
||||
});
|
||||
|
||||
graphql_union!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
|
||||
graphql_union!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
|
||||
instance_resolvers: |&_| {
|
||||
Concrete => match *self { WithGenerics::Generic(_) => Some(Concrete) }
|
||||
}
|
||||
|
@ -96,17 +99,30 @@ graphql_union!(ResolversWithTrailingComma: () |&self| {
|
|||
description: "A description"
|
||||
});
|
||||
|
||||
graphql_object!(<'a> Root: () as "Root" |&self| {
|
||||
field custom_name() -> CustomName { CustomName::Concrete(Concrete) }
|
||||
field with_lifetime() -> WithLifetime<'a> { WithLifetime::Int(PhantomData) }
|
||||
field with_generics() -> WithGenerics<i32> { WithGenerics::Generic(123) }
|
||||
field description_first() -> DescriptionFirst { DescriptionFirst::Concrete(Concrete) }
|
||||
field resolvers_first() -> ResolversFirst { ResolversFirst::Concrete(Concrete) }
|
||||
field commas_with_trailing() -> CommasWithTrailing { CommasWithTrailing::Concrete(Concrete) }
|
||||
field resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
|
||||
#[crate::impl_object_internal]
|
||||
impl<'a> Root {
|
||||
fn custom_name() -> CustomName {
|
||||
CustomName::Concrete(Concrete)
|
||||
}
|
||||
fn with_lifetime() -> WithLifetime<'a> {
|
||||
WithLifetime::Int(PhantomData)
|
||||
}
|
||||
fn with_generics() -> WithGenerics<i32> {
|
||||
WithGenerics::Generic(123)
|
||||
}
|
||||
fn description_first() -> DescriptionFirst {
|
||||
DescriptionFirst::Concrete(Concrete)
|
||||
}
|
||||
fn resolvers_first() -> ResolversFirst {
|
||||
ResolversFirst::Concrete(Concrete)
|
||||
}
|
||||
fn commas_with_trailing() -> CommasWithTrailing {
|
||||
CommasWithTrailing::Concrete(Concrete)
|
||||
}
|
||||
fn resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
|
||||
ResolversWithTrailingComma::Concrete(Concrete)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_type_info_query<F>(type_name: &str, f: F)
|
||||
where
|
||||
|
|
54
juniper/src/macros/tests/util.rs
Normal file
54
juniper/src/macros/tests/util.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::{DefaultScalarValue, GraphQLType, RootNode, Value, Variables};
|
||||
use std::default::Default;
|
||||
|
||||
pub fn run_query<Query, Mutation, Context>(query: &str) -> Value
|
||||
where
|
||||
Query: GraphQLType<DefaultScalarValue, TypeInfo = (), Context = Context> + Default,
|
||||
Mutation: GraphQLType<DefaultScalarValue, TypeInfo = (), Context = Context> + Default,
|
||||
Context: Default,
|
||||
{
|
||||
let schema = RootNode::new(Query::default(), Mutation::default());
|
||||
let (result, errs) =
|
||||
crate::execute(query, None, &schema, &Variables::new(), &Context::default())
|
||||
.expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_info_query<Query, Mutation, Context>(type_name: &str) -> Value
|
||||
where
|
||||
Query: GraphQLType<DefaultScalarValue, TypeInfo = (), Context = Context> + Default,
|
||||
Mutation: GraphQLType<DefaultScalarValue, TypeInfo = (), Context = Context> + Default,
|
||||
Context: Default,
|
||||
{
|
||||
let query = format!(
|
||||
r#"
|
||||
{{
|
||||
__type(name: "{}") {{
|
||||
name,
|
||||
description,
|
||||
fields {{
|
||||
name
|
||||
description
|
||||
args {{
|
||||
name
|
||||
description
|
||||
type {{
|
||||
name
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
type_name
|
||||
);
|
||||
let result = run_query::<Query, Mutation, Context>(&query);
|
||||
result
|
||||
.as_object_value()
|
||||
.expect("Result is not an object")
|
||||
.get_field_value("__type")
|
||||
.expect("__type field missing")
|
||||
.clone()
|
||||
}
|
|
@ -31,27 +31,31 @@ struct Foo {
|
|||
|
||||
struct Query;
|
||||
|
||||
graphql_object!(Query: () where Scalar = <S> |&self| {
|
||||
field int_field() -> i32 {
|
||||
#[crate::impl_object_internal(Scalar = S)]
|
||||
impl<'a, S> Query
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
fn int_field() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
field float_field() -> f64 {
|
||||
fn float_field() -> f64 {
|
||||
3.14
|
||||
}
|
||||
|
||||
field string_field() -> String {
|
||||
fn string_field() -> String {
|
||||
"".into()
|
||||
}
|
||||
|
||||
field bool_field() -> bool {
|
||||
fn bool_field() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
field enum_field(_foo: Foo) -> Enum {
|
||||
fn enum_field(_foo: Foo) -> Enum {
|
||||
Enum::EnumValue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn scalar_meta<T>(name: &'static str) -> MetaType<DefaultScalarValue>
|
||||
where
|
||||
|
|
|
@ -73,52 +73,68 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
graphql_object!(<'a> SchemaType<'a, S>: SchemaType<'a, S> as "__Schema"
|
||||
where Scalar = <S: 'a> |&self|
|
||||
#[crate::impl_object_internal(
|
||||
name = "__Schema"
|
||||
Context = SchemaType<'a, S>,
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> SchemaType<'a, S>
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
field types() -> Vec<TypeType<S>> {
|
||||
fn types(&self) -> Vec<TypeType<S>> {
|
||||
self.type_list()
|
||||
.into_iter()
|
||||
.filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false))
|
||||
.collect()
|
||||
.filter(|t| {
|
||||
t.to_concrete()
|
||||
.map(|t| t.name() != Some("_EmptyMutation"))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
field query_type() -> TypeType<S> {
|
||||
fn query_type(&self) -> TypeType<S> {
|
||||
self.query_type()
|
||||
}
|
||||
|
||||
field mutation_type() -> Option<TypeType<S>> {
|
||||
fn mutation_type(&self) -> Option<TypeType<S>> {
|
||||
self.mutation_type()
|
||||
}
|
||||
|
||||
// Included for compatibility with the introspection query in GraphQL.js
|
||||
field subscription_type() -> Option<TypeType<S>> {
|
||||
fn subscription_type(&self) -> Option<TypeType<S>> {
|
||||
None
|
||||
}
|
||||
|
||||
field directives() -> Vec<&DirectiveType<S>> {
|
||||
fn directives(&self) -> Vec<&DirectiveType<S>> {
|
||||
self.directive_list()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(<'a> TypeType<'a, S>: SchemaType<'a, S> as "__Type"
|
||||
where Scalar = <S: 'a> |&self|
|
||||
#[crate::impl_object_internal(
|
||||
name = "__Type"
|
||||
Context = SchemaType<'a, S>,
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> TypeType<'a, S>
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
field name() -> Option<&str> {
|
||||
fn name(&self) -> Option<&str> {
|
||||
match *self {
|
||||
TypeType::Concrete(t) => t.name(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
field description() -> Option<&String> {
|
||||
fn description(&self) -> Option<&String> {
|
||||
match *self {
|
||||
TypeType::Concrete(t) => t.description(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
field kind() -> TypeKind {
|
||||
fn kind(&self) -> TypeKind {
|
||||
match *self {
|
||||
TypeType::Concrete(t) => t.type_kind(),
|
||||
TypeType::List(_) => TypeKind::List,
|
||||
|
@ -126,190 +142,242 @@ graphql_object!(<'a> TypeType<'a, S>: SchemaType<'a, S> as "__Type"
|
|||
}
|
||||
}
|
||||
|
||||
field fields(include_deprecated = false: bool) -> Option<Vec<&Field<S>>> {
|
||||
#[graphql(arguments(include_deprecated(default = false)))]
|
||||
fn fields(&self, include_deprecated: bool) -> Option<Vec<&Field<S>>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) |
|
||||
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) =>
|
||||
Some(fields
|
||||
.iter()
|
||||
.filter(|f| include_deprecated || !f.deprecation_status.is_deprecated())
|
||||
.filter(|f| !f.name.starts_with("__"))
|
||||
.collect()),
|
||||
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. }))
|
||||
| TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => {
|
||||
Some(
|
||||
fields
|
||||
.iter()
|
||||
.filter(|f| include_deprecated || !f.deprecation_status.is_deprecated())
|
||||
.filter(|f| !f.name.starts_with("__"))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
field of_type() -> Option<&Box<TypeType<S>>> {
|
||||
fn of_type(&self) -> Option<&Box<TypeType<S>>> {
|
||||
match *self {
|
||||
TypeType::Concrete(_) => None,
|
||||
TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l),
|
||||
}
|
||||
}
|
||||
|
||||
field input_fields() -> Option<&Vec<Argument<S>>> {
|
||||
fn input_fields(&self) -> Option<&Vec<Argument<S>>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) =>
|
||||
Some(input_fields),
|
||||
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta {
|
||||
ref input_fields,
|
||||
..
|
||||
})) => Some(input_fields),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
field interfaces(&executor) -> Option<Vec<TypeType<S>>> {
|
||||
fn interfaces(&self, schema: &SchemaType<'a, S>) -> Option<Vec<TypeType<S>>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
|
||||
let schema = executor.context();
|
||||
Some(interface_names
|
||||
TypeType::Concrete(&MetaType::Object(ObjectMeta {
|
||||
ref interface_names,
|
||||
..
|
||||
})) => Some(
|
||||
interface_names
|
||||
.iter()
|
||||
.filter_map(|n| schema.type_by_name(n))
|
||||
.collect())
|
||||
}
|
||||
.collect(),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
field possible_types(&executor) -> Option<Vec<TypeType<S>>> {
|
||||
let schema = executor.context();
|
||||
fn possible_types(&self, schema: &SchemaType<'a, S>) -> Option<Vec<TypeType<S>>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => {
|
||||
Some(of_type_names
|
||||
TypeType::Concrete(&MetaType::Union(UnionMeta {
|
||||
ref of_type_names, ..
|
||||
})) => Some(
|
||||
of_type_names
|
||||
.iter()
|
||||
.filter_map(|tn| schema.type_by_name(tn))
|
||||
.collect())
|
||||
}
|
||||
TypeType::Concrete(&MetaType::Interface(InterfaceMeta{name: ref iface_name, .. })) => {
|
||||
Some(schema.concrete_type_list()
|
||||
.collect(),
|
||||
),
|
||||
TypeType::Concrete(&MetaType::Interface(InterfaceMeta {
|
||||
name: ref iface_name,
|
||||
..
|
||||
})) => Some(
|
||||
schema
|
||||
.concrete_type_list()
|
||||
.iter()
|
||||
.filter_map(|&ct|
|
||||
if let MetaType::Object(ObjectMeta{
|
||||
.filter_map(|&ct| {
|
||||
if let MetaType::Object(ObjectMeta {
|
||||
ref name,
|
||||
ref interface_names,
|
||||
..
|
||||
}) = *ct {
|
||||
}) = *ct
|
||||
{
|
||||
if interface_names.contains(&iface_name.to_string()) {
|
||||
schema.type_by_name(name)
|
||||
} else { None }
|
||||
} else { None }
|
||||
)
|
||||
.collect())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql(arguments(include_deprecated(default = false)))]
|
||||
fn enum_values(&self, include_deprecated: bool) -> Option<Vec<&EnumValue>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => {
|
||||
Some(
|
||||
values
|
||||
.iter()
|
||||
.filter(|f| include_deprecated || !f.deprecation_status.is_deprecated())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field enum_values(include_deprecated = false: bool) -> Option<Vec<&EnumValue>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) =>
|
||||
Some(values
|
||||
.iter()
|
||||
.filter(|f| include_deprecated || !f.deprecation_status.is_deprecated())
|
||||
.collect()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
graphql_object!(<'a> Field<'a, S>: SchemaType<'a, S> as "__Field"
|
||||
where Scalar = <S: 'a> |&self|
|
||||
#[crate::impl_object_internal(
|
||||
name = "__Field",
|
||||
Context = SchemaType<'a, S>,
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> Field<'a, S>
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
field name() -> &String {
|
||||
fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
field description() -> &Option<String> {
|
||||
fn description(&self) -> &Option<String> {
|
||||
&self.description
|
||||
}
|
||||
|
||||
field args() -> Vec<&Argument<S>> {
|
||||
self.arguments.as_ref().map_or_else(Vec::new, |v| v.iter().collect())
|
||||
fn args(&self) -> Vec<&Argument<S>> {
|
||||
self.arguments
|
||||
.as_ref()
|
||||
.map_or_else(Vec::new, |v| v.iter().collect())
|
||||
}
|
||||
|
||||
field type(&executor) -> TypeType<S> {
|
||||
executor.context().make_type(&self.field_type)
|
||||
#[graphql(name = "type")]
|
||||
fn _type(&self, context: &SchemaType<'a, S>) -> TypeType<S> {
|
||||
context.make_type(&self.field_type)
|
||||
}
|
||||
|
||||
field is_deprecated() -> bool {
|
||||
fn is_deprecated(&self) -> bool {
|
||||
self.deprecation_status.is_deprecated()
|
||||
}
|
||||
|
||||
field deprecation_reason() -> Option<&String> {
|
||||
fn deprecation_reason(&self) -> Option<&String> {
|
||||
self.deprecation_status.reason()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(<'a> Argument<'a, S>: SchemaType<'a, S> as "__InputValue"
|
||||
where Scalar = <S: 'a> |&self|
|
||||
#[crate::impl_object_internal(
|
||||
name = "__InputValue",
|
||||
Context = SchemaType<'a, S>,
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> Argument<'a, S>
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
field name() -> &String {
|
||||
fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
field description() -> &Option<String> {
|
||||
fn description(&self) -> &Option<String> {
|
||||
&self.description
|
||||
}
|
||||
|
||||
field type(&executor) -> TypeType<S> {
|
||||
executor.context().make_type(&self.arg_type)
|
||||
#[graphql(name = "type")]
|
||||
fn _type(&self, context: &SchemaType<'a, S>) -> TypeType<S> {
|
||||
context.make_type(&self.arg_type)
|
||||
}
|
||||
|
||||
field default_value() -> Option<String> {
|
||||
fn default_value(&self) -> Option<String> {
|
||||
self.default_value.as_ref().map(|v| format!("{}", v))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(EnumValue: () as "__EnumValue" where Scalar = <S> |&self| {
|
||||
field name() -> &String {
|
||||
#[crate::impl_object_internal(
|
||||
name = "__EnumValue",
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> EnumValue
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
field description() -> &Option<String> {
|
||||
fn description(&self) -> &Option<String> {
|
||||
&self.description
|
||||
}
|
||||
|
||||
field is_deprecated() -> bool {
|
||||
fn is_deprecated(&self) -> bool {
|
||||
self.deprecation_status.is_deprecated()
|
||||
}
|
||||
|
||||
field deprecation_reason() -> Option<&String> {
|
||||
fn deprecation_reason(&self) -> Option<&String> {
|
||||
self.deprecation_status.reason()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(<'a> DirectiveType<'a, S>: SchemaType<'a, S> as "__Directive"
|
||||
where Scalar = <S: 'a> |&self|
|
||||
#[crate::impl_object_internal(
|
||||
name = "__Directive",
|
||||
Context = SchemaType<'a, S>,
|
||||
Scalar = S,
|
||||
)]
|
||||
impl<'a, S> DirectiveType<'a, S>
|
||||
where
|
||||
S: crate::ScalarValue + 'a,
|
||||
{
|
||||
field name() -> &String {
|
||||
fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
field description() -> &Option<String> {
|
||||
fn description(&self) -> &Option<String> {
|
||||
&self.description
|
||||
}
|
||||
|
||||
field locations() -> &Vec<DirectiveLocation> {
|
||||
fn locations(&self) -> &Vec<DirectiveLocation> {
|
||||
&self.locations
|
||||
}
|
||||
|
||||
field args() -> &Vec<Argument<S>> {
|
||||
fn args(&self) -> &Vec<Argument<S>> {
|
||||
&self.arguments
|
||||
}
|
||||
|
||||
// Included for compatibility with the introspection query in GraphQL.js
|
||||
field deprecated "Use the locations array instead"
|
||||
on_operation() -> bool {
|
||||
#[graphql(deprecated = "Use the locations array instead")]
|
||||
fn on_operation(&self) -> bool {
|
||||
self.locations.contains(&DirectiveLocation::Query)
|
||||
}
|
||||
|
||||
// Included for compatibility with the introspection query in GraphQL.js
|
||||
field deprecated "Use the locations array instead"
|
||||
on_fragment() -> bool {
|
||||
self.locations.contains(&DirectiveLocation::FragmentDefinition) ||
|
||||
self.locations.contains(&DirectiveLocation::InlineFragment) ||
|
||||
self.locations.contains(&DirectiveLocation::FragmentSpread)
|
||||
#[graphql(deprecated = "Use the locations array instead")]
|
||||
fn on_fragment(&self) -> bool {
|
||||
self.locations
|
||||
.contains(&DirectiveLocation::FragmentDefinition)
|
||||
|| self.locations.contains(&DirectiveLocation::InlineFragment)
|
||||
|| self.locations.contains(&DirectiveLocation::FragmentSpread)
|
||||
}
|
||||
|
||||
// Included for compatibility with the introspection query in GraphQL.js
|
||||
field deprecated "Use the locations array instead"
|
||||
on_field() -> bool {
|
||||
#[graphql(deprecated = "Use the locations array instead")]
|
||||
fn on_field(&self) -> bool {
|
||||
self.locations.contains(&DirectiveLocation::Field)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -29,80 +29,93 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
|
|||
}
|
||||
});
|
||||
|
||||
graphql_object!(<'a> &'a Human: Database as "Human" |&self| {
|
||||
description: "A humanoid creature in the Star Wars universe."
|
||||
|
||||
interfaces: [&Character]
|
||||
|
||||
field id() -> &str as "The id of the human"{
|
||||
#[crate::impl_object_internal(
|
||||
Context = Database,
|
||||
Scalar = crate::DefaultScalarValue,
|
||||
interfaces = [&dyn Character],
|
||||
)]
|
||||
/// A humanoid creature in the Star Wars universe.
|
||||
impl<'a> &'a Human {
|
||||
/// The id of the human
|
||||
fn id(&self) -> &str {
|
||||
self.id()
|
||||
}
|
||||
|
||||
field name() -> Option<&str> as "The name of the human" {
|
||||
/// The name of the human
|
||||
fn name(&self) -> Option<&str> {
|
||||
Some(self.name())
|
||||
}
|
||||
|
||||
field friends(&executor) -> Vec<&Character>
|
||||
as "The friends of the human" {
|
||||
executor.context().get_friends(self.as_character())
|
||||
/// The friends of the human
|
||||
fn friends(&self, ctx: &Database) -> Vec<&Character> {
|
||||
ctx.get_friends(self.as_character())
|
||||
}
|
||||
|
||||
field appears_in() -> &[Episode] as "Which movies they appear in" {
|
||||
/// Which movies they appear in
|
||||
fn appears_in(&self) -> &[Episode] {
|
||||
self.appears_in()
|
||||
}
|
||||
|
||||
field home_planet() -> &Option<String> as "The home planet of the human" {
|
||||
/// The home planet of the human
|
||||
fn home_planet(&self) -> &Option<String> {
|
||||
self.home_planet()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(<'a> &'a Droid: Database as "Droid" |&self| {
|
||||
description: "A mechanical creature in the Star Wars universe."
|
||||
|
||||
interfaces: [&Character]
|
||||
|
||||
field id() -> &str as "The id of the droid" {
|
||||
#[crate::impl_object_internal(
|
||||
Context = Database,
|
||||
Scalar = crate::DefaultScalarValue,
|
||||
interfaces = [&dyn Character],
|
||||
)]
|
||||
/// A mechanical creature in the Star Wars universe.
|
||||
impl<'a> &'a Droid {
|
||||
/// The id of the droid
|
||||
fn id(&self) -> &str {
|
||||
self.id()
|
||||
}
|
||||
|
||||
field name() -> Option<&str> as "The name of the droid" {
|
||||
/// The name of the droid
|
||||
fn name(&self) -> Option<&str> {
|
||||
Some(self.name())
|
||||
}
|
||||
|
||||
field friends(&executor) -> Vec<&Character>
|
||||
as "The friends of the droid" {
|
||||
executor.context().get_friends(self.as_character())
|
||||
/// The friends of the droid
|
||||
fn friends(&self, ctx: &Database) -> Vec<&Character> {
|
||||
ctx.get_friends(self.as_character())
|
||||
}
|
||||
|
||||
field appears_in() -> &[Episode] as "Which movies they appear in" {
|
||||
/// Which movies they appear in
|
||||
fn appears_in(&self) -> &[Episode] {
|
||||
self.appears_in()
|
||||
}
|
||||
|
||||
field primary_function() -> &Option<String> as "The primary function of the droid" {
|
||||
/// The primary function of the droid
|
||||
fn primary_function(&self) -> &Option<String> {
|
||||
self.primary_function()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
graphql_object!(Database: Database as "Query" |&self| {
|
||||
description: "The root query object of the schema"
|
||||
|
||||
field human(
|
||||
id: String as "id of the human"
|
||||
) -> Option<&Human> {
|
||||
#[crate::impl_object_internal(
|
||||
name = "Query",
|
||||
Context = Database,
|
||||
Scalar = crate::DefaultScalarValue,
|
||||
)]
|
||||
/// The root query object of the schema
|
||||
impl Database {
|
||||
#[graphql(arguments(id(description = "id of the human")))]
|
||||
fn human(&self, id: String) -> Option<&Human> {
|
||||
self.get_human(&id)
|
||||
}
|
||||
|
||||
field droid(
|
||||
id: String as "id of the droid"
|
||||
) -> Option<&Droid> {
|
||||
#[graphql(arguments(id(description = "id of the droid")))]
|
||||
fn droid(&self, id: String) -> Option<&Droid> {
|
||||
self.get_droid(&id)
|
||||
}
|
||||
|
||||
field hero(
|
||||
episode: Option<Episode> as
|
||||
"If omitted, returns the hero of the whole saga. If provided, returns \
|
||||
the hero of that particular episode"
|
||||
) -> Option<&Character> {
|
||||
#[graphql(arguments(episode(
|
||||
description = "If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode"
|
||||
)))]
|
||||
fn hero(&self, episode: Option<Episode>) -> Option<&Character> {
|
||||
Some(self.get_hero(episode).as_character())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -344,56 +344,56 @@ pub(crate) fn schema_introspection_result() -> value::Value {
|
|||
{
|
||||
"kind": "ENUM",
|
||||
"name": "__TypeKind",
|
||||
"description": "GraphQL type kind\nThe GraphQL specification defines a number of type kinds - the meta type of a type.",
|
||||
"description": "GraphQL type kind\n\nThe GraphQL specification defines a number of type kinds - the meta type of a type.",
|
||||
"fields": Null,
|
||||
"inputFields": Null,
|
||||
"interfaces": Null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "SCALAR",
|
||||
"description": "## Scalar types\nScalar types appear as the leaf nodes of GraphQL queries. Strings, numbers, and booleans are the built in types, and while it's possible to define your own, it's relatively uncommon.",
|
||||
"description": "## Scalar types\n\nScalar types appear as the leaf nodes of GraphQL queries. Strings, numbers, and booleans are the built in types, and while it's possible to define your own, it's relatively uncommon.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "OBJECT",
|
||||
"description": "## Object types\nThe most common type to be implemented by users. Objects have fields and can implement interfaces.",
|
||||
"description": "## Object types\n\nThe most common type to be implemented by users. Objects have fields and can implement interfaces.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "INTERFACE",
|
||||
"description": "## Interface types\nInterface types are used to represent overlapping fields between multiple types, and can be queried for their concrete type.",
|
||||
"description": "## Interface types\n\nInterface types are used to represent overlapping fields between multiple types, and can be queried for their concrete type.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "UNION",
|
||||
"description": "## Union types\nUnions are similar to interfaces but can not contain any fields on their own.",
|
||||
"description": "## Union types\n\nUnions are similar to interfaces but can not contain any fields on their own.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "ENUM",
|
||||
"description": "## Enum types\nLike scalars, enum types appear as the leaf nodes of GraphQL queries.",
|
||||
"description": "## Enum types\n\nLike scalars, enum types appear as the leaf nodes of GraphQL queries.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "INPUT_OBJECT",
|
||||
"description": "## Input objects\nRepresents complex values provided in queries _into_ the system.",
|
||||
"description": "## Input objects\n\nRepresents complex values provided in queries _into_ the system.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "LIST",
|
||||
"description": "## List types\nRepresent lists of other types. This library provides implementations for vectors and slices, but other Rust types can be extended to serve as GraphQL lists.",
|
||||
"description": "## List types\n\nRepresent lists of other types. This library provides implementations for vectors and slices, but other Rust types can be extended to serve as GraphQL lists.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
},
|
||||
{
|
||||
"name": "NON_NULL",
|
||||
"description": "## Non-null types\nIn GraphQL, nullable types are the default. By putting a `!` after a type, it becomes non-nullable.",
|
||||
"description": "## Non-null types\n\nIn GraphQL, nullable types are the default. By putting a `!` after a type, it becomes non-nullable.",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": Null
|
||||
}
|
||||
|
@ -827,6 +827,7 @@ pub(crate) fn schema_introspection_result() -> value::Value {
|
|||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": Null,
|
||||
"description": "id of the droid",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
|
|
|
@ -12,33 +12,33 @@ use crate::schema::meta::{Argument, MetaType};
|
|||
|
||||
/// GraphQL type kind
|
||||
///
|
||||
/// The GraphQL specification defines a number of type kinds - the meta type
|
||||
/// The GraphQL specification defines a number of type kinds - the meta type\
|
||||
/// of a type.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)]
|
||||
#[graphql(name = "__TypeKind")]
|
||||
pub enum TypeKind {
|
||||
/// ## Scalar types
|
||||
///
|
||||
/// Scalar types appear as the leaf nodes of GraphQL queries. Strings,
|
||||
/// numbers, and booleans are the built in types, and while it's possible
|
||||
/// Scalar types appear as the leaf nodes of GraphQL queries. Strings,\
|
||||
/// numbers, and booleans are the built in types, and while it's possible\
|
||||
/// to define your own, it's relatively uncommon.
|
||||
Scalar,
|
||||
|
||||
/// ## Object types
|
||||
///
|
||||
/// The most common type to be implemented by users. Objects have fields
|
||||
/// The most common type to be implemented by users. Objects have fields\
|
||||
/// and can implement interfaces.
|
||||
Object,
|
||||
|
||||
/// ## Interface types
|
||||
///
|
||||
/// Interface types are used to represent overlapping fields between
|
||||
/// Interface types are used to represent overlapping fields between\
|
||||
/// multiple types, and can be queried for their concrete type.
|
||||
Interface,
|
||||
|
||||
/// ## Union types
|
||||
///
|
||||
/// Unions are similar to interfaces but can not contain any fields on
|
||||
/// Unions are similar to interfaces but can not contain any fields on\
|
||||
/// their own.
|
||||
Union,
|
||||
|
||||
|
@ -55,14 +55,14 @@ pub enum TypeKind {
|
|||
|
||||
/// ## List types
|
||||
///
|
||||
/// Represent lists of other types. This library provides implementations
|
||||
/// for vectors and slices, but other Rust types can be extended to serve
|
||||
/// Represent lists of other types. This library provides implementations\
|
||||
/// for vectors and slices, but other Rust types can be extended to serve\
|
||||
/// as GraphQL lists.
|
||||
List,
|
||||
|
||||
/// ## Non-null types
|
||||
///
|
||||
/// In GraphQL, nullable types are the default. By putting a `!` after a
|
||||
/// In GraphQL, nullable types are the default. By putting a `!` after a\
|
||||
/// type, it becomes non-nullable.
|
||||
#[graphql(name = "NON_NULL")]
|
||||
NonNull,
|
||||
|
|
Loading…
Reference in a new issue