diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a9364ee534..c7d94bceb26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,9 @@
- The schema link package (`@apollo/client/link/schema`) will now validate incoming queries against its client-side schema, and return `errors` as a GraphQL server would.
[@amannn](https://github.com/amannn) in [#7094](https://github.com/apollographql/apollo-client/pull/7094)
+- Allow optional arguments in `keyArgs: [...]` arrays for `InMemoryCache` field policies.
+ [@benjamn](https://github.com/benjamn) in [#7109](https://github.com/apollographql/apollo-client/pull/7109)
+
## Apollo Client 3.2.2
## Bug Fixes
diff --git a/src/cache/inmemory/__tests__/__snapshots__/policies.ts.snap b/src/cache/inmemory/__tests__/__snapshots__/policies.ts.snap
index 1ebbd93b8f7..296372a0cd1 100644
--- a/src/cache/inmemory/__tests__/__snapshots__/policies.ts.snap
+++ b/src/cache/inmemory/__tests__/__snapshots__/policies.ts.snap
@@ -839,6 +839,378 @@ Object {
}
`;
+exports[`type policies field policies can include optional arguments in keyArgs 1`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 2`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 3`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 4`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 5`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":3}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 6`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":3}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 7`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":3}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":4}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
+exports[`type policies field policies can include optional arguments in keyArgs 8`] = `
+Object {
+ "Author:{\\"name\\":\\"Nadia Eghbal\\"}": Object {
+ "__typename": "Author",
+ "name": "Nadia Eghbal",
+ "writings": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2,\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":1,\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"a\\":3}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":2}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"b\\":4}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{\\"type\\":\\"Book\\"}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ "writings:{}": Array [
+ Object {
+ "__typename": "Book",
+ "isbn": "0578675862",
+ "title": "Working in Public: The Making and Maintenance of Open Source Software",
+ },
+ ],
+ },
+ "ROOT_QUERY": Object {
+ "__typename": "Query",
+ "author": Object {
+ "__ref": "Author:{\\"name\\":\\"Nadia Eghbal\\"}",
+ },
+ },
+}
+`;
+
exports[`type policies field policies read, merge, and modify functions can access options.storage 1`] = `
Object {
"ROOT_QUERY": Object {
diff --git a/src/cache/inmemory/__tests__/policies.ts b/src/cache/inmemory/__tests__/policies.ts
index dcf75babe1b..4b9c5ad0006 100644
--- a/src/cache/inmemory/__tests__/policies.ts
+++ b/src/cache/inmemory/__tests__/policies.ts
@@ -2,7 +2,7 @@ import gql from "graphql-tag";
import { InMemoryCache } from "../inMemoryCache";
import { ReactiveVar, makeVar } from "../reactiveVars";
-import { Reference, StoreObject, ApolloClient, NetworkStatus, TypedDocumentNode } from "../../../core";
+import { Reference, StoreObject, ApolloClient, NetworkStatus, TypedDocumentNode, DocumentNode } from "../../../core";
import { MissingFieldError } from "../..";
import { relayStylePagination } from "../../../utilities";
import { MockLink } from '../../../utilities/testing/mocking/mockLink';
@@ -742,6 +742,190 @@ describe("type policies", function () {
});
});
+ it("can include optional arguments in keyArgs", function () {
+ const cache = new InMemoryCache({
+ typePolicies: {
+ Author: {
+ keyFields: ["name"],
+ fields: {
+ writings: {
+ keyArgs: ["a", "b", "type"]
+ },
+ },
+ },
+ },
+ });
+
+ const data = {
+ author: {
+ __typename: "Author",
+ name: "Nadia Eghbal",
+ writings: [{
+ __typename: "Book",
+ isbn: "0578675862",
+ title: "Working in Public: The Making and Maintenance of " +
+ "Open Source Software",
+ }],
+ },
+ };
+
+ function check(
+ query: DocumentNode | TypedDocumentNode,
+ variables?: TVars,
+ ) {
+ cache.writeQuery({ query, variables, data });
+ expect(cache.readQuery({ query, variables })).toEqual(data);
+ }
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(type: "Book") {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(type: "Book", b: 2, a: 1) {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(b: 2, a: 1) {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(b: 2) {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(a: 3) {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings(unrelated: "oyez") {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query AuthorWritings ($type: String) {
+ author {
+ name
+ writings(b: 4, type: $type, unrelated: "oyez") {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `, { type: void 0 as any });
+ expect(cache.extract()).toMatchSnapshot();
+
+ check(gql`
+ query {
+ author {
+ name
+ writings {
+ ... on Book {
+ title
+ isbn
+ }
+ }
+ }
+ }
+ `);
+ expect(cache.extract()).toMatchSnapshot();
+
+ const storeFieldNames: string[] = [];
+
+ cache.modify({
+ id: cache.identify({
+ __typename: "Author",
+ name: "Nadia Eghbal",
+ }),
+
+ fields: {
+ writings(value, { storeFieldName }) {
+ storeFieldNames.push(storeFieldName);
+ expect(value).toEqual(data.author.writings);
+ return value;
+ },
+ },
+ })
+
+ expect(storeFieldNames.sort()).toEqual([
+ "writings",
+ 'writings:{"a":1,"b":2,"type":"Book"}',
+ 'writings:{"a":1,"b":2}',
+ 'writings:{"a":3}',
+ 'writings:{"b":2}',
+ 'writings:{"b":4}',
+ 'writings:{"type":"Book"}',
+ "writings:{}",
+ ]);
+ });
+
it("can return KeySpecifier arrays from keyArgs functions", function () {
const cache = new InMemoryCache({
typePolicies: {
diff --git a/src/cache/inmemory/policies.ts b/src/cache/inmemory/policies.ts
index 8f2160ba9f5..b9dfc376a1b 100644
--- a/src/cache/inmemory/policies.ts
+++ b/src/cache/inmemory/policies.ts
@@ -882,7 +882,7 @@ function keyArgsFnFromSpecifier(
): KeyArgsFunction {
return (args, context) => {
return args ? `${context.fieldName}:${
- JSON.stringify(computeKeyObject(args, specifier))
+ JSON.stringify(computeKeyObject(args, specifier, false))
}` : context.fieldName;
};
}
@@ -907,7 +907,7 @@ function keyFieldsFnFromSpecifier(
}
const keyObject = context.keyObject =
- computeKeyObject(object, specifier, aliasMap);
+ computeKeyObject(object, specifier, true, aliasMap);
return `${context.typename}:${JSON.stringify(keyObject)}`;
};
@@ -959,6 +959,7 @@ function makeAliasMap(
function computeKeyObject(
response: Record,
specifier: KeySpecifier,
+ strict: boolean,
aliasMap?: AliasMap,
): Record {
// The order of adding properties to keyObj affects its JSON serialization,
@@ -971,17 +972,17 @@ function computeKeyObject(
if (typeof prevKey === "string") {
const subsets = aliasMap && aliasMap.subsets;
const subset = subsets && subsets[prevKey];
- keyObj[prevKey] = computeKeyObject(response[prevKey], s, subset);
+ keyObj[prevKey] = computeKeyObject(response[prevKey], s, strict, subset);
}
} else {
const aliases = aliasMap && aliasMap.aliases;
const responseName = aliases && aliases[s] || s;
- invariant(
- hasOwn.call(response, responseName),
- // TODO Make this appropriate for keyArgs as well
- `Missing field '${responseName}' while computing key fields`,
- );
- keyObj[prevKey = s] = response[responseName];
+ if (hasOwn.call(response, responseName)) {
+ keyObj[prevKey = s] = response[responseName];
+ } else {
+ invariant(!strict, `Missing field '${responseName}' while computing key fields`);
+ prevKey = void 0;
+ }
}
});
return keyObj;