Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TypeDirective for ArgumentDefinition, Enum, EnumValue, InputFieldDefinition, InputObject, Interface #1509

Merged

Conversation

tomoikey
Copy link
Contributor

@tomoikey tomoikey commented Apr 9, 2024

Purpose

Up until now, TypeDirective was only supported for FIELD_DEFINITION and OBJECT.
However, this was insufficient, so I have made adjustments to allow the implementation of TypeDirective for ArgumentDefinition, Enum, EnumValue, InputFieldDefinition, InputObject, and Interface as well.
I have attached the complete sample code and the outputted GraphQL schema below for your review.
I would appreciate it if you could take a look.

Sample Code

Rust

#[TypeDirective(location = "FieldDefinition")]
fn type_directive_field_definition(description: String) {}

#[TypeDirective(location = "ArgumentDefinition")]
fn type_directive_argument_definition(description: String) {}

#[TypeDirective(location = "InputFieldDefinition")]
fn type_directive_input_field_definition(description: String) {}

#[TypeDirective(location = "Object")]
fn type_directive_object(description: String) {}

#[TypeDirective(location = "InputObject")]
fn type_directive_input_object(description: String) {}

#[TypeDirective(location = "Enum")]
fn type_directive_enum(description: String) {}

#[TypeDirective(location = "EnumValue")]
fn type_directive_enum_value(description: String) {}

#[TypeDirective(location = "Interface")]
fn type_directive_interface(description: String) {}

#[derive(InputObject)]
#[graphql(directive = type_directive_input_object::apply("This is INPUT_OBJECT in InputObject".to_string()))]
struct TestInput {
    #[graphql(directive = type_directive_input_field_definition::apply("This is INPUT_FIELD_DEFINITION".to_string()))]
    field: String,
}

#[derive(OneofObject)]
#[graphql(directive = type_directive_input_object::apply("This is INPUT_OBJECT in OneofObject".to_string()))]
enum TestOneOfObject {
    #[graphql(directive = type_directive_input_field_definition::apply("This is INPUT_FIELD_DEFINITION in OneofObject".to_string()))]
    Foo(String),
    #[graphql(directive = type_directive_input_field_definition::apply("This is INPUT_FIELD_DEFINITION in OneofObject".to_string()))]
    Bar(i32),
}

#[derive(SimpleObject)]
#[graphql(directive = type_directive_object::apply("This is OBJECT in SimpleObject".to_string()))]
struct TestSimpleObject {
    #[graphql(directive = type_directive_field_definition::apply("This is FIELD_DEFINITION in SimpleObject".to_string()))]
    field: String,
}

#[derive(SimpleObject)]
#[graphql(complex, directive = type_directive_object::apply("This is OBJECT in (Complex / Simple)Object".to_string()))]
struct TestComplexObject {
    #[graphql(directive = type_directive_field_definition::apply("This is FIELD_DEFINITION in (Complex / Simple)Object".to_string()))]
    field: String,
}

#[ComplexObject]
impl TestComplexObject {
    async fn test(
        &self,
        #[graphql(directive = type_directive_argument_definition::apply("This is ARGUMENT_DEFINITION in ComplexObject".to_string()))]
        _arg: String,
    ) -> &'static str {
        "test"
    }
}

#[derive(Enum, Copy, Clone, PartialEq, Eq)]
#[graphql(directive = type_directive_enum::apply("This is ENUM in Enum".to_string()))]
enum TestEnum {
    #[graphql(directive = type_directive_enum_value::apply("This is ENUM_VALUE in Enum".to_string()))]
    Foo,
    #[graphql(directive = type_directive_enum_value::apply("This is ENUM_VALUE in Enum".to_string()))]
    Bar,
}

struct TestObjectForInterface;

#[Object]
impl TestObjectForInterface {
    async fn field(
        &self,
        #[graphql(directive = type_directive_argument_definition::apply("This is ARGUMENT_DEFINITION in Interface".to_string()))]
        _arg: String
    ) -> &'static str {
        "hello"
    }
}
#[derive(Interface)]
#[graphql(
    field(
        name = "field",
        ty = "String",
        directive = type_directive_field_definition::apply("This is INTERFACE in Interface".to_string()),
        arg(
            name = "_arg",
            ty = "String",
            directive = type_directive_argument_definition::apply("This is ARGUMENT_DEFINITION in Interface".to_string())
        )
    ),
    directive = type_directive_interface::apply("This is INTERFACE in Interface".to_string())
)]
enum TestInterface {
    TestSimpleObjectForInterface(TestObjectForInterface),
}

struct Query;

