All Downloads are FREE. Search and download functionalities are using the official Maven repository.

graphql.normalized.ExecutableNormalizedOperationToAstCompiler Maven / Gradle / Ivy

There is a newer version: 230521-nf-execution
Show newest version
package graphql.normalized;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import graphql.Internal;
import graphql.language.Argument;
import graphql.language.ArrayValue;
import graphql.language.Document;
import graphql.language.Field;
import graphql.language.InlineFragment;
import graphql.language.NullValue;
import graphql.language.ObjectField;
import graphql.language.ObjectValue;
import graphql.language.OperationDefinition;
import graphql.language.Selection;
import graphql.language.SelectionSet;
import graphql.language.TypeName;
import graphql.language.Value;
import graphql.schema.GraphQLSchema;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static graphql.collect.ImmutableKit.map;
import static graphql.language.Argument.newArgument;
import static graphql.language.Field.newField;
import static graphql.language.InlineFragment.newInlineFragment;
import static graphql.language.SelectionSet.newSelectionSet;
import static graphql.language.TypeName.newTypeName;

@Internal
public class ExecutableNormalizedOperationToAstCompiler {
    public static Document compileToDocument(GraphQLSchema schema,
                                             OperationDefinition.Operation operationKind,
                                             String operationName,
                                             List topLevelFields) {
        List> selections = selectionsForNormalizedFields(schema, topLevelFields);
        SelectionSet selectionSet = new SelectionSet(selections);

        return Document.newDocument()
                .definition(OperationDefinition.newOperationDefinition()
                        .name(operationName)
                        .operation(operationKind)
                        .selectionSet(selectionSet)
                        .build())
                .build();
    }

    private static List> selectionsForNormalizedFields(GraphQLSchema schema,
                                                                    List executableNormalizedFields) {
        ImmutableList.Builder> selections = ImmutableList.builder();

        // All conditional fields go here instead of directly to selections so they can be grouped together
        // in the same inline fragement in the output
        Map> conditionalFieldsByObjectTypeName = new LinkedHashMap<>();

        for (ExecutableNormalizedField nf : executableNormalizedFields) {
            Map> groupFieldsForChild = selectionForNormalizedField(schema, nf);
            if (nf.isConditional(schema)) {
                groupFieldsForChild.forEach((objectTypeName, fields) -> {
                    List fieldList = conditionalFieldsByObjectTypeName.computeIfAbsent(objectTypeName, ignored -> new ArrayList<>());
                    fieldList.addAll(fields);
                });
            } else {
                List fields = groupFieldsForChild.values().iterator().next();
                selections.addAll(fields);
            }
        }

        conditionalFieldsByObjectTypeName.forEach((objectTypeName, fields) -> {
            TypeName typeName = newTypeName(objectTypeName).build();
            InlineFragment inlineFragment = newInlineFragment().
                    typeCondition(typeName)
                    .selectionSet(selectionSet(fields))
                    .build();
            selections.add(inlineFragment);
        });

        return selections.build();
    }

    private static Map> selectionForNormalizedField(GraphQLSchema schema,
                                                                        ExecutableNormalizedField executableNormalizedField) {
        Map> groupedFields = new LinkedHashMap<>();
        for (String objectTypeName : executableNormalizedField.getObjectTypeNames()) {
            List> subSelections = selectionsForNormalizedFields(schema, executableNormalizedField.getChildren());
            SelectionSet selectionSet = null;
            if (subSelections.size() > 0) {
                selectionSet = newSelectionSet()
                        .selections(subSelections)
                        .build();
            }
            List arguments = createArguments(executableNormalizedField);
            Field field = newField()
                    .name(executableNormalizedField.getFieldName())
                    .alias(executableNormalizedField.getAlias())
                    .selectionSet(selectionSet)
                    .arguments(arguments)
                    .build();

            groupedFields.computeIfAbsent(objectTypeName, ignored -> new ArrayList<>()).add(field);
        }
        return groupedFields;
    }

    private static SelectionSet selectionSet(List fields) {
        return newSelectionSet().selections(fields).build();
    }

    private static List createArguments(ExecutableNormalizedField executableNormalizedField) {
        ImmutableList.Builder result = ImmutableList.builder();
        ImmutableMap normalizedArguments = executableNormalizedField.getNormalizedArguments();
        for (String argName : normalizedArguments.keySet()) {
            Argument argument = newArgument()
                    .name(argName)
                    .value(argValue(normalizedArguments.get(argName).getValue()))
                    .build();
            result.add(argument);
        }
        return result.build();
    }

    private static Value argValue(Object value) {
        if (value instanceof List) {
            ArrayValue.Builder arrayValue = ArrayValue.newArrayValue();
            arrayValue.values(map((List) value, ExecutableNormalizedOperationToAstCompiler::argValue));
            return arrayValue.build();
        }
        if (value instanceof Map) {
            ObjectValue.Builder objectValue = ObjectValue.newObjectValue();
            Map map = (Map) value;
            for (String fieldName : map.keySet()) {
                Value fieldValue = argValue(((NormalizedInputValue) map.get(fieldName)).getValue());
                objectValue.objectField(ObjectField.newObjectField().name(fieldName).value(fieldValue).build());
            }
            return objectValue.build();
        }
        if (value == null) {
            return NullValue.newNullValue().build();
        }
        return (Value) value;
    }
}