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

Rename EntityCache to EntityStore. #5618

Merged
merged 1 commit into from
Nov 25, 2019
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
2 changes: 1 addition & 1 deletion src/cache/inmemory/__tests__/diffAgainstStore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import gql, { disableFragmentWarnings } from 'graphql-tag';

import { Reference, makeReference, isReference } from '../../../utilities/graphql/storeUtils';
import { defaultNormalizedCacheFactory } from '../entityCache';
import { defaultNormalizedCacheFactory } from '../entityStore';
import { StoreReader } from '../readFromStore';
import { StoreWriter } from '../writeToStore';
import { defaultDataIdFromObject } from '../policies';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import gql from 'graphql-tag';
import { EntityCache, supportsResultCaching } from '../entityCache';
import { EntityStore, supportsResultCaching } from '../entityStore';
import { InMemoryCache } from '../inMemoryCache';

describe('EntityCache', () => {
describe('EntityStore', () => {
it('should support result caching if so configured', () => {
const cacheWithResultCaching = new EntityCache.Root({
const storeWithResultCaching = new EntityStore.Root({
resultCaching: true,
});

const cacheWithoutResultCaching = new EntityCache.Root({
const storeWithoutResultCaching = new EntityStore.Root({
resultCaching: false,
});

expect(supportsResultCaching({ some: "arbitrary object " })).toBe(false);
expect(supportsResultCaching(cacheWithResultCaching)).toBe(true);
expect(supportsResultCaching(cacheWithoutResultCaching)).toBe(false);
expect(supportsResultCaching(storeWithResultCaching)).toBe(true);
expect(supportsResultCaching(storeWithoutResultCaching)).toBe(false);

const layerWithCaching = cacheWithResultCaching.addLayer("with caching", () => {});
const layerWithCaching = storeWithResultCaching.addLayer("with caching", () => {});
expect(supportsResultCaching(layerWithCaching)).toBe(true);
const anotherLayer = layerWithCaching.addLayer("another layer", () => {});
expect(supportsResultCaching(anotherLayer)).toBe(true);
expect(
anotherLayer
.removeLayer("with caching")
.removeLayer("another layer")
).toBe(cacheWithResultCaching);
expect(supportsResultCaching(cacheWithResultCaching)).toBe(true);
).toBe(storeWithResultCaching);
expect(supportsResultCaching(storeWithResultCaching)).toBe(true);

const layerWithoutCaching = cacheWithoutResultCaching.addLayer("with caching", () => {});
const layerWithoutCaching = storeWithoutResultCaching.addLayer("with caching", () => {});
expect(supportsResultCaching(layerWithoutCaching)).toBe(false);
expect(layerWithoutCaching.removeLayer("with caching")).toBe(cacheWithoutResultCaching);
expect(supportsResultCaching(cacheWithoutResultCaching)).toBe(false);
expect(layerWithoutCaching.removeLayer("with caching")).toBe(storeWithoutResultCaching);
expect(supportsResultCaching(storeWithoutResultCaching)).toBe(false);
});

function newBookAuthorCache() {
Expand Down
2 changes: 1 addition & 1 deletion src/cache/inmemory/__tests__/readFromStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { stripSymbols } from '../../../utilities/testing/stripSymbols';
import { StoreObject } from '../types';
import { StoreReader } from '../readFromStore';
import { makeReference } from '../../../utilities/graphql/storeUtils';
import { defaultNormalizedCacheFactory } from '../entityCache';
import { defaultNormalizedCacheFactory } from '../entityStore';
import { withError } from './diffAgainstStore';
import { Policies } from '../policies';

Expand Down
46 changes: 23 additions & 23 deletions src/cache/inmemory/__tests__/recordingCache.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NormalizedCacheObject } from '../types';
import { EntityCache } from '../entityCache';
import { EntityStore } from '../entityStore';

describe('OptimisticCacheLayer', () => {
function makeLayer(root: EntityCache) {
describe('Optimistic EntityStore layering', () => {
function makeLayer(root: EntityStore) {
return root.addLayer('whatever', () => {});
}

Expand All @@ -16,31 +16,31 @@ describe('OptimisticCacheLayer', () => {
Human: { __typename: 'Human', name: 'John' },
};

const underlyingCache = new EntityCache.Root({ seed: data });
const underlyingStore = new EntityStore.Root({ seed: data });

let cache = makeLayer(underlyingCache);
let store = makeLayer(underlyingStore);
beforeEach(() => {
cache = makeLayer(underlyingCache);
store = makeLayer(underlyingStore);
});

it('should passthrough values if not defined in recording', () => {
expect(cache.get('Human')).toBe(data.Human);
expect(cache.get('Animal')).toBe(data.Animal);
expect(store.get('Human')).toBe(data.Human);
expect(store.get('Animal')).toBe(data.Animal);
});

it('should return values defined during recording', () => {
cache.merge('Human', dataToRecord.Human);
expect(cache.get('Human')).toEqual(dataToRecord.Human);
expect(underlyingCache.get('Human')).toBe(data.Human);
store.merge('Human', dataToRecord.Human);
expect(store.get('Human')).toEqual(dataToRecord.Human);
expect(underlyingStore.get('Human')).toBe(data.Human);
});

it('should return undefined for values deleted during recording', () => {
expect(cache.get('Animal')).toBe(data.Animal);
expect(store.get('Animal')).toBe(data.Animal);
// delete should be registered in the recording:
cache.delete('Animal');
expect(cache.get('Animal')).toBeUndefined();
expect(cache.toObject()).toHaveProperty('Animal');
expect(underlyingCache.get('Animal')).toBe(data.Animal);
store.delete('Animal');
expect(store.get('Animal')).toBeUndefined();
expect(store.toObject()).toHaveProperty('Animal');
expect(underlyingStore.get('Animal')).toBe(data.Animal);
});
});

Expand All @@ -54,15 +54,15 @@ describe('OptimisticCacheLayer', () => {
Human: { __typename: 'Human', name: 'John' },
};

const underlyingCache = new EntityCache.Root({ seed: data });
let cache = makeLayer(underlyingCache);
const underlyingStore = new EntityStore.Root({ seed: data });
let store = makeLayer(underlyingStore);
let recording: NormalizedCacheObject;

beforeEach(() => {
cache = makeLayer(underlyingCache);
cache.merge('Human', dataToRecord.Human);
cache.delete('Animal');
recording = cache.toObject();
store = makeLayer(underlyingStore);
store.merge('Human', dataToRecord.Human);
store.delete('Animal');
recording = store.toObject();
});

it('should contain the property indicating deletion', () => {
Expand All @@ -77,7 +77,7 @@ describe('OptimisticCacheLayer', () => {
});

it('should keep the original data unaffected', () => {
expect(underlyingCache.toObject()).toEqual(data);
expect(underlyingStore.toObject()).toEqual(data);
});
});
});
4 changes: 2 additions & 2 deletions src/cache/inmemory/__tests__/roundtrip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import gql from 'graphql-tag';

import { withError } from './diffAgainstStore';
import { withWarning } from './writeToStore';
import { EntityCache } from '../entityCache';
import { EntityStore } from '../entityStore';
import { StoreReader } from '../readFromStore';
import { StoreWriter } from '../writeToStore';
import { Policies } from '../policies';
Expand Down Expand Up @@ -42,7 +42,7 @@ function storeRoundtrip(query: DocumentNode, result: any, variables = {}) {

// Make sure the result is identical if we haven't written anything new
// to the store. https://github.com/apollographql/apollo-client/pull/3394
expect(store).toBeInstanceOf(EntityCache);
expect(store).toBeInstanceOf(EntityStore);
expect(reader.readQueryFromStore(readOptions)).toBe(reconstructedResult);

const immutableResult = reader.readQueryFromStore(readOptions);
Expand Down
2 changes: 1 addition & 1 deletion src/cache/inmemory/__tests__/writeToStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { addTypenameToDocument } from '../../../utilities/graphql/transform';
import { cloneDeep } from '../../../utilities/common/cloneDeep';
import { StoreWriter } from '../writeToStore';
import { defaultNormalizedCacheFactory } from '../entityCache';
import { defaultNormalizedCacheFactory } from '../entityStore';
import { InMemoryCache } from '../inMemoryCache';
import { Policies } from '../policies';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ function makeDepKey(dataId: string, fieldName?: string) {
return JSON.stringify(parts);
}

function depend(store: EntityCache, dataId: string, fieldName?: string) {
function depend(store: EntityStore, dataId: string, fieldName?: string) {
if (store.depend) {
store.depend(makeDepKey(dataId, fieldName));
}
}

function dirty(store: EntityCache, dataId: string, fieldName?: string) {
function dirty(store: EntityStore, dataId: string, fieldName?: string) {
if (store.depend) {
store.depend.dirty(makeDepKey(dataId));
if (typeof fieldName === "string") {
Expand All @@ -36,24 +36,24 @@ function dirty(store: EntityCache, dataId: string, fieldName?: string) {
}
}

export abstract class EntityCache implements NormalizedCache {
export abstract class EntityStore implements NormalizedCache {
protected data: NormalizedCacheObject = Object.create(null);

// It seems like this property ought to be protected rather than public,
// but TypeScript doesn't realize it's inherited from a shared base
// class by both Root and Layer classes, so Layer methods are forbidden
// from accessing the .depend property of an arbitrary EntityCache
// from accessing the .depend property of an arbitrary EntityStore
// instance, because it might be a Root instance (and vice-versa).
public readonly depend: DependType = null;

public abstract addLayer(
layerId: string,
replay: (layer: EntityCache) => any,
): EntityCache;
replay: (layer: EntityStore) => any,
): EntityStore;

public abstract removeLayer(layerId: string): EntityCache;
public abstract removeLayer(layerId: string): EntityStore;

// Although the EntityCache class is abstract, it contains concrete
// Although the EntityStore class is abstract, it contains concrete
// implementations of the various NormalizedCache interface methods that
// are inherited by the Root and Layer subclasses.

Expand Down Expand Up @@ -107,7 +107,7 @@ export abstract class EntityCache implements NormalizedCache {
delete this.refs[dataId];
// Note that we do not delete the this.rootIds[dataId] retainment
// count for this ID, since an object with the same ID could appear in
// the cache again, and should not have to be retained again.
// the store again, and should not have to be retained again.
// delete this.rootIds[dataId];

if (this.depend && storeObject) {
Expand Down Expand Up @@ -162,9 +162,9 @@ export abstract class EntityCache implements NormalizedCache {
}

// The goal of garbage collection is to remove IDs from the Root layer of the
// cache that are no longer reachable starting from any IDs that have been
// store that are no longer reachable starting from any IDs that have been
// explicitly retained (see retain and release, above). Returns an array of
// dataId strings that were removed from the cache.
// dataId strings that were removed from the store.
public gc() {
const ids = this.getRootIdSet();
const snapshot = this.toObject();
Expand All @@ -175,13 +175,13 @@ export abstract class EntityCache implements NormalizedCache {
// were not previously contained by the Set.
Object.keys(this.findChildRefIds(id)).forEach(ids.add, ids);
// By removing IDs from the snapshot object here, we protect them from
// getting removed from the root cache layer below.
// getting removed from the root store layer below.
delete snapshot[id];
}
});
const idsToRemove = Object.keys(snapshot);
if (idsToRemove.length) {
let root: EntityCache = this;
let root: EntityStore = this;
while (root instanceof Layer) root = root.parent;
idsToRemove.forEach(root.delete, root);
}
Expand All @@ -197,7 +197,7 @@ export abstract class EntityCache implements NormalizedCache {
if (!hasOwn.call(this.refs, dataId)) {
const found = this.refs[dataId] = Object.create(null);
const workSet = new Set([this.data[dataId]]);
// Within the cache, only arrays and objects can contain child entity
// Within the store, only arrays and objects can contain child entity
// references, so we can prune the traversal using this predicate:
const canTraverse = (obj: any) => obj !== null && typeof obj === 'object';
workSet.forEach(obj => {
Expand All @@ -216,9 +216,9 @@ export abstract class EntityCache implements NormalizedCache {
}
}

export namespace EntityCache {
// Refer to this class as EntityCache.Root outside this namespace.
export class Root extends EntityCache {
export namespace EntityStore {
// Refer to this class as EntityStore.Root outside this namespace.
export class Root extends EntityStore {
// Although each Root instance gets its own unique this.depend
// function, any Layer instances created by calling addLayer need to
// share a single distinct dependency function. Since this shared
Expand All @@ -244,8 +244,8 @@ export namespace EntityCache {

public addLayer(
layerId: string,
replay: (layer: EntityCache) => any,
): EntityCache {
replay: (layer: EntityStore) => any,
): EntityStore {
// The replay function will be called in the Layer constructor.
return new Layer(layerId, this, replay, this.sharedLayerDepend);
}
Expand All @@ -258,12 +258,12 @@ export namespace EntityCache {
}

// Not exported, since all Layer instances are created by the addLayer method
// of the EntityCache.Root class.
class Layer extends EntityCache {
// of the EntityStore.Root class.
class Layer extends EntityStore {
constructor(
public readonly id: string,
public readonly parent: Layer | EntityCache.Root,
public readonly replay: (layer: EntityCache) => any,
public readonly parent: Layer | EntityStore.Root,
public readonly replay: (layer: EntityStore) => any,
public readonly depend: DependType,
) {
super();
Expand All @@ -272,12 +272,12 @@ class Layer extends EntityCache {

public addLayer(
layerId: string,
replay: (layer: EntityCache) => any,
): EntityCache {
replay: (layer: EntityStore) => any,
): EntityStore {
return new Layer(layerId, this, replay, this.depend);
}

public removeLayer(layerId: string): EntityCache {
public removeLayer(layerId: string): EntityStore {
// Remove all instances of the given id, not just the first one.
const parent = this.parent.removeLayer(layerId);

Expand Down Expand Up @@ -362,7 +362,7 @@ class Layer extends EntityCache {
}
}

const storeObjectReconciler: ReconcilerFunction<[EntityCache]> = function (
const storeObjectReconciler: ReconcilerFunction<[EntityStore]> = function (
existingObject,
incomingObject,
property,
Expand All @@ -386,7 +386,7 @@ const storeObjectReconciler: ReconcilerFunction<[EntityCache]> = function (
const iType = getTypenameFromStoreObject(store, incoming);
// If both objects have a typename and the typename is different, let the
// incoming object win. The typename can change when a different subtype
// of a union or interface is written to the cache.
// of a union or interface is written to the store.
if (
typeof eType === 'string' &&
typeof iType === 'string' &&
Expand Down Expand Up @@ -415,13 +415,13 @@ const storeObjectReconciler: ReconcilerFunction<[EntityCache]> = function (
return incoming;
}

export function supportsResultCaching(store: any): store is EntityCache {
export function supportsResultCaching(store: any): store is EntityStore {
// When result caching is disabled, store.depend will be null.
return !!(store instanceof EntityCache && store.depend);
return !!(store instanceof EntityStore && store.depend);
}

export function defaultNormalizedCacheFactory(
seed?: NormalizedCacheObject,
): NormalizedCache {
return new EntityCache.Root({ resultCaching: true, seed });
return new EntityStore.Root({ resultCaching: true, seed });
}
Loading