@graphql-tools/schema
Advanced tools
Comparing version
import { GraphQLSchema } from 'graphql'; | ||
export declare function addCatchUndefinedToSchema(schema: GraphQLSchema): void; | ||
export declare function addCatchUndefinedToSchema(schema: GraphQLSchema): GraphQLSchema; |
import { GraphQLSchema } from 'graphql'; | ||
import { ILogger } from './types'; | ||
export declare function addErrorLoggingToSchema(schema: GraphQLSchema, logger?: ILogger): void; | ||
export declare function addErrorLoggingToSchema(schema: GraphQLSchema, logger?: ILogger): GraphQLSchema; |
import { GraphQLSchema, GraphQLFieldResolver } from 'graphql'; | ||
export declare function addSchemaLevelResolver(schema: GraphQLSchema, fn: GraphQLFieldResolver<any, any>): void; | ||
export declare function addSchemaLevelResolver(schema: GraphQLSchema, fn: GraphQLFieldResolver<any, any>): GraphQLSchema; |
import { GraphQLSchema } from 'graphql'; | ||
import { IResolvers } from '@graphql-tools/utils'; | ||
export declare function extendResolversFromInterfaces(schema: GraphQLSchema, resolvers: IResolvers): {}; | ||
export declare function extendResolversFromInterfaces(schema: GraphQLSchema, resolvers: IResolvers): IResolvers; |
287
index.cjs.js
@@ -12,20 +12,21 @@ 'use strict'; | ||
// TODO test that schema is a schema, fn is a function | ||
const rootTypes = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()].filter(x => Boolean(x)); | ||
rootTypes.forEach(type => { | ||
if (type != null) { | ||
const fnToRunOnlyOnce = runAtMostOncePerRequest(fn); | ||
return utils.mapSchema(schema, { | ||
[utils.MapperKind.ROOT_FIELD]: (fieldConfig, _fieldName, typeName, schema) => { | ||
// XXX this should run at most once per request to simulate a true root resolver | ||
// for graphql-js this is an approximation that works with queries but not mutations | ||
const rootResolveFn = runAtMostOncePerRequest(fn); | ||
const fields = type.getFields(); | ||
Object.keys(fields).forEach(fieldName => { | ||
// XXX if the type is a subscription, a same query AST will be ran multiple times so we | ||
// deactivate here the runOnce if it's a subscription. This may not be optimal though... | ||
if (type === schema.getSubscriptionType()) { | ||
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, fn); | ||
} | ||
else { | ||
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, rootResolveFn); | ||
} | ||
}); | ||
} | ||
// XXX if the type is a subscription, a same query AST will be ran multiple times so we | ||
// deactivate here the runOnce if it's a subscription. This may not be optimal though... | ||
const subscription = schema.getSubscriptionType(); | ||
if (subscription != null && subscription.name === typeName) { | ||
return { | ||
...fieldConfig, | ||
resolve: wrapResolver(fieldConfig.resolve, fn), | ||
}; | ||
} | ||
return { | ||
...fieldConfig, | ||
resolve: wrapResolver(fieldConfig.resolve, fnToRunOnlyOnce), | ||
}; | ||
}, | ||
}); | ||
@@ -342,3 +343,3 @@ } | ||
: schemaOrOptions; | ||
const { schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, } = options; | ||
let { schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, } = options; | ||
const { allowResolversNotInSchema = false, requireResolversForResolveType } = resolverValidationOptions; | ||
@@ -348,3 +349,2 @@ const resolvers = inheritResolversFromInterfaces | ||
: inputResolvers; | ||
const typeMap = schema.getTypeMap(); | ||
Object.keys(resolvers).forEach(typeName => { | ||
@@ -362,104 +362,149 @@ const resolverValue = resolvers[typeName]; | ||
} | ||
const type = schema.getType(typeName); | ||
if (type == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`"${typeName}" defined in resolvers, but not in schema`); | ||
} | ||
else if (graphql.isSpecifiedScalarType(type)) { | ||
// allow -- without recommending -- overriding of specified scalar types | ||
const resolverValue = resolvers[typeName]; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
type[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
} | ||
} | ||
const type = schema.getType(typeName); | ||
if (!type && typeName !== '__schema') { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
}); | ||
schema = utils.mapSchema(schema, { | ||
[utils.MapperKind.SCALAR_TYPE]: type => { | ||
const config = type.toConfig(); | ||
const resolverValue = resolvers[type.name]; | ||
if (!graphql.isSpecifiedScalarType(type) && resolverValue != null) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
config[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
return new graphql.GraphQLScalarType(config); | ||
} | ||
throw new Error(`"${typeName}" defined in resolvers, but not in schema`); | ||
} | ||
if (graphql.isScalarType(type)) { | ||
// Support -- without recommending -- overriding default scalar types | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
type[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
} | ||
else if (graphql.isEnumType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else if (!type.getValue(fieldName)) { | ||
if (allowResolversNotInSchema) { | ||
}, | ||
[utils.MapperKind.ENUM_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
const config = type.toConfig(); | ||
const enumValueConfigMap = config.values; | ||
if (resolverValue != null) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else if (!enumValueConfigMap[fieldName]) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`); | ||
} | ||
else { | ||
enumValueConfigMap[fieldName].value = resolverValue[fieldName]; | ||
} | ||
}); | ||
return new graphql.GraphQLEnumType(config); | ||
} | ||
}, | ||
[utils.MapperKind.UNION_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} was defined in resolvers, but not present within ${typeName}`); | ||
} | ||
}); | ||
const config = type.toConfig(); | ||
const values = type.getValues(); | ||
const newValues = values.reduce((prev, value) => ({ | ||
...prev, | ||
[value.name]: { | ||
value: Object.keys(resolverValue).includes(value.name) ? resolverValue[value.name] : value.name, | ||
deprecationReason: value.deprecationReason, | ||
description: value.description, | ||
astNode: value.astNode, | ||
}, | ||
}), {}); | ||
// healSchema called later to update all fields to new type | ||
typeMap[typeName] = new graphql.GraphQLEnumType({ | ||
...config, | ||
values: newValues, | ||
}); | ||
} | ||
else if (graphql.isUnionType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} was defined in resolvers, but ${typeName} is not an object or interface type`); | ||
}); | ||
} | ||
else if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const fields = type.getFields(); | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
throw new Error(`${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type`); | ||
}); | ||
return new graphql.GraphQLUnionType(config); | ||
} | ||
}, | ||
[utils.MapperKind.OBJECT_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
const fields = config.fields; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
}); | ||
return new graphql.GraphQLObjectType(config); | ||
} | ||
}, | ||
[utils.MapperKind.INTERFACE_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
const fields = config.fields; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
}); | ||
return new graphql.GraphQLInterfaceType(config); | ||
} | ||
}, | ||
[utils.MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => { | ||
const resolverValue = resolvers[typeName]; | ||
if (resolverValue != null) { | ||
const fieldResolve = resolverValue[fieldName]; | ||
if (typeof fieldResolve === 'function') { | ||
// for convenience. Allows shorter syntax in resolver definition file | ||
field.resolve = fieldResolve; | ||
} | ||
else { | ||
if (typeof fieldResolve !== 'object') { | ||
throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); | ||
if (fieldResolve != null) { | ||
const newFieldConfig = { ...fieldConfig }; | ||
if (typeof fieldResolve === 'function') { | ||
// for convenience. Allows shorter syntax in resolver definition file | ||
newFieldConfig.resolve = fieldResolve; | ||
} | ||
setFieldProperties(field, fieldResolve); | ||
else { | ||
if (typeof fieldResolve !== 'object') { | ||
throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); | ||
} | ||
setFieldProperties(newFieldConfig, fieldResolve); | ||
} | ||
return newFieldConfig; | ||
} | ||
}); | ||
} | ||
} | ||
}, | ||
}); | ||
checkForResolveTypeResolver(schema, requireResolversForResolveType); | ||
// serialize all default values prior to healing fields with new scalar/enum types. | ||
utils.forEachDefaultValue(schema, utils.serializeInputValue); | ||
// schema may have new scalar/enum types that require healing | ||
utils.healSchema(schema); | ||
// reparse all default values with new parsing functions. | ||
utils.forEachDefaultValue(schema, utils.parseInputValue); | ||
if (defaultFieldResolver != null) { | ||
utils.forEachField(schema, field => { | ||
if (!field.resolve) { | ||
field.resolve = defaultFieldResolver; | ||
} | ||
schema = utils.mapSchema(schema, { | ||
[utils.MapperKind.OBJECT_FIELD]: fieldConfig => ({ | ||
...fieldConfig, | ||
resolve: fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver, | ||
}), | ||
}); | ||
@@ -482,5 +527,7 @@ } | ||
} | ||
utils.forEachField(schema, (field, typeName, fieldName) => { | ||
const errorHint = `${typeName}.${fieldName}`; | ||
field.resolve = decorateWithLogger(field.resolve, logger, errorHint); | ||
return utils.mapSchema(schema, { | ||
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({ | ||
...fieldConfig, | ||
resolve: decorateWithLogger(fieldConfig.resolve, logger, `${typeName}.${fieldName}`), | ||
}), | ||
}); | ||
@@ -500,5 +547,7 @@ } | ||
function addCatchUndefinedToSchema(schema) { | ||
utils.forEachField(schema, (field, typeName, fieldName) => { | ||
const errorHint = `${typeName}.${fieldName}`; | ||
field.resolve = decorateToCatchUndefined(field.resolve, errorHint); | ||
return utils.mapSchema(schema, { | ||
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({ | ||
...fieldConfig, | ||
resolve: decorateToCatchUndefined(fieldConfig.resolve, `${typeName}.${fieldName}`), | ||
}), | ||
}); | ||
@@ -518,4 +567,4 @@ } | ||
// Arguments are now validated and cleaned up | ||
const schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
addResolversToSchema({ | ||
let schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
schema = addResolversToSchema({ | ||
schema, | ||
@@ -528,6 +577,6 @@ resolvers: resolverMap, | ||
if (!allowUndefinedInResolve) { | ||
addCatchUndefinedToSchema(schema); | ||
schema = addCatchUndefinedToSchema(schema); | ||
} | ||
if (logger != null) { | ||
addErrorLoggingToSchema(schema, logger); | ||
schema = addErrorLoggingToSchema(schema, logger); | ||
} | ||
@@ -537,4 +586,6 @@ if (typeof resolvers['__schema'] === 'function') { | ||
// not doing that now, because I'd have to rewrite a lot of tests. | ||
addSchemaLevelResolver(schema, resolvers['__schema']); | ||
schema = addSchemaLevelResolver(schema, resolvers['__schema']); | ||
} | ||
// directive resolvers are implemented using SchemaDirectiveVisitor.visitSchemaDirectives | ||
// schema visiting modifies the schema in place | ||
if (directiveResolvers != null) { | ||
@@ -541,0 +592,0 @@ attachDirectiveResolvers(schema, directiveResolvers); |
291
index.esm.js
@@ -1,3 +0,3 @@ | ||
import { defaultFieldResolver, isScalarType, getNamedType, Kind, print, buildASTSchema, extendSchema, parse, isAbstractType, isSchema, isEnumType, GraphQLEnumType, isUnionType, isObjectType, isInterfaceType } from 'graphql'; | ||
import { forEachField, SchemaDirectiveVisitor, forEachDefaultValue, serializeInputValue, healSchema, parseInputValue, mergeDeep } from '@graphql-tools/utils'; | ||
import { defaultFieldResolver, isScalarType, getNamedType, Kind, print, buildASTSchema, extendSchema, parse, isAbstractType, isSchema, isSpecifiedScalarType, GraphQLScalarType, GraphQLEnumType, GraphQLUnionType, GraphQLObjectType, GraphQLInterfaceType } from 'graphql'; | ||
import { mapSchema, MapperKind, forEachField, SchemaDirectiveVisitor, mergeDeep } from '@graphql-tools/utils'; | ||
@@ -8,20 +8,21 @@ // wraps all resolvers of query, mutation or subscription fields | ||
// TODO test that schema is a schema, fn is a function | ||
const rootTypes = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()].filter(x => Boolean(x)); | ||
rootTypes.forEach(type => { | ||
if (type != null) { | ||
const fnToRunOnlyOnce = runAtMostOncePerRequest(fn); | ||
return mapSchema(schema, { | ||
[MapperKind.ROOT_FIELD]: (fieldConfig, _fieldName, typeName, schema) => { | ||
// XXX this should run at most once per request to simulate a true root resolver | ||
// for graphql-js this is an approximation that works with queries but not mutations | ||
const rootResolveFn = runAtMostOncePerRequest(fn); | ||
const fields = type.getFields(); | ||
Object.keys(fields).forEach(fieldName => { | ||
// XXX if the type is a subscription, a same query AST will be ran multiple times so we | ||
// deactivate here the runOnce if it's a subscription. This may not be optimal though... | ||
if (type === schema.getSubscriptionType()) { | ||
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, fn); | ||
} | ||
else { | ||
fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, rootResolveFn); | ||
} | ||
}); | ||
} | ||
// XXX if the type is a subscription, a same query AST will be ran multiple times so we | ||
// deactivate here the runOnce if it's a subscription. This may not be optimal though... | ||
const subscription = schema.getSubscriptionType(); | ||
if (subscription != null && subscription.name === typeName) { | ||
return { | ||
...fieldConfig, | ||
resolve: wrapResolver(fieldConfig.resolve, fn), | ||
}; | ||
} | ||
return { | ||
...fieldConfig, | ||
resolve: wrapResolver(fieldConfig.resolve, fnToRunOnlyOnce), | ||
}; | ||
}, | ||
}); | ||
@@ -338,3 +339,3 @@ } | ||
: schemaOrOptions; | ||
const { schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, } = options; | ||
let { schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, } = options; | ||
const { allowResolversNotInSchema = false, requireResolversForResolveType } = resolverValidationOptions; | ||
@@ -344,3 +345,2 @@ const resolvers = inheritResolversFromInterfaces | ||
: inputResolvers; | ||
const typeMap = schema.getTypeMap(); | ||
Object.keys(resolvers).forEach(typeName => { | ||
@@ -358,104 +358,149 @@ const resolverValue = resolvers[typeName]; | ||
} | ||
const type = schema.getType(typeName); | ||
if (type == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`"${typeName}" defined in resolvers, but not in schema`); | ||
} | ||
else if (isSpecifiedScalarType(type)) { | ||
// allow -- without recommending -- overriding of specified scalar types | ||
const resolverValue = resolvers[typeName]; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
type[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
} | ||
} | ||
const type = schema.getType(typeName); | ||
if (!type && typeName !== '__schema') { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
}); | ||
schema = mapSchema(schema, { | ||
[MapperKind.SCALAR_TYPE]: type => { | ||
const config = type.toConfig(); | ||
const resolverValue = resolvers[type.name]; | ||
if (!isSpecifiedScalarType(type) && resolverValue != null) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
config[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
return new GraphQLScalarType(config); | ||
} | ||
throw new Error(`"${typeName}" defined in resolvers, but not in schema`); | ||
} | ||
if (isScalarType(type)) { | ||
// Support -- without recommending -- overriding default scalar types | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else { | ||
type[fieldName] = resolverValue[fieldName]; | ||
} | ||
}); | ||
} | ||
else if (isEnumType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else if (!type.getValue(fieldName)) { | ||
if (allowResolversNotInSchema) { | ||
}, | ||
[MapperKind.ENUM_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
const config = type.toConfig(); | ||
const enumValueConfigMap = config.values; | ||
if (resolverValue != null) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
} | ||
else if (!enumValueConfigMap[fieldName]) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`); | ||
} | ||
else { | ||
enumValueConfigMap[fieldName].value = resolverValue[fieldName]; | ||
} | ||
}); | ||
return new GraphQLEnumType(config); | ||
} | ||
}, | ||
[MapperKind.UNION_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} was defined in resolvers, but not present within ${typeName}`); | ||
} | ||
}); | ||
const config = type.toConfig(); | ||
const values = type.getValues(); | ||
const newValues = values.reduce((prev, value) => ({ | ||
...prev, | ||
[value.name]: { | ||
value: Object.keys(resolverValue).includes(value.name) ? resolverValue[value.name] : value.name, | ||
deprecationReason: value.deprecationReason, | ||
description: value.description, | ||
astNode: value.astNode, | ||
}, | ||
}), {}); | ||
// healSchema called later to update all fields to new type | ||
typeMap[typeName] = new GraphQLEnumType({ | ||
...config, | ||
values: newValues, | ||
}); | ||
} | ||
else if (isUnionType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} was defined in resolvers, but ${typeName} is not an object or interface type`); | ||
}); | ||
} | ||
else if (isObjectType(type) || isInterfaceType(type)) { | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
// this is for isTypeOf and resolveType and all the other stuff. | ||
type[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const fields = type.getFields(); | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
throw new Error(`${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type`); | ||
}); | ||
return new GraphQLUnionType(config); | ||
} | ||
}, | ||
[MapperKind.OBJECT_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
const fields = config.fields; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
}); | ||
return new GraphQLObjectType(config); | ||
} | ||
}, | ||
[MapperKind.INTERFACE_TYPE]: type => { | ||
const resolverValue = resolvers[type.name]; | ||
if (resolverValue != null) { | ||
const config = type.toConfig(); | ||
const fields = config.fields; | ||
Object.keys(resolverValue).forEach(fieldName => { | ||
if (fieldName.startsWith('__')) { | ||
config[fieldName.substring(2)] = resolverValue[fieldName]; | ||
return; | ||
} | ||
const field = fields[fieldName]; | ||
if (field == null) { | ||
if (allowResolversNotInSchema) { | ||
return; | ||
} | ||
throw new Error(`${type.name}.${fieldName} defined in resolvers, but not in schema`); | ||
} | ||
}); | ||
return new GraphQLInterfaceType(config); | ||
} | ||
}, | ||
[MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => { | ||
const resolverValue = resolvers[typeName]; | ||
if (resolverValue != null) { | ||
const fieldResolve = resolverValue[fieldName]; | ||
if (typeof fieldResolve === 'function') { | ||
// for convenience. Allows shorter syntax in resolver definition file | ||
field.resolve = fieldResolve; | ||
} | ||
else { | ||
if (typeof fieldResolve !== 'object') { | ||
throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); | ||
if (fieldResolve != null) { | ||
const newFieldConfig = { ...fieldConfig }; | ||
if (typeof fieldResolve === 'function') { | ||
// for convenience. Allows shorter syntax in resolver definition file | ||
newFieldConfig.resolve = fieldResolve; | ||
} | ||
setFieldProperties(field, fieldResolve); | ||
else { | ||
if (typeof fieldResolve !== 'object') { | ||
throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); | ||
} | ||
setFieldProperties(newFieldConfig, fieldResolve); | ||
} | ||
return newFieldConfig; | ||
} | ||
}); | ||
} | ||
} | ||
}, | ||
}); | ||
checkForResolveTypeResolver(schema, requireResolversForResolveType); | ||
// serialize all default values prior to healing fields with new scalar/enum types. | ||
forEachDefaultValue(schema, serializeInputValue); | ||
// schema may have new scalar/enum types that require healing | ||
healSchema(schema); | ||
// reparse all default values with new parsing functions. | ||
forEachDefaultValue(schema, parseInputValue); | ||
if (defaultFieldResolver != null) { | ||
forEachField(schema, field => { | ||
if (!field.resolve) { | ||
field.resolve = defaultFieldResolver; | ||
} | ||
schema = mapSchema(schema, { | ||
[MapperKind.OBJECT_FIELD]: fieldConfig => ({ | ||
...fieldConfig, | ||
resolve: fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver, | ||
}), | ||
}); | ||
@@ -478,5 +523,7 @@ } | ||
} | ||
forEachField(schema, (field, typeName, fieldName) => { | ||
const errorHint = `${typeName}.${fieldName}`; | ||
field.resolve = decorateWithLogger(field.resolve, logger, errorHint); | ||
return mapSchema(schema, { | ||
[MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({ | ||
...fieldConfig, | ||
resolve: decorateWithLogger(fieldConfig.resolve, logger, `${typeName}.${fieldName}`), | ||
}), | ||
}); | ||
@@ -496,5 +543,7 @@ } | ||
function addCatchUndefinedToSchema(schema) { | ||
forEachField(schema, (field, typeName, fieldName) => { | ||
const errorHint = `${typeName}.${fieldName}`; | ||
field.resolve = decorateToCatchUndefined(field.resolve, errorHint); | ||
return mapSchema(schema, { | ||
[MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({ | ||
...fieldConfig, | ||
resolve: decorateToCatchUndefined(fieldConfig.resolve, `${typeName}.${fieldName}`), | ||
}), | ||
}); | ||
@@ -514,4 +563,4 @@ } | ||
// Arguments are now validated and cleaned up | ||
const schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
addResolversToSchema({ | ||
let schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
schema = addResolversToSchema({ | ||
schema, | ||
@@ -524,6 +573,6 @@ resolvers: resolverMap, | ||
if (!allowUndefinedInResolve) { | ||
addCatchUndefinedToSchema(schema); | ||
schema = addCatchUndefinedToSchema(schema); | ||
} | ||
if (logger != null) { | ||
addErrorLoggingToSchema(schema, logger); | ||
schema = addErrorLoggingToSchema(schema, logger); | ||
} | ||
@@ -533,4 +582,6 @@ if (typeof resolvers['__schema'] === 'function') { | ||
// not doing that now, because I'd have to rewrite a lot of tests. | ||
addSchemaLevelResolver(schema, resolvers['__schema']); | ||
schema = addSchemaLevelResolver(schema, resolvers['__schema']); | ||
} | ||
// directive resolvers are implemented using SchemaDirectiveVisitor.visitSchemaDirectives | ||
// schema visiting modifies the schema in place | ||
if (directiveResolvers != null) { | ||
@@ -537,0 +588,0 @@ attachDirectiveResolvers(schema, directiveResolvers); |
{ | ||
"name": "@graphql-tools/schema", | ||
"version": "5.0.1-alpha-cf46456.0", | ||
"version": "5.0.1-alpha-d0ee15c.0", | ||
"description": "A set of utils for faster development of GraphQL tools", | ||
@@ -9,3 +9,3 @@ "peerDependencies": { | ||
"dependencies": { | ||
"@graphql-tools/utils": "5.0.1-alpha-cf46456.0", | ||
"@graphql-tools/utils": "5.0.1-alpha-d0ee15c.0", | ||
"tslib": "1.11.1" | ||
@@ -12,0 +12,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
159067
8.9%1243
8.94%+ Added
- Removed