From d3919a535b423e40ffeece0fc4736c141ba1d497 Mon Sep 17 00:00:00 2001 From: yaacovCR Date: Mon, 20 Jan 2020 13:13:33 -0500 Subject: [PATCH] fix(transforms): refactor TransformRootFields to allow flexible transform ordering Fixes #27, now possible because info object is not modified, see https://github.com/yaacovCR/graphql-tools-fork/commit/1676ab010204ab2c50ae07d13ecf560b73ff5f76 --- src/Interfaces.ts | 2 +- src/stitching/delegateToSchema.ts | 9 ++- src/stitching/resolvers.ts | 1 - src/test/testAlternateMergeSchemas.ts | 12 ++-- src/transforms/RenameRootFields.ts | 5 ++ src/transforms/TransformObjectFields.ts | 3 - src/transforms/TransformRootFields.ts | 88 ++++++------------------- 7 files changed, 36 insertions(+), 84 deletions(-) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 4fba105a2d9..7983a8a89c2 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -116,7 +116,7 @@ export function isSubschemaConfig(value: SchemaLikeObject): value is SubschemaCo export interface IDelegateToSchemaOptions { schema: GraphQLSchema | SubschemaConfig; operation?: Operation; - fieldName: string; + fieldName?: string; returnType?: GraphQLOutputType; args?: { [key: string]: any }; context?: TContext; diff --git a/src/stitching/delegateToSchema.ts b/src/stitching/delegateToSchema.ts index 0d52be6c394..25738ada262 100644 --- a/src/stitching/delegateToSchema.ts +++ b/src/stitching/delegateToSchema.ts @@ -80,7 +80,7 @@ export default function delegateToSchema( rootValue, info, operation = getDelegatingOperation(info.parentType, info.schema), - fieldName, + fieldName = info.fieldName, returnType = info.returnType, args, context, @@ -117,7 +117,7 @@ export function createDelegatingRequest({ schema: subschema, info, operation = getDelegatingOperation(info.parentType, info.schema), - fieldName, + fieldName = info.fieldName, args, transforms = [], skipValidation, @@ -175,8 +175,8 @@ export function delegateRequest({ schema: subschema, rootValue, info, - operation, - fieldName, + operation = getDelegatingOperation(info.parentType, info.schema), + fieldName = info.fieldName, returnType = info.returnType, context, transforms = [], @@ -229,7 +229,6 @@ export function delegateRequest({ // "subscribe" to the subscription result and map the result through the transforms return mapAsyncIterator(subscriptionResult, result => { const transformedResult = applyResultTransforms(result, transforms); - // wrap with fieldName to return for an additional round of resolutioon // with payload as rootValue return { diff --git a/src/stitching/resolvers.ts b/src/stitching/resolvers.ts index dae0007cfac..086f0b3009e 100644 --- a/src/stitching/resolvers.ts +++ b/src/stitching/resolvers.ts @@ -101,7 +101,6 @@ function defaultCreateProxyingResolver( ): GraphQLFieldResolver { return (parent, args, context, info) => delegateToSchema({ schema: subschemaConfig, - fieldName, context, info, }); diff --git a/src/test/testAlternateMergeSchemas.ts b/src/test/testAlternateMergeSchemas.ts index 03f625cf2be..e51232528cc 100644 --- a/src/test/testAlternateMergeSchemas.ts +++ b/src/test/testAlternateMergeSchemas.ts @@ -443,18 +443,18 @@ describe('transform object fields', () => { } return true; }), - new RenameObjectFields((_typeName, fieldName) => { - if (fieldName === 'camel_case') { - return 'camelCase'; - } - return fieldName; - }), new RenameRootFields((_operation, fieldName) => { if (fieldName === 'allItems') { return 'items'; } return fieldName; }), + new RenameObjectFields((_typeName, fieldName) => { + if (fieldName === 'camel_case') { + return 'camelCase'; + } + return fieldName; + }), ]); }); diff --git a/src/transforms/RenameRootFields.ts b/src/transforms/RenameRootFields.ts index c2fb9b39e19..6fb7d9e60b6 100644 --- a/src/transforms/RenameRootFields.ts +++ b/src/transforms/RenameRootFields.ts @@ -1,4 +1,5 @@ import { GraphQLField, GraphQLSchema } from 'graphql'; +import { Request } from '../Interfaces'; import { Transform } from './transforms'; import TransformRootFields from './TransformRootFields'; @@ -28,4 +29,8 @@ export default class RenameRootFields implements Transform { public transformSchema(originalSchema: GraphQLSchema): GraphQLSchema { return this.transformer.transformSchema(originalSchema); } + + public transformRequest(originalRequest: Request): Request { + return this.transformer.transformRequest(originalRequest); + } } diff --git a/src/transforms/TransformObjectFields.ts b/src/transforms/TransformObjectFields.ts index cb3c622a291..b395c452987 100644 --- a/src/transforms/TransformObjectFields.ts +++ b/src/transforms/TransformObjectFields.ts @@ -57,9 +57,6 @@ export default class TransformObjectFields implements Transform { public transformSchema(originalSchema: GraphQLSchema): GraphQLSchema { this.transformedSchema = visitSchema(originalSchema, { - [VisitSchemaKind.ROOT_OBJECT]: () => { - return undefined; - }, [VisitSchemaKind.OBJECT_TYPE]: (type: GraphQLObjectType) => { return this.transformFields(type, this.objectFieldTransformer); } diff --git a/src/transforms/TransformRootFields.ts b/src/transforms/TransformRootFields.ts index eea4d067e93..44f5c39ec84 100644 --- a/src/transforms/TransformRootFields.ts +++ b/src/transforms/TransformRootFields.ts @@ -1,13 +1,12 @@ import { - GraphQLObjectType, GraphQLSchema, GraphQLField, GraphQLFieldConfig, } from 'graphql'; -import isEmptyObject from '../utils/isEmptyObject'; +import { Request } from '../Interfaces'; import { Transform } from './transforms'; -import { visitSchema } from '../utils/visitSchema'; -import { VisitSchemaKind } from '../Interfaces'; +import { TransformObjectFields } from '.'; +import { FieldNodeTransformer } from './TransformObjectFields'; export type RootTransformer = ( operation: 'Query' | 'Mutation' | 'Subscription', @@ -22,75 +21,28 @@ export type RootTransformer = ( type RenamedField = { name: string; field?: GraphQLFieldConfig }; export default class TransformRootFields implements Transform { - private transform: RootTransformer; + private transformer: TransformObjectFields; - constructor(transform: RootTransformer) { - this.transform = transform; + constructor(rootFieldTransformer: RootTransformer, fieldNodeTransformer?: FieldNodeTransformer) { + const rootToObjectFieldTransformer = + (typeName: string, fieldName: string, field: GraphQLField) => { + if (typeName === 'Query' || typeName === 'Mutation' || typeName === 'Subscription') { + return rootFieldTransformer(typeName, fieldName, field); + } else { + return undefined; + } + }; + this.transformer = new TransformObjectFields( + rootToObjectFieldTransformer, + fieldNodeTransformer, + ); } public transformSchema(originalSchema: GraphQLSchema): GraphQLSchema { - return visitSchema(originalSchema, { - [VisitSchemaKind.QUERY]: (type: GraphQLObjectType) => { - return transformFields( - type, - (fieldName: string, field: GraphQLField) => - this.transform('Query', fieldName, field), - ); - }, - [VisitSchemaKind.MUTATION]: (type: GraphQLObjectType) => { - return transformFields( - type, - (fieldName: string, field: GraphQLField) => - this.transform('Mutation', fieldName, field), - ); - }, - [VisitSchemaKind.SUBSCRIPTION]: (type: GraphQLObjectType) => { - return transformFields( - type, - (fieldName: string, field: GraphQLField) => - this.transform('Subscription', fieldName, field), - ); - }, - }); + return this.transformer.transformSchema(originalSchema); } -} -function transformFields( - type: GraphQLObjectType, - transformer: ( - fieldName: string, - field: GraphQLField, - ) => - | GraphQLFieldConfig - | RenamedField - | null - | undefined, -): GraphQLObjectType { - const typeConfig = type.toConfig(); - const fields = type.getFields(); - const newFields = {}; - Object.keys(fields).forEach(fieldName => { - const field = fields[fieldName]; - const newField = transformer(fieldName, field); - if (typeof newField === 'undefined') { - newFields[fieldName] = typeConfig.fields[fieldName]; - } else if (newField !== null) { - if ((newField as RenamedField).name) { - newFields[(newField as RenamedField).name] = - (newField as RenamedField).field ? - (newField as RenamedField).field : - typeConfig.fields[fieldName]; - } else { - newFields[fieldName] = newField; - } - } - }); - if (isEmptyObject(newFields)) { - return null; - } else { - return new GraphQLObjectType({ - ...type, - fields: newFields, - }); + public transformRequest(originalRequest: Request): Request { + return this.transformer.transformRequest(originalRequest); } }