Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

(graphcache) - Fix updates config for renamed root schema types (#984)

* Fix store.updates not using mutation/subscription typenames

* Update SchemaPredicates for new updates structure

* Add test for altered root names

* Fix unsupported optional chaining in schemaPredicates

* Reformat schema code in store.ts

* Add changeset

authored by

Phil Pluckthun and committed by
GitHub
6dbbacd3 aee95980

+86 -61
+7
.changeset/four-pigs-kneel.md
··· 1 + --- 2 + '@urql/exchange-graphcache': patch 3 + --- 4 + 5 + Fix updaters config not working when Mutation/Subscription root names were altered. 6 + For instance, a Mutation named `mutation_root` could cause `store.updates` to be misread and cause a 7 + runtime error.
+14 -22
exchanges/graphcache/src/ast/schemaPredicates.ts
··· 12 12 import { warn, invariant } from '../helpers/help'; 13 13 import { 14 14 KeyingConfig, 15 - UpdatesConfig, 15 + UpdateResolver, 16 16 ResolverConfig, 17 17 OptimisticMutationConfig, 18 18 } from '../types'; ··· 143 143 144 144 export function expectValidUpdatesConfig( 145 145 schema: GraphQLSchema, 146 - updates: UpdatesConfig 146 + updates: Record<string, Record<string, UpdateResolver>> 147 147 ): void { 148 148 if (process.env.NODE_ENV === 'production') { 149 149 return; 150 150 } 151 151 152 - /* eslint-disable prettier/prettier */ 153 - const schemaMutations = schema.getMutationType() 154 - ? Object.keys((schema.getMutationType() as GraphQLObjectType).toConfig().fields) 155 - : []; 156 - const schemaSubscriptions = schema.getSubscriptionType() 157 - ? Object.keys((schema.getSubscriptionType() as GraphQLObjectType).toConfig().fields) 158 - : []; 159 - const givenMutations = updates.Mutation 160 - ? Object.keys(updates.Mutation) 161 - : []; 162 - const givenSubscriptions = updates.Subscription 163 - ? Object.keys(updates.Subscription) 164 - : []; 165 - /* eslint-enable prettier/prettier */ 152 + const mutation = schema.getMutationType(); 153 + const subscription = schema.getSubscriptionType(); 154 + const mutationFields = mutation ? mutation.getFields() : {}; 155 + const subscriptionFields = subscription ? subscription.getFields() : {}; 156 + const givenMutations = (mutation && updates[mutation.name]) || {}; 157 + const givenSubscription = (subscription && updates[subscription.name]) || {}; 166 158 167 - for (const givenMutation of givenMutations) { 168 - if (schemaMutations.indexOf(givenMutation) === -1) { 159 + for (const fieldName in givenMutations) { 160 + if (mutationFields[fieldName] === undefined) { 169 161 warn( 170 162 'Invalid mutation field: `' + 171 - givenMutation + 163 + fieldName + 172 164 '` is not in the defined schema, but the `updates.Mutation` option is referencing it.', 173 165 21 174 166 ); 175 167 } 176 168 } 177 169 178 - for (const givenSubscription of givenSubscriptions) { 179 - if (schemaSubscriptions.indexOf(givenSubscription) === -1) { 170 + for (const fieldName in givenSubscription) { 171 + if (subscriptionFields[fieldName] === undefined) { 180 172 warn( 181 173 'Invalid subscription field: `' + 182 - givenSubscription + 174 + fieldName + 183 175 '` is not in the defined schema, but the `updates.Subscription` option is referencing it.', 184 176 22 185 177 );
+39
exchanges/graphcache/src/store/store.test.ts
··· 828 828 expect(warnMessage).toContain('https://bit.ly/2XbVrpR#24'); 829 829 }); 830 830 831 + it('should use different rootConfigs', function () { 832 + const fakeUpdater = jest.fn(); 833 + 834 + const store = new Store({ 835 + schema: minifyIntrospectionQuery( 836 + require('../test-utils/altered_root_schema.json') 837 + ), 838 + updates: { 839 + Mutation: { 840 + toggleTodo: fakeUpdater, 841 + }, 842 + }, 843 + }); 844 + 845 + const mutationData = { 846 + __typename: 'mutation_root', 847 + toggleTodo: { 848 + __typename: 'Todo', 849 + id: 1, 850 + }, 851 + }; 852 + write(store, { query: Todos }, todosData); 853 + write( 854 + store, 855 + { 856 + query: gql` 857 + mutation { 858 + toggleTodo(id: 1) { 859 + id 860 + } 861 + } 862 + `, 863 + }, 864 + mutationData 865 + ); 866 + 867 + expect(fakeUpdater).toBeCalledTimes(1); 868 + }); 869 + 831 870 it('should not warn for an introspection result root', function () { 832 871 // NOTE: Do not wrap this require in `minifyIntrospectionQuery`! 833 872 // eslint-disable-next-line
+23 -37
exchanges/graphcache/src/store/store.ts
··· 15 15 Data, 16 16 QueryInput, 17 17 UpdatesConfig, 18 + UpdateResolver, 18 19 OptimisticMutationConfig, 19 20 KeyingConfig, 20 21 DataFields, 21 22 } from '../types'; 22 - import { invariant } from '../helpers/help'; 23 23 24 + import { invariant } from '../helpers/help'; 24 25 import { read, readFragment } from '../operations/query'; 25 26 import { writeFragment, startWrite } from '../operations/write'; 26 27 import { invalidateEntity } from '../operations/invalidate'; ··· 42 43 data: InMemoryData.InMemoryData; 43 44 44 45 resolvers: ResolverConfig; 45 - updates: UpdatesConfig; 46 + updates: Record<string, Record<string, UpdateResolver>>; 46 47 optimisticMutations: OptimisticMutationConfig; 47 48 keys: KeyingConfig; 48 49 schema?: GraphQLSchema; ··· 57 58 this.optimisticMutations = opts.optimistic || {}; 58 59 this.keys = opts.keys || {}; 59 60 60 - this.updates = { 61 - Mutation: (opts.updates && opts.updates.Mutation) || {}, 62 - Subscription: (opts.updates && opts.updates.Subscription) || {}, 63 - } as UpdatesConfig; 64 - 65 61 let queryName = 'Query'; 66 62 let mutationName = 'Mutation'; 67 63 let subscriptionName = 'Subscription'; ··· 70 66 const queryType = schema.getQueryType(); 71 67 const mutationType = schema.getMutationType(); 72 68 const subscriptionType = schema.getSubscriptionType(); 73 - if (queryType) queryName = queryType.name; 74 - if (mutationType) mutationName = mutationType.name; 75 - if (subscriptionType) subscriptionName = subscriptionType.name; 76 - 77 - if (process.env.NODE_ENV !== 'production') { 78 - if (this.keys) { 79 - SchemaPredicates.expectValidKeyingConfig(this.schema, this.keys); 80 - } 81 - 82 - const hasUpdates = 83 - Object.keys(this.updates.Mutation).length > 0 || 84 - Object.keys(this.updates.Subscription).length > 0; 85 - if (hasUpdates) { 86 - SchemaPredicates.expectValidUpdatesConfig(this.schema, this.updates); 87 - } 88 - 89 - if (this.resolvers) { 90 - SchemaPredicates.expectValidResolversConfig( 91 - this.schema, 92 - this.resolvers 93 - ); 94 - } 95 - 96 - if (this.optimisticMutations) { 97 - SchemaPredicates.expectValidOptimisticMutationsConfig( 98 - this.schema, 99 - this.optimisticMutations 100 - ); 101 - } 102 - } 69 + queryName = queryType ? queryType.name : queryName; 70 + mutationName = mutationType ? mutationType.name : mutationName; 71 + subscriptionName = subscriptionType 72 + ? subscriptionType.name 73 + : subscriptionName; 103 74 } 104 75 76 + this.updates = { 77 + [mutationName]: (opts.updates && opts.updates.Mutation) || {}, 78 + [subscriptionName]: (opts.updates && opts.updates.Subscription) || {}, 79 + }; 80 + 105 81 this.rootFields = { 106 82 query: queryName, 107 83 mutation: mutationName, ··· 115 91 }; 116 92 117 93 this.data = InMemoryData.make(queryName); 94 + 95 + if (this.schema && process.env.NODE_ENV !== 'production') { 96 + SchemaPredicates.expectValidKeyingConfig(this.schema, this.keys); 97 + SchemaPredicates.expectValidUpdatesConfig(this.schema, this.updates); 98 + SchemaPredicates.expectValidResolversConfig(this.schema, this.resolvers); 99 + SchemaPredicates.expectValidOptimisticMutationsConfig( 100 + this.schema, 101 + this.optimisticMutations 102 + ); 103 + } 118 104 } 119 105 120 106 keyOfField = keyOfField;
+3 -2
exchanges/graphcache/src/test-utils/altered_root_schema.json
··· 5 5 "__typename": "__Type" 6 6 }, 7 7 "mutationType": { 8 - "name": "Mutation" 8 + "name": "mutation_root", 9 + "__typename": "__Type" 9 10 }, 10 11 "subscriptionType": null, 11 12 "types": [ ··· 160 161 }, 161 162 { 162 163 "kind": "OBJECT", 163 - "name": "Mutation", 164 + "name": "mutation_root", 164 165 "fields": [ 165 166 { 166 167 "name": "toggleTodo",