Skip to content

Commit

Permalink
Make cache.batch return the result of calling options.update.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Aug 26, 2021
1 parent 2a5e62c commit f805c81
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/cache/core/__tests__/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class TestCache extends ApolloCache<unknown> {
}

public performTransaction(transaction: <TSerialized>(c: ApolloCache<TSerialized>) => void): void {
transaction(this);
}

public read<T, TVariables = any>(query: Cache.ReadOptions<TVariables>): T | null {
Expand Down
9 changes: 7 additions & 2 deletions src/cache/core/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,16 @@ export abstract class ApolloCache<TSerialized> implements DataProxy {
// provide a default batch implementation that's just another way of calling
// performTransaction. Subclasses of ApolloCache (such as InMemoryCache) can
// override the batch method to do more interesting things with its options.
public batch(options: Cache.BatchOptions<this>) {
public batch<U>(options: Cache.BatchOptions<this, U>): U {
const optimisticId =
typeof options.optimistic === "string" ? options.optimistic :
options.optimistic === false ? null : void 0;
this.performTransaction(options.update, optimisticId);
let updateResult: U;
this.performTransaction(
() => updateResult = options.update(this),
optimisticId,
);
return updateResult!;
}

public abstract performTransaction(
Expand Down
6 changes: 3 additions & 3 deletions src/cache/core/types/Cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export namespace Cache {
broadcast?: boolean;
}

export interface BatchOptions<C extends ApolloCache<any>> {
export interface BatchOptions<C extends ApolloCache<any>, U = void> {
// Same as the first parameter of performTransaction, except the cache
// argument will have the subclass type rather than ApolloCache.
update(cache: C): void;
update(cache: C): U;

// Passing a string for this option creates a new optimistic layer, with the
// given string as its layer.id, just like passing a string for the
Expand All @@ -66,7 +66,7 @@ export namespace Cache {
// against the current top layer of the cache), and passing false is the
// same as passing null (running the operation against root/non-optimistic
// cache data).
optimistic: string | boolean;
optimistic?: string | boolean;

// If you specify the ID of an optimistic layer using this option, that
// layer will be removed as part of the batch transaction, triggering at
Expand Down
100 changes: 98 additions & 2 deletions src/cache/inmemory/__tests__/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1377,20 +1377,22 @@ describe('Cache', () => {

const dirtied = new Map<Cache.WatchOptions, Cache.DiffResult<any>>();

cache.batch({
const aUpdateResult = cache.batch({
update(cache) {
cache.writeQuery({
query: aQuery,
data: {
a: "ay",
},
});
return "aQuery updated";
},
optimistic: true,
onWatchUpdated(w, diff) {
dirtied.set(w, diff);
},
});
expect(aUpdateResult).toBe("aQuery updated");

expect(dirtied.size).toBe(2);
expect(dirtied.has(aInfo.watch)).toBe(true);
Expand Down Expand Up @@ -1418,20 +1420,22 @@ describe('Cache', () => {

dirtied.clear();

cache.batch({
const bUpdateResult = cache.batch({
update(cache) {
cache.writeQuery({
query: bQuery,
data: {
b: "bee",
},
});
// Not returning anything, so beUpdateResult will be undefined.
},
optimistic: true,
onWatchUpdated(w, diff) {
dirtied.set(w, diff);
},
});
expect(bUpdateResult).toBeUndefined();

expect(dirtied.size).toBe(2);
expect(dirtied.has(aInfo.watch)).toBe(false);
Expand Down Expand Up @@ -1654,6 +1658,98 @@ describe('Cache', () => {
abInfo.cancel();
bInfo.cancel();
});

it("returns options.update result for optimistic and non-optimistic batches", () => {
const cache = new InMemoryCache;
const expected = Symbol.for("expected");

expect(cache.batch({
optimistic: false,
update(c) {
c.writeQuery({
query: gql`query { value }`,
data: { value: 12345 },
});
return expected;
},
})).toBe(expected);

expect(cache.batch({
optimistic: false,
update(c) {
c.reset();
return expected;
},
})).toBe(expected);

expect(cache.batch({
optimistic: false,
update(c) {
c.writeQuery({
query: gql`query { optimistic }`,
data: { optimistic: false },
});
return expected;
},
onWatchUpdated() {
throw new Error("onWatchUpdated should not have been called");
},
})).toBe(expected);

expect(cache.batch({
optimistic: true,
update(c) {
return expected;
},
})).toBe(expected);

expect(cache.batch({
optimistic: true,
update(c) {
c.writeQuery({
query: gql`query { optimistic }`,
data: { optimistic: true },
});
return expected;
},
onWatchUpdated() {
throw new Error("onWatchUpdated should not have been called");
},
})).toBe(expected);

expect(cache.batch({
// The optimistic option defaults to true.
// optimistic: true,
update(c) {
return expected;
},
})).toBe(expected);

expect(cache.batch({
optimistic: "some optimistic ID",
update(c) {
expect(c.readQuery({
query: gql`query { __typename }`,
})).toEqual({ __typename: "Query" });
return expected;
},
})).toBe(expected);

const optimisticId = "some optimistic ID";
expect(cache.batch({
optimistic: optimisticId,
update(c) {
c.writeQuery({
query: gql`query { optimistic }`,
data: { optimistic: optimisticId },
});
return expected;
},
onWatchUpdated() {
throw new Error("onWatchUpdated should not have been called");
},
})).toBe(expected);
});
});

describe('performTransaction', () => {
Expand Down
9 changes: 6 additions & 3 deletions src/cache/inmemory/inMemoryCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,22 +383,23 @@ export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {

private txCount = 0;

public batch(options: Cache.BatchOptions<InMemoryCache>) {
public batch<U>(options: Cache.BatchOptions<InMemoryCache, U>): U {
const {
update,
optimistic = true,
removeOptimistic,
onWatchUpdated,
} = options;

const perform = (layer?: EntityStore) => {
let updateResult: U;
const perform = (layer?: EntityStore): U => {
const { data, optimisticData } = this;
++this.txCount;
if (layer) {
this.data = this.optimisticData = layer;
}
try {
update(this);
return updateResult = update(this);
} finally {
--this.txCount;
this.data = data;
Expand Down Expand Up @@ -477,6 +478,8 @@ export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {
// options.onWatchUpdated.
this.broadcastWatches(options);
}

return updateResult!;
}

public performTransaction(
Expand Down

0 comments on commit f805c81

Please sign in to comment.