#[Object]
impl Query {
    pub async fn test_argument(
        &self,
        #[graphql(directive = type_directive_argument_definition::apply("This is ARGUMENT_DEFINITION in Object".to_string()))]
        _arg: String,
    ) -> &'static str {
        "hello"
    }

    pub async fn test_input_object(&self, _arg: TestInput) -> &'static str {
        "hello"
    }

    pub async fn test_complex_object(&self) -> TestComplexObject {
        TestComplexObject {
            field: "hello".to_string(),
        }
    }

    pub async fn test_simple_object(&self) -> TestSimpleObject {
        TestSimpleObject {
            field: "hello".to_string(),
        }
    }

    pub async fn test_one_of_object(&self, _arg: TestOneOfObject) -> &'static str {
        "hello"
    }

    pub async fn test_enum(&self, _arg: TestEnum) -> &'static str {
        "hello"
    }

    pub async fn test_interface(&self) -> TestObjectForInterface {
        TestObjectForInterface
    }
}

let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
    .register_output_type::<TestInterface>()
    .finish();
let sdl = schema.sdl();
let expected = include_str!("schemas/test_fed2_compose_2.schema.graphql");
assert_eq!(expected, sdl);

Generated GraphQL

directive @oneOf on INPUT_OBJECT





type Query {
	testArgument(arg: String! @type_directive_argument_definition(description: "This is ARGUMENT_DEFINITION in Object")): String!
	testInputObject(arg: TestInput!): String!
	testComplexObject: TestComplexObject!
	testSimpleObject: TestSimpleObject!
	testOneOfObject(arg: TestOneOfObject!): String!
	testEnum(arg: TestEnum!): String!
	testInterface: TestObjectForInterface!
}


type TestComplexObject @type_directive_object(description: "This is OBJECT in (Complex / Simple)Object") {
	field: String! @type_directive_field_definition(description: "This is FIELD_DEFINITION in (Complex / Simple)Object")
	test(arg: String! @type_directive_argument_definition(description: "This is ARGUMENT_DEFINITION in ComplexObject")): String!
}

enum TestEnum @type_directive_enum(description: "This is ENUM in Enum") {
	FOO @type_directive_enum_value(description: "This is ENUM_VALUE in Enum")
	BAR @type_directive_enum_value(description: "This is ENUM_VALUE in Enum")
}

input TestInput @type_directive_input_object(description: "This is INPUT_OBJECT in InputObject") {
	field: String! @type_directive_input_field_definition(description: "This is INPUT_FIELD_DEFINITION")
}

interface TestInterface @type_directive_interface(description: "This is INTERFACE in Interface") {
	field(arg: String! @type_directive_argument_definition(description: "This is ARGUMENT_DEFINITION in Interface")): String! @type_directive_field_definition(description: "This is INTERFACE in Interface")
}

type TestObjectForInterface implements TestInterface {
	field(arg: String! @type_directive_argument_definition(description: "This is ARGUMENT_DEFINITION in Interface")): String!
}

input TestOneOfObject @oneOf @type_directive_input_object(description: "This is INPUT_OBJECT in OneofObject") {
	foo: String @type_directive_input_field_definition(description: "This is INPUT_FIELD_DEFINITION in OneofObject")
	bar: Int @type_directive_input_field_definition(description: "This is INPUT_FIELD_DEFINITION in OneofObject")
}

type TestSimpleObject @type_directive_object(description: "This is OBJECT in SimpleObject") {
	field: String! @type_directive_field_definition(description: "This is FIELD_DEFINITION in SimpleObject")
}

directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @type_directive_argument_definition(description: String!) on ARGUMENT_DEFINITION
directive @type_directive_enum(description: String!) on ENUM
directive @type_directive_enum_value(description: String!) on ENUM_VALUE
directive @type_directive_field_definition(description: String!) on FIELD_DEFINITION
directive @type_directive_input_field_definition(description: String!) on INPUT_FIELD_DEFINITION
directive @type_directive_input_object(description: String!) on INPUT_OBJECT
directive @type_directive_interface(description: String!) on INTERFACE
directive @type_directive_object(description: String!) on OBJECT
schema {
	query: Query
}

@xamgore
Copy link
Contributor

xamgore commented Apr 27, 2024

Well, that's definitely a worthy PR, thank you so much!

@sunli829, could you check it, please? Seems like the most of the code has the same foundation as FIELD_DEFINITION, tests are included.

@sunli829 sunli829 merged commit ba9b34b into async-graphql:master May 1, 2024
2 of 7 checks passed
@sunli829
Copy link
Collaborator

sunli829 commented May 1, 2024

thanks 🙂

@xamgore
Copy link
Contributor

xamgore commented May 1, 2024

@tomoikey could you check the failed checks above? Does MSRV need update?

Sorry, my bad. It has appeared due to the consequential avalanche merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants