diff --git a/packages/utils/src/Interfaces.ts b/packages/utils/src/Interfaces.ts index 33f4b4d85f4..bcd11634d6a 100644 --- a/packages/utils/src/Interfaces.ts +++ b/packages/utils/src/Interfaces.ts @@ -199,21 +199,21 @@ export type InputFieldFilter = ( ) => boolean; export type FieldFilter = ( - typeName?: string, - fieldName?: string, - fieldConfig?: GraphQLFieldConfig | GraphQLInputFieldConfig + typeName: string, + fieldName: string, + fieldConfig: GraphQLFieldConfig | GraphQLInputFieldConfig ) => boolean; export type ObjectFieldFilter = ( - typeName?: string, - fieldName?: string, - fieldConfig?: GraphQLFieldConfig + typeName: string, + fieldName: string, + fieldConfig: GraphQLFieldConfig ) => boolean; export type RootFieldFilter = ( - operation?: 'Query' | 'Mutation' | 'Subscription', - rootFieldName?: string, - fieldConfig?: GraphQLFieldConfig + operation: 'Query' | 'Mutation' | 'Subscription', + rootFieldName: string, + fieldConfig: GraphQLFieldConfig ) => boolean; export type TypeFilter = (typeName: string, type: GraphQLType) => boolean; diff --git a/packages/wrap/src/generateProxyingResolvers.ts b/packages/wrap/src/generateProxyingResolvers.ts index 307fb92e5c8..c71dccb2705 100644 --- a/packages/wrap/src/generateProxyingResolvers.ts +++ b/packages/wrap/src/generateProxyingResolvers.ts @@ -1,6 +1,6 @@ import { GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, OperationTypeNode } from 'graphql'; -import { getResponseKeyFromInfo } from '@graphql-tools/utils'; +import { Maybe, getResponseKeyFromInfo } from '@graphql-tools/utils'; import { delegateToSchema, getSubschema, @@ -20,7 +20,7 @@ export function generateProxyingResolvers( const transformedSchema = applySchemaTransforms(targetSchema, subschemaConfig); - const operationTypes: Record = { + const operationTypes: Record> = { query: targetSchema.getQueryType(), mutation: targetSchema.getMutationType(), subscription: targetSchema.getSubscriptionType(), diff --git a/packages/wrap/src/makeRemoteExecutableSchema.ts b/packages/wrap/src/makeRemoteExecutableSchema.ts index 0245e53ba6a..7fcf10bd59f 100644 --- a/packages/wrap/src/makeRemoteExecutableSchema.ts +++ b/packages/wrap/src/makeRemoteExecutableSchema.ts @@ -24,7 +24,7 @@ export function makeRemoteExecutableSchema({ export function defaultCreateRemoteResolver( executor: Executor, - subscriber: Subscriber + subscriber?: Subscriber | undefined ): GraphQLFieldResolver { return (_parent, _args, context, info) => delegateToSchema({ diff --git a/packages/wrap/src/transforms/FilterObjectFieldDirectives.ts b/packages/wrap/src/transforms/FilterObjectFieldDirectives.ts index 4d471964613..74e7ec64be9 100644 --- a/packages/wrap/src/transforms/FilterObjectFieldDirectives.ts +++ b/packages/wrap/src/transforms/FilterObjectFieldDirectives.ts @@ -20,13 +20,17 @@ export default class FilterObjectFieldDirectives implements Transform { ): GraphQLSchema { const transformer = new TransformObjectFields( (_typeName: string, _fieldName: string, fieldConfig: GraphQLFieldConfig) => { - const keepDirectives = fieldConfig.astNode.directives.filter(dir => { - const directiveDef = originalWrappingSchema.getDirective(dir.name.value); - const directiveValue = directiveDef ? getArgumentValues(directiveDef, dir) : undefined; - return this.filter(dir.name.value, directiveValue); - }); - - if (keepDirectives.length !== fieldConfig.astNode.directives.length) { + const keepDirectives = + fieldConfig.astNode?.directives?.filter(dir => { + const directiveDef = originalWrappingSchema.getDirective(dir.name.value); + const directiveValue = directiveDef ? getArgumentValues(directiveDef, dir) : undefined; + return this.filter(dir.name.value, directiveValue); + }) ?? []; + + if ( + fieldConfig.astNode?.directives != null && + keepDirectives.length !== fieldConfig.astNode.directives.length + ) { fieldConfig = { ...fieldConfig, astNode: { diff --git a/packages/wrap/src/transforms/HoistField.ts b/packages/wrap/src/transforms/HoistField.ts index fedb4735ec8..b9463510028 100644 --- a/packages/wrap/src/transforms/HoistField.ts +++ b/packages/wrap/src/transforms/HoistField.ts @@ -9,7 +9,14 @@ import { GraphQLFieldResolver, } from 'graphql'; -import { appendObjectFields, removeObjectFields, Request, ExecutionResult, relocatedError } from '@graphql-tools/utils'; +import { + appendObjectFields, + removeObjectFields, + Request, + ExecutionResult, + relocatedError, + assertSome, +} from '@graphql-tools/utils'; import { Transform, defaultMergedResolver, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'; @@ -24,7 +31,7 @@ export default class HoistField implements Transform { private readonly oldFieldName: string; private readonly argFilters: Array<(arg: GraphQLArgument) => boolean>; private readonly argLevels: Record; - private readonly transformer: Transform; + private readonly transformer: MapFields; constructor( typeName: string, @@ -45,6 +52,7 @@ export default class HoistField implements Transform { const pathToField = path.slice(); const oldFieldName = pathToField.pop(); + assertSome(oldFieldName); this.oldFieldName = oldFieldName; this.pathToField = pathToField; @@ -59,7 +67,7 @@ export default class HoistField implements Transform { { [typeName]: value => unwrapValue(value, alias), }, - errors => unwrapErrors(errors, alias) + errors => (errors != null ? unwrapErrors(errors, alias) : undefined) ); this.argLevels = argLevels; } @@ -97,7 +105,7 @@ export default class HoistField implements Transform { if (hoistingToRootField) { const targetSchema = subschemaConfig.schema; - const operation = this.typeName === targetSchema.getQueryType().name ? 'query' : 'mutation'; + const operation = this.typeName === targetSchema.getQueryType()?.name ? 'query' : 'mutation'; const createProxyingResolver = subschemaConfig.createProxyingResolver ?? defaultCreateProxyingResolver; resolve = createProxyingResolver({ subschemaConfig, @@ -112,26 +120,32 @@ export default class HoistField implements Transform { const newTargetField = { ...targetField, - resolve, + resolve: resolve!, }; const level = this.pathToField.length; - Object.keys(targetField.args).forEach(argName => { - const argConfig = targetField.args[argName]; - const arg = { - ...argConfig, - name: argName, - description: argConfig.description, - defaultValue: argConfig.defaultValue, - extensions: argConfig.extensions, - astNode: argConfig.astNode, - } as GraphQLArgument; - if (this.argFilters[level](arg)) { - argsMap[argName] = arg; - this.argLevels[arg.name] = level; + const args = targetField.args; + if (args != null) { + for (const argName in args) { + const argConfig = args[argName]; + if (argConfig == null) { + continue; + } + const arg = { + ...argConfig, + name: argName, + description: argConfig.description, + defaultValue: argConfig.defaultValue, + extensions: argConfig.extensions, + astNode: argConfig.astNode, + } as GraphQLArgument; + if (this.argFilters[level](arg)) { + argsMap[argName] = arg; + this.argLevels[arg.name] = level; + } } - }); + } newTargetField.args = argsMap; @@ -180,11 +194,17 @@ export function wrapFieldNode( kind: Kind.SELECTION_SET, selections: [acc], }, - arguments: fieldNode.arguments.filter(arg => argLevels[arg.name.value] === index), + arguments: + fieldNode.arguments != null + ? fieldNode.arguments.filter(arg => argLevels[arg.name.value] === index) + : undefined, }), { ...fieldNode, - arguments: fieldNode.arguments.filter(arg => argLevels[arg.name.value] === path.length), + arguments: + fieldNode.arguments != null + ? fieldNode.arguments.filter(arg => argLevels[arg.name.value] === path.length) + : undefined, } ); } @@ -218,7 +238,7 @@ export function unwrapValue(originalValue: any, alias: string): any { return originalValue; } -function unwrapErrors(errors: ReadonlyArray, alias: string): Array { +function unwrapErrors(errors: ReadonlyArray | undefined, alias: string): Array | undefined { if (errors === undefined) { return undefined; } diff --git a/packages/wrap/src/transforms/MapFields.ts b/packages/wrap/src/transforms/MapFields.ts index 7cc4cf0b35b..79ac4ac4a79 100644 --- a/packages/wrap/src/transforms/MapFields.ts +++ b/packages/wrap/src/transforms/MapFields.ts @@ -30,6 +30,7 @@ export default class MapFields implements Transform { transformedSchema?: GraphQLSchema ): GraphQLSchema { const subscriptionTypeName = originalWrappingSchema.getSubscriptionType()?.name; + const objectValueTransformerMap = this.objectValueTransformerMap; this.transformer = new TransformCompositeFields( () => undefined, (typeName, fieldName, fieldNode, fragments, transformationContext) => { @@ -45,7 +46,7 @@ export default class MapFields implements Transform { return fieldNodeTransformer(fieldNode, fragments, transformationContext); }, - this.objectValueTransformerMap != null + objectValueTransformerMap != null ? (data, transformationContext) => { if (data == null) { return data; @@ -60,7 +61,7 @@ export default class MapFields implements Transform { } } - const transformer = this.objectValueTransformerMap[typeName]; + const transformer = objectValueTransformerMap[typeName]; if (transformer == null) { return data; } diff --git a/packages/wrap/src/transforms/MapLeafValues.ts b/packages/wrap/src/transforms/MapLeafValues.ts index a9f4cd402e1..9f9ecfd8e1c 100644 --- a/packages/wrap/src/transforms/MapLeafValues.ts +++ b/packages/wrap/src/transforms/MapLeafValues.ts @@ -66,7 +66,7 @@ export default class MapLeafValues implements Transform ): Array { return operations.map((operation: OperationDefinitionNode) => { - const variableDefinitionMap: Record = operation.variableDefinitions.reduce( + const variableDefinitionMap: Record = ( + operation.variableDefinitions ?? [] + ).reduce( (prev, def) => ({ ...prev, [def.variable.name.value]: def, @@ -138,7 +140,7 @@ export default class MapLeafValues implements Transform, variableValues: Record - ): FieldNode { + ): FieldNode | undefined { const targetField = this.typeInfo.getFieldDef(); if (!targetField.name.startsWith('__')) { diff --git a/packages/wrap/src/transforms/RenameInputObjectFields.ts b/packages/wrap/src/transforms/RenameInputObjectFields.ts index 6e2d80ecbc8..a7084ea3a15 100644 --- a/packages/wrap/src/transforms/RenameInputObjectFields.ts +++ b/packages/wrap/src/transforms/RenameInputObjectFields.ts @@ -6,18 +6,27 @@ import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/de import TransformInputObjectFields from './TransformInputObjectFields'; +type RenamerFunction = ( + typeName: string, + fieldName: string, + inputFieldConfig: GraphQLInputFieldConfig +) => string | undefined; + export default class RenameInputObjectFields implements Transform { - private readonly renamer: (typeName: string, fieldName: string, inputFieldConfig: GraphQLInputFieldConfig) => string; + private readonly renamer: RenamerFunction; private readonly transformer: TransformInputObjectFields; private reverseMap: Record>; - constructor(renamer: (typeName: string, fieldName: string, inputFieldConfig: GraphQLInputFieldConfig) => string) { + constructor(renamer: RenamerFunction) { this.renamer = renamer; this.transformer = new TransformInputObjectFields( - (typeName: string, inputFieldName: string, inputFieldConfig: GraphQLInputFieldConfig) => { + (typeName, inputFieldName, inputFieldConfig) => { const newName = renamer(typeName, inputFieldName, inputFieldConfig); if (newName !== undefined && newName !== inputFieldName) { - return [renamer(typeName, inputFieldName, inputFieldConfig), inputFieldConfig]; + const value = renamer(typeName, inputFieldName, inputFieldConfig); + if (value != null) { + return [value, inputFieldConfig]; + } } }, (typeName: string, inputFieldName: string, inputFieldNode: ObjectFieldNode) => { diff --git a/packages/wrap/src/transforms/TransformCompositeFields.ts b/packages/wrap/src/transforms/TransformCompositeFields.ts index 2ad4ed10daa..3225776d9ec 100644 --- a/packages/wrap/src/transforms/TransformCompositeFields.ts +++ b/packages/wrap/src/transforms/TransformCompositeFields.ts @@ -11,7 +11,7 @@ import { FragmentDefinitionNode, } from 'graphql'; -import { Request, MapperKind, mapSchema, visitData, ExecutionResult } from '@graphql-tools/utils'; +import { Request, MapperKind, mapSchema, visitData, ExecutionResult, Maybe } from '@graphql-tools/utils'; import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'; @@ -19,13 +19,13 @@ import { FieldTransformer, FieldNodeTransformer, DataTransformer, ErrorsTransfor export default class TransformCompositeFields> implements Transform { private readonly fieldTransformer: FieldTransformer; - private readonly fieldNodeTransformer: FieldNodeTransformer; - private readonly dataTransformer: DataTransformer; - private readonly errorsTransformer: ErrorsTransformer; + private readonly fieldNodeTransformer: FieldNodeTransformer | undefined; + private readonly dataTransformer: DataTransformer | undefined; + private readonly errorsTransformer: ErrorsTransformer | undefined; private transformedSchema: GraphQLSchema; private typeInfo: TypeInfo; private mapping: Record>; - private subscriptionTypeName: string; + private subscriptionTypeName: string | undefined; constructor( fieldTransformer: FieldTransformer, @@ -90,10 +90,11 @@ export default class TransformCompositeFields> im _delegationContext: DelegationContext, transformationContext: Record ): ExecutionResult { - if (this.dataTransformer != null) { - result.data = visitData(result.data, value => this.dataTransformer(value, transformationContext)); + const dataTransformer = this.dataTransformer; + if (dataTransformer != null) { + result.data = visitData(result.data, value => dataTransformer(value, transformationContext)); } - if (this.errorsTransformer != null) { + if (this.errorsTransformer != null && Array.isArray(result.errors)) { result.errors = this.errorsTransformer(result.errors, transformationContext); } return result; @@ -120,8 +121,8 @@ export default class TransformCompositeFields> im typeInfo: TypeInfo, fragments: Record, transformationContext: Record - ): SelectionSetNode { - const parentType: GraphQLType = typeInfo.getParentType(); + ): SelectionSetNode | undefined { + const parentType: Maybe = typeInfo.getParentType(); if (parentType == null) { return undefined; } @@ -151,7 +152,7 @@ export default class TransformCompositeFields> im }); } - let transformedSelection: SelectionNode | Array; + let transformedSelection: SelectionNode | Array | undefined; if (this.fieldNodeTransformer == null) { transformedSelection = selection; } else { diff --git a/packages/wrap/src/transforms/TransformEnumValues.ts b/packages/wrap/src/transforms/TransformEnumValues.ts index 009cf5e0d56..3c2db7fa897 100644 --- a/packages/wrap/src/transforms/TransformEnumValues.ts +++ b/packages/wrap/src/transforms/TransformEnumValues.ts @@ -1,6 +1,6 @@ import { GraphQLSchema, GraphQLEnumValueConfig, ExecutionResult } from 'graphql'; -import { Request, MapperKind, mapSchema } from '@graphql-tools/utils'; +import { Request, MapperKind, mapSchema, Maybe } from '@graphql-tools/utils'; import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'; @@ -62,7 +62,7 @@ export default class TransformEnumValues implements Transform { const transformedEnumValue = this.enumValueTransformer(typeName, externalValue, enumValueConfig); if (Array.isArray(transformedEnumValue)) { const newExternalValue = transformedEnumValue[0]; @@ -86,7 +86,7 @@ function mapEnumValues(typeName: string, value: string, mapping: Record, mapping: Record> ): LeafValueTransformer { if (valueTransformer == null) { diff --git a/packages/wrap/src/transforms/TransformInputObjectFields.ts b/packages/wrap/src/transforms/TransformInputObjectFields.ts index f823a511a33..8ad53cd2ca0 100644 --- a/packages/wrap/src/transforms/TransformInputObjectFields.ts +++ b/packages/wrap/src/transforms/TransformInputObjectFields.ts @@ -1,6 +1,5 @@ import { GraphQLSchema, - GraphQLType, DocumentNode, typeFromAST, TypeInfo, @@ -16,7 +15,7 @@ import { NamedTypeNode, } from 'graphql'; -import { Request, MapperKind, mapSchema, transformInputValue } from '@graphql-tools/utils'; +import { Maybe, Request, MapperKind, mapSchema, transformInputValue } from '@graphql-tools/utils'; import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'; @@ -24,8 +23,8 @@ import { InputFieldTransformer, InputFieldNodeTransformer, InputObjectNodeTransf export default class TransformInputObjectFields implements Transform { private readonly inputFieldTransformer: InputFieldTransformer; - private readonly inputFieldNodeTransformer: InputFieldNodeTransformer; - private readonly inputObjectNodeTransformer: InputObjectNodeTransformer; + private readonly inputFieldNodeTransformer: InputFieldNodeTransformer | undefined; + private readonly inputObjectNodeTransformer: InputObjectNodeTransformer | undefined; private transformedSchema: GraphQLSchema; private mapping: Record>; @@ -141,8 +140,8 @@ export default class TransformInputObjectFields implements Transform { private transformDocument( document: DocumentNode, mapping: Record>, - inputFieldNodeTransformer: InputFieldNodeTransformer, - inputObjectNodeTransformer: InputObjectNodeTransformer, + inputFieldNodeTransformer: InputFieldNodeTransformer | undefined, + inputObjectNodeTransformer: InputObjectNodeTransformer | undefined, request: Request, delegationContext?: DelegationContext ): DocumentNode { @@ -151,8 +150,9 @@ export default class TransformInputObjectFields implements Transform { document, visitWithTypeInfo(typeInfo, { leave: { - [Kind.OBJECT]: (node: ObjectValueNode): ObjectValueNode => { - const parentType: GraphQLType = typeInfo.getInputType() as GraphQLInputObjectType; + [Kind.OBJECT]: (node: ObjectValueNode): ObjectValueNode | undefined => { + // The casting is kind of legit here as we are in a visitor + const parentType = typeInfo.getInputType() as Maybe; if (parentType != null) { const parentTypeName = parentType.name; const newInputFields: Array = []; diff --git a/packages/wrap/src/transforms/TransformInterfaceFields.ts b/packages/wrap/src/transforms/TransformInterfaceFields.ts index 956ac4e5673..a186c9ba320 100644 --- a/packages/wrap/src/transforms/TransformInterfaceFields.ts +++ b/packages/wrap/src/transforms/TransformInterfaceFields.ts @@ -10,7 +10,7 @@ import TransformCompositeFields from './TransformCompositeFields'; export default class TransformInterfaceFields implements Transform { private readonly interfaceFieldTransformer: FieldTransformer; - private readonly fieldNodeTransformer: FieldNodeTransformer; + private readonly fieldNodeTransformer: FieldNodeTransformer | undefined; private transformer: TransformCompositeFields; constructor(interfaceFieldTransformer: FieldTransformer, fieldNodeTransformer?: FieldNodeTransformer) { diff --git a/packages/wrap/src/transforms/TransformObjectFields.ts b/packages/wrap/src/transforms/TransformObjectFields.ts index 7ced6e12f2c..9b1bb89f53c 100644 --- a/packages/wrap/src/transforms/TransformObjectFields.ts +++ b/packages/wrap/src/transforms/TransformObjectFields.ts @@ -10,7 +10,7 @@ import TransformCompositeFields from './TransformCompositeFields'; export default class TransformObjectFields implements Transform { private readonly objectFieldTransformer: FieldTransformer; - private readonly fieldNodeTransformer: FieldNodeTransformer; + private readonly fieldNodeTransformer: FieldNodeTransformer | undefined; private transformer: TransformCompositeFields; constructor(objectFieldTransformer: FieldTransformer, fieldNodeTransformer?: FieldNodeTransformer) { diff --git a/packages/wrap/src/transforms/TransformQuery.ts b/packages/wrap/src/transforms/TransformQuery.ts index 06221726df6..6a1690a4b53 100644 --- a/packages/wrap/src/transforms/TransformQuery.ts +++ b/packages/wrap/src/transforms/TransformQuery.ts @@ -8,10 +8,14 @@ export type QueryTransformer = ( selectionSet: SelectionSetNode, fragments: Record, delegationContext: DelegationContext, - transformationContext: Record, + transformationContext: Record ) => SelectionSetNode; -export type ResultTransformer = (result: any, delegationContext: DelegationContext, transformationContext: Record) => any; +export type ResultTransformer = ( + result: any, + delegationContext: DelegationContext, + transformationContext: Record +) => any; export type ErrorPathTransformer = (path: ReadonlyArray) => Array; @@ -26,7 +30,7 @@ export default class TransformQuery implements Transform { path, queryTransformer, resultTransformer = result => result, - errorPathTransformer = errorPath => [].concat(errorPath), + errorPathTransformer = errorPath => [...errorPath], fragments = {}, }: { path: Array; @@ -52,14 +56,19 @@ export default class TransformQuery implements Transform { const document = visit(originalRequest.document, { [Kind.FIELD]: { enter: node => { - if (index === pathLength || node.name.value !== this.path[index]) { + if (index === pathLength || node.name.value !== this.path[index] || node.selectionSet == null) { return false; } index++; if (index === pathLength) { - const selectionSet = this.queryTransformer(node.selectionSet, this.fragments, delegationContext, transformationContext); + const selectionSet = this.queryTransformer( + node.selectionSet, + this.fragments, + delegationContext, + transformationContext + ); return { ...node, @@ -92,7 +101,11 @@ export default class TransformQuery implements Transform { }; } - private transformData(data: any, delegationContext: DelegationContext, transformationContext: Record): any { + private transformData( + data: any, + delegationContext: DelegationContext, + transformationContext: Record + ): any { const leafIndex = this.path.length - 1; let index = 0; let newData = data; @@ -114,7 +127,11 @@ export default class TransformQuery implements Transform { private transformErrors(errors: ReadonlyArray): ReadonlyArray { return errors.map(error => { - const path: ReadonlyArray = error.path; + const path: ReadonlyArray | undefined = error.path; + + if (path == null) { + return error; + } let match = true; let index = 0; diff --git a/packages/wrap/src/transforms/TransformRootFields.ts b/packages/wrap/src/transforms/TransformRootFields.ts index ba224efbf5c..c675f80786f 100644 --- a/packages/wrap/src/transforms/TransformRootFields.ts +++ b/packages/wrap/src/transforms/TransformRootFields.ts @@ -10,7 +10,7 @@ import TransformObjectFields from './TransformObjectFields'; export default class TransformRootFields implements Transform { private readonly rootFieldTransformer: RootFieldTransformer; - private readonly fieldNodeTransformer: FieldNodeTransformer; + private readonly fieldNodeTransformer: FieldNodeTransformer | undefined; private transformer: TransformObjectFields; constructor(rootFieldTransformer: RootFieldTransformer, fieldNodeTransformer?: FieldNodeTransformer) { diff --git a/packages/wrap/src/transforms/WrapFields.ts b/packages/wrap/src/transforms/WrapFields.ts index dc1beb1b38e..817e692fea8 100644 --- a/packages/wrap/src/transforms/WrapFields.ts +++ b/packages/wrap/src/transforms/WrapFields.ts @@ -18,6 +18,7 @@ import { modifyObjectFields, ExecutionResult, relocatedError, + assertSome, } from '@graphql-tools/utils'; import { Transform, defaultMergedResolver, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate'; @@ -35,8 +36,8 @@ export default class WrapFields implements Transform; private readonly wrappingTypeNames: Array; private readonly numWraps: number; - private readonly fieldNames: Array; - private readonly transformer: Transform; + private readonly fieldNames: Array | undefined; + private readonly transformer: MapFields; constructor( outerTypeName: string, @@ -53,6 +54,7 @@ export default class WrapFields implements Transform( { [outerTypeName]: { @@ -79,10 +81,11 @@ export default class WrapFields implements Transform, transformedSchema?: GraphQLSchema ): GraphQLSchema { + const fieldNames = this.fieldNames; const targetFieldConfigMap = selectObjectFields( originalWrappingSchema, this.outerTypeName, - !this.fieldNames ? () => true : fieldName => this.fieldNames.includes(fieldName) + !fieldNames ? () => true : fieldName => fieldNames.includes(fieldName) ); const newTargetFieldConfigMap: GraphQLFieldConfigMap = Object.create(null); @@ -119,11 +122,11 @@ export default class WrapFields implements Transform; + let resolve: GraphQLFieldResolver | undefined; if (transformedSchema) { if (wrappingRootField) { const targetSchema = subschemaConfig.schema; - const operation = this.outerTypeName === targetSchema.getQueryType().name ? 'query' : 'mutation'; + const operation = this.outerTypeName === targetSchema.getQueryType()?.name ? 'query' : 'mutation'; const createProxyingResolver = subschemaConfig.createProxyingResolver ?? defaultCreateProxyingResolver; resolve = createProxyingResolver({ subschemaConfig, @@ -172,7 +175,7 @@ export default class WrapFields implements Transform, fields: Array = [], visitedFragmentNames = {} @@ -303,9 +306,9 @@ export function dehoistValue(originalValue: any, context: WrapFieldsTransformati } function dehoistErrors( - errors: ReadonlyArray, + errors: ReadonlyArray | undefined, context: WrapFieldsTransformationContext -): Array { +): Array | undefined { if (errors === undefined) { return undefined; } diff --git a/packages/wrap/src/transforms/WrapQuery.ts b/packages/wrap/src/transforms/WrapQuery.ts index 04b7dbc84ba..978d2a68962 100644 --- a/packages/wrap/src/transforms/WrapQuery.ts +++ b/packages/wrap/src/transforms/WrapQuery.ts @@ -28,7 +28,7 @@ export default class WrapQuery implements Transform { [Kind.FIELD]: { enter: (node: FieldNode) => { fieldPath.push(node.name.value); - if (ourPath === JSON.stringify(fieldPath)) { + if (node.selectionSet != null && ourPath === JSON.stringify(fieldPath)) { const wrapResult = this.wrapper(node.selectionSet); // Selection can be either a single selection or a selection set. If it's just one selection, @@ -68,7 +68,7 @@ export default class WrapQuery implements Transform { let data = rootData; const path = [...this.path]; while (path.length > 1) { - const next = path.shift(); + const next = path.shift()!; if (data[next]) { data = data[next]; } diff --git a/packages/wrap/src/transforms/WrapType.ts b/packages/wrap/src/transforms/WrapType.ts index 83d99089e1a..8d32d768675 100644 --- a/packages/wrap/src/transforms/WrapType.ts +++ b/packages/wrap/src/transforms/WrapType.ts @@ -7,7 +7,7 @@ import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/de import WrapFields from './WrapFields'; export default class WrapType implements Transform { - private readonly transformer: Transform; + private readonly transformer: WrapFields; constructor(outerTypeName: string, innerTypeName: string, fieldName: string) { this.transformer = new WrapFields(outerTypeName, [fieldName], [innerTypeName]); @@ -26,7 +26,7 @@ export default class WrapType implements Transform { delegationContext: DelegationContext, transformationContext: Record ): Request { - return this.transformer.transformRequest(originalRequest, delegationContext, transformationContext); + return this.transformer.transformRequest(originalRequest, delegationContext, transformationContext as any); } public transformResult( @@ -34,6 +34,6 @@ export default class WrapType implements Transform { delegationContext: DelegationContext, transformationContext: Record ): ExecutionResult { - return this.transformer.transformResult(originalResult, delegationContext, transformationContext); + return this.transformer.transformResult(originalResult, delegationContext, transformationContext as any); } } diff --git a/packages/wrap/src/types.ts b/packages/wrap/src/types.ts index 57bec411122..cd03f5afd1f 100644 --- a/packages/wrap/src/types.ts +++ b/packages/wrap/src/types.ts @@ -17,11 +17,11 @@ import { Executor, Subscriber, Request } from '@graphql-tools/utils'; export interface IMakeRemoteExecutableSchemaOptions> { schema: GraphQLSchema | string; - executor?: Executor; + executor: Executor; subscriber?: Subscriber; createResolver?: ( executor: Executor, - subscriber: Subscriber + subscriber?: Subscriber | undefined ) => GraphQLFieldResolver; buildSchemaOptions?: BuildSchemaOptions; } @@ -45,7 +45,7 @@ export type InputObjectNodeTransformer = ( inputObjectNode: ObjectValueNode, request: Request, delegationContext?: DelegationContext -) => ObjectValueNode; +) => ObjectValueNode | undefined; export type FieldTransformer> = ( typeName: string, @@ -71,7 +71,7 @@ export type FieldNodeTransformer = ( fieldNode: FieldNode, fragments: Record, transformationContext: Record -) => SelectionNode | Array; +) => SelectionNode | Array | undefined; export type LeafValueTransformer = (typeName: string, value: any) => any; @@ -80,6 +80,6 @@ export type DataTransformer = (value: any, transformationContext: Record; export type ErrorsTransformer = ( - errors: ReadonlyArray, + errors: ReadonlyArray | undefined, transformationContext: Record -) => Array; +) => Array | undefined; diff --git a/packages/wrap/src/wrapSchema.ts b/packages/wrap/src/wrapSchema.ts index 27b045ff282..0068da2ea02 100644 --- a/packages/wrap/src/wrapSchema.ts +++ b/packages/wrap/src/wrapSchema.ts @@ -46,7 +46,7 @@ function createWrappingSchema( Object.keys(config.fields).forEach(fieldName => { config.fields[fieldName].resolve = defaultMergedResolver; - config.fields[fieldName].subscribe = null; + config.fields[fieldName].subscribe = undefined; }); return new GraphQLObjectType(config); diff --git a/packages/wrap/tests/gatsbyTransforms.test.ts b/packages/wrap/tests/gatsbyTransforms.test.ts index 3831e37be25..9ebb579ef2e 100644 --- a/packages/wrap/tests/gatsbyTransforms.test.ts +++ b/packages/wrap/tests/gatsbyTransforms.test.ts @@ -7,7 +7,7 @@ import { GraphQLFieldConfigMap, } from 'graphql'; -import { mapSchema, MapperKind, addTypes, modifyObjectFields } from '@graphql-tools/utils'; +import { mapSchema, MapperKind, addTypes, modifyObjectFields, assertSome } from '@graphql-tools/utils'; import { wrapSchema, RenameTypes } from '../src'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { addMocksToSchema } from '@graphql-tools/mock'; @@ -36,7 +36,9 @@ class NamespaceUnderFieldTransform { } transformSchema(schema: GraphQLSchema) { - const queryConfig = schema.getQueryType().toConfig(); + const QueryType = schema.getQueryType(); + assertSome(QueryType) + const queryConfig = QueryType.toConfig(); const nestedQuery = new GraphQLObjectType({ ...queryConfig, diff --git a/packages/wrap/tests/transformFilterInputObjectFields.test.ts b/packages/wrap/tests/transformFilterInputObjectFields.test.ts index 44c050fe8bc..810d9b60858 100644 --- a/packages/wrap/tests/transformFilterInputObjectFields.test.ts +++ b/packages/wrap/tests/transformFilterInputObjectFields.test.ts @@ -1,6 +1,7 @@ import { wrapSchema, FilterInputObjectFields } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { graphql, astFromValue, Kind, GraphQLString } from 'graphql'; +import { assertSome } from '@graphql-tools/utils'; describe('FilterInputObjectFields', () => { test('filtering works', async () => { @@ -36,6 +37,8 @@ describe('FilterInputObjectFields', () => { (typeName, fieldName) => (typeName !== 'InputObject' || fieldName !== 'field2'), (typeName, inputObjectNode) => { if (typeName === 'InputObject') { + const value = astFromValue('field2', GraphQLString) + assertSome(value) return { ...inputObjectNode, fields: [...inputObjectNode.fields, { @@ -44,7 +47,7 @@ describe('FilterInputObjectFields', () => { kind: Kind.NAME, value: 'field2', }, - value: astFromValue('field2', GraphQLString), + value, }], }; } @@ -63,6 +66,7 @@ describe('FilterInputObjectFields', () => { }`; const result = await graphql(transformedSchema, query); + assertSome(result.data) expect(result.data.test.field1).toBe('field1'); expect(result.data.test.field2).toBe('field2'); }); diff --git a/packages/wrap/tests/transformFilterTypes.test.ts b/packages/wrap/tests/transformFilterTypes.test.ts index bfde574fd8d..931445475e7 100644 --- a/packages/wrap/tests/transformFilterTypes.test.ts +++ b/packages/wrap/tests/transformFilterTypes.test.ts @@ -1,5 +1,6 @@ import { wrapSchema, FilterTypes } from '@graphql-tools/wrap'; import { graphql, GraphQLSchema, GraphQLNamedType } from 'graphql'; +import { assertSome } from '@graphql-tools/utils'; import { bookingSchema } from './fixtures/schemas'; describe('FilterTypes', () => { @@ -62,6 +63,7 @@ describe('FilterTypes', () => { `, ); expect(result.errors).toBeDefined(); + assertSome(result.errors) expect(result.errors.length).toBe(1); expect(result.errors[0].message).toBe( 'Cannot query field "customer" on type "Booking".', diff --git a/packages/wrap/tests/transformMapLeafValues.test.ts b/packages/wrap/tests/transformMapLeafValues.test.ts index 9850a6ad21d..09650ac9cbb 100644 --- a/packages/wrap/tests/transformMapLeafValues.test.ts +++ b/packages/wrap/tests/transformMapLeafValues.test.ts @@ -1,6 +1,7 @@ import { wrapSchema, MapLeafValues } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { graphql } from 'graphql'; +import { assertSome } from '@graphql-tools/utils'; describe('MapLeafValues', () => { test('works', async () => { @@ -48,6 +49,7 @@ describe('MapLeafValues', () => { }`; const result = await graphql(transformedSchema, query); + assertSome(result.data) expect(result.data.testEnum).toBe('THREE'); expect(result.data.testScalar).toBe(15); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts b/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts index 8fd64f6b53a..f4b06c18ad9 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts @@ -1,5 +1,7 @@ import { wrapSchema, RemoveObjectFieldDeprecations } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; +import { assertGraphQLObjectType } from '../../testing/assertion'; +import { assertSome } from '@graphql-tools/utils'; describe('RemoveObjectFieldDeprecations', () => { const originalSchema = makeExecutableSchema({ @@ -20,11 +22,15 @@ describe('RemoveObjectFieldDeprecations', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.first) expect(fields.first.deprecationReason).toEqual('do not remove'); + assertSome(fields.second) expect(fields.second.deprecationReason).toBeUndefined(); - expect(fields.first.astNode.directives.length).toEqual(1); - expect(fields.second.astNode.directives.length).toEqual(0); + expect(fields.first.astNode?.directives?.length).toEqual(1); + expect(fields.second.astNode?.directives?.length).toEqual(0); }); test('removes deprecations by reason regex', async () => { @@ -35,10 +41,14 @@ describe('RemoveObjectFieldDeprecations', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.first) expect(fields.first.deprecationReason).toBeUndefined(); + assertSome(fields.second) expect(fields.second.deprecationReason).toBeUndefined(); - expect(fields.first.astNode.directives.length).toEqual(0); - expect(fields.second.astNode.directives.length).toEqual(0); + expect(fields.first.astNode?.directives?.length).toEqual(0); + expect(fields.second.astNode?.directives?.length).toEqual(0); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts b/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts index 331c78eaa4a..6186cada3a6 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts @@ -1,5 +1,7 @@ import { wrapSchema, RemoveObjectFieldDirectives } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; +import { assertGraphQLObjectType } from '../../testing/assertion'; +import { assertSome } from '@graphql-tools/utils'; describe('RemoveObjectFieldDirectives', () => { const originalSchema = makeExecutableSchema({ @@ -24,11 +26,17 @@ describe('RemoveObjectFieldDirectives', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); - expect(fields.id.astNode.directives.length).toEqual(1); - expect(fields.first.astNode.directives.length).toEqual(0); - expect(fields.second.astNode.directives.length).toEqual(0); - expect(fields.third.astNode.directives.length).toEqual(0); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.id) + expect(fields.id.astNode?.directives?.length).toEqual(1); + assertSome(fields.first) + expect(fields.first.astNode?.directives?.length).toEqual(0); + assertSome(fields.second) + expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields.third) + expect(fields.third.astNode?.directives?.length).toEqual(0); }); test('removes directives by name regex', async () => { @@ -39,11 +47,17 @@ describe('RemoveObjectFieldDirectives', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); - expect(fields.id.astNode.directives.length).toEqual(1); - expect(fields.first.astNode.directives.length).toEqual(0); - expect(fields.second.astNode.directives.length).toEqual(0); - expect(fields.third.astNode.directives.length).toEqual(0); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.id) + expect(fields.id.astNode?.directives?.length).toEqual(1); + assertSome(fields.first) + expect(fields.first.astNode?.directives?.length).toEqual(0); + assertSome(fields.second) + expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields.third) + expect(fields.third.astNode?.directives?.length).toEqual(0); }); test('removes directives by argument', async () => { @@ -54,11 +68,17 @@ describe('RemoveObjectFieldDirectives', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); - expect(fields.id.astNode.directives.length).toEqual(0); - expect(fields.first.astNode.directives.length).toEqual(1); - expect(fields.second.astNode.directives.length).toEqual(0); - expect(fields.third.astNode.directives.length).toEqual(0); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.id) + expect(fields.id.astNode?.directives?.length).toEqual(0); + assertSome(fields.first) + expect(fields.first.astNode?.directives?.length).toEqual(1); + assertSome(fields.second) + expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields.third) + expect(fields.third.astNode?.directives?.length).toEqual(0); }); test('removes directives by argument regex', async () => { @@ -69,10 +89,16 @@ describe('RemoveObjectFieldDirectives', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); - expect(fields.id.astNode.directives.length).toEqual(0); - expect(fields.first.astNode.directives.length).toEqual(0); - expect(fields.second.astNode.directives.length).toEqual(0); - expect(fields.third.astNode.directives.length).toEqual(0); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); + assertSome(fields.id) + expect(fields.id.astNode?.directives?.length).toEqual(0); + assertSome(fields.first) + expect(fields.first.astNode?.directives?.length).toEqual(0); + assertSome(fields.second) + expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields.third) + expect(fields.third.astNode?.directives?.length).toEqual(0); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts b/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts index 27781011cfd..1989214acfa 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts @@ -1,5 +1,6 @@ import { wrapSchema, RemoveObjectFieldsWithDeprecation } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; +import { assertGraphQLObjectType } from '../../testing/assertion'; describe('RemoveObjectFieldsWithDeprecation', () => { const originalSchema = makeExecutableSchema({ @@ -20,7 +21,9 @@ describe('RemoveObjectFieldsWithDeprecation', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeDefined(); expect(fields.second).toBeUndefined(); }); @@ -33,7 +36,9 @@ describe('RemoveObjectFieldsWithDeprecation', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeUndefined(); expect(fields.second).toBeUndefined(); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts b/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts index 6e661ea1b56..8edc9c142ff 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts @@ -1,5 +1,6 @@ import { wrapSchema, RemoveObjectFieldsWithDirective } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; +import { assertGraphQLObjectType } from '../../testing/assertion'; describe('RemoveObjectFieldsWithDirective', () => { const originalSchema = makeExecutableSchema({ @@ -25,7 +26,9 @@ describe('RemoveObjectFieldsWithDirective', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeUndefined(); expect(fields.second).toBeUndefined(); expect(fields.third).toBeUndefined(); @@ -40,7 +43,9 @@ describe('RemoveObjectFieldsWithDirective', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeUndefined(); expect(fields.second).toBeUndefined(); expect(fields.third).toBeUndefined(); @@ -55,7 +60,9 @@ describe('RemoveObjectFieldsWithDirective', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeDefined(); expect(fields.second).toBeUndefined(); expect(fields.third).toBeUndefined(); @@ -70,7 +77,9 @@ describe('RemoveObjectFieldsWithDirective', () => { ], }); - const fields = transformedSchema.getType('Test').getFields(); + const Test = transformedSchema.getType('Test') + assertGraphQLObjectType(Test) + const fields = Test.getFields(); expect(fields.first).toBeUndefined(); expect(fields.second).toBeUndefined(); expect(fields.third).toBeUndefined(); diff --git a/packages/wrap/tests/transformRenameInputObjectFields.test.ts b/packages/wrap/tests/transformRenameInputObjectFields.test.ts index 3ebbe735dc4..4ad2abd005e 100644 --- a/packages/wrap/tests/transformRenameInputObjectFields.test.ts +++ b/packages/wrap/tests/transformRenameInputObjectFields.test.ts @@ -1,6 +1,7 @@ import { wrapSchema, RenameInputObjectFields } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { graphql } from 'graphql'; +import { assertSome } from '@graphql-tools/utils'; describe('RenameInputObjectFields', () => { test('renaming with arguments works', async () => { @@ -33,7 +34,7 @@ describe('RenameInputObjectFields', () => { schema, transforms: [ new RenameInputObjectFields( - (typeName: string, fieldName: string) => { + (typeName, fieldName) => { if (typeName === 'InputObject' && fieldName === 'field2') { return 'field3'; } @@ -53,6 +54,7 @@ describe('RenameInputObjectFields', () => { }`; const result = await graphql(transformedSchema, query); + assertSome(result.data) expect(result.data.test.field1).toBe('field1'); expect(result.data.test.field2).toBe('field2'); }); @@ -115,6 +117,7 @@ describe('RenameInputObjectFields', () => { } } const result = await graphql(transformedSchema, query, {}, {}, variables); + assertSome(result.data) expect(result.data.test.field1).toBe('field1'); expect(result.data.test.field2).toBe('field2'); }); diff --git a/packages/wrap/tests/transformTransformEnumValues.test.ts b/packages/wrap/tests/transformTransformEnumValues.test.ts index d5b2a935a5c..164d6ee3025 100644 --- a/packages/wrap/tests/transformTransformEnumValues.test.ts +++ b/packages/wrap/tests/transformTransformEnumValues.test.ts @@ -2,6 +2,13 @@ import { wrapSchema, TransformEnumValues } from '@graphql-tools/wrap'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { graphql, GraphQLEnumType } from 'graphql'; +function assertGraphQLEnumType(input: unknown): asserts input is GraphQLEnumType { + if (input instanceof GraphQLEnumType) { + return + } + throw new Error("Expected GraphQLEnumType.") +} + describe('TransformEnumValues', () => { test('works', async () => { const schema = makeExecutableSchema({ @@ -74,7 +81,9 @@ describe('TransformEnumValues', () => { const result = await graphql(transformedSchema, query); expect(result.errors).toBeUndefined(); - expect((transformedSchema.getType('TestEnum') as GraphQLEnumType).getValue('UNO').value).toBe('ONE'); + const TestEnum = transformedSchema.getType('TestEnum') + assertGraphQLEnumType(TestEnum) + expect(TestEnum.getValue('UNO')?.value).toBe('ONE'); }); test('works with variables', async () => {