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

graphql.schema.idl.SchemaExtensionsChecker Maven / Gradle / Ivy

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

import graphql.Assert;
import graphql.GraphQLError;
import graphql.Internal;
import graphql.language.Directive;
import graphql.language.ObjectTypeDefinition;
import graphql.language.OperationTypeDefinition;
import graphql.language.SchemaDefinition;
import graphql.language.SchemaExtensionDefinition;
import graphql.language.Type;
import graphql.language.TypeDefinition;
import graphql.language.TypeName;
import graphql.schema.idl.errors.MissingTypeError;
import graphql.schema.idl.errors.OperationRedefinitionError;
import graphql.schema.idl.errors.OperationTypesMustBeObjects;
import graphql.schema.idl.errors.QueryOperationMissingError;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

@Internal
public class SchemaExtensionsChecker {

    static Map gatherOperationDefs(TypeDefinitionRegistry typeRegistry) {
        List noErrors = new ArrayList<>();
        Map operationTypeDefinitionMap = gatherOperationDefs(noErrors, typeRegistry.schemaDefinition().orElse(null), typeRegistry.getSchemaExtensionDefinitions());
        Assert.assertTrue(noErrors.isEmpty(), () -> "If you call this method it MUST have previously been error checked");
        return operationTypeDefinitionMap;
    }

    static Map gatherOperationDefs(List errors, SchemaDefinition schema, List schemaExtensionDefinitions) {
        Map operationDefs = new LinkedHashMap<>();
        if (schema != null) {
            defineOperationDefs(errors, schema.getOperationTypeDefinitions(), operationDefs);
        }
        for (SchemaExtensionDefinition schemaExtensionDefinition : schemaExtensionDefinitions) {
            defineOperationDefs(errors, schemaExtensionDefinition.getOperationTypeDefinitions(), operationDefs);
        }
        return operationDefs;
    }

    static void defineOperationDefs(List errors, Collection newOperationDefs, Map existingOperationDefs) {
        for (OperationTypeDefinition operationTypeDefinition : newOperationDefs) {
            OperationTypeDefinition oldEntry = existingOperationDefs.get(operationTypeDefinition.getName());
            if (oldEntry != null) {
                errors.add(new OperationRedefinitionError(oldEntry, operationTypeDefinition));
            } else {
                existingOperationDefs.put(operationTypeDefinition.getName(), operationTypeDefinition);
            }
        }
    }

    static List checkSchemaInvariants(List errors, TypeDefinitionRegistry typeRegistry) {
        /*
            https://github.com/facebook/graphql/pull/90/files#diff-fe406b08746616e2f5f00909488cce66R1000

            GraphQL type system definitions can omit the schema definition when the query
            and mutation root types are named `Query` and `Mutation`, respectively.
         */
        // schema
        SchemaDefinition schemaDef = typeRegistry.schemaDefinition().orElse(null);
        Map operationTypeMap = SchemaExtensionsChecker.gatherOperationDefs(errors, schemaDef, typeRegistry.getSchemaExtensionDefinitions());
        List operationTypeDefinitions = new ArrayList<>(operationTypeMap.values());

        operationTypeDefinitions
                .forEach(checkOperationTypesExist(typeRegistry, errors));

        operationTypeDefinitions
                .forEach(checkOperationTypesAreObjects(typeRegistry, errors));

        // ensure we have a "query" one
        Optional query = operationTypeDefinitions.stream().filter(op -> "query".equals(op.getName())).findFirst();
        if (!query.isPresent()) {
            // its ok if they have a type named Query
            Optional queryType = typeRegistry.getType("Query");
            if (!queryType.isPresent()) {
                errors.add(new QueryOperationMissingError());
            }
        }
        return operationTypeDefinitions;
    }

    static List gatherSchemaDirectives(TypeDefinitionRegistry typeRegistry) {
        List noErrors = new ArrayList<>();
        List directiveList = gatherSchemaDirectives(typeRegistry, noErrors);
        Assert.assertTrue(noErrors.isEmpty(), () -> "If you call this method it MUST have previously been error checked");
        return directiveList;
    }

    static List gatherSchemaDirectives(TypeDefinitionRegistry typeRegistry, List errors) {
        List directiveList = new ArrayList<>();
        SchemaDefinition schemaDefinition = typeRegistry.schemaDefinition().orElse(null);
        if (schemaDefinition != null) {
            directiveList.addAll(schemaDefinition.getDirectives());
        }
        for (SchemaExtensionDefinition schemaExtensionDefinition : typeRegistry.getSchemaExtensionDefinitions()) {
            directiveList.addAll(schemaExtensionDefinition.getDirectives());
        }
        return directiveList;
    }

    private static Consumer checkOperationTypesExist(TypeDefinitionRegistry typeRegistry, List errors) {
        return op -> {
            TypeName unwrapped = TypeInfo.typeInfo(op.getTypeName()).getTypeName();
            if (!typeRegistry.hasType(unwrapped)) {
                errors.add(new MissingTypeError("operation", op, op.getName(), unwrapped));
            }
        };
    }

    private static Consumer checkOperationTypesAreObjects(TypeDefinitionRegistry typeRegistry, List errors) {
        return op -> {
            // make sure it is defined as a ObjectTypeDef
            Type queryType = op.getTypeName();
            Optional type = typeRegistry.getType(queryType);
            type.ifPresent(typeDef -> {
                if (!(typeDef instanceof ObjectTypeDefinition)) {
                    errors.add(new OperationTypesMustBeObjects(op));
                }
            });
        };
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy