diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6b5f92e6d..b1a79680132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,21 @@ `@client` directives.
[@justinmakaila](https://github.com/justinmakaila) in [#3482](https://github.com/apollographql/apollo-client/pull/3482) +### Apollo Cache In-Memory (1.3.12) + +- Avoid using `DepTrackingCache` for optimistic reads. + [PR #4521](https://github.com/apollographql/apollo-client/pull/4251) + +- When creating an `InMemoryCache` object, it's now possible to disable the + result caching behavior introduced in [#3394](https://github.com/apollographql/apollo-client/pull/3394), + either for diagnostic purposes or because the benefit of caching repeated + reads is not worth the extra memory usage in your application: + ```ts + new InMemoryCache({ + resultCaching: false + }) + ``` + Part of [PR #4521](https://github.com/apollographql/apollo-client/pull/4251). ## Apollo Client (2.4.7) diff --git a/package.json b/package.json index 7a21cf20eef..290488a35a1 100644 --- a/package.json +++ b/package.json @@ -71,12 +71,10 @@ }, "lint-staged": { "*.ts*": [ - "prettier --ignore-path \"./config/prettierignore\" --trailing-comma all --single-quote --write", - "git add" + "prettier --ignore-path \"./config/prettierignore\" --trailing-comma all --single-quote --write" ], "*.js*": [ - "prettier --ignore-path \"./config/prettierignore\" --trailing-comma all --single-quote --write", - "git add" + "prettier --ignore-path \"./config/prettierignore\" --trailing-comma all --single-quote --write" ] }, "pre-commit": "lint-staged", diff --git a/packages/apollo-cache-inmemory/package-lock.json b/packages/apollo-cache-inmemory/package-lock.json index a158d8945c8..268b4dd112e 100644 --- a/packages/apollo-cache-inmemory/package-lock.json +++ b/packages/apollo-cache-inmemory/package-lock.json @@ -1,62 +1,43 @@ { "name": "apollo-cache-inmemory", - "version": "1.3.11", + "version": "1.3.12-beta.1", "lockfileVersion": 1, "requires": true, "dependencies": { "apollo-cache": { - "version": "file:../apollo-cache", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.1.21.tgz", + "integrity": "sha512-5ErNb78KHtrJNimkDBTEigcvHkIqUmS7QJIk4lpZZ+XLVVgvk2fD+GhD1PLP+s8vHfAKVbO6vdbRxCCjGGrh5w==", "requires": { - "apollo-utilities": "file:../apollo-utilities" - }, - "dependencies": { - "apollo-utilities": { - "version": "file:../apollo-utilities", - "bundled": true, - "requires": { - "fast-json-stable-stringify": "^2.0.0", - "fclone": "^1.0.11" - }, - "dependencies": { - "fast-json-stable-stringify": { - "version": "2.0.0", - "bundled": true - }, - "fclone": { - "version": "1.0.11", - "bundled": true - } - } - } + "apollo-utilities": "^1.0.26" } }, "apollo-utilities": { - "version": "file:../apollo-utilities", + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.26.tgz", + "integrity": "sha512-URw7o3phymliqYCYatcird2YRPUU2eWCNvip64U9gQrX56mEfK4m99yBIDCMTpmcvOFsKLii1sIEZsHIs/bvnw==", "requires": { "fast-json-stable-stringify": "^2.0.0" }, "dependencies": { "fast-json-stable-stringify": { "version": "2.0.0", - "bundled": true - }, - "fclone": { - "version": "1.0.11", - "bundled": true + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" } } }, "immutable-tuple": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/immutable-tuple/-/immutable-tuple-0.4.8.tgz", - "integrity": "sha512-1m29EVSrF+LJJAyVo1v12NsIalVKjyi4HNQVQDBx+LNCIuRXnfeMCHuLao5CyN1m3Sn0T63U5JEkmPArPCipQA==" + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/immutable-tuple/-/immutable-tuple-0.4.9.tgz", + "integrity": "sha512-LWbJPZnidF8eczu7XmcnLBsumuyRBkpwIRPCZxlojouhBo5jEBO4toj6n7hMy6IxHU/c+MqDSWkvaTpPlMQcyA==" }, "optimism": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.6.6.tgz", - "integrity": "sha512-1Y6LY7pYbXP5y6yeXYfXhxJi9hsxYAZWpt7bBp4seAwfcYtaN7tq9wot/pdrhyI809/K4gDm3BcZcEkmwGevjg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.6.8.tgz", + "integrity": "sha512-bN5n1KCxSqwBDnmgDnzMtQTHdL+uea2HYFx1smvtE+w2AMl0Uy31g0aXnP/Nt85OINnMJPRpJyfRQLTCqn5Weg==", "requires": { - "immutable-tuple": "^0.4.4" + "immutable-tuple": "^0.4.9" } } } diff --git a/packages/apollo-cache-inmemory/package.json b/packages/apollo-cache-inmemory/package.json index ecfd2eb9726..9c6a4ccf45f 100644 --- a/packages/apollo-cache-inmemory/package.json +++ b/packages/apollo-cache-inmemory/package.json @@ -1,6 +1,6 @@ { "name": "apollo-cache-inmemory", - "version": "1.3.11", + "version": "1.3.12-beta.1", "description": "Core abstract of Caching layer for Apollo Client", "author": "James Baxley ", "contributors": [ @@ -44,7 +44,7 @@ "dependencies": { "apollo-cache": "file:../apollo-cache", "apollo-utilities": "file:../apollo-utilities", - "optimism": "^0.6.6" + "optimism": "^0.6.8" }, "peerDependencies": { "graphql": "0.11.7 || ^0.12.0 || ^0.13.0 || ^14.0.0" diff --git a/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/cache.ts.snap b/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/cache.ts.snap index 08cc1a7e210..0cd8b06df71 100644 --- a/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/cache.ts.snap +++ b/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/cache.ts.snap @@ -1,8 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 1` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 1`] = ` Object { "bar": Object { "i": 7, @@ -19,9 +17,7 @@ Object { } `; -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 2` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 2`] = ` Object { "bar": Object { "i": 7, @@ -42,9 +38,7 @@ Object { } `; -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 3` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 3`] = ` Object { "bar": Object { "i": 10, @@ -65,9 +59,7 @@ Object { } `; -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 4` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 4`] = ` Object { "bar": Object { "i": 10, @@ -88,9 +80,7 @@ Object { } `; -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 5` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 5`] = ` Object { "bar": Object { "i": 7, @@ -111,9 +101,129 @@ Object { } `; -exports[ - `Cache writeFragment will write some deeply nested data into the store at any id 6` -] = ` +exports[`Cache writeFragment will write some deeply nested data into the store at any id (1/2) 6`] = ` +Object { + "bar": Object { + "i": 10, + "j": 11, + "k": 12, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": "Bar", + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 1`] = ` +Object { + "bar": Object { + "i": 7, + }, + "foo": Object { + "e": 4, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 2`] = ` +Object { + "bar": Object { + "i": 7, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 3`] = ` +Object { + "bar": Object { + "i": 10, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 4`] = ` +Object { + "bar": Object { + "i": 10, + "j": 11, + "k": 12, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 5`] = ` +Object { + "bar": Object { + "i": 7, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": "Bar", + }, + }, +} +`; + +exports[`Cache writeFragment will write some deeply nested data into the store at any id (2/2) 6`] = ` Object { "bar": Object { "i": 10, diff --git a/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/mapCache.ts.snap b/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/mapCache.ts.snap index d644838f32c..0ff0bd5d836 100644 --- a/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/mapCache.ts.snap +++ b/packages/apollo-cache-inmemory/src/__tests__/__snapshots__/mapCache.ts.snap @@ -1,8 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 1` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 1`] = ` Object { "bar": Object { "i": 7, @@ -19,9 +17,7 @@ Object { } `; -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 2` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 2`] = ` Object { "bar": Object { "i": 7, @@ -42,9 +38,7 @@ Object { } `; -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 3` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 3`] = ` Object { "bar": Object { "i": 10, @@ -65,9 +59,7 @@ Object { } `; -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 4` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 4`] = ` Object { "bar": Object { "i": 10, @@ -88,9 +80,7 @@ Object { } `; -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 5` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 5`] = ` Object { "bar": Object { "i": 7, @@ -111,9 +101,7 @@ Object { } `; -exports[ - `MapCache Cache writeFragment will write some deeply nested data into the store at any id 6` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (1/2) 6`] = ` Object { "bar": Object { "i": 10, @@ -134,9 +122,129 @@ Object { } `; -exports[ - `MapCache writing to the store throws when trying to write an object without id that was previously queried with id 1` -] = ` +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 1`] = ` +Object { + "bar": Object { + "i": 7, + }, + "foo": Object { + "e": 4, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 2`] = ` +Object { + "bar": Object { + "i": 7, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 3`] = ` +Object { + "bar": Object { + "i": 10, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 4`] = ` +Object { + "bar": Object { + "i": 10, + "j": 11, + "k": 12, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": undefined, + }, + }, +} +`; + +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 5`] = ` +Object { + "bar": Object { + "i": 7, + "j": 8, + "k": 9, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": "Bar", + }, + }, +} +`; + +exports[`MapCache Cache writeFragment will write some deeply nested data into the store at any id (2/2) 6`] = ` +Object { + "bar": Object { + "i": 10, + "j": 11, + "k": 12, + }, + "foo": Object { + "e": 4, + "f": 5, + "g": 6, + "h": Object { + "generated": false, + "id": "bar", + "type": "id", + "typename": "Bar", + }, + }, +} +`; + +exports[`MapCache writing to the store throws when trying to write an object without id that was previously queried with id 1`] = ` "Error writing result to store for query: query Failure { item { diff --git a/packages/apollo-cache-inmemory/src/__tests__/cache.ts b/packages/apollo-cache-inmemory/src/__tests__/cache.ts index 0cd4f2cc631..f0cb6e27ed1 100644 --- a/packages/apollo-cache-inmemory/src/__tests__/cache.ts +++ b/packages/apollo-cache-inmemory/src/__tests__/cache.ts @@ -3,39 +3,66 @@ import gql, { disableFragmentWarnings } from 'graphql-tag'; import { stripSymbols } from 'apollo-utilities'; import { cloneDeep } from 'lodash'; -import { InMemoryCache, ApolloReducerConfig, NormalizedCache } from '..'; +import { InMemoryCache, InMemoryCacheConfig, NormalizedCache } from '..'; disableFragmentWarnings(); describe('Cache', () => { - function createCache({ - initialState, - config, - }: { - initialState?: any; - config?: ApolloReducerConfig; - } = {}): ApolloCache { - return new InMemoryCache( - config || { addTypename: false }, - // XXX this is the old format. The tests need to be updated but since it is mapped down - ).restore(initialState ? initialState.apollo.data : {}); + function itWithInitialData( + message: string, + initialDataForCaches: ({ [key: string]: any })[], + callback: (...caches: InMemoryCache[]) => any, + ) { + const cachesList: InMemoryCache[][] = [ + initialDataForCaches.map( + data => new InMemoryCache({ + addTypename: false, + }).restore(cloneDeep(data)) + ), + initialDataForCaches.map( + data => new InMemoryCache({ + addTypename: false, + resultCaching: false, + }).restore(cloneDeep(data)) + ), + ]; + + cachesList.forEach((caches, i) => { + it(message + ` (${i+1}/${cachesList.length})`, () => callback(...caches)); + }); + } + + function itWithCacheConfig( + message: string, + config: InMemoryCacheConfig, + callback: (cache: InMemoryCache) => any, + ) { + const caches = [ + new InMemoryCache({ + addTypename: false, + ...config, + resultCaching: true, + }), + new InMemoryCache({ + addTypename: false, + ...config, + resultCaching: false, + }), + ]; + + caches.forEach((cache, i) => { + it(message + ` (${i+1}/${caches.length})`, () => callback(cache)); + }); } describe('readQuery', () => { - it('will read some data from the store', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - a: 1, - b: 2, - c: 3, - }, - }, - }, - }, - }); + itWithInitialData('will read some data from the store', [{ + ROOT_QUERY: { + a: 1, + b: 2, + c: 3, + }, + }], proxy => { expect( stripSymbols( proxy.readQuery({ @@ -74,41 +101,33 @@ describe('Cache', () => { ).toEqual({ a: 1, b: 2, c: 3 }); }); - it('will read some deeply nested data from the store', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - a: 1, - b: 2, - c: 3, - d: { - type: 'id', - id: 'foo', - generated: false, - }, - }, - foo: { - e: 4, - f: 5, - g: 6, - h: { - type: 'id', - id: 'bar', - generated: false, - }, - }, - bar: { - i: 7, - j: 8, - k: 9, - }, - }, - }, + itWithInitialData('will read some deeply nested data from the store', [{ + ROOT_QUERY: { + a: 1, + b: 2, + c: 3, + d: { + type: 'id', + id: 'foo', + generated: false, }, - }); - + }, + foo: { + e: 4, + f: 5, + g: 6, + h: { + type: 'id', + id: 'bar', + generated: false, + }, + }, + bar: { + i: 7, + j: 8, + k: 9, + }, + }], proxy => { expect( stripSymbols( proxy.readQuery({ @@ -170,20 +189,12 @@ describe('Cache', () => { }); }); - it('will read some data from the store with variables', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - 'field({"literal":true,"value":42})': 1, - 'field({"literal":false,"value":42})': 2, - }, - }, - }, - }, - }); - + itWithInitialData('will read some data from the store with variables', [{ + ROOT_QUERY: { + 'field({"literal":true,"value":42})': 1, + 'field({"literal":false,"value":42})': 2, + }, + }], proxy => { expect( stripSymbols( proxy.readQuery({ @@ -202,19 +213,11 @@ describe('Cache', () => { ).toEqual({ a: 1, b: 2 }); }); - it('will read some data from the store with null variables', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - 'field({"literal":false,"value":null})': 1, - }, - }, - }, - }, - }); - + itWithInitialData('will read some data from the store with null variables', [{ + ROOT_QUERY: { + 'field({"literal":false,"value":null})': 1, + }, + }], proxy => { expect( stripSymbols( proxy.readQuery({ @@ -232,20 +235,12 @@ describe('Cache', () => { ).toEqual({ a: 1 }); }); - it('should not mutate arguments passed in', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - 'field({"literal":true,"value":42})': 1, - 'field({"literal":false,"value":42})': 2, - }, - }, - }, - }, - }); - + itWithInitialData('should not mutate arguments passed in', [{ + ROOT_QUERY: { + 'field({"literal":true,"value":42})': 1, + 'field({"literal":false,"value":42})': 2, + }, + }], proxy => { const options = { query: gql` query($literal: Boolean, $value: Int) { @@ -266,9 +261,10 @@ describe('Cache', () => { }); describe('readFragment', () => { - it('will throw an error when there is no fragment', () => { - const proxy = createCache(); - + itWithInitialData('will throw an error when there is no fragment', [ + // Empty data, but still want to test with/without result caching. + {}, + ], proxy => { expect(() => { proxy.readFragment({ id: 'x', @@ -297,9 +293,9 @@ describe('Cache', () => { ); }); - it('will throw an error when there is more than one fragment but no fragment name', () => { - const proxy = createCache(); - + itWithInitialData('will throw an error when there is more than one fragment but no fragment name', [ + {}, + ], proxy => { expect(() => { proxy.readFragment({ id: 'x', @@ -338,44 +334,36 @@ describe('Cache', () => { ); }); - it('will read some deeply nested data from the store at any id', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - ROOT_QUERY: { - __typename: 'Type1', - a: 1, - b: 2, - c: 3, - d: { - type: 'id', - id: 'foo', - generated: false, - }, - }, - foo: { - __typename: 'Foo', - e: 4, - f: 5, - g: 6, - h: { - type: 'id', - id: 'bar', - generated: false, - }, - }, - bar: { - __typename: 'Bar', - i: 7, - j: 8, - k: 9, - }, - }, - }, + itWithInitialData('will read some deeply nested data from the store at any id', [{ + ROOT_QUERY: { + __typename: 'Type1', + a: 1, + b: 2, + c: 3, + d: { + type: 'id', + id: 'foo', + generated: false, }, - }); - + }, + foo: { + __typename: 'Foo', + e: 4, + f: 5, + g: 6, + h: { + type: 'id', + id: 'bar', + generated: false, + }, + }, + bar: { + __typename: 'Bar', + i: 7, + j: 8, + k: 9, + }, + }], proxy => { expect( stripSymbols( proxy.readFragment({ @@ -490,21 +478,13 @@ describe('Cache', () => { ).toEqual({ i: 7, j: 8, k: 9 }); }); - it('will read some data from the store with variables', () => { - const proxy = createCache({ - initialState: { - apollo: { - data: { - foo: { - __typename: 'Foo', - 'field({"literal":true,"value":42})': 1, - 'field({"literal":false,"value":42})': 2, - }, - }, - }, - }, - }); - + itWithInitialData('will read some data from the store with variables', [{ + foo: { + __typename: 'Foo', + 'field({"literal":true,"value":42})': 1, + 'field({"literal":false,"value":42})': 2, + }, + }], proxy => { expect( stripSymbols( proxy.readFragment({ @@ -524,27 +504,18 @@ describe('Cache', () => { ).toEqual({ a: 1, b: 2 }); }); - it('will return null when an id that can’t be found is provided', () => { - const client1 = createCache(); - const client2 = createCache({ - initialState: { - apollo: { - data: { - bar: { __typename: 'Bar', a: 1, b: 2, c: 3 }, - }, - }, - }, - }); - const client3 = createCache({ - initialState: { - apollo: { - data: { - foo: { __typename: 'Foo', a: 1, b: 2, c: 3 }, - }, - }, - }, - }); - + itWithInitialData('will return null when an id that can’t be found is provided', [ + // client1 + {}, + // client2 + { + bar: { __typename: 'Bar', a: 1, b: 2, c: 3 }, + }, + // client3 + { + foo: { __typename: 'Foo', a: 1, b: 2, c: 3 }, + }, + ], (client1, client2, client3) => { expect( stripSymbols( client1.readFragment({ @@ -591,9 +562,9 @@ describe('Cache', () => { }); describe('writeQuery', () => { - it('will write some data to the store', () => { - const proxy = createCache(); - + itWithInitialData('will write some data to the store', [ + {} + ], proxy => { proxy.writeQuery({ data: { a: 1 }, query: gql` @@ -647,9 +618,9 @@ describe('Cache', () => { }); }); - it('will write some deeply nested data to the store', () => { - const proxy = createCache(); - + itWithInitialData('will write some deeply nested data to the store', [ + {} + ], proxy => { proxy.writeQuery({ data: { a: 1, d: { e: 4 } }, query: gql` @@ -767,9 +738,9 @@ describe('Cache', () => { }); }); - it('will write some data to the store with variables', () => { - const proxy = createCache(); - + itWithInitialData('will write some data to the store with variables', [ + {}, + ], proxy => { proxy.writeQuery({ data: { a: 1, @@ -794,9 +765,10 @@ describe('Cache', () => { }, }); }); - it('will write some data to the store with variables where some are null', () => { - const proxy = createCache(); + itWithInitialData('will write some data to the store with variables where some are null', [ + {} + ], proxy => { proxy.writeQuery({ data: { a: 1, @@ -824,9 +796,9 @@ describe('Cache', () => { }); describe('writeFragment', () => { - it('will throw an error when there is no fragment', () => { - const proxy = createCache(); - + itWithInitialData('will throw an error when there is no fragment', [ + {}, + ], proxy => { expect(() => { proxy.writeFragment({ data: {}, @@ -857,9 +829,9 @@ describe('Cache', () => { ); }); - it('will throw an error when there is more than one fragment but no fragment name', () => { - const proxy = createCache(); - + itWithInitialData('will throw an error when there is more than one fragment but no fragment name', [ + {} + ], proxy => { expect(() => { proxy.writeFragment({ data: {}, @@ -900,11 +872,10 @@ describe('Cache', () => { ); }); - it('will write some deeply nested data into the store at any id', () => { - const proxy = createCache({ - config: { dataIdFromObject: (o: any) => o.id, addTypename: false }, - }); - + itWithCacheConfig('will write some deeply nested data into the store at any id', { + dataIdFromObject: (o: any) => o.id, + addTypename: false, + }, proxy => { proxy.writeFragment({ data: { __typename: 'Foo', e: 4, h: { id: 'bar', i: 7 } }, id: 'foo', @@ -1019,10 +990,10 @@ describe('Cache', () => { expect((proxy as InMemoryCache).extract()).toMatchSnapshot(); }); - it('writes data that can be read back', () => { - const proxy = createCache({ - config: { addTypename: true }, - }); + + itWithCacheConfig('writes data that can be read back', { + addTypename: true, + }, proxy => { const readWriteFragment = gql` fragment aFragment on query { getSomething { @@ -1047,11 +1018,9 @@ describe('Cache', () => { expect(stripSymbols(result)).toEqual(data); }); - it('will write some data to the store with variables', () => { - const proxy = createCache({ - config: { addTypename: true }, - }); - + itWithCacheConfig('will write some data to the store with variables', { + addTypename: true, + }, proxy => { proxy.writeFragment({ data: { a: 1, @@ -1082,9 +1051,9 @@ describe('Cache', () => { }); describe('performTransaction', () => { - it('will not broadcast mid-transaction', () => { - const cache = createCache(); - + itWithInitialData('will not broadcast mid-transaction', [ + {}, + ], cache => { let numBroadcasts = 0; const query = gql` @@ -1130,9 +1099,9 @@ describe('Cache', () => { }); describe('performOptimisticTransaction', () => { - it('will only broadcast once', () => { - const cache = createCache(); - + itWithInitialData('will only broadcast once', [ + {}, + ], cache => { let numBroadcasts = 0; const query = gql` @@ -1171,7 +1140,7 @@ describe('Cache', () => { }); expect(numBroadcasts).toEqual(0); - }, 1); + }, 1 as any); expect(numBroadcasts).toEqual(1); }); diff --git a/packages/apollo-cache-inmemory/src/inMemoryCache.ts b/packages/apollo-cache-inmemory/src/inMemoryCache.ts index ce80f6c0b61..41dd5ade92d 100644 --- a/packages/apollo-cache-inmemory/src/inMemoryCache.ts +++ b/packages/apollo-cache-inmemory/src/inMemoryCache.ts @@ -21,14 +21,21 @@ import { import { StoreReader } from './readFromStore'; import { StoreWriter } from './writeToStore'; -import { defaultNormalizedCacheFactory, DepTrackingCache } from './depTrackingCache'; +import { DepTrackingCache } from './depTrackingCache'; import { wrap, CacheKeyNode, OptimisticWrapperFunction } from './optimism'; +import { ObjectCache } from './objectCache'; import { record } from './recordingCache'; -const defaultConfig: ApolloReducerConfig = { + +export interface InMemoryCacheConfig extends ApolloReducerConfig { + resultCaching?: boolean; +} + +const defaultConfig: InMemoryCacheConfig = { fragmentMatcher: new HeuristicFragmentMatcher(), dataIdFromObject: defaultDataIdFromObject, addTypename: true, + resultCaching: true, }; export function defaultDataIdFromObject(result: any): string | null { @@ -45,7 +52,7 @@ export function defaultDataIdFromObject(result: any): string | null { export class InMemoryCache extends ApolloCache { protected data: NormalizedCache; - protected config: ApolloReducerConfig; + protected config: InMemoryCacheConfig; protected optimistic: OptimisticStoreItem[] = []; private watches = new Set(); private addTypename: boolean; @@ -58,7 +65,7 @@ export class InMemoryCache extends ApolloCache { // don't forget to turn it back on! private silenceBroadcast: boolean = false; - constructor(config: ApolloReducerConfig = {}) { + constructor(config: InMemoryCacheConfig = {}) { super(); this.config = { ...defaultConfig, ...config }; @@ -78,7 +85,9 @@ export class InMemoryCache extends ApolloCache { } this.addTypename = this.config.addTypename; - this.data = defaultNormalizedCacheFactory(); + this.data = this.config.resultCaching + ? new DepTrackingCache() + : new ObjectCache(); this.storeReader = new StoreReader(this.cacheKeyRoot); this.storeWriter = new StoreWriter(); @@ -134,9 +143,10 @@ export class InMemoryCache extends ApolloCache { return null; } - const store = (query.optimistic && this.optimistic.length) - ? defaultNormalizedCacheFactory(this.extract(true)) - : this.data; + const store = + query.optimistic && this.optimistic.length + ? new ObjectCache(this.extract(true)) + : this.data; return this.storeReader.readQueryFromStore({ store, @@ -164,9 +174,10 @@ export class InMemoryCache extends ApolloCache { } public diff(query: Cache.DiffOptions): Cache.DiffResult { - const store = (query.optimistic && this.optimistic.length) - ? defaultNormalizedCacheFactory(this.extract(true)) - : this.data; + const store = + query.optimistic && this.optimistic.length + ? new ObjectCache(this.extract(true)) + : this.data; return this.storeReader.diffQueryAgainstStore({ store: store, diff --git a/packages/apollo-cache-inmemory/src/index.ts b/packages/apollo-cache-inmemory/src/index.ts index 5ef7f2df296..d3e1779cb8b 100644 --- a/packages/apollo-cache-inmemory/src/index.ts +++ b/packages/apollo-cache-inmemory/src/index.ts @@ -1,4 +1,9 @@ -export { InMemoryCache, defaultDataIdFromObject } from './inMemoryCache'; +export { + InMemoryCache, + InMemoryCacheConfig, + defaultDataIdFromObject, +} from './inMemoryCache'; + export * from './readFromStore'; export * from './writeToStore'; export * from './fragmentMatcher';