From ff35462bab5913580af5fa5aa6c82994a1ace669 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 24 Feb 2023 16:29:12 -0500 Subject: [PATCH 1/5] Test combining fuzzy possibleTypes with keyFields inheritance. --- src/cache/inmemory/__tests__/policies.ts | 132 +++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/src/cache/inmemory/__tests__/policies.ts b/src/cache/inmemory/__tests__/policies.ts index 843d7ce8542..14cc2b78e2f 100644 --- a/src/cache/inmemory/__tests__/policies.ts +++ b/src/cache/inmemory/__tests__/policies.ts @@ -627,6 +627,138 @@ describe("type policies", function () { })).toBe('DeathAdder:{"tagId":"LethalAbacus666"}'); }); + it("typePolicies can be inherited from supertypes with fuzzy possibleTypes", () => { + const cache = new InMemoryCache({ + possibleTypes: { + EntitySupertype: [".*Entity"], + }, + typePolicies: { + Query: { + fields: { + coworkers: { + merge(existing, incoming) { + return existing ? existing.concat(incoming) : incoming; + }, + }, + }, + }, + + // The point of this test is to ensure keyFields: ["uid"] can be + // registered for all __typename strings matching the RegExp /.*Entity/, + // without manually enumerating all of them. + EntitySupertype: { + keyFields: ["uid"], + }, + }, + }); + + type Coworker = { + __typename: "CoworkerEntity" | "ManagerEntity"; + uid: string; + name: string; + } + + const query: TypedDocumentNode<{ + coworkers: Coworker[]; + }> = gql` + query { + coworkers { + uid + name + } + } + `; + + cache.writeQuery({ + query, + data: { + coworkers: [ + { __typename: "CoworkerEntity", uid: "qwer", name: "Alessia" }, + { __typename: "CoworkerEntity", uid: "asdf", name: "Jerel" }, + { __typename: "CoworkerEntity", uid: "zxcv", name: "Lenz" }, + { __typename: "ManagerEntity", uid: "uiop", name: "Jeff" }, + ], + }, + }); + + expect(cache.extract()).toEqual({ + ROOT_QUERY: { + __typename: "Query", + coworkers: [ + { __ref: 'CoworkerEntity:{"uid":"qwer"}' }, + { __ref: 'CoworkerEntity:{"uid":"asdf"}' }, + { __ref: 'CoworkerEntity:{"uid":"zxcv"}' }, + { __ref: 'ManagerEntity:{"uid":"uiop"}' }, + ], + }, + 'CoworkerEntity:{"uid":"qwer"}': { + __typename: "CoworkerEntity", + uid: "qwer", + name: "Alessia", + }, + 'CoworkerEntity:{"uid":"asdf"}': { + __typename: "CoworkerEntity", + uid: "asdf", + name: "Jerel", + }, + 'CoworkerEntity:{"uid":"zxcv"}': { + __typename: "CoworkerEntity", + uid: "zxcv", + name: "Lenz", + }, + 'ManagerEntity:{"uid":"uiop"}': { + __typename: "ManagerEntity", + uid: "uiop", + name: "Jeff", + }, + }); + + interface CoworkerWithAlias extends Omit { + idAlias: string; + } + + const queryWithAlias: TypedDocumentNode<{ + coworkers: CoworkerWithAlias[]; + }> = gql` + query { + coworkers { + idAlias: uid + name + } + } + `; + + expect(cache.readQuery({ query: queryWithAlias })).toEqual({ + coworkers: [ + { __typename: "CoworkerEntity", idAlias: "qwer", name: "Alessia" }, + { __typename: "CoworkerEntity", idAlias: "asdf", name: "Jerel" }, + { __typename: "CoworkerEntity", idAlias: "zxcv", name: "Lenz" }, + { __typename: "ManagerEntity", idAlias: "uiop", name: "Jeff" }, + ], + }); + + cache.writeQuery({ + query: queryWithAlias, + data: { + coworkers: [ + { __typename: "CoworkerEntity", idAlias: "hjkl", name: "Martijn" }, + { __typename: "ManagerEntity", idAlias: "vbnm", name: "Hugh" }, + ], + }, + }); + + expect(cache.readQuery({ query })).toEqual({ + coworkers: [ + { __typename: "CoworkerEntity", uid: "qwer", name: "Alessia" }, + { __typename: "CoworkerEntity", uid: "asdf", name: "Jerel" }, + { __typename: "CoworkerEntity", uid: "zxcv", name: "Lenz" }, + { __typename: "ManagerEntity", uid: "uiop", name: "Jeff" }, + { __typename: "CoworkerEntity", uid: "hjkl", name: "Martijn" }, + { __typename: "ManagerEntity", uid: "vbnm", name: "Hugh" }, + ], + }); + }); + describe("field policies", function () { it(`can filter arguments using keyArgs`, function () { const cache = new InMemoryCache({ From 08d52a662364c302d2315a4d9bb25c54d3fca9b4 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Fri, 24 Feb 2023 17:31:22 -0500 Subject: [PATCH 2/5] Fix the fuzzy possibleTypes + keyFields inheritance test. --- src/cache/inmemory/policies.ts | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/cache/inmemory/policies.ts b/src/cache/inmemory/policies.ts index 5f7b76d3285..121ef24ffd8 100644 --- a/src/cache/inmemory/policies.ts +++ b/src/cache/inmemory/policies.ts @@ -565,11 +565,33 @@ export class Policies { // and merge functions often need to cooperate, so changing only one // of them would be a recipe for inconsistency. // - // Once the TypePolicy for typename has been accessed, its - // properties can still be updated directly using addTypePolicies, - // but future changes to supertype policies will not be reflected in - // this policy, because this code runs at most once per typename. - const supertypes = this.supertypeMap.get(typename); + // Once the TypePolicy for typename has been accessed, its properties can + // still be updated directly using addTypePolicies, but future changes to + // inherited supertype policies will not be reflected in this subtype + // policy, because this code runs at most once per typename. + let supertypes = this.supertypeMap.get(typename); + if (!supertypes && this.fuzzySubtypes.size) { + // To make the inheritance logic work for unknown typename strings that + // may have fuzzy supertypes, we give this typename an empty supertype + // set and then populate it with any fuzzy supertypes that match. + supertypes = this.getSupertypeSet(typename, true)!; + // This only works for typenames that are directly matched by a fuzzy + // supertype. What if there is an intermediate chain of supertypes? + // While possible, that situation can only be solved effectively by + // specifying the intermediate relationships via possibleTypes, manually + // and in a non-fuzzy way. + this.fuzzySubtypes.forEach((regExp, fuzzy) => { + if (regExp.test(typename)) { + // The fuzzy parameter is just the original string version of regExp + // (not a valid __typename string), but we can look up the + // associated supertype(s) in this.supertypeMap. + const fuzzySupertypes = this.supertypeMap.get(fuzzy); + if (fuzzySupertypes) { + fuzzySupertypes.forEach(supertype => supertypes!.add(supertype)); + } + } + }); + } if (supertypes && supertypes.size) { supertypes.forEach(supertype => { const { fields, ...rest } = this.getTypePolicy(supertype); From d4cab18a291d027cde1916ed01adcad594e2891b Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 8 Mar 2023 13:05:15 -0500 Subject: [PATCH 3/5] Add .changeset/ entry file for PR #10633. --- .changeset/odd-students-crash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/odd-students-crash.md diff --git a/.changeset/odd-students-crash.md b/.changeset/odd-students-crash.md new file mode 100644 index 00000000000..ede83e9c182 --- /dev/null +++ b/.changeset/odd-students-crash.md @@ -0,0 +1,5 @@ +--- +'@apollo/client': patch +--- + +Fix type policy inheritance involving fuzzy `possibleTypes` From 08549be4552e3a21f4bd0ba797852a330e25d320 Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 8 Mar 2023 13:01:45 -0500 Subject: [PATCH 4/5] Bump bundlesize limit to 34.1KB (currently 33.98KB). --- config/bundlesize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/bundlesize.ts b/config/bundlesize.ts index df0e4599b11..c65cce731d9 100644 --- a/config/bundlesize.ts +++ b/config/bundlesize.ts @@ -3,7 +3,7 @@ import { join } from "path"; import { gzipSync } from "zlib"; import bytes from "bytes"; -const gzipBundleByteLengthLimit = bytes("33.97KB"); +const gzipBundleByteLengthLimit = bytes("34.1KB"); const minFile = join("dist", "apollo-client.min.cjs"); const minPath = join(__dirname, "..", minFile); const gzipByteLen = gzipSync(readFileSync(minPath)).byteLength; From f9ab7a2f9f2f2524b238808a4ae646d88125296e Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 8 Mar 2023 17:14:56 -0500 Subject: [PATCH 5/5] Test full cache.extract() at end of test. https://github.com/apollographql/apollo-client/pull/10633#discussion_r1129985422 --- src/cache/inmemory/__tests__/policies.ts | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/cache/inmemory/__tests__/policies.ts b/src/cache/inmemory/__tests__/policies.ts index 14cc2b78e2f..11ac14dcb16 100644 --- a/src/cache/inmemory/__tests__/policies.ts +++ b/src/cache/inmemory/__tests__/policies.ts @@ -757,6 +757,50 @@ describe("type policies", function () { { __typename: "ManagerEntity", uid: "vbnm", name: "Hugh" }, ], }); + + expect(cache.extract()).toEqual({ + ROOT_QUERY: { + __typename: "Query", + coworkers: [ + { __ref: 'CoworkerEntity:{"uid":"qwer"}' }, + { __ref: 'CoworkerEntity:{"uid":"asdf"}' }, + { __ref: 'CoworkerEntity:{"uid":"zxcv"}' }, + { __ref: 'ManagerEntity:{"uid":"uiop"}' }, + { __ref: 'CoworkerEntity:{"uid":"hjkl"}' }, + { __ref: 'ManagerEntity:{"uid":"vbnm"}' }, + ], + }, + 'CoworkerEntity:{"uid":"qwer"}': { + __typename: "CoworkerEntity", + uid: "qwer", + name: "Alessia", + }, + 'CoworkerEntity:{"uid":"asdf"}': { + __typename: "CoworkerEntity", + uid: "asdf", + name: "Jerel", + }, + 'CoworkerEntity:{"uid":"zxcv"}': { + __typename: "CoworkerEntity", + uid: "zxcv", + name: "Lenz", + }, + 'ManagerEntity:{"uid":"uiop"}': { + __typename: "ManagerEntity", + uid: "uiop", + name: "Jeff", + }, + 'CoworkerEntity:{"uid":"hjkl"}': { + __typename: "CoworkerEntity", + uid: "hjkl", + name: "Martijn", + }, + 'ManagerEntity:{"uid":"vbnm"}': { + __typename: "ManagerEntity", + uid: "vbnm", + name: "Hugh", + }, + }); }); describe("field policies", function () {