Skip to content

Commit

Permalink
(graphcache) - Allow partial schema to be passed for altered root nam…
Browse files Browse the repository at this point in the history
…es (#1379)

* (graphcache) - Allow partial schema to be passed for altered root names

* Fix up PartialIntrospectionSchema type
  • Loading branch information
kitten authored Feb 10, 2021
1 parent a861de8 commit d0e067a
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 41 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-bees-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/exchange-graphcache': minor
---

Allow `schema` option to be passed with a partial introspection result that only contains `queryType`, `mutationType`, and `subscriptionType` with their respective names. This allows you to pass `{ __schema: { queryType: { name: 'Query' } } }` and the likes to Graphcache's `cacheExchange` to alter the default root names without enabling full schema awareness.
41 changes: 29 additions & 12 deletions exchanges/graphcache/src/ast/schema.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
IntrospectionQuery,
IntrospectionSchema,
IntrospectionInputValue,
IntrospectionTypeRef,
IntrospectionType,
Expand Down Expand Up @@ -28,13 +29,24 @@ export interface SchemaIntrospector {
query: string | null;
mutation: string | null;
subscription: string | null;
types: Record<string, SchemaObject | SchemaUnion>;
types?: Record<string, SchemaObject | SchemaUnion>;
isSubType(abstract: string, possible: string): boolean;
}

export interface PartialIntrospectionSchema {
queryType: { name: string; kind?: any };
mutationType?: { name: string; kind?: any };
subscriptionType?: { name: string; kind?: any };
types?: IntrospectionSchema['types'];
}

export type IntrospectionData =
| IntrospectionQuery
| { __schema: PartialIntrospectionSchema };

export const buildClientSchema = ({
__schema,
}: IntrospectionQuery): SchemaIntrospector => {
}: IntrospectionData): SchemaIntrospector => {
const typemap: Record<string, SchemaObject | SchemaUnion> = {};

const buildNameMap = <T extends { name: string }>(
Expand Down Expand Up @@ -72,21 +84,13 @@ export const buildClientSchema = ({
}
};

for (let i = 0; i < __schema.types.length; i++) {
const type = __schema.types[i];
if (type && type.name) {
const out = buildType(type);
if (out) typemap[type.name] = out;
}
}

return {
const schema: SchemaIntrospector = {
query: __schema.queryType ? __schema.queryType.name : null,
mutation: __schema.mutationType ? __schema.mutationType.name : null,
subscription: __schema.subscriptionType
? __schema.subscriptionType.name
: null,
types: typemap,
types: undefined,
isSubType(abstract: string, possible: string) {
const abstractType = typemap[abstract];
const possibleType = typemap[possible];
Expand All @@ -104,4 +108,17 @@ export const buildClientSchema = ({
}
},
};

if (__schema.types) {
schema.types = typemap;
for (let i = 0; i < __schema.types.length; i++) {
const type = __schema.types[i];
if (type && type.name) {
const out = buildType(type);
if (out) typemap[type.name] = out;
}
}
}

return schema;
};
34 changes: 17 additions & 17 deletions exchanges/graphcache/src/ast/schemaPredicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export const isInterfaceOfType = (
const typeCondition = getTypeCondition(node);
if (!typeCondition || typename === typeCondition) return true;
if (
schema.types[typeCondition] &&
schema.types[typeCondition].kind === 'OBJECT'
schema.types![typeCondition] &&
schema.types![typeCondition].kind === 'OBJECT'
)
return typeCondition === typename;
expectAbstractType(schema, typeCondition!);
Expand All @@ -68,7 +68,7 @@ const getField = (
fieldName: string
) => {
expectObjectType(schema, typename);
const object = schema.types[typename] as SchemaObject;
const object = schema.types![typename] as SchemaObject;
const field = object.fields[fieldName];
if (!field) {
warn(
Expand All @@ -88,7 +88,7 @@ const getField = (

function expectObjectType(schema: SchemaIntrospector, typename: string) {
invariant(
schema.types[typename] && schema.types[typename].kind === 'OBJECT',
schema.types![typename] && schema.types![typename].kind === 'OBJECT',
'Invalid Object type: The type `' +
typename +
'` is not an object in the defined schema, ' +
Expand All @@ -99,9 +99,9 @@ function expectObjectType(schema: SchemaIntrospector, typename: string) {

function expectAbstractType(schema: SchemaIntrospector, typename: string) {
invariant(
schema.types[typename] &&
(schema.types[typename].kind === 'INTERFACE' ||
schema.types[typename].kind === 'UNION'),
schema.types![typename] &&
(schema.types![typename].kind === 'INTERFACE' ||
schema.types![typename].kind === 'UNION'),
'Invalid Abstract type: The type `' +
typename +
'` is not an Interface or Union type in the defined schema, ' +
Expand All @@ -116,7 +116,7 @@ export function expectValidKeyingConfig(
): void {
if (process.env.NODE_ENV !== 'production') {
for (const key in keys) {
if (!schema.types[key]) {
if (!schema.types![key]) {
warn(
'Invalid Object type: The type `' +
key +
Expand All @@ -137,7 +137,7 @@ export function expectValidUpdatesConfig(
}

if (schema.mutation) {
const mutationFields = (schema.types[schema.mutation] as SchemaObject)
const mutationFields = (schema.types![schema.mutation] as SchemaObject)
.fields;
const givenMutations = updates[schema.mutation] || {};
for (const fieldName in givenMutations) {
Expand All @@ -153,7 +153,7 @@ export function expectValidUpdatesConfig(
}

if (schema.subscription) {
const subscriptionFields = (schema.types[
const subscriptionFields = (schema.types![
schema.subscription
] as SchemaObject).fields;
const givenSubscription = updates[schema.subscription] || {};
Expand Down Expand Up @@ -200,7 +200,7 @@ export function expectValidResolversConfig(
for (const key in resolvers) {
if (key === 'Query') {
if (schema.query) {
const validQueries = (schema.types[schema.query] as SchemaObject)
const validQueries = (schema.types![schema.query] as SchemaObject)
.fields;
for (const resolverQuery in resolvers.Query) {
if (!validQueries[resolverQuery]) {
Expand All @@ -211,18 +211,18 @@ export function expectValidResolversConfig(
warnAboutResolver('Query');
}
} else {
if (!schema.types[key]) {
if (!schema.types![key]) {
warnAboutResolver(key);
} else if (
schema.types[key].kind === 'INTERFACE' ||
schema.types[key].kind === 'UNION'
schema.types![key].kind === 'INTERFACE' ||
schema.types![key].kind === 'UNION'
) {
warnAboutAbstractResolver(
key,
schema.types[key].kind as 'INTERFACE' | 'UNION'
schema.types![key].kind as 'INTERFACE' | 'UNION'
);
} else {
const validTypeProperties = (schema.types[key] as SchemaObject).fields;
const validTypeProperties = (schema.types![key] as SchemaObject).fields;
for (const resolverProperty in resolvers[key]) {
if (!validTypeProperties[resolverProperty]) {
warnAboutResolver(key + '.' + resolverProperty);
Expand All @@ -242,7 +242,7 @@ export function expectValidOptimisticMutationsConfig(
}

if (schema.mutation) {
const validMutations = (schema.types[schema.mutation] as SchemaObject)
const validMutations = (schema.types![schema.mutation] as SchemaObject)
.fields;
for (const mutation in optimisticMutations) {
if (!validMutations[mutation]) {
Expand Down
6 changes: 2 additions & 4 deletions exchanges/graphcache/src/cacheExchange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { IntrospectionQuery } from 'graphql';

import {
Exchange,
formatDocument,
Expand Down Expand Up @@ -30,7 +28,7 @@ import {
import { query, write, writeOptimistic } from './operations';
import { makeDict, isDictEmpty } from './helpers/dict';
import { addCacheOutcome, toRequestPolicy } from './helpers/operation';
import { filterVariables, getMainOperation } from './ast';
import { IntrospectionData, filterVariables, getMainOperation } from './ast';
import { Store, noopDataState, hydrateData, reserveLayer } from './store';

import {
Expand All @@ -57,7 +55,7 @@ export interface CacheExchangeOpts {
resolvers?: ResolverConfig;
optimistic?: OptimisticMutationConfig;
keys?: KeyingConfig;
schema?: IntrospectionQuery;
schema?: IntrospectionData;
storage?: StorageAdapter;
}

Expand Down
3 changes: 2 additions & 1 deletion exchanges/graphcache/src/offlineExchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import {
getName,
} from './ast';

import { makeDict } from './helpers/dict';
import {
SerializedRequest,
OptimisticMutationConfig,
Variables,
} from './types';

import { makeDict } from './helpers/dict';
import { cacheExchange, CacheExchangeOpts } from './cacheExchange';
import { toRequestPolicy } from './helpers/operation';

Expand Down
16 changes: 13 additions & 3 deletions exchanges/graphcache/src/store/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -881,9 +881,19 @@ describe('Store with storage', () => {
const fakeUpdater = jest.fn();

const store = new Store({
schema: minifyIntrospectionQuery(
require('../test-utils/altered_root_schema.json')
),
schema: {
__schema: {
queryType: {
name: 'query_root',
},
mutationType: {
name: 'mutation_root',
},
subscriptionType: {
name: 'subscription_root',
},
},
},
updates: {
Mutation: {
toggleTodo: fakeUpdater,
Expand Down
10 changes: 6 additions & 4 deletions exchanges/graphcache/src/store/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DocumentNode, IntrospectionQuery } from 'graphql';

import { DocumentNode } from 'graphql';
import { TypedDocumentNode, formatDocument, createRequest } from '@urql/core';

import {
Expand All @@ -25,6 +24,7 @@ import { keyOfField } from './keys';
import * as InMemoryData from './data';

import {
IntrospectionData,
SchemaIntrospector,
buildClientSchema,
expectValidKeyingConfig,
Expand All @@ -40,7 +40,7 @@ export interface StoreOpts {
resolvers?: ResolverConfig;
optimistic?: OptimisticMutationConfig;
keys?: KeyingConfig;
schema?: IntrospectionQuery;
schema?: IntrospectionData;
}

export class Store implements Cache {
Expand All @@ -66,10 +66,12 @@ export class Store implements Cache {
let mutationName = 'Mutation';
let subscriptionName = 'Subscription';
if (opts.schema) {
const schema = (this.schema = buildClientSchema(opts.schema));
const schema = buildClientSchema(opts.schema);
queryName = schema.query || queryName;
mutationName = schema.mutation || mutationName;
subscriptionName = schema.subscription || subscriptionName;
// Only add schema introspector if it has types info
if (schema.types) this.schema = schema;
}

this.updates = {
Expand Down

0 comments on commit d0e067a

Please sign in to comment.