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.

810: Graphcache warnings for `opts.keys` (#820)

* Graphcache: Add tests for error checking in schemaPredicates.

* Graphcache: console.warn() if creating a store and `opts.keys` contains invalid keys.

* Graphcache: changes based on PR comments.

authored by

Amy Boyd and committed by
GitHub
aa85ba0b 5295e33e

+90 -4
+12
docs/graphcache/errors.md
··· 272 272 273 273 This may either happen because you're missing the `__typename` and `id` or `_id` field or if the last two 274 274 aren't applicable to this entity a custom `keys` entry. 275 + 276 + ## (20) Invalid Object type <a id="20"></a> 277 + 278 + > Invalid Object type: The type `???` is not an object in the defined schema, 279 + > but the `keys` option is referencing it. 280 + 281 + When you're passing an introspected schema to the cache exchange, it is 282 + able to check whether your `opts.keys` is valid. 283 + This error occurs when an unknown type is found in `opts.keys`. 284 + 285 + Check whether your schema is up-to-date, or whether you're using an invalid 286 + typename in `opts.keys`, maybe due to a typo.
+20
exchanges/graphcache/src/ast/schemaPredicates.test.ts
··· 1 1 import { buildClientSchema } from 'graphql'; 2 + import { mocked } from 'ts-jest/utils'; 2 3 import * as SchemaPredicates from './schemaPredicates'; 3 4 4 5 describe('SchemaPredicates', () => { ··· 52 53 expect( 53 54 SchemaPredicates.isInterfaceOfType(schema, 'Todo', 'NoTodosError') 54 55 ).toBeFalsy(); 56 + }); 57 + 58 + it('should throw if a requested type does not exist', () => { 59 + expect(() => 60 + SchemaPredicates.isFieldNullable(schema, 'SomeInvalidType', 'complete') 61 + ).toThrow( 62 + 'The type `SomeInvalidType` is not an object in the defined schema, but the GraphQL document is traversing it.\nhttps://bit.ly/2XbVrpR#3' 63 + ); 64 + }); 65 + 66 + it('should warn in console if a requested field does not exist', () => { 67 + expect( 68 + SchemaPredicates.isFieldNullable(schema, 'Todo', 'goof') 69 + ).toBeFalsy(); 70 + 71 + expect(console.warn).toBeCalledTimes(1); 72 + const warnMessage = mocked(console.warn).mock.calls[0][0]; 73 + expect(warnMessage).toContain('The field `goof` does not exist on `Todo`'); 74 + expect(warnMessage).toContain('https://bit.ly/2XbVrpR#4'); 55 75 }); 56 76 });
+20
exchanges/graphcache/src/ast/schemaPredicates.ts
··· 10 10 } from 'graphql'; 11 11 12 12 import { warn, invariant } from '../helpers/help'; 13 + import { KeyingConfig } from '../types'; 13 14 14 15 export const isFieldNullable = ( 15 16 schema: GraphQLSchema, ··· 111 112 5 112 113 ); 113 114 } 115 + 116 + export function expectValidKeyingConfig( 117 + schema: GraphQLSchema, 118 + keys: KeyingConfig 119 + ): void { 120 + if (process.env.NODE_ENV !== 'production') { 121 + const types = Object.keys(schema.getTypeMap()); 122 + Object.keys(keys).forEach(key => { 123 + if (types.indexOf(key) === -1) { 124 + warn( 125 + 'Invalid Object type: The type `' + 126 + key + 127 + '` is not an object in the defined schema, but the `keys` option is referencing it.', 128 + 20 129 + ); 130 + } 131 + }); 132 + } 133 + }
+2 -1
exchanges/graphcache/src/helpers/help.ts
··· 23 23 | 16 24 24 | 17 25 25 | 18 26 - | 19; 26 + | 19 27 + | 20; 27 28 28 29 type DebugNode = ExecutableDefinitionNode | InlineFragmentNode; 29 30
+29 -1
exchanges/graphcache/src/store/store.test.ts
··· 1 1 import gql from 'graphql-tag'; 2 - 2 + import { mocked } from 'ts-jest/utils'; 3 3 import { Data, StorageAdapter } from '../types'; 4 4 import { query } from '../operations/query'; 5 5 import { write, writeOptimistic } from '../operations/write'; ··· 107 107 expect(store.keyOfEntity({ __typename: 'Any' })).toBe(null); 108 108 expect(store.keyOfEntity({ __typename: 'User' })).toBe('User:me'); 109 109 expect(store.keyOfEntity({ __typename: 'None' })).toBe(null); 110 + }); 111 + 112 + it('should not warn if keys do exist in the schema', function () { 113 + new Store({ 114 + schema: require('../test-utils/simple_schema.json'), 115 + keys: { 116 + Todo: () => 'Todo', 117 + }, 118 + }); 119 + 120 + expect(console.warn).not.toBeCalled(); 121 + }); 122 + 123 + it("should warn if a key doesn't exist in the schema", function () { 124 + new Store({ 125 + schema: require('../test-utils/simple_schema.json'), 126 + keys: { 127 + Todo: () => 'todo', 128 + NotInSchema: () => 'foo', 129 + }, 130 + }); 131 + 132 + expect(console.warn).toBeCalledTimes(1); 133 + const warnMessage = mocked(console.warn).mock.calls[0][0]; 134 + expect(warnMessage).toContain( 135 + 'The type `NotInSchema` is not an object in the defined schema, but the `keys` option is referencing it' 136 + ); 137 + expect(warnMessage).toContain('https://bit.ly/2XbVrpR#20'); 110 138 }); 111 139 }); 112 140
+5
exchanges/graphcache/src/store/store.ts
··· 27 27 import { invalidate, invalidateEntity } from '../operations/invalidate'; 28 28 import { keyOfField } from './keys'; 29 29 import * as InMemoryData from './data'; 30 + import * as SchemaPredicates from '../ast/schemaPredicates'; 30 31 31 32 type RootField = 'query' | 'mutation' | 'subscription'; 32 33 ··· 73 74 if (queryType) queryName = queryType.name; 74 75 if (mutationType) mutationName = mutationType.name; 75 76 if (subscriptionType) subscriptionName = subscriptionType.name; 77 + 78 + if (this.keys) { 79 + SchemaPredicates.expectValidKeyingConfig(this.schema, this.keys); 80 + } 76 81 } 77 82 78 83 this.rootFields = {
+2 -2
scripts/jest/setup.js
··· 10 10 const originalConsole = console; 11 11 global.console = { 12 12 ...originalConsole, 13 - warn() { /* noop */ }, 14 - error(message) { throw new Error(message); } 13 + warn: jest.SpyInstance = () => { /* noop */ }, 14 + error: jest.SpyInstance = (message) => { throw new Error(message); } 15 15 }; 16 16 17 17 jest.spyOn(console, 'log');