graphql-rust / juniper

GraphQL server library for Rust
Other
5.72k stars 425 forks source link

Interface not working as expected with imlp #1161

Closed IvanRodriCalleja closed 1 year ago

IvanRodriCalleja commented 1 year ago

Hi,

I don't know if the problems is a misunderstood from my side, if i am using interface wrong or if it is an issue. The problem is related to using an interface when i implement the interface to my models (via impl not struct) the definition of the interface is not in the result schema i have to define it in the implementation but then it uses the interface method.

Let me explain it better, i am doing an initial POC implementing the relay schema (at the moment not conections) to see how to have relay support in Juniper so i created the following:

//node.rs
#[graphql_interface(for = [Faction, Ship])]
pub trait Node {
    fn id(&self) -> ID;
}

//faction.rs
pub struct Faction {
    pub id: String,
    pub name: String,
}

#[graphql_interface]
impl Node for Faction {
    fn id(&self) -> ID {
        ID::new(&self.id)
    }
}

#[graphql_object(impl = NodeValue)]
impl Faction {
    fn name(&self) -> &str {
        &self.name
    }
}

//ship.rs
pub struct Ship {
    pub id: String,
    pub name: String,
}

#[graphql_interface]
impl Node for Ship {
    fn id(&self) -> ID {
        ID::new(&self.id)
    }
}

#[graphql_object(impl = NodeValue)]
impl Ship {
    fn name(&self) -> &str {
        &self.name
    }
}

I created a Node interface with an id function and each model has the Node implementation for the interface, the result of this schema is:

type Ship implements Node {
  name: String!
}

type Faction implements Node {
  name: String!
}

interface Node {
  id: ID!
}

From what i understand this is not correct and the schema result should be:

type Ship implements Node {
  id: ID!
  name: String!
}

type Faction implements Node {
  id: ID!
  name: String!
}

interface Node {
  id: ID!
}

If i want to have this definition i should have the following rust implementation:

//node.rs
#[graphql_interface(for = [Faction, Ship])]
pub trait Node {
    fn id(&self) -> ID;
}

//faction.rs
pub struct Faction {
    pub id: String,
    pub name: String,
}

#[graphql_interface]
impl Node for Faction {
    fn id(&self) -> String {
        ID::new(&self.id)
    }
}

#[graphql_object(impl = NodeValue)]
impl Faction {
    fn id(&self) -> String {
        ID::new(&self.id)
    }

    fn name(&self) -> &str {
        &self.name
    }
}

//ship.rs
pub struct Ship {
    pub id: String,
    pub name: String,
}

#[graphql_interface]
impl Node for Ship {
    fn id(&self) -> String {
        ID::new(&self.id)
    }
}

#[graphql_object(impl = NodeValue)]
impl Ship {
    fn id(&self) -> String {
        ID::new(&self.id)
    }

    fn name(&self) -> &str {
        &self.name
    }
}

As you can see i added the id function resolver to each of the implementation so now it is duplicated but the schema is how i want it to be.

When i consume any of the model throw the NodeValue for examlpe for NodeValue::Ship() (like the node(id: "") query) the id is resolved by the impl Node for Ship interface but if i have a simple relation like Vec<Ship> this is resolved by ´impl Ship´ function but i expect to always use the id function from impl Node for Ship.

So my question is, is it the expected behaviour and i understood wrong? is the implementation wrong on my side? do you think it is a bug?

IvanRodriCalleja commented 1 year ago

I created a reproduction repository just in case it is easier https://github.com/IvanRodriCalleja/juniper_interface_issue

ilslv commented 1 year ago

@IvanRodriCalleja this is a known issue on 0.15.x that is already fixed on master. There is no plans to backport this fix to 0.15.x, as master Interfaces are completely redesigned.

IvanRodriCalleja commented 1 year ago

Works perfect!!

Thanks to point to the issues, i didn't find them.