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

graphql_interface and graphql_object does not mix well #814

Closed
sylvain101010 opened this issue Dec 3, 2020 · 6 comments · Fixed by #1009
Closed

graphql_interface and graphql_object does not mix well #814

sylvain101010 opened this issue Dec 3, 2020 · 6 comments · Fixed by #1009
Assignees
Labels
bug Something isn't working k::api Related to API (application interface)

Comments

@sylvain101010
Copy link

sylvain101010 commented Dec 3, 2020

Describe the bug

A struct using #[graphql_object] (for complexe fields resolvers) and #[graphql_interface] does not generate a valid schema (as_schema_language)

To Reproduce

use juniper::{graphql_interface, graphql_object, graphql_interface};

#[graphql_interface(for = User)]
pub trait Namespace {
    fn id(&self) -> Option<uuid::Uuid>;
    fn created_at(&self) -> chrono::DateTime<chrono::Utc>;
    fn name(&self) -> String;
}


#[derive(Debug, Clone)]
pub struct User {
    id: uuid::Uuid,
    created_at: chrono::DateTime<chrono::Utc>,
    name: String,
    other: String,
}

#[graphql_object]
#[graphql(impl = NamespaceValue)]
impl User {
    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<uuid::Uuid> {
        Some(self.id)
    }

    fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.created_at
    }

    fn name(&self) -> String {
        self.name.clone()
    }
}

generates

interface Namespace {
  id: Uuid
  createdAt: DateTimeUtc!
  name: String!
}


type User {
  username: String!
}

"DateTime"
scalar DateTimeUtc
"Uuid"
scalar Uuid

Expected behavior
The expected generated schema is as follow

type User implements Namespace {
  id: Uuid!
  createdAt: DateTimeUtc!
  name: String!
  username: String!
}

Additional context
branch: master
rev: 4ffd276

@sylvain101010 sylvain101010 added bug Something isn't working needs-triage labels Dec 3, 2020
@sylvain101010
Copy link
Author

Also,
When I try as follow:

#[graphql_object(
    impl = NamespaceValue,
)]
impl User {
    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<uuid::Uuid> {
        Some(self.id)
    }

    fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.created_at
    }

    fn name(&self) -> String {
        self.name.clone()
    }
}

it generates the following code:

type User implements Namespace {
  username: String!
}

while it should have generated:

type User implements Namespace {
  id: Uuid!
  createdAt: DateTimeUtc!
  name: String!
  username: String!
}

@LegNeato
Copy link
Member

LegNeato commented Dec 3, 2020

Thanks for the high-quality report! This does indeed look like a bug. I might be able to look at it over the weekend, but feel free to take a crack at it...the schema generation code is pretty straightforward and contained.

@sylvain101010
Copy link
Author

sylvain101010 commented Dec 3, 2020

After exploring the tests (https://github.com/graphql-rust/juniper/blob/a4871887bb6b30029bd4671af716c0649a0cc60c/juniper/src/tests/fixtures/starwars/schema.rs / https://github.com/graphql-rust/juniper/blob/a4871887bb6b30029bd4671af716c0649a0cc60c/juniper/src/tests/fixtures/starwars/starwars.graphql) I also noticed that the interface methods are duplicated in both in the impl Human and impl Character for Human so I'm not if it's finally a bug, or if the macro is working as expected, but is just not intuitive to work with.

For example, this code:

#[derive(Debug, Clone)]
pub struct User {
    id: uuid::Uuid,
    created_at: chrono::DateTime<chrono::Utc>,
    name: String,
}

#[graphql_object(
    impl = NamespaceInterface,
)]
impl User {
    pub fn id(&self) -> Option<ID> {
        Some(self.id.into())
    }

    pub fn created_at(&self) -> Time {
        self.created_at.into()
    }

    pub fn name(&self) -> String {
        self.name.clone()
    }

    pub fn username() -> String {
        panic!("not implemented"); // TODO
    }
}

#[graphql_interface]
impl Namespace for User {
    fn id(&self) -> Option<ID> {
        User::id(self)
    }

    fn created_at(&self) -> Time {
        User::created_at(self)
    }

    fn name(&self) -> String {
        User::name(&self)
    }
}

generates the good output, but is 'weird'

@LegNeato
Copy link
Member

LegNeato commented Dec 4, 2020

Not being intuitive is a bug :-)

@tyranron
Copy link
Member

tyranron commented Dec 4, 2020

@LegNeato @skerkour

The problem is that #[graphql_object] macro expansion cannot work with multiple attributes, like #[graphql_interface] does. That's why:

#[graphql_object]
#[graphql(impl = NamespaceValue)]
impl User {

is invalid definition at the moment, where #[graphql(impl = NamespaceValue)] attribute is fully omitted.

Even if #[graphql_object] would have supported that, it should have been defined like that:

#[graphql_object]
#[graphql_object(impl = NamespaceValue)]
impl User {

like the #[graphql_interface]/#[graphql_union] allows at the moment.

This is for the reasons explained here. But I think that point needs re-checking.

Also, need to think about some compile-time check to prevent the situation described above, so it would be complain about User not implementing Namespace in schema.

@tyranron tyranron added k::api Related to API (application interface) and removed good-first-issue labels Jul 21, 2021
@tyranron tyranron self-assigned this Jul 21, 2021
@ilslv ilslv mentioned this issue Jan 6, 2022
15 tasks
tyranron pushed a commit that referenced this issue Jan 26, 2022
- remove support for `#[graphql_interface(dyn)]`
- describe all interface trait methods with type's fields or impl block instead of `#[graphql_interface]` attribute on `impl Trait`
- forbid default impls on non-skipped trait methods
- support additional nullable arguments on implementer
- support returning sub-type on implementer
@tyranron
Copy link
Member

Also, need to think about some compile-time check to prevent the situation described above, so it would be complain about User not implementing Namespace in schema.

With #1009 being merged we have compile-time checks for that, even with a human-readable error message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working k::api Related to API (application interface)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants