Fragments not working with interfaces
When I create a Fragment with a type which is an interface, or has fields which are interfaces, the generated code does not work at runtime.
For example let's have this schema:
interface Book {
title: String!
author: String!
}
type Textbook implements Book {
title: String!
author: String!
courses: [String!]!
}
type ColoringBook implements Book {
title: String!
author: String!
colors: [String!]!
}
type Query {
books: [Book!]!
}
And this query:
fragment BookFragment on Book {
title
... on Textbook {
courses
}
... on ColoringBook {
colors
}
}
query GetBooks {
books {
...BookFragment
}
}
These parameters are set on build.yaml:
when_extensions:
when: true
maybeWhen: true
When we generate the code, we can see that nothing implements GBookFragment__asTextbook, yet we have this kind of generted code:
extension GBookFragmentWhenExtension on GBookFragment {
_T when<_T>({
required _T Function(GBookFragment__asTextbook) textbook,
required _T Function(GBookFragment__asColoringBook) coloringBook,
required _T Function() orElse,
}) {
switch (G__typename) {
case 'Textbook':
return textbook((this as GBookFragment__asTextbook));
case 'ColoringBook':
return coloringBook((this as GBookFragment__asColoringBook));
default:
return orElse();
}
}
}
Since nothing implements GBookFragment__asTextbook, the cast (this as GBookFragment__asTextbook) will fail.
Is there a workaround for that?
There's actually a deeper bug, the subtype-specific data does not even end up in the models
final response = GGetBooksData.fromJson({
"books": [
{
"__typename": "Textbook",
"title": "Textbook",
"courses": ["Math", "Science"]
},
{
"__typename": "ColoringBook",
"title": "ColoringBook",
"colors": ["Red", "Blue"]
}
]
});
final GBookFragment textbook = response!.books[0]; // is of type _$GGetBooksData_books, with no data from the type conditions
@knaeckeKami have you an idea on how we can fix this issue? Is it an issue from ferry or from another package?
It's an issue in the code generation in gql_code_builder. I didn't have time to look into this bug specifically yet. It might be an issue in the mergeSelections() or buildInlineFragmentClasses() function.
FWIW this seems to only happen when fragments are nested. So as a workaround you could try to flatten the query.
@nhannah Pretty sure this is the issue you were talking about 👀
Hi, I had some time to look into the issue. I don't know what is really causing the issue, but I came up with what should like the data.gql.dart file :
In the class GGetBooksData, instead of BuiltList<GGetBooksData_books> get books we should have BuiltList<GBookFragmentData> get books.
The class GGetBooksData_books does not seem to be useful in this case.
Now I need to see how to change this 😅
Cool! I also looked a bit into it already.
I think there is something what with the mergeSelections function that doesn't handle nested fragments with type conditions properly
@bestdan this is the issue we are seeing today
@letsar This issue should now be resolved on the master branch of gql_code_builder. I'd appreciate your feedback!
Hi @knaeckeKami, I had finally the time to test this solution a little more. In a simple case it seems to work, but in a more complex one I have code generation issues.
Fore example let's take this schema
interface Author {
displayName: String!
}
type Person implements Author {
firstName: String!
lastName: String!
displayName: String!
}
type Company implements Author {
name: String!
displayName: String!
}
interface Book {
title: String!
author: Author!
}
type Textbook implements Book {
title: String!
author: Author!
courses: [String!]!
}
type ColoringBook implements Book {
title: String!
author: Author!
colors: [String!]!
}
type Query {
books: [Book!]!
}
and this query
fragment AuthorFragment on Author {
displayName
... on Person {
firstName
lastName
}
... on Company {
name
}
}
fragment BookFragment on Book {
author {
...AuthorFragment
}
title
... on Textbook {
courses
}
... on ColoringBook {
colors
}
}
query GetBooks {
books {
...BookFragment
}
}
In the *.data.gql.dart there are some unknown implements generated (GBookFragment__asCompany and GBookFragment__asPerson).
In my project I have another issue, but I need to create a simple repro first before sharing with you.
@knaeckeKami have you an insight on how to fix the late issue? If you have in mind a workaround it can work too 😅 . We have a lot of interfaces at multiple levels in our graph and I would love to know how to not have to duplicate a lot of code.
Sorry, I do not have a workaround or fix at the moment. I think there is a pretty fundamental issue in the gql_code_builder, and fixing this would be a greater change - I do not have time to do this at the moment.
Thank you for your response @knaeckeKami. I understand this can take a lot of time.