Skip to content
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

Add implicit cache redirect #1034

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

import {
CustomResolverMap,
CustomResolver,
} from './data/readFromStore';

import {
Expand Down Expand Up @@ -129,6 +130,9 @@ export default class ApolloClient {
* @param dataIdFromObject A function that returns a object identifier given a particular result
* object.
*
* @param defaultIdLookup A function used to find objects in the cache by their key.
* Should return an object identifer given a particular result object and arguments.
*
* @param queryTransformer A function that takes a {@link SelectionSet} and modifies it in place
* in some way. The query transformer is then applied to the every GraphQL document before it is
* sent to the server.
Expand All @@ -149,6 +153,7 @@ export default class ApolloClient {
reduxRootSelector,
initialState,
dataIdFromObject,
defaultIdLookup,
resultTransformer,
resultComparator,
ssrMode = false,
Expand All @@ -164,6 +169,7 @@ export default class ApolloClient {
reduxRootSelector?: string | ApolloStateSelector,
initialState?: any,
dataIdFromObject?: IdGetter,
defaultIdLookup?: CustomResolver,
resultTransformer?: ResultTransformer,
resultComparator?: ResultComparator,
ssrMode?: boolean,
Expand Down Expand Up @@ -220,6 +226,7 @@ export default class ApolloClient {

this.reducerConfig = {
dataIdFromObject,
defaultIdLookup,
mutationBehaviorReducers,
customResolvers,
};
Expand Down
31 changes: 22 additions & 9 deletions src/data/readFromStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NormalizedCache,
isJsonValue,
isIdValue,
toIdValue,
IdValue,
} from './storeUtils';

Expand Down Expand Up @@ -85,6 +86,7 @@ type ReadStoreContext = {
returnPartialData: boolean;
hasMissingField: boolean;
customResolvers: CustomResolverMap;
defaultIdLookup: CustomResolver;
}

let haveWarned = false;
Expand Down Expand Up @@ -146,16 +148,27 @@ const readStoreResolver: Resolver = (
const fieldValue = (obj || {})[storeKeyName];

if (typeof fieldValue === 'undefined') {
if (context.customResolvers && obj && (obj.__typename || objId === 'ROOT_QUERY')) {
if (obj && (obj.__typename || objId === 'ROOT_QUERY')) {
const typename = obj.__typename || 'Query';

// Look for the type in the custom resolver map
const type = context.customResolvers[typename];
if (type) {
// Look for the field in the custom resolver map
const resolver = type[fieldName];
if (resolver) {
return resolver(obj, args);
if (context.customResolvers) {

// Look for the type in the custom resolver map
const type = context.customResolvers[typename];
if (type) {
// Look for the field in the custom resolver map
const resolver = type[fieldName];
if (resolver) {
return resolver(obj, args);
}
}
}

if (args && context.defaultIdLookup) {
const defaultId = context.defaultIdLookup(obj, args);

if (defaultId && context.store[defaultId]) {
return toIdValue(defaultId);
}
}
}
Expand Down Expand Up @@ -201,7 +214,7 @@ export function diffQueryAgainstStore({
store,
returnPartialData,
customResolvers: config && config.customResolvers,

defaultIdLookup: config && config.defaultIdLookup,
// Flag set during execution
hasMissingField: false,
};
Expand Down
2 changes: 2 additions & 0 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {

import {
CustomResolverMap,
CustomResolver,
} from './data/readFromStore';

import assign = require('lodash/assign');
Expand Down Expand Up @@ -178,6 +179,7 @@ export function createApolloStore({

export type ApolloReducerConfig = {
dataIdFromObject?: IdGetter;
defaultIdLookup?: CustomResolver;
mutationBehaviorReducers?: MutationBehaviorReducerMap;
customResolvers?: CustomResolverMap;
}
Expand Down
58 changes: 58 additions & 0 deletions test/readFromStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import mockNetworkInterface from './mocks/mockNetworkInterface';
import { assert } from 'chai';
import ApolloClient from '../src';
import assign = require('lodash/assign');
import omit = require('lodash/omit');

import {
readQueryFromStore,
} from '../src/data/readFromStore';

import { NetworkStatus } from '../src/queries/store';

import {
NormalizedCache,
StoreObject,
Expand Down Expand Up @@ -647,4 +651,58 @@ describe('reading from the store', () => {
computedField: 'This is a string!5bit',
});
});

it(`does cache lookups when defaultIdLookup is supplied`, () => {
const dataIdFromObject = (obj: any) => {
return obj.id;
};

const defaultIdLookup = (obj: any, args: any): string => {
return args.id;
};

const listQuery = gql`{ people { id name } }`;

const listData = {
people: [
{
id: '4',
name: 'Luke Skywalker',
__typename: 'Person',
},
],
};

const netListQuery = gql`{ people { id name __typename } }`;

const itemQuery = gql`{ person(id: 4) { id name } }`;

// We don't expect the item query to go to the server at all
const networkInterface = mockNetworkInterface({
request: { query: netListQuery },
result: { data: listData },
});

const client = new ApolloClient({
networkInterface,
dataIdFromObject,
defaultIdLookup,
});

return client.query({ query: listQuery }).then(() => {
return client.query({ query: itemQuery });
}).then((itemResult) => {
assert.deepEqual(itemResult, {
loading: false,
networkStatus: NetworkStatus.ready,
data: {
person: {
__typename: 'Person',
id: '4',
name: 'Luke Skywalker',
},
},
});
});
});
});