dynamic-graphql-derive 0.10.2

Dynamic GraphQL schema macro
Documentation
use dynamic_graphql::App;
use dynamic_graphql::Interface;
use dynamic_graphql::ResolvedObject;
use dynamic_graphql::ResolvedObjectFields;
use dynamic_graphql::SimpleObject;

use crate::schema_utils::normalize_schema;

#[test]
fn test_schema_simple_object_mark_with() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> String;
    }

    #[derive(SimpleObject)]
    #[graphql(mark(Node))]
    struct FooNode {
        the_id: String,
    }

    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish().unwrap();

    let sdl = schema.sdl();
    insta::assert_snapshot!(normalize_schema(&sdl), @r#"
    type FooNode implements Node {
      theId: String!
    }

    interface Node {
      theId: String!
    }

    type Query {
      foo: FooNode!
    }

    "Directs the executor to include this field or fragment only when the `if` argument is true."
    directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    "Directs the executor to skip this field or fragment when the `if` argument is true."
    directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    schema {
      query: Query
    }
    "#);
}

#[test]
fn test_schema_simple_object_with_implement() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> &String;
    }

    #[derive(SimpleObject)]
    #[graphql(implements(Node))]
    struct FooNode {
        some_field: String,
        #[graphql(skip)]
        id: String,
    }

    impl Node for FooNode {
        fn the_id(&self) -> &String {
            &self.id
        }
    }

    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish().unwrap();

    let sdl = schema.sdl();
    insta::assert_snapshot!(normalize_schema(&sdl), @r#"
    type FooNode implements Node {
      someField: String!
      theId: String!
    }

    interface Node {
      theId: String!
    }

    type Query {
      foo: FooNode!
    }

    "Directs the executor to include this field or fragment only when the `if` argument is true."
    directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    "Directs the executor to skip this field or fragment when the `if` argument is true."
    directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    schema {
      query: Query
    }
    "#);
}

#[test]
fn test_schema_simple_object_with_error() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> String;
    }

    #[derive(SimpleObject)]
    #[graphql(mark(Node))]
    struct FooNode {
        other_field: String,
    }

    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish();

    assert!(schema.is_err());
    assert_eq!(
        schema.err().unwrap().to_string(),
        r#"Object "FooNode" requires field "theId" defined by interface "Node""#
    );
}

#[test]
fn test_schema_resolved_object_mark_with() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> String;
    }

    #[derive(ResolvedObject)]
    #[graphql(mark(Node))]
    struct FooNode;

    #[ResolvedObjectFields]
    impl FooNode {
        fn the_id(&self) -> String {
            "the_id".to_string()
        }
    }
    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish().unwrap();

    let sdl = schema.sdl();
    insta::assert_snapshot!(normalize_schema(&sdl), @r#"
    type FooNode implements Node {
      theId: String!
    }

    interface Node {
      theId: String!
    }

    type Query {
      foo: FooNode!
    }

    "Directs the executor to include this field or fragment only when the `if` argument is true."
    directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    "Directs the executor to skip this field or fragment when the `if` argument is true."
    directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    schema {
      query: Query
    }
    "#);
}

#[test]
fn test_schema_resolved_object_with_implement() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> String;
    }

    #[derive(ResolvedObject)]
    #[graphql(implements(Node))]
    struct FooNode;

    #[ResolvedObjectFields]
    impl FooNode {
        fn other_fields(&self) -> String {
            "other".to_string()
        }
    }

    impl Node for FooNode {
        fn the_id(&self) -> String {
            "the_id".to_string()
        }
    }

    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish().unwrap();

    let sdl = schema.sdl();
    insta::assert_snapshot!(normalize_schema(&sdl), @r#"
    type FooNode implements Node {
      otherFields: String!
      theId: String!
    }

    interface Node {
      theId: String!
    }

    type Query {
      foo: FooNode!
    }

    "Directs the executor to include this field or fragment only when the `if` argument is true."
    directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    "Directs the executor to skip this field or fragment when the `if` argument is true."
    directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

    schema {
      query: Query
    }
    "#);
}

#[test]
fn test_schema_resolved_object_with_error() {
    #[Interface]
    trait Node {
        fn the_id(&self) -> String;
    }

    #[derive(ResolvedObject)]
    #[graphql(mark(Node))]
    struct FooNode;

    #[ResolvedObjectFields]
    impl FooNode {
        fn other_field(&self) -> String {
            "the_id".to_string()
        }
    }

    #[derive(SimpleObject)]
    #[graphql(root)]
    struct Query {
        foo: FooNode,
    }

    #[derive(App)]
    struct App(Query, FooNode);

    let schema = App::create_schema().finish();

    assert!(schema.is_err());
    assert_eq!(
        schema.err().unwrap().to_string(),
        r#"Object "FooNode" requires field "theId" defined by interface "Node""#
    );
}