Juniper: graphql_interface and graphql_object does not mix well

Created on 3 Dec 2020  路  5Comments  路  Source: graphql-rust/juniper

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: 4ffd276a5b33ae4423f295675f977ed6cc7fb88a

bug good-first-issue

Most helpful comment

Not being intuitive is a bug :-)

All 5 comments

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!
}

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.

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'

Not being intuitive is a bug :-)

@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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

davidpdrsn picture davidpdrsn  路  5Comments

ysimonson picture ysimonson  路  5Comments

snnsnn picture snnsnn  路  4Comments

theduke picture theduke  路  4Comments

hrbigelow picture hrbigelow  路  5Comments