Intended outcome:
I have a query like this in my app:
fragment ItemIndexFragment on Item {
id
name
... on SpecialItem {
field1
field2
}
... on ExtraSpecialItem {
field3
field4
}
}
Expected outcome is that this query just works with a watchQuery
.
Actual outcome:
While upgrading ember-apollo-client to the latest apollo-client (and my app that uses it), I encountered a specific route that broke during the upgrade. The watchQuery
observable's next
function gets called with data: undefined
and stale: true
. AFAICT, the only thing special about this route is the fact that it is on an Interface type and includes conditional fragments depending on the result type.
If I remove the conditional fragments, the query "works", except without the data I want.
I was upgrading all the way from v0.6.0. Bisecting with apollo-client versions revealed that the query worked as expected all the way through v0.8.7. At v0.9.0, I encountered https://github.com/apollographql/apollo-client/issues/1336, and with v0.10.1, I experience the described broken behavior.
How to reproduce the issue:
Issue a watchQuery
on a Interface type with a query that includes conditional fragments on the result type.
Unsure if this is related to #1337 but it's the only thing I could find that seemed like it might be.
Edit: d'oh, updated to reflect that this was a query on an Interface type, not a Union
Hi @bgentry! Thanks for the excellent bug report. I think I know where the issue comes from. Am I right in guessing that in order to make it work pre 0.9 you had to pass returnPartialData: true
?
Can you share the piece of the schema that defines Item
, SpecialItem
, ExtraSpecialItem
and their relationship? Or if you could write a failing test case that reproduces this issue, that would be even better!
Am I right in guessing that in order to make it work pre 0.9 you had to pass returnPartialData: true?
Nope! The call to watchQuery
has no arguments other than { query }
.
Also just realized that I was mistaken earlier. It's not a Union, it's an Interface type. Have been away from GraphQL for a few weeks :flushed:
While it's not a reproduction, this sort of schema (excerpt from the Star Wars schema) should be enough to reveal the issue:
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
And then the following query:
query characters {
characters {
name
... on Human {
totalCredits
}
}
}
@bgentry I have a hunch that this schema and query with a single fragment won't be enough to reproduce the issue, because if all the data for that fragment is in the store, it should work (I'm pretty sure we test that exact case). As far as I know the fragment matching currently eagerly matches everything, so if the data is in the store, it will be returned.
I suspect that the error you observed arises when the fragment doesn't match in reality. Apollo Client probably assumes that some fragment should have matched, but because none matched it thinks that the data returned is partial and thus throws an error. I'll look into this first chance I get to check if that's really the case.
I'll look more closely at the server response and see if there's anything
unusual like that going on. Otherwise I'll see if I can put together an
actual reproduction.
On Sun, Mar 5, 2017 at 11:36 PM Jonas Helfer notifications@github.com
wrote:
@bgentry https://github.com/bgentry I have a hunch that this schema and
query with a single fragment won't be enough to reproduce the issue,
because if all the data for that fragment is in the store, it should work
(I'm pretty sure we test that exact case). As far as I know the fragment
matching currently eagerly matches everything, so if the data is in the
store, it will be returned.I suspect that the error you observed arises when the fragment doesn't
match in reality. Apollo Client probably assumes that some fragment
should have matched, but because none matched it thinks that the data
returned is partial and thus throws an error. I'll look into this first
chance I get to make sure I actually know how the fragment matching works.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/apollo-client/issues/1363#issuecomment-284322831,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAG9cUBr4i1Rlmav3_DDJizdxy7ABGC4ks5ri7eMgaJpZM4MTmOT
.
@helfer the server appears to be returning all requested fields. All of the expected data is in the store (confirmed with the Apollo Chrome extension and the Redux extension).
I played around with the server to limit the types of objects returned, and I narrowed it down substantially. My interface has two implementation types. One of them adds extra fields, the other contains only the fields in the base interface type. Consider the case where this is added to the above schema:
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
If I modify the server handler so that it only returns a single type (i.e. Human
in this example), everything loads without errors. If I also load the type that doesn't have any of its own fields (aside from those from the interface type), then I get the error.
Edit:
I also tried modifying my query so that it fetches unnecessary fields for the type that doesn't have any extra fields:
... on Droid {
name
}
But the problem persists in that case. Maybe it's something to do with how fragments are matched on interface type implementations that don't have any of their own fields?
I added a failing test here: https://github.com/apollographql/apollo-client/compare/master...bgentry:failing-interface-test
It actually fails with a timeout, which is not what I expected, but afaict the test is written correctly:
1) client should be able to handle inlined fragments on an Interface type:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
at Timeout.<anonymous> (/Users/bgentry/Code/apollo-client/node_modules/mocha/lib/runnable.js:232:19)
at ontimeout (timers.js:380:14)
at tryOnTimeout (timers.js:244:5)
at Timer.listOnTimeout (timers.js:214:5)
I got the same result when using the actual query and response data that causes a failure in my app.
@bgentry Yay, thanks!
A thought I had yesterday is that I think this should also fail if we do the store roundtrip and then assert that isMissing
is false I don't know if the storeRoundtrip
function exposes that, so we may have to write a modified store roundtrip test.
We hit the same issue in our app, too.
We're going to try to solve all the interface and union related issues by implementing a fragmentMatcher
function. Right now fragment matching is a bit heuristic due to the fact that we don't load the schema on the client. Is anyone interested in helping out with the implementation? @bgentry @csillag ?
I'd love to but I simply don't have the bandwidth right now, trying to get a venture off the ground. Sorry I can't be more helpful.
Same here. We are trying to roll out a software project which should be in production soon, and we have tight milestones. At this point, we are just trying to use this thing, we can't really afford to get deep into development.
However, once the rush will over, I hope to share with you some of our additions... but unfortunately, none of them is related to getting the basics to work.
Thanks guys! @fubhy is on it. I think we can get it fixed pretty soon, so stay tuned!
@helfer is there a work-around in the time being? we are not able to implement pagination because of this issue :(
What is the current status of this issue?
This also blocks quite a few (mission critical) things for us.
@csillag @mdebbar I'm currently working on #1483, it should be out some time tomorrow!
Awesome.
Has this been resolved? I'm on 1.0.1 and it doesn't seem to be working for me.
@LilyMGoh: the new fragment matcher works, but it's not the default yet. We'll provide some updated documentation soon. In the meantime, here's an example of how to use it:
const fm = new IntrospectionFragmentMatcher({
introspectionQueryResultData: {
__schema: {
types: [{
kind: 'UNION',
name: 'Item',
possibleTypes: [{ name: 'GreenItem' }, { name: 'RedItem }]
}]
}
});
const client = new ApolloClient({ fragmentMatcher: fm });
Seems like this has been resolved as I see no more issues related to it 🙂
@helfer I'm facing this issue😕
Most helpful comment
Thanks guys! @fubhy is on it. I think we can get it fixed pretty soon, so stay tuned!