-
Notifications
You must be signed in to change notification settings - Fork 125
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
[gql_code_builder ] bugfix for reuse-fragments feature #417
base: master
Are you sure you want to change the base?
[gql_code_builder ] bugfix for reuse-fragments feature #417
Conversation
…e-fragments. - Fix reuse-fragments for inline fragments serialization runtime error. - Fix reuse-fragments for inline fragments: - Below query will consider CommunityContent interface while deciding reuse-fragment of CommunityFeed which implements CommunityContent. ```graphql node: { ... on CommunityContent { liked viewed } ... on CommunityFeed { ...CommunityFeedFragment } ... on CommunityReply { ...CommunityReplyFragment } } ``` - Additionally fix WhenExtension to not to create redundant mappers for interfaces which doesn't make sense because interfaces don't have __typename. (cherry picked from commit 7206742)
…not-existing fragment used
Thanks, I'll have a proper look later. One thing that i noticed on the first glance, you seem to have implemented another way of calculating the possibleTypes map. Would it be feasible to leverage https://github.com/gql-dart/gql/blob/master/codegen/gql_code_builder/lib/src/utils/possible_types.dart instead ? |
sorry for the late response! |
@knaeckeKami how are you doing well? just merged upstream/master branch. |
Thanks! It seems there are now difference in the generated code in end_to_end_test, despite the reuse_fragments flag being disabled. |
@knaeckeKami oh, sorry for unexpected trouble. Is that related to WhenExtension ? |
Not sure yet, didn't have a proper look, but you can check the diff in the CI output: https://github.com/gql-dart/gql/actions/runs/6485343593/job/17611211975?pr=417 |
superclassSelections: { | ||
name: SourceSelections(url: null, selections: selections), | ||
...Map.fromEntries(superclassSelections.entries.map((e) => MapEntry( | ||
"${e.key}__as${inlineFragment.typeCondition!.on.name.value}", | ||
e.value))), | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@knaeckeKami I think the cause is that this fix about buildInlineFragmentClasses
is not checking the feature flag.
This fix is made to let ..FragmentData
classes cover more interfaces like by adding GheroFieldsFragment__asHuman
interface to GheroFieldsFragmentData__asHuman
. And I think changes made by this are all additions for missing interfaces but no regressions of existing interfaces. So there would be no regression issue even without the feature flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just run the code generation without the flag and unfortunately, it produces code that does not compile.
the following graphql document caues problems:
query HeroWithInterfaceSubTypedFragments($episode: Episode!) {
hero(episode: $episode) {
...heroFieldsFragment
}
}
fragment heroFieldsFragment on Character {
id
name
...on Human {
...humanFieldsFragment
}
...on Droid {
...droidFieldsFragment
}
}
fragment humanFieldsFragment on Human {
homePlanet
friends {
...on Droid {
id
name
...droidFieldsFragment
}
...on Human {
id
name
homePlanet
}
}
}
fragment droidFieldsFragment on Droid {
primaryFunction
}
this adds another superclass to GheroFieldsFragmentData__asHuman which conflicts with the implementation:
'GheroFieldsFragmentData__asHuman.friends' ('BuiltList<GheroFieldsFragmentData__asHuman_friends?>? Function()') isn't a valid override of 'GheroFieldsFragment__asHuman.friends' ('BuiltList<GheroFieldsFragment__asHuman_friends?>? Function()'). (Documentation)
The member being overridden (hero_with_interface_subtyped_fragments.data.gql.dart:151).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@knaeckeKami sorry to bother you, I will turn off new logics when there is no feature flag. Will share again soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no worries! i really appreciate you working on this! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@knaeckeKami
How are you doing well? Finally came back here.
Sorry for late response. Just made the update. 😀
Thanks! A user reported some issues with unions with a lot of possible values, see here: https://github.com/LiLatee/flutter_ferry_unions_issue I noticed that the DFS searches in all permutations of the given union types, which leads to exponential code generation time . This is a possible fix: https://github.com/gql-dart/gql/tree/possible_reuse_fragment_fix Please add a test that checks if this bug is fixed by this PR |
Hi @knaeckeKami @dehypnosis! 😄 I've prepared a repo with an example that generates a code with errors. In short, overridden fields have a wrong return type. I think that the problem exists because I have two different fragments defined on a single type defined in schema. In my case I have two fragments:: Is there a quick solution for that? 🤔 |
I've spent a few more hours and created an even much simpler example to reproduce the issue and I really cannot understand what's the problem here. I've found three solutions to fix the issue by modifying fragments:
Each of these modifications fixes the issue and I need to use just one of them. Is there a chance that you could take a look at that in the near future? 🙏 |
Hi! I also looked a bit into it.
query HeroWithInterfaceSubTypedFragments($episode: Episode!) {
hero(episode: $episode) {
...heroFieldsFragment
}
}
fragment heroFieldsFragment on Character {
id
name
...on Human {
...humanFieldsFragment
}
...on Droid {
...droidFieldsFragment
}
}
fragment humanFieldsFragment on Human {
homePlanet
friends {
...on Droid {
id
name
...droidFieldsFragment
}
...on Human {
id
name
homePlanet
}
}
}
fragment droidFieldsFragment on Droid {
primaryFunction
}
And it seems to be triggered by Inline fragment spreads with typeconditon where the selection is just one fragment spread. But I don't know much more yet. |
I now understand what the issue is. I found a way to fix it in gql_code_builder, but not sure if this breaks smth else. @LiLatee can you try dependency_overrides:
gql_code_builder:
git:
url: https://github.com/gql-dart/gql/tree/repro_reuse_fragments
ref: repro_reuse_fragments
path: codegen/gql_code_builder and give me feedback? |
@knaeckeKami btw I guess that's a proper dependency overrides gql_code_builder:
git:
ref: repro_reuse_fragments
url: https://github.com/gql-dart/gql.git
path: codegen/gql_code_builder |
yes, thanks for fixing the broken link. Sorry. I did not push all the code. please try again. |
Ok, I had a suspicion that it would be not that easy ;) Would appreciate if you could create failing reproduction to the end_to_end_test repo. But I know now what the issue is, and I think I can find a fix in the near future. |
Sure, I can try to take care of it tomorrow 👌
Would be awesome 🤞 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented on the code I think is the culprit
typeOverrides: typeOverrides, | ||
superclassSelections: { | ||
name: SourceSelections(url: null, selections: selections), | ||
if (dataClassAliasMap.isNotEmpty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this condition is not correct.
Inline Fragments can add new fields in selections which can cause conflicts in extending the fragments.
It also causes the build error in the end to end test package.
When Removing this, it works again
@knaeckeKami should I create a failing case for the code before you commented that condition above? |
Failing in the sense that it's more code or that the code doesn't compile? Anyway, yes please share. I know that this might increase the code size but this optimization is not correct in the way it is now. But maybe we can find I way to make it work again. |
Sorry guys, I'm busy doing my day works nowadays. I will dive into to take a look in this weekend if the issue is not resolved until then. |
@knaeckeKami oh I've finally found one issue. It looks like the order of the imports in .graphql files is important. This example uses repro_reuse_fragments version (commit 6ae9dfa) |
Hi @knaeckeKami
This PR is a succession of #413
After the above PR, our team have developed our ongoing project with dev release version which includes above PR.
And we made a small improvement and a bugfix to make the reuse-fragment feature stable.
So here is a PR to finalize this feature.
Changes
ferry_generator:graphql_builder.data_class_config.reuse_fragments
option enabled.