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

Enable optimistic read of cache using client #2429

Merged
merged 7 commits into from
Aug 10, 2018
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## vNext

### Apollo Client (vNext)

- Allow an `optimistic` param to be passed into `ApolloClient.readQuery` and
`ApolloClient.readFragment`, that when set to `true`, will allow
optimistic results to be returned. Is `false` by default. <br/>
[@jay1337](https://github.com/jay1337) in [#2429](https://github.com/apollographql/apollo-client/pull/2429)

### Apollo Cache In-Memory (vNext)

- Fix typo in `console.warn` regarding fragment matching error message. <br/>
Expand Down
16 changes: 9 additions & 7 deletions packages/apollo-cache-inmemory/src/__tests__/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { InMemoryCache, ApolloReducerConfig, NormalizedCache } from '..';
disableFragmentWarnings();

describe('Cache', () => {
function createCache({
initialState,
config,
}: {
initialState?: any;
config?: ApolloReducerConfig;
} = {}): ApolloCache<NormalizedCache> {
function createCache(
{
initialState,
config,
}: {
initialState?: any;
config?: ApolloReducerConfig;
} = {},
): ApolloCache<NormalizedCache> {
return new InMemoryCache(
config || { addTypename: false },
// XXX this is the old format. The tests need to be updated but since it is mapped down
Expand Down
8 changes: 4 additions & 4 deletions packages/apollo-cache-inmemory/src/fragmentMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ export class HeuristicFragmentMatcher implements FragmentMatcherInterface {
// difference, so we warn the user, but still try to match it (backcompat).
warnOnceInDevelopment(
'You are using the simple (heuristic) fragment matcher, but your ' +
'queries contain union or interface types. Apollo Client will not be ' +
'able to accurately map fragments. To make this error go away, use ' +
'the `IntrospectionFragmentMatcher` as described in the docs: ' +
'https://www.apollographql.com/docs/react/recipes/fragment-matching.html',
'queries contain union or interface types. Apollo Client will not be ' +
'able to accurately map fragments. To make this error go away, use ' +
'the `IntrospectionFragmentMatcher` as described in the docs: ' +
'https://www.apollographql.com/docs/react/recipes/fragment-matching.html',
'error',
);

Expand Down
8 changes: 2 additions & 6 deletions packages/apollo-cache-inmemory/src/writeToStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,7 @@ function writeFieldToStore({
if (generated && !escapedId.generated && !typenameChanged) {
throw new Error(
`Store error: the application attempted to write an object with no provided id` +
` but the store already contains an id of ${
escapedId.id
} for this object. The selectionSet` +
` but the store already contains an id of ${escapedId.id} for this object. The selectionSet` +
` that was trying to be written is:\n` +
print(field),
);
Expand All @@ -440,9 +438,7 @@ function writeFieldToStore({
if (hadTypename && !hasTypename) {
throw new Error(
`Store error: the application attempted to write an object with no provided typename` +
` but the store already contains an object with typename of ${
escapedId.typename
} for the object of id ${escapedId.id}. The selectionSet` +
` but the store already contains an object with typename of ${escapedId.typename} for the object of id ${escapedId.id}. The selectionSet` +
` that was trying to be written is:\n` +
print(field),
);
Expand Down
62 changes: 30 additions & 32 deletions packages/apollo-client/benchmark/github-reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@ export function collectAndReportBenchmarks(uploadToGithub: Boolean) {
Promise.all(groupPromises)
.then(() => {
log('Running benchmarks.');
return new Promise<{ [name: string]: { mean: number; moe: number } }>(
resolve => {
const retMap: { [name: string]: { mean: number; moe: number } } = {};
return new Promise<{
[name: string]: { mean: number; moe: number };
}>(resolve => {
const retMap: { [name: string]: { mean: number; moe: number } } = {};

bsuite
.on('error', (error: any) => {
log('Error: ', error);
})
.on('cycle', (event: any) => {
retMap[event.target.name] = {
mean: event.target.stats.mean * 1000,
moe: event.target.stats.moe * 1000,
};
log('Mean time in ms: ', event.target.stats.mean * 1000);
log(String(event.target));
log('');
})
.on('complete', (_: any) => {
resolve(retMap);
})
.run({ async: false });
},
);
bsuite
.on('error', (error: any) => {
log('Error: ', error);
})
.on('cycle', (event: any) => {
retMap[event.target.name] = {
mean: event.target.stats.mean * 1000,
moe: event.target.stats.moe * 1000,
};
log('Mean time in ms: ', event.target.stats.mean * 1000);
log(String(event.target));
log('');
})
.on('complete', (_: any) => {
resolve(retMap);
})
.run({ async: false });
});
})
.then(res => {
let message = '';
Expand All @@ -64,23 +64,21 @@ export function collectAndReportBenchmarks(uploadToGithub: Boolean) {
} else {
const normalizedMean = res[element].mean / res['baseline'].mean;
if (normalizedMean > thresholds[element]) {
const perfDropMessage = `Performance drop detected for benchmark: "${element}", ${
res[element].mean
} / ${res['baseline'].mean} = ${normalizedMean} > ${
thresholds[element]
}`;
const perfDropMessage = `Performance drop detected for benchmark: "${element}", ${res[
element
].mean} / ${res['baseline']
.mean} = ${normalizedMean} > ${thresholds[element]}`;
console.error(perfDropMessage);
if (message === '') {
message = `Performance drop detected for benchmark: "${element}"`;
pass = false;
}
} else {
console.log(
`No performance drop detected for benchmark: "${element}", ${
res[element].mean
} / ${res['baseline'].mean} = ${normalizedMean} <= ${
thresholds[element]
}`,
`No performance drop detected for benchmark: "${element}", ${res[
element
].mean} / ${res['baseline']
.mean} = ${normalizedMean} <= ${thresholds[element]}`,
);
}
}
Expand Down
12 changes: 10 additions & 2 deletions packages/apollo-client/src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,15 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
* GraphQL query without making a network request. This method will start at
* the root query. To start at a specific id returned by `dataIdFromObject`
* use `readFragment`.
*
* @param optimistic Set to `true` to allow `readQuery` to return
* optimisic results. Is `false` by default.
*/
public readQuery<T, TVariables = OperationVariables>(
options: DataProxy.Query<TVariables>,
optimistic: boolean = false,
): T | null {
return this.initProxy().readQuery<T>(options);
return this.initProxy().readQuery<T>(options, optimistic);
}

/**
Expand All @@ -311,11 +315,15 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
* with multiple fragments that represent what you are reading. If you pass
* in a document with multiple fragments then you must also specify a
* `fragmentName`.
*
* @param optimistic Set to `true` to allow `readFragment` to return
* optimisic results. Is `false` by default.
*/
public readFragment<T, TVariables = OperationVariables>(
options: DataProxy.Fragment<TVariables>,
optimistic: boolean = false,
): T | null {
return this.initProxy().readFragment<T>(options);
return this.initProxy().readFragment<T>(options, optimistic);
}

/**
Expand Down
19 changes: 10 additions & 9 deletions packages/apollo-client/src/__tests__/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2073,15 +2073,16 @@ describe('ApolloClient', () => {
}
`;

['network-only', 'cache-and-network'].forEach(
(fetchPolicy: FetchPolicy) => {
const observable = client.watchQuery({
query,
fetchPolicy,
});
expect(observable.options.fetchPolicy).toEqual('cache-first');
},
);
[
'network-only',
'cache-and-network',
].forEach((fetchPolicy: FetchPolicy) => {
const observable = client.watchQuery({
query,
fetchPolicy,
});
expect(observable.options.fetchPolicy).toEqual('cache-first');
});
},
);

Expand Down
65 changes: 33 additions & 32 deletions packages/apollo-client/src/__tests__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,9 @@ describe('client', () => {
});

expect(() => {
client.query(gql`
{
client.query(gql`{
a
}
` as any);
}` as any);
}).toThrowError(
'query option is required. You must specify your GraphQL document in the query option.',
);
Expand Down Expand Up @@ -623,40 +621,43 @@ describe('client', () => {
});
});

xit('should pass a network error correctly on a query using an observable network interface with a warning', done => {
withWarning(() => {
const query = gql`
query people {
allPeople(first: 1) {
people {
name
xit(
'should pass a network error correctly on a query using an observable network interface with a warning',
done => {
withWarning(() => {
const query = gql`
query people {
allPeople(first: 1) {
people {
name
}
}
}
}
`;
`;

const networkError = new Error('Some kind of network error.');
const networkError = new Error('Some kind of network error.');

const link = ApolloLink.from([
() => {
return new Observable(_ => {
throw networkError;
});
},
]);
const link = ApolloLink.from([
() => {
return new Observable(_ => {
throw networkError;
});
},
]);

const client = new ApolloClient({
link,
cache: new InMemoryCache({ addTypename: false }),
});
const client = new ApolloClient({
link,
cache: new InMemoryCache({ addTypename: false }),
});

client.query({ query }).catch((error: ApolloError) => {
expect(error.networkError).toBeDefined();
expect(error.networkError!.message).toEqual(networkError.message);
done();
});
}, /deprecated/);
});
client.query({ query }).catch((error: ApolloError) => {
expect(error.networkError).toBeDefined();
expect(error.networkError!.message).toEqual(networkError.message);
done();
});
}, /deprecated/);
},
);

it('should pass a network error correctly on a query with apollo-link network interface', done => {
const query = gql`
Expand Down
Loading