@graphql-tools/schema
Advanced tools
Comparing version 6.0.0-alpha.1 to 6.0.0-alpha.2
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 { IDirectiveResolvers } from '@graphql-tools/utils'; | ||
export declare function attachDirectiveResolvers(schema: GraphQLSchema, directiveResolvers: IDirectiveResolvers): void; | ||
export declare function attachDirectiveResolvers(schema: GraphQLSchema, directiveResolvers: IDirectiveResolvers): 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; |
336
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), | ||
}; | ||
}, | ||
}); | ||
@@ -121,23 +122,25 @@ } | ||
} | ||
const schemaDirectives = Object.create(null); | ||
Object.keys(directiveResolvers).forEach(directiveName => { | ||
schemaDirectives[directiveName] = class extends utils.SchemaDirectiveVisitor { | ||
visitFieldDefinition(field) { | ||
const resolver = directiveResolvers[directiveName]; | ||
const originalResolver = field.resolve != null ? field.resolve : graphql.defaultFieldResolver; | ||
const directiveArgs = this.args; | ||
field.resolve = (...args) => { | ||
const [source /* original args */, , context, info] = args; | ||
return resolver(() => new Promise((resolve, reject) => { | ||
const result = originalResolver.apply(field, args); | ||
if (result instanceof Error) { | ||
reject(result); | ||
} | ||
resolve(result); | ||
}), source, directiveArgs, context, info); | ||
}; | ||
} | ||
}; | ||
return utils.mapSchema(schema, { | ||
[utils.MapperKind.OBJECT_FIELD]: fieldConfig => { | ||
const newFieldConfig = { ...fieldConfig }; | ||
const directives = utils.getDirectives(schema, fieldConfig); | ||
Object.keys(directives).forEach(directiveName => { | ||
if (directiveResolvers[directiveName]) { | ||
const resolver = directiveResolvers[directiveName]; | ||
const originalResolver = newFieldConfig.resolve != null ? newFieldConfig.resolve : graphql.defaultFieldResolver; | ||
const directiveArgs = directives[directiveName]; | ||
newFieldConfig.resolve = (source, originalArgs, context, info) => { | ||
return resolver(() => new Promise((resolve, reject) => { | ||
const result = originalResolver(source, originalArgs, context, info); | ||
if (result instanceof Error) { | ||
reject(result); | ||
} | ||
resolve(result); | ||
}), source, directiveArgs, context, info); | ||
}; | ||
} | ||
}); | ||
return newFieldConfig; | ||
}, | ||
}); | ||
utils.SchemaDirectiveVisitor.visitSchemaDirectives(schema, schemaDirectives); | ||
} | ||
@@ -343,3 +346,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; | ||
@@ -349,3 +352,2 @@ const resolvers = inheritResolversFromInterfaces | ||
: inputResolvers; | ||
const typeMap = schema.getTypeMap(); | ||
Object.keys(resolvers).forEach(typeName => { | ||
@@ -363,104 +365,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, | ||
}), | ||
}); | ||
@@ -483,5 +530,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}`), | ||
}), | ||
}); | ||
@@ -501,9 +550,11 @@ } | ||
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}`), | ||
}), | ||
}); | ||
} | ||
function makeExecutableSchema({ typeDefs, resolvers = {}, logger, allowUndefinedInResolve = true, resolverValidationOptions = {}, directiveResolvers, schemaDirectives, parseOptions = {}, inheritResolversFromInterfaces = false, }) { | ||
function makeExecutableSchema({ typeDefs, resolvers = {}, logger, allowUndefinedInResolve = true, resolverValidationOptions = {}, directiveResolvers, schemaDirectives, schemaTransforms = [], parseOptions = {}, inheritResolversFromInterfaces = false, }) { | ||
// Validate and clean up arguments | ||
@@ -519,4 +570,4 @@ if (typeof resolverValidationOptions !== 'object') { | ||
// Arguments are now validated and cleaned up | ||
const schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
addResolversToSchema({ | ||
let schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
schema = addResolversToSchema({ | ||
schema, | ||
@@ -529,6 +580,6 @@ resolvers: resolverMap, | ||
if (!allowUndefinedInResolve) { | ||
addCatchUndefinedToSchema(schema); | ||
schema = addCatchUndefinedToSchema(schema); | ||
} | ||
if (logger != null) { | ||
addErrorLoggingToSchema(schema, logger); | ||
schema = addErrorLoggingToSchema(schema, logger); | ||
} | ||
@@ -538,6 +589,11 @@ 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']); | ||
} | ||
schemaTransforms.forEach(schemaTransform => { | ||
schema = schemaTransform(schema); | ||
}); | ||
// directive resolvers are implemented using SchemaDirectiveVisitor.visitSchemaDirectives | ||
// schema visiting modifies the schema in place | ||
if (directiveResolvers != null) { | ||
attachDirectiveResolvers(schema, directiveResolvers); | ||
schema = attachDirectiveResolvers(schema, directiveResolvers); | ||
} | ||
@@ -544,0 +600,0 @@ if (schemaDirectives != null) { |
340
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, getDirectives, mergeDeep, SchemaDirectiveVisitor } 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), | ||
}; | ||
}, | ||
}); | ||
@@ -117,23 +118,25 @@ } | ||
} | ||
const schemaDirectives = Object.create(null); | ||
Object.keys(directiveResolvers).forEach(directiveName => { | ||
schemaDirectives[directiveName] = class extends SchemaDirectiveVisitor { | ||
visitFieldDefinition(field) { | ||
const resolver = directiveResolvers[directiveName]; | ||
const originalResolver = field.resolve != null ? field.resolve : defaultFieldResolver; | ||
const directiveArgs = this.args; | ||
field.resolve = (...args) => { | ||
const [source /* original args */, , context, info] = args; | ||
return resolver(() => new Promise((resolve, reject) => { | ||
const result = originalResolver.apply(field, args); | ||
if (result instanceof Error) { | ||
reject(result); | ||
} | ||
resolve(result); | ||
}), source, directiveArgs, context, info); | ||
}; | ||
} | ||
}; | ||
return mapSchema(schema, { | ||
[MapperKind.OBJECT_FIELD]: fieldConfig => { | ||
const newFieldConfig = { ...fieldConfig }; | ||
const directives = getDirectives(schema, fieldConfig); | ||
Object.keys(directives).forEach(directiveName => { | ||
if (directiveResolvers[directiveName]) { | ||
const resolver = directiveResolvers[directiveName]; | ||
const originalResolver = newFieldConfig.resolve != null ? newFieldConfig.resolve : defaultFieldResolver; | ||
const directiveArgs = directives[directiveName]; | ||
newFieldConfig.resolve = (source, originalArgs, context, info) => { | ||
return resolver(() => new Promise((resolve, reject) => { | ||
const result = originalResolver(source, originalArgs, context, info); | ||
if (result instanceof Error) { | ||
reject(result); | ||
} | ||
resolve(result); | ||
}), source, directiveArgs, context, info); | ||
}; | ||
} | ||
}); | ||
return newFieldConfig; | ||
}, | ||
}); | ||
SchemaDirectiveVisitor.visitSchemaDirectives(schema, schemaDirectives); | ||
} | ||
@@ -339,3 +342,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; | ||
@@ -345,3 +348,2 @@ const resolvers = inheritResolversFromInterfaces | ||
: inputResolvers; | ||
const typeMap = schema.getTypeMap(); | ||
Object.keys(resolvers).forEach(typeName => { | ||
@@ -359,104 +361,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, | ||
}), | ||
}); | ||
@@ -479,5 +526,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}`), | ||
}), | ||
}); | ||
@@ -497,9 +546,11 @@ } | ||
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}`), | ||
}), | ||
}); | ||
} | ||
function makeExecutableSchema({ typeDefs, resolvers = {}, logger, allowUndefinedInResolve = true, resolverValidationOptions = {}, directiveResolvers, schemaDirectives, parseOptions = {}, inheritResolversFromInterfaces = false, }) { | ||
function makeExecutableSchema({ typeDefs, resolvers = {}, logger, allowUndefinedInResolve = true, resolverValidationOptions = {}, directiveResolvers, schemaDirectives, schemaTransforms = [], parseOptions = {}, inheritResolversFromInterfaces = false, }) { | ||
// Validate and clean up arguments | ||
@@ -515,4 +566,4 @@ if (typeof resolverValidationOptions !== 'object') { | ||
// Arguments are now validated and cleaned up | ||
const schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
addResolversToSchema({ | ||
let schema = buildSchemaFromTypeDefinitions(typeDefs, parseOptions); | ||
schema = addResolversToSchema({ | ||
schema, | ||
@@ -525,6 +576,6 @@ resolvers: resolverMap, | ||
if (!allowUndefinedInResolve) { | ||
addCatchUndefinedToSchema(schema); | ||
schema = addCatchUndefinedToSchema(schema); | ||
} | ||
if (logger != null) { | ||
addErrorLoggingToSchema(schema, logger); | ||
schema = addErrorLoggingToSchema(schema, logger); | ||
} | ||
@@ -534,6 +585,11 @@ 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']); | ||
} | ||
schemaTransforms.forEach(schemaTransform => { | ||
schema = schemaTransform(schema); | ||
}); | ||
// directive resolvers are implemented using SchemaDirectiveVisitor.visitSchemaDirectives | ||
// schema visiting modifies the schema in place | ||
if (directiveResolvers != null) { | ||
attachDirectiveResolvers(schema, directiveResolvers); | ||
schema = attachDirectiveResolvers(schema, directiveResolvers); | ||
} | ||
@@ -540,0 +596,0 @@ if (schemaDirectives != null) { |
import { IExecutableSchemaDefinition } from './types'; | ||
export declare function makeExecutableSchema<TContext = any>({ typeDefs, resolvers, logger, allowUndefinedInResolve, resolverValidationOptions, directiveResolvers, schemaDirectives, parseOptions, inheritResolversFromInterfaces, }: IExecutableSchemaDefinition<TContext>): import("graphql").GraphQLSchema; | ||
export declare function makeExecutableSchema<TContext = any>({ typeDefs, resolvers, logger, allowUndefinedInResolve, resolverValidationOptions, directiveResolvers, schemaDirectives, schemaTransforms, parseOptions, inheritResolversFromInterfaces, }: IExecutableSchemaDefinition<TContext>): import("graphql").GraphQLSchema; |
{ | ||
"name": "@graphql-tools/schema", | ||
"version": "6.0.0-alpha.1", | ||
"version": "6.0.0-alpha.2", | ||
"description": "A set of utils for faster development of GraphQL tools", | ||
@@ -9,4 +9,4 @@ "peerDependencies": { | ||
"dependencies": { | ||
"@graphql-tools/utils": "6.0.0-alpha.1", | ||
"tslib": "1.11.1" | ||
"@graphql-tools/utils": "6.0.0-alpha.2", | ||
"tslib": "~2.0.0" | ||
}, | ||
@@ -13,0 +13,0 @@ "repository": "git@github.com:ardatan/graphql-tools.git", |
@@ -1,2 +0,2 @@ | ||
import { ITypeDefinitions, IResolvers, IResolverValidationOptions, IDirectiveResolvers, SchemaDirectiveVisitorClass, GraphQLParseOptions } from 'packages/utils/src'; | ||
import { ITypeDefinitions, IResolvers, IResolverValidationOptions, IDirectiveResolvers, SchemaDirectiveVisitorClass, GraphQLParseOptions, SchemaTransform } from '@graphql-tools/utils'; | ||
export interface ILogger { | ||
@@ -13,4 +13,5 @@ log: (error: Error) => void; | ||
schemaDirectives?: Record<string, SchemaDirectiveVisitorClass>; | ||
schemaTransforms?: Array<SchemaTransform>; | ||
parseOptions?: GraphQLParseOptions; | ||
inheritResolversFromInterfaces?: boolean; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
160608
1254
+ Added@graphql-tools/utils@6.0.0-alpha.2(transitive)
+ Addedtslib@1.14.12.0.3(transitive)
- Removed@graphql-tools/utils@6.0.0-alpha.1(transitive)
- Removedtslib@1.11.12.7.0(transitive)
Updatedtslib@~2.0.0