From fc8c2070fb9830f8ed20d51444a6c074d4402b0f Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 22 May 2019 18:42:27 -0400 Subject: [PATCH 1/3] Pretend that __typename exists on the root Query. Should fix #4848. --- .../src/__tests__/fragmentMatcher.ts | 47 ++++++++++++++++++- .../src/fragmentMatcher.ts | 20 +++++--- .../apollo-cache-inmemory/src/writeToStore.ts | 5 +- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts b/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts index ddb14f02dc9..23b2c30a304 100644 --- a/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts +++ b/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts @@ -1,5 +1,50 @@ import { IntrospectionFragmentMatcher } from '../fragmentMatcher'; import { defaultNormalizedCacheFactory } from '../objectCache'; +import { InMemoryCache } from '../inMemoryCache'; +import gql from 'graphql-tag'; +import { print } from 'graphql'; + +describe('FragmentMatcher', () => { + it('can match against the root Query', () => { + const cache = new InMemoryCache({ + addTypename: true, + }); + + const query = gql` + query AllPeople { + people { + id + name + } + ...PeopleTypes + } + fragment PeopleTypes on Query { + __type(name: "Person") { + name + kind + } + } + `; + + const data = { + people: [ + { + __typename: 'Person', + id: 123, + name: 'Ben', + }, + ], + __type: { + __typename: '__Type', + name: 'Person', + kind: 'OBJECT', + }, + }; + + cache.writeQuery({ query, data }); + expect(cache.readQuery({ query })).toEqual(data); + }); +}); describe('IntrospectionFragmentMatcher', () => { it('will throw an error if match is called if it is not ready', () => { @@ -45,7 +90,7 @@ describe('IntrospectionFragmentMatcher', () => { store, returnPartialData: false, hasMissingField: false, - customResolvers: {}, + cacheRedirects: {}, }; expect(ifm.match(idValue as any, 'Item', readStoreContext)).toBe(true); diff --git a/packages/apollo-cache-inmemory/src/fragmentMatcher.ts b/packages/apollo-cache-inmemory/src/fragmentMatcher.ts index 7d1fcad4d6f..7dec20d5bff 100644 --- a/packages/apollo-cache-inmemory/src/fragmentMatcher.ts +++ b/packages/apollo-cache-inmemory/src/fragmentMatcher.ts @@ -41,13 +41,16 @@ export class HeuristicFragmentMatcher implements FragmentMatcherInterface { context: ReadStoreContext, ): boolean | 'heuristic' { const obj = context.store.get(idValue.id); + const isRootQuery = idValue.id === 'ROOT_QUERY'; if (!obj) { // https://github.com/apollographql/apollo-client/pull/3507 - return idValue.id === 'ROOT_QUERY'; + return isRootQuery; } - if (!obj.__typename) { + const { __typename = isRootQuery && 'Query' } = obj; + + if (!__typename) { if (shouldWarn()) { invariant.warn(`You're using fragments in your queries, but either don't have the addTypename: true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename. @@ -67,7 +70,7 @@ export class HeuristicFragmentMatcher implements FragmentMatcherInterface { return 'heuristic'; } - if (obj.__typename === typeCondition) { + if (__typename === typeCondition) { return true; } @@ -120,25 +123,28 @@ export class IntrospectionFragmentMatcher implements FragmentMatcherInterface { ); const obj = context.store.get(idValue.id); + const isRootQuery = idValue.id === 'ROOT_QUERY'; if (!obj) { // https://github.com/apollographql/apollo-client/pull/4620 - return idValue.id === 'ROOT_QUERY'; + return isRootQuery; } + const { __typename = isRootQuery && 'Query' } = obj; + invariant( - obj.__typename, + __typename, `Cannot match fragment because __typename property is missing: ${JSON.stringify( obj, )}`, ); - if (obj.__typename === typeCondition) { + if (__typename === typeCondition) { return true; } const implementingTypes = this.possibleTypesMap[typeCondition]; - if (implementingTypes && implementingTypes.indexOf(obj.__typename) > -1) { + if (implementingTypes && implementingTypes.indexOf(__typename) > -1) { return true; } diff --git a/packages/apollo-cache-inmemory/src/writeToStore.ts b/packages/apollo-cache-inmemory/src/writeToStore.ts index c5b566d5f6b..3985d1ed37a 100644 --- a/packages/apollo-cache-inmemory/src/writeToStore.ts +++ b/packages/apollo-cache-inmemory/src/writeToStore.ts @@ -230,11 +230,12 @@ export class StoreWriter { // TODO we need to rewrite the fragment matchers for this to work properly and efficiently // Right now we have to pretend that we're passing in an idValue and that there's a store // on the context. - const idValue = toIdValue({ id: 'self', typename: undefined }); + const id = dataId || 'self'; + const idValue = toIdValue({ id, typename: undefined }); const fakeContext: ReadStoreContext = { // NOTE: fakeContext always uses ObjectCache // since this is only to ensure the return value of 'matches' - store: new ObjectCache({ self: result }), + store: new ObjectCache({ [id]: result }), cacheRedirects: {}, }; const match = context.fragmentMatcherFunction( From 34c9ce84964802e31df807a86f84d61ad5012bb2 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 22 May 2019 19:23:43 -0400 Subject: [PATCH 2/3] Mention PR #4853 in CHANGELOG.md. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25b2749d13e..0b484e06013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ **Note:** This is a cumulative changelog that outlines all of the Apollo Client project child package changes that were bundled into a specific `apollo-client` release. +## Apollo Client (vNEXT) + +### Apollo Cache In-Memory + +- Pretend that `__typename` exists on the root Query when matching fragments.
+ [@benjamn](https://github.com/benjamn) in [#4853](https://github.com/apollographql/apollo-client/pull/4853) + ## Apollo Client (2.6.0) - In production, `invariant(condition, message)` failures will now include From 4efff6244631832ebf96a01d31c0fc6165deba83 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Thu, 23 May 2019 12:31:06 -0400 Subject: [PATCH 3/3] Fix manual merge conflict resolutions. --- .../apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts b/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts index 312c13a9fe1..a3f74d3fc07 100644 --- a/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts +++ b/packages/apollo-cache-inmemory/src/__tests__/fragmentMatcher.ts @@ -1,9 +1,8 @@ import { IntrospectionFragmentMatcher } from '../fragmentMatcher'; import { defaultNormalizedCacheFactory } from '../objectCache'; -import { ReadStoreContext } from '..'; +import { ReadStoreContext } from '../types'; import { InMemoryCache } from '../inMemoryCache'; import gql from 'graphql-tag'; -import { print } from 'graphql'; describe('FragmentMatcher', () => { it('can match against the root Query', () => {