Skip to content

Commit

Permalink
Stop exposing Policies to read/merge/key{Fields,Args} functions. (#6304)
Browse files Browse the repository at this point in the history
When I was first writing this code, I imagined the Policies object might
be useful in advanced cases. Since then, I have not found any practical
uses for options.policies that can't be satisfied by narrower APIs, and
Policies API has changed quite a bit.

To preserve our freedom to change the internal Policies API without
breaking anyone's code, and to simplify the context object types of read,
merge, keyFields, and keyArgs functions, I think we should stop passing
options.policies to the various cache policy functions.

For anyone who really needs access to the Policies object, it can still be
obtained via cache.policies.
  • Loading branch information
benjamn authored May 18, 2020
1 parent 800b581 commit 0652299
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 33 deletions.
29 changes: 13 additions & 16 deletions docs/source/caching/cache-field-behavior.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,13 @@ type FieldPolicy<
type KeySpecifier = (string | KeySpecifier)[];

type KeyArgsFunction = (
field: FieldNode,
args: Record<string, any> | null,
context: {
typename: string;
variables: Record<string, any>;
policies: Policies;
fieldName: string;
field: FieldNode | null;
},
) => string | null | void;
) => string | KeySpecifier | null | void;

type FieldReadFunction<TExisting, TReadResult = TExisting> = (
existing: Readonly<TExisting> | undefined,
Expand Down Expand Up @@ -531,8 +531,9 @@ interface FieldFunctionOptions {
// this field. Possibly undefined, if no variables were provided.
variables?: Record<string, any>;

// Utilities for handling { __ref: string } references.
// Easily detect { __ref: string } reference objects.
isReference(obj: any): obj is Reference;

// Returns a Reference object if obj can be identified, which requires,
// at minimum, a __typename and any necessary key fields. If true is
// passed for the optional mergeIntoStore argument, the object's fields
Expand All @@ -559,16 +560,12 @@ interface FieldFunctionOptions {
// if your read function does any expensive work.
storage: Record<string, any>;

// Call this function to invalidate any cached queries that previously
// consumed this field. If you use options.storage to cache the result
// of an expensive read function, updating options.storage and then
// calling options.invalidate() can be a good way to deliver the new
// result asynchronously.
invalidate(): void;

// In rare advanced use cases, a read or merge function may wish to
// consult the current Policies object, for example to call
// getStoreFieldName manually.
policies: Policies;
// Instead of just merging objects with { ...existing, ...incoming }, this
// helper function can be used to merge objects in a way that respects any
// custom merge functions defined for their fields.
mergeObjects<T extends StoreObject | Reference>(
existing: T,
incoming: T,
): T | undefined;
}
```
7 changes: 0 additions & 7 deletions src/cache/inmemory/__tests__/policies.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import gql from "graphql-tag";

import { InMemoryCache, ReactiveVar } from "../inMemoryCache";
import { Policies } from "../policies";
import { Reference, StoreObject } from "../../../core";
import { MissingFieldError } from "../..";

Expand Down Expand Up @@ -166,7 +165,6 @@ describe("type policies", function () {
expect(context.typename).toBe("Book");
expect(context.selectionSet!.kind).toBe("SelectionSet");
expect(context.fragmentMap).toEqual({});
expect(context.policies).toBeInstanceOf(Policies);
return ["isbn"];
},
},
Expand Down Expand Up @@ -621,7 +619,6 @@ describe("type policies", function () {
expect(context.typename).toBe("Thread");
expect(context.fieldName).toBe("comments");
expect(context.field!.name.value).toBe("comments");
expect(context.policies).toBeInstanceOf(Policies);

if (typeof args!.limit === "number") {
if (typeof args!.offset === "number") {
Expand Down Expand Up @@ -1940,11 +1937,9 @@ describe("type policies", function () {
args,
toReference,
isReference,
policies,
}) {
expect(!existing || Object.isFrozen(existing)).toBe(true);
expect(typeof toReference).toBe("function");
expect(policies).toBeInstanceOf(Policies);
const slice = existing.slice(
args!.offset,
args!.offset + args!.limit,
Expand All @@ -1957,11 +1952,9 @@ describe("type policies", function () {
args,
toReference,
isReference,
policies,
}) {
expect(!existing || Object.isFrozen(existing)).toBe(true);
expect(typeof toReference).toBe("function");
expect(policies).toBeInstanceOf(Policies);
const copy = existing ? existing.slice(0) : [];
const limit = args!.offset + args!.limit;
for (let i = args!.offset; i < limit; ++i) {
Expand Down
11 changes: 1 addition & 10 deletions src/cache/inmemory/policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ type KeyFieldsContext = {
typename?: string;
selectionSet?: SelectionSetNode;
fragmentMap?: FragmentMap;
policies: Policies;
// May be set by the KeyFieldsFunction to report fields that were involved
// in computing the ID. Never passed in by the caller.
keyObject?: Record<string, any>;
Expand Down Expand Up @@ -84,7 +83,6 @@ export type KeyArgsFunction = (
typename: string;
fieldName: string;
field: FieldNode | null;
policies: Policies;
},
) => KeySpecifier | ReturnType<IdGetter>;

Expand Down Expand Up @@ -122,11 +120,6 @@ export interface FieldFunctionOptions<

variables?: TVars;

// In rare advanced use cases, a read or merge function may wish to
// consult the current Policies object, for example to call
// getStoreFieldName manually.
policies: Policies;

// Utilities for dealing with { __ref } objects.
isReference: typeof isReference;
toReference: ToReferenceFunction;
Expand Down Expand Up @@ -266,7 +259,6 @@ export class Policies {
typename,
selectionSet,
fragmentMap,
policies: this,
};

let id: string | undefined;
Expand Down Expand Up @@ -467,7 +459,7 @@ export class Policies {
let keyFn = policy && policy.keyFn;
if (keyFn && typename) {
const args = field ? argumentsObjectFromField(field, argsOrVars) : argsOrVars;
const context = { typename, fieldName, field, policies: this };
const context: Parameters<KeyArgsFunction>[1] = { typename, fieldName, field };
while (keyFn) {
const specifierOrString = keyFn(args, context);
if (Array.isArray(specifierOrString)) {
Expand Down Expand Up @@ -678,7 +670,6 @@ function makeFieldFunctionOptions(
fieldName,
storeFieldName,
variables,
policies,
isReference,
toReference,
storage,
Expand Down

0 comments on commit 0652299

Please sign in to comment.