Skip to content

Commit

Permalink
Add result transforming to bundled transformers
Browse files Browse the repository at this point in the history
Adds result wrapping capability to the following generic transformers

= TransformCompositeFields
= TransformInterfaceFields
= TransformObjectFields
= TransformRootFields
= ExtendSchema
= MapFields

Adds result visiting usage to the following transfromers
= WrapFields
= WrapType
= HoistField

Result visiting was introduced within RenameTypes within the initial
commit.
  • Loading branch information
yaacovCR committed Jul 5, 2020
1 parent bb5b833 commit 803f032
Show file tree
Hide file tree
Showing 11 changed files with 552 additions and 78 deletions.
3 changes: 2 additions & 1 deletion packages/utils/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ export interface Transform {

export type FieldNodeMapper = (
fieldNode: FieldNode,
fragments: Record<string, FragmentDefinitionNode>
fragments: Record<string, FragmentDefinitionNode>,
context: Record<string, any>
) => SelectionNode | Array<SelectionNode>;

export type FieldNodeMappers = Record<string, Record<string, FieldNodeMapper>>;
Expand Down
37 changes: 33 additions & 4 deletions packages/wrap/src/transforms/ExtendSchema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { GraphQLSchema, extendSchema, parse } from 'graphql';

import { Transform, IFieldResolver, IResolvers, Request, FieldNodeMappers } from '@graphql-tools/utils';
import {
Transform,
IFieldResolver,
IResolvers,
Request,
FieldNodeMappers,
ExecutionResult,
} from '@graphql-tools/utils';
import { addResolversToSchema } from '@graphql-tools/schema';
import { defaultMergedResolver } from '@graphql-tools/delegate';

import { ObjectValueTransformerMap, ErrorsTransformer } from '../types';

import MapFields from './MapFields';

export default class ExtendSchema implements Transform {
Expand All @@ -17,16 +26,24 @@ export default class ExtendSchema implements Transform {
resolvers = {},
defaultFieldResolver,
fieldNodeTransformerMap,
objectValueTransformerMap,
errorsTransformer,
}: {
typeDefs?: string;
resolvers?: IResolvers;
defaultFieldResolver?: IFieldResolver<any, any>;
fieldNodeTransformerMap?: FieldNodeMappers;
objectValueTransformerMap?: ObjectValueTransformerMap;
errorsTransformer?: ErrorsTransformer;
}) {
this.typeDefs = typeDefs;
this.resolvers = resolvers;
this.defaultFieldResolver = defaultFieldResolver != null ? defaultFieldResolver : defaultMergedResolver;
this.transformer = new MapFields(fieldNodeTransformerMap != null ? fieldNodeTransformerMap : {});
this.transformer = new MapFields(
fieldNodeTransformerMap != null ? fieldNodeTransformerMap : {},
objectValueTransformerMap,
errorsTransformer
);
}

public transformSchema(schema: GraphQLSchema): GraphQLSchema {
Expand All @@ -41,7 +58,19 @@ export default class ExtendSchema implements Transform {
});
}

public transformRequest(originalRequest: Request): Request {
return this.transformer.transformRequest(originalRequest);
public transformRequest(
originalRequest: Request,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): Request {
return this.transformer.transformRequest(originalRequest, delegationContext, transformationContext);
}

public transformResult(
originalResult: ExecutionResult,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): ExecutionResult {
return this.transformer.transformResult(originalResult, delegationContext, transformationContext);
}
}
106 changes: 91 additions & 15 deletions packages/wrap/src/transforms/HoistField.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
import { GraphQLSchema, GraphQLObjectType, getNullableType } from 'graphql';
import { GraphQLSchema, GraphQLObjectType, getNullableType, FieldNode, Kind, GraphQLError } from 'graphql';

import {
wrapFieldNode,
renameFieldNode,
appendObjectFields,
removeObjectFields,
Transform,
Request,
ExecutionResult,
relocatedError,
} from '@graphql-tools/utils';

import { defaultMergedResolver } from '@graphql-tools/delegate';

import MapFields from './MapFields';
import { createMergedResolver } from '@graphql-tools/delegate';

export default class HoistField implements Transform {
private readonly typeName: string;
private readonly path: Array<string>;
private readonly newFieldName: string;
private readonly pathToField: Array<string>;
private readonly oldFieldName: string;
private readonly transformer: Transform;

constructor(typeName: string, path: Array<string>, newFieldName: string) {
constructor(typeName: string, path: Array<string>, newFieldName: string, alias = '__gqtlw__') {
this.typeName = typeName;
this.path = path;
this.newFieldName = newFieldName;

this.pathToField = this.path.slice();
this.oldFieldName = this.pathToField.pop();
this.transformer = new MapFields({
[typeName]: {
[newFieldName]: fieldNode => wrapFieldNode(renameFieldNode(fieldNode, this.oldFieldName), this.pathToField),
const pathToField = path.slice();
const oldFieldName = pathToField.pop();

this.oldFieldName = oldFieldName;
this.pathToField = pathToField;
this.transformer = new MapFields(
{
[typeName]: {
[newFieldName]: fieldNode => wrapFieldNode(renameFieldNode(fieldNode, oldFieldName), pathToField, alias),
},
},
});
{
[typeName]: value => unwrapValue(value, alias),
},
errors => unwrapErrors(errors, alias)
);
}

public transformSchema(schema: GraphQLSchema): GraphQLSchema {
Expand All @@ -53,14 +62,81 @@ export default class HoistField implements Transform {
newSchema = appendObjectFields(newSchema, this.typeName, {
[this.newFieldName]: {
type: targetType,
resolve: createMergedResolver({ fromPath: this.pathToField }),
resolve: defaultMergedResolver,
},
});

return this.transformer.transformSchema(newSchema);
}

public transformRequest(originalRequest: Request): Request {
return this.transformer.transformRequest(originalRequest);
public transformRequest(
originalRequest: Request,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): Request {
return this.transformer.transformRequest(originalRequest, delegationContext, transformationContext);
}

public transformResult(
originalResult: ExecutionResult,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): ExecutionResult {
return this.transformer.transformResult(originalResult, delegationContext, transformationContext);
}
}

export function wrapFieldNode(fieldNode: FieldNode, path: Array<string>, alias: string): FieldNode {
let newFieldNode = fieldNode;
path.forEach(fieldName => {
newFieldNode = {
kind: Kind.FIELD,
alias: {
kind: Kind.NAME,
value: alias,
},
name: {
kind: Kind.NAME,
value: fieldName,
},
selectionSet: {
kind: Kind.SELECTION_SET,
selections: [fieldNode],
},
};
});

return newFieldNode;
}

export function unwrapValue(originalValue: any, alias: string): any {
let newValue = originalValue;

let object = newValue[alias];
while (object != null) {
newValue = object;
object = newValue[alias];
}

delete originalValue[alias];
Object.assign(originalValue, newValue);

return originalValue;
}

function unwrapErrors(errors: ReadonlyArray<GraphQLError>, alias: string): Array<GraphQLError> {
if (errors === undefined) {
return undefined;
}

return errors.map(error => {
const originalPath = error.path;
if (originalPath == null) {
return error;
}

const newPath = originalPath.filter(pathSegment => pathSegment !== alias);

return relocatedError(error, newPath);
});
}
56 changes: 47 additions & 9 deletions packages/wrap/src/transforms/MapFields.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,74 @@
import { GraphQLSchema } from 'graphql';

import { Transform, Request, FieldNodeMappers } from '@graphql-tools/utils';
import { Transform, Request, FieldNodeMappers, ExecutionResult } from '@graphql-tools/utils';

import TransformCompositeFields from './TransformCompositeFields';

import { ObjectValueTransformerMap, ErrorsTransformer } from '../types';

export default class MapFields implements Transform {
private readonly transformer: TransformCompositeFields;

constructor(fieldNodeTransformerMap: FieldNodeMappers) {
constructor(
fieldNodeTransformerMap: FieldNodeMappers,
objectValueTransformerMap?: ObjectValueTransformerMap,
errorsTransformer?: ErrorsTransformer
) {
this.transformer = new TransformCompositeFields(
(_typeName, _fieldName, fieldConfig) => fieldConfig,
(typeName, fieldName, fieldNode, fragments) => {
(typeName, fieldName, fieldNode, fragments, context) => {
const typeTransformers = fieldNodeTransformerMap[typeName];
if (typeTransformers == null) {
return fieldNode;
return undefined;
}

const fieldNodeTransformer = typeTransformers[fieldName];
if (fieldNodeTransformer == null) {
return fieldNode;
return undefined;
}

return fieldNodeTransformer(fieldNode, fragments);
}
return fieldNodeTransformer(fieldNode, fragments, context);
},
objectValueTransformerMap != null
? (data, context) => {
if (data == null) {
return data;
}

const typeName = data.__typename;
if (typeName == null) {
return data;
}

const transformer = objectValueTransformerMap[typeName];
if (transformer == null) {
return data;
}

return transformer(data, context);
}
: undefined,
errorsTransformer != null ? errorsTransformer : undefined
);
}

public transformSchema(schema: GraphQLSchema): GraphQLSchema {
return this.transformer.transformSchema(schema);
}

public transformRequest(request: Request): Request {
return this.transformer.transformRequest(request);
public transformRequest(
originalRequest: Request,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): Request {
return this.transformer.transformRequest(originalRequest, delegationContext, transformationContext);
}

public transformResult(
originalResult: ExecutionResult,
delegationContext?: Record<string, any>,
transformationContext?: Record<string, any>
): ExecutionResult {
return this.transformer.transformResult(originalResult, delegationContext, transformationContext);
}
}

0 comments on commit 803f032

Please sign in to comment.