graphql.schema.DataFetchingFieldSelectionSetImpl Maven / Gradle / Ivy
package graphql.schema;
import graphql.Internal;
import graphql.execution.ExecutionContext;
import graphql.execution.FieldCollector;
import graphql.execution.FieldCollectorParameters;
import graphql.execution.MergedField;
import graphql.execution.MergedSelectionSet;
import graphql.execution.ValuesResolver;
import graphql.introspection.Introspection;
import graphql.language.Field;
import graphql.language.FragmentDefinition;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
@Internal
public class DataFetchingFieldSelectionSetImpl implements DataFetchingFieldSelectionSet {
private final static DataFetchingFieldSelectionSet NOOP = new DataFetchingFieldSelectionSet() {
@Override
public MergedSelectionSet get() {
return MergedSelectionSet.newMergedSelectionSet().build();
}
@Override
public Map> getArguments() {
return emptyMap();
}
@Override
public Map getDefinitions() {
return emptyMap();
}
@Override
public boolean contains(String fieldGlobPattern) {
return false;
}
@Override
public SelectedField getField(String fieldName) {
return null;
}
@Override
public List getFields() {
return emptyList();
}
@Override
public List getFields(String fieldGlobPattern) {
return emptyList();
}
};
public static DataFetchingFieldSelectionSet newCollector(ExecutionContext executionContext, GraphQLType fieldType, MergedField mergedField) {
GraphQLType unwrappedType = GraphQLTypeUtil.unwrapAll(fieldType);
if (unwrappedType instanceof GraphQLFieldsContainer) {
return new DataFetchingFieldSelectionSetImpl(executionContext, (GraphQLFieldsContainer) unwrappedType, mergedField);
} else {
// we can only collect fields on object types and interfaces. Scalars, Unions etc... cant be done.
return NOOP;
}
}
private static GraphQLObjectType asObjectTypeOrNull(GraphQLType unwrappedType) {
return unwrappedType instanceof GraphQLObjectType ? (GraphQLObjectType) unwrappedType : null;
}
private final FieldCollector fieldCollector = new FieldCollector();
private final ValuesResolver valuesResolver = new ValuesResolver();
private final MergedField parentFields;
private final GraphQLSchema graphQLSchema;
private final GraphQLFieldsContainer parentFieldType;
private final Map variables;
private final Map fragmentsByName;
private Map selectionSetFields;
private Map selectionSetFieldDefinitions;
private Map> selectionSetFieldArgs;
private Set flattenedFields;
private DataFetchingFieldSelectionSetImpl(ExecutionContext executionContext, GraphQLFieldsContainer parentFieldType, MergedField parentFields) {
this(parentFields, parentFieldType, executionContext.getGraphQLSchema(), executionContext.getVariables(), executionContext.getFragmentsByName());
}
public DataFetchingFieldSelectionSetImpl(MergedField parentFields, GraphQLFieldsContainer parentFieldType, GraphQLSchema graphQLSchema, Map variables, Map fragmentsByName) {
this.parentFields = parentFields;
this.graphQLSchema = graphQLSchema;
this.parentFieldType = parentFieldType;
this.variables = variables;
this.fragmentsByName = fragmentsByName;
}
@Override
public MergedSelectionSet get() {
// by having a .get() method we get lazy evaluation.
computeValuesLazily();
return MergedSelectionSet.newMergedSelectionSet().subFields(selectionSetFields).build();
}
@Override
public Map> getArguments() {
computeValuesLazily();
return selectionSetFieldArgs;
}
@Override
public Map getDefinitions() {
computeValuesLazily();
return selectionSetFieldDefinitions;
}
@Override
public boolean contains(String fieldGlobPattern) {
if (fieldGlobPattern == null || fieldGlobPattern.isEmpty()) {
return false;
}
computeValuesLazily();
PathMatcher globMatcher = globMatcher(fieldGlobPattern);
for (String flattenedField : flattenedFields) {
Path path = Paths.get(flattenedField);
if (globMatcher.matches(path)) {
return true;
}
}
return false;
}
@Override
public SelectedField getField(String fqFieldName) {
computeValuesLazily();
MergedField fields = selectionSetFields.get(fqFieldName);
if (fields == null) {
return null;
}
GraphQLFieldDefinition fieldDefinition = selectionSetFieldDefinitions.get(fqFieldName);
Map arguments = selectionSetFieldArgs.get(fqFieldName);
arguments = arguments == null ? emptyMap() : arguments;
return new SelectedFieldImpl(fqFieldName, fields, fieldDefinition, arguments);
}
@Override
public List getFields(String fieldGlobPattern) {
if (fieldGlobPattern == null || fieldGlobPattern.isEmpty()) {
return emptyList();
}
computeValuesLazily();
List targetNames = new ArrayList<>();
PathMatcher globMatcher = globMatcher(fieldGlobPattern);
for (String flattenedField : flattenedFields) {
Path path = Paths.get(flattenedField);
if (globMatcher.matches(path)) {
targetNames.add(flattenedField);
}
}
return targetNames.stream()
.map(this::getField)
.collect(Collectors.toList());
}
@Override
public List getFields() {
computeValuesLazily();
return flattenedFields.stream()
.map(this::getField)
.collect(Collectors.toList());
}
private class SelectedFieldImpl implements SelectedField {
private final String qualifiedName;
private final String name;
private final GraphQLFieldDefinition fieldDefinition;
private final DataFetchingFieldSelectionSet selectionSet;
private final Map arguments;
private SelectedFieldImpl(String qualifiedName, MergedField parentFields, GraphQLFieldDefinition fieldDefinition, Map arguments) {
this.qualifiedName = qualifiedName;
this.name = parentFields.getName();
this.fieldDefinition = fieldDefinition;
this.arguments = arguments;
GraphQLType unwrappedType = GraphQLTypeUtil.unwrapAll(fieldDefinition.getType());
if (unwrappedType instanceof GraphQLFieldsContainer) {
this.selectionSet = new DataFetchingFieldSelectionSetImpl(parentFields, (GraphQLFieldsContainer) unwrappedType, graphQLSchema, variables, fragmentsByName);
} else {
this.selectionSet = NOOP;
}
}
@Override
public String getName() {
return name;
}
@Override
public String getQualifiedName() {
return qualifiedName;
}
@Override
public GraphQLFieldDefinition getFieldDefinition() {
return fieldDefinition;
}
@Override
public Map getArguments() {
return arguments;
}
@Override
public DataFetchingFieldSelectionSet getSelectionSet() {
return selectionSet;
}
}
private PathMatcher globMatcher(String fieldGlobPattern) {
return FileSystems.getDefault().getPathMatcher("glob:" + fieldGlobPattern);
}
private void computeValuesLazily() {
synchronized (this) {
if (selectionSetFields != null) {
return;
}
selectionSetFields = new LinkedHashMap<>();
selectionSetFieldDefinitions = new LinkedHashMap<>();
selectionSetFieldArgs = new LinkedHashMap<>();
flattenedFields = new LinkedHashSet<>();
traverseFields(parentFields, parentFieldType, "");
}
}
private final static String SEP = "/";
private void traverseFields(MergedField fieldList, GraphQLFieldsContainer parentFieldType, String fieldPrefix) {
FieldCollectorParameters parameters = FieldCollectorParameters.newParameters()
.schema(graphQLSchema)
.objectType(asObjectTypeOrNull(parentFieldType))
.fragments(fragmentsByName)
.variables(variables)
.build();
MergedSelectionSet collectedFields = fieldCollector.collectFields(parameters, fieldList);
for (Map.Entry entry : collectedFields.getSubFields().entrySet()) {
String fieldName = mkFieldName(fieldPrefix, entry.getKey());
MergedField collectedFieldList = entry.getValue();
selectionSetFields.put(fieldName, collectedFieldList);
Field field = collectedFieldList.getSingleField();
GraphQLFieldDefinition fieldDef = Introspection.getFieldDef(graphQLSchema, parentFieldType, field.getName());
GraphQLType unwrappedType = GraphQLTypeUtil.unwrapAll(fieldDef.getType());
Map argumentValues = valuesResolver.getArgumentValues(fieldDef.getArguments(), field.getArguments(), variables);
selectionSetFieldArgs.put(fieldName, argumentValues);
selectionSetFieldDefinitions.put(fieldName, fieldDef);
flattenedFields.add(fieldName);
if (unwrappedType instanceof GraphQLFieldsContainer) {
traverseFields(collectedFieldList, (GraphQLFieldsContainer) unwrappedType, fieldName);
}
}
}
private String mkFieldName(String fieldPrefix, String fieldName) {
return (!fieldPrefix.isEmpty() ? fieldPrefix + SEP : "") + fieldName;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy