-
-
Notifications
You must be signed in to change notification settings - Fork 796
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes selectionSet hints with WrapFields transform
See #1725
- Loading branch information
Showing
5 changed files
with
255 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { GraphQLSchema, SelectionSetNode, TypeInfo, GraphQLOutputType, Kind } from 'graphql'; | ||
|
||
import { Transform, Request } from '@graphql-tools/utils'; | ||
import VisitSelectionSets from './VisitSelectionSets'; | ||
|
||
export default class AddSelectionSetsByField implements Transform { | ||
private readonly transformer: VisitSelectionSets; | ||
|
||
constructor( | ||
sourceSchema: GraphQLSchema, | ||
selectionSetsByType: Record<string, SelectionSetNode>, | ||
selectionSetsByField: Record<string, Record<string, SelectionSetNode>>, | ||
initialType: GraphQLOutputType | ||
) { | ||
this.transformer = new VisitSelectionSets(sourceSchema, initialType, (node, typeInfo) => | ||
visitSelectionSet(node, typeInfo, selectionSetsByType, selectionSetsByField) | ||
); | ||
} | ||
|
||
public transformRequest(originalRequest: Request): Request { | ||
return this.transformer.transformRequest(originalRequest); | ||
} | ||
} | ||
|
||
function visitSelectionSet( | ||
node: SelectionSetNode, | ||
typeInfo: TypeInfo, | ||
selectionSetsByType: Record<string, SelectionSetNode>, | ||
selectionSetsByField: Record<string, Record<string, SelectionSetNode>> | ||
): SelectionSetNode { | ||
const parentType = typeInfo.getParentType(); | ||
if (parentType != null) { | ||
const parentTypeName = parentType.name; | ||
let selections = node.selections; | ||
|
||
if (parentTypeName in selectionSetsByType) { | ||
const selectionSet = selectionSetsByType[parentTypeName]; | ||
if (selectionSet != null) { | ||
selections = selections.concat(selectionSet.selections); | ||
} | ||
} | ||
|
||
if (parentTypeName in selectionSetsByField) { | ||
node.selections.forEach(selection => { | ||
if (selection.kind === Kind.FIELD) { | ||
const name = selection.name.value; | ||
const selectionSet = selectionSetsByField[parentTypeName][name]; | ||
if (selectionSet != null) { | ||
selections = selections.concat(selectionSet.selections); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
if (selections !== node.selections) { | ||
return { | ||
...node, | ||
selections, | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { | ||
DocumentNode, | ||
GraphQLSchema, | ||
Kind, | ||
SelectionSetNode, | ||
TypeInfo, | ||
visit, | ||
visitWithTypeInfo, | ||
GraphQLOutputType, | ||
OperationDefinitionNode, | ||
FragmentDefinitionNode, | ||
SelectionNode, | ||
DefinitionNode, | ||
} from 'graphql'; | ||
|
||
import { Transform, Request, collectFields, GraphQLExecutionContext } from '@graphql-tools/utils'; | ||
|
||
export default class VisitSelectionSets implements Transform { | ||
private readonly schema: GraphQLSchema; | ||
private readonly initialType: GraphQLOutputType; | ||
private readonly visitor: (node: SelectionSetNode, typeInfo: TypeInfo) => SelectionSetNode; | ||
|
||
constructor( | ||
schema: GraphQLSchema, | ||
initialType: GraphQLOutputType, | ||
visitor: (node: SelectionSetNode, typeInfo: TypeInfo) => SelectionSetNode | ||
) { | ||
this.schema = schema; | ||
this.initialType = initialType; | ||
this.visitor = visitor; | ||
} | ||
|
||
public transformRequest(originalRequest: Request): Request { | ||
const document = visitSelectionSets(originalRequest, this.schema, this.initialType, this.visitor); | ||
return { | ||
...originalRequest, | ||
document, | ||
}; | ||
} | ||
} | ||
|
||
function visitSelectionSets( | ||
request: Request, | ||
schema: GraphQLSchema, | ||
initialType: GraphQLOutputType, | ||
visitor: (node: SelectionSetNode, typeInfo: TypeInfo) => SelectionSetNode | ||
): DocumentNode { | ||
const { document, variables } = request; | ||
|
||
const operations: Array<OperationDefinitionNode> = []; | ||
const fragments: Record<string, FragmentDefinitionNode> = Object.create(null); | ||
document.definitions.forEach(def => { | ||
if (def.kind === Kind.OPERATION_DEFINITION) { | ||
operations.push(def); | ||
} else if (def.kind === Kind.FRAGMENT_DEFINITION) { | ||
fragments[def.name.value] = def; | ||
} | ||
}); | ||
|
||
const partialExecutionContext = { | ||
schema, | ||
variableValues: variables, | ||
fragments, | ||
} as GraphQLExecutionContext; | ||
|
||
const typeInfo = new TypeInfo(schema, undefined, initialType); | ||
const newDefinitions: Array<DefinitionNode> = operations.map(operation => { | ||
const type = | ||
operation.operation === 'query' | ||
? schema.getQueryType() | ||
: operation.operation === 'mutation' | ||
? schema.getMutationType() | ||
: schema.getSubscriptionType(); | ||
|
||
const fields = collectFields( | ||
partialExecutionContext, | ||
type, | ||
operation.selectionSet, | ||
Object.create(null), | ||
Object.create(null) | ||
); | ||
|
||
const newSelections: Array<SelectionNode> = []; | ||
Object.keys(fields).forEach(responseKey => { | ||
const fieldNodes = fields[responseKey]; | ||
fieldNodes.forEach(fieldNode => { | ||
const selectionSet = fieldNode.selectionSet; | ||
|
||
if (selectionSet == null) { | ||
newSelections.push(fieldNode); | ||
return; | ||
} | ||
|
||
const newSelectionSet = visit( | ||
selectionSet, | ||
visitWithTypeInfo(typeInfo, { | ||
[Kind.SELECTION_SET]: node => visitor(node, typeInfo), | ||
}) | ||
); | ||
|
||
if (newSelectionSet === selectionSet) { | ||
newSelections.push(fieldNode); | ||
return; | ||
} | ||
|
||
newSelections.push({ | ||
...fieldNode, | ||
selectionSet: newSelectionSet, | ||
}); | ||
}); | ||
}); | ||
|
||
return { | ||
...operation, | ||
selectionSet: { | ||
kind: Kind.SELECTION_SET, | ||
selections: newSelections, | ||
}, | ||
}; | ||
}); | ||
|
||
Object.values(fragments).forEach(fragment => { | ||
newDefinitions.push( | ||
visit( | ||
fragment, | ||
visitWithTypeInfo(typeInfo, { | ||
[Kind.SELECTION_SET]: node => visitor(node, typeInfo), | ||
}) | ||
) | ||
); | ||
}); | ||
|
||
return { | ||
...document, | ||
definitions: newDefinitions, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
export { default as CheckResultAndHandleErrors } from './CheckResultAndHandleErrors'; | ||
export { checkResultAndHandleErrors } from './CheckResultAndHandleErrors'; | ||
export { default as ExpandAbstractTypes } from './ExpandAbstractTypes'; | ||
export { default as AddSelectionSetsByField } from './AddSelectionSetsByField'; | ||
export { default as AddMergedTypeSelectionSets } from './AddSelectionSetsByType'; | ||
export { default as VisitSelectionSets } from './VisitSelectionSets'; | ||
export { default as AddSelectionSets } from './AddSelectionSets'; | ||
export { default as AddArgumentsAsVariables } from './AddArgumentsAsVariables'; | ||
export { default as FilterToSchema } from './FilterToSchema'; | ||
export { default as AddTypenameToAbstract } from './AddTypenameToAbstract'; | ||
|
||
// superseded by AddFragmentsByField | ||
// superseded by VisitSelectionSets and AddSelectionSets | ||
export { default as AddSelectionSetsByField } from './AddSelectionSetsByField'; | ||
export { default as AddMergedTypeSelectionSets } from './AddSelectionSetsByType'; | ||
export { default as ReplaceFieldWithFragment } from './ReplaceFieldWithFragment'; | ||
// superseded by AddSelectionSetsByField | ||
export { default as AddFragmentsByField } from './AddFragmentsByField'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters