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

graphql.schema.GraphQLSchema Maven / Gradle / Ivy

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


import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import graphql.Directives;
import graphql.Internal;
import graphql.PublicApi;
import graphql.language.SchemaDefinition;
import graphql.language.SchemaExtensionDefinition;
import graphql.schema.validation.InvalidSchemaException;
import graphql.schema.validation.SchemaValidationError;
import graphql.schema.validation.SchemaValidator;
import graphql.schema.visibility.GraphqlFieldVisibility;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertShouldNeverHappen;
import static graphql.Assert.assertTrue;
import static graphql.DirectivesUtil.directivesByName;
import static graphql.collect.ImmutableKit.emptyList;
import static graphql.collect.ImmutableKit.map;
import static graphql.collect.ImmutableKit.nonNullCopyOf;
import static graphql.schema.GraphqlTypeComparators.byNameAsc;
import static graphql.schema.GraphqlTypeComparators.sortTypes;
import static java.util.Arrays.asList;

/**
 * The schema represents the combined type system of the graphql engine.  This is how the engine knows
 * what graphql queries represent what data.
 * 

* See http://graphql.org/learn/schema/#type-language for more details */ @PublicApi public class GraphQLSchema { private final GraphQLObjectType queryType; private final GraphQLObjectType mutationType; private final GraphQLObjectType subscriptionType; private final ImmutableSet additionalTypes; private final ImmutableSet directives; private final ImmutableMap schemaDirectives; private final SchemaDefinition definition; private final ImmutableList extensionDefinitions; private final GraphQLCodeRegistry codeRegistry; private final ImmutableMap typeMap; private final ImmutableMap> interfaceNameToObjectTypes; private final ImmutableMap> interfaceNameToObjectTypeNames; private final String description; /** * @param queryType the query type * @deprecated use the {@link #newSchema()} builder pattern instead, as this constructor will be made private in a future version. */ @Internal @Deprecated public GraphQLSchema(GraphQLObjectType queryType) { this(queryType, null, Collections.emptySet()); } /** * @param queryType the query type * @param mutationType the mutation type * @param additionalTypes additional types * @deprecated use the {@link #newSchema()} builder pattern instead, as this constructor will be made private in a future version. */ @Internal @Deprecated public GraphQLSchema(GraphQLObjectType queryType, GraphQLObjectType mutationType, Set additionalTypes) { this(queryType, mutationType, null, additionalTypes); } /** * @param queryType the query type * @param mutationType the mutation type * @param subscriptionType the subscription type * @param additionalTypes additional types * @deprecated use the {@link #newSchema()} builder pattern instead, as this constructor will be made private in a future version. */ @Internal @Deprecated public GraphQLSchema(GraphQLObjectType queryType, GraphQLObjectType mutationType, GraphQLObjectType subscriptionType, Set additionalTypes) { this(newSchema().query(queryType).mutation(mutationType).subscription(subscriptionType).additionalTypes(additionalTypes), false); } @Internal private GraphQLSchema(Builder builder, boolean afterTransform) { assertNotNull(builder.additionalTypes, () -> "additionalTypes can't be null"); assertNotNull(builder.queryType, () -> "queryType can't be null"); assertNotNull(builder.additionalDirectives, () -> "directives can't be null"); assertNotNull(builder.codeRegistry, () -> "codeRegistry can't be null"); this.queryType = builder.queryType; this.mutationType = builder.mutationType; this.subscriptionType = builder.subscriptionType; this.additionalTypes = ImmutableSet.copyOf(builder.additionalTypes); this.directives = ImmutableSet.copyOf(builder.additionalDirectives); this.schemaDirectives = ImmutableMap.copyOf(builder.schemaDirectives); this.definition = builder.definition; this.extensionDefinitions = nonNullCopyOf(builder.extensionDefinitions); this.codeRegistry = builder.codeRegistry; // sorted by type name SchemaUtil schemaUtil = new SchemaUtil(); this.typeMap = ImmutableMap.copyOf(schemaUtil.allTypes(this, additionalTypes, afterTransform)); this.interfaceNameToObjectTypes = buildInterfacesToObjectTypes(schemaUtil.groupImplementations(this)); this.interfaceNameToObjectTypeNames = buildInterfacesToObjectName(interfaceNameToObjectTypes); this.description = builder.description; } // This can be removed once we no longer extract legacy code from types such as data fetchers but for now // we need it to make an efficient copy that does not walk the types twice @Internal private GraphQLSchema(GraphQLSchema otherSchema, GraphQLCodeRegistry codeRegistry) { this.queryType = otherSchema.queryType; this.mutationType = otherSchema.mutationType; this.subscriptionType = otherSchema.subscriptionType; this.additionalTypes = otherSchema.additionalTypes; this.directives = otherSchema.directives; this.schemaDirectives = otherSchema.schemaDirectives; this.definition = otherSchema.definition; this.extensionDefinitions = nonNullCopyOf(otherSchema.extensionDefinitions); this.codeRegistry = codeRegistry; this.typeMap = otherSchema.typeMap; this.interfaceNameToObjectTypes = otherSchema.interfaceNameToObjectTypes; this.interfaceNameToObjectTypeNames = otherSchema.interfaceNameToObjectTypeNames; this.description = otherSchema.description; } private ImmutableMap> buildInterfacesToObjectTypes(Map> groupImplementations) { ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> e : groupImplementations.entrySet()) { ImmutableList sortedObjectTypes = ImmutableList.copyOf(sortTypes(byNameAsc(), e.getValue())); map.put(e.getKey(), sortedObjectTypes); } return map.build(); } private ImmutableMap> buildInterfacesToObjectName(ImmutableMap> byInterface) { ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> e : byInterface.entrySet()) { ImmutableList objectTypeNames = map(e.getValue(), GraphQLObjectType::getName); map.put(e.getKey(), objectTypeNames); } return map.build(); } public GraphQLCodeRegistry getCodeRegistry() { return codeRegistry; } public Set getAdditionalTypes() { return additionalTypes; } public GraphQLType getType(String typeName) { return typeMap.get(typeName); } /** * Called to return a named {@link graphql.schema.GraphQLObjectType} from the schema * * @param typeName the name of the type * @return a graphql object type or null if there is one * @throws graphql.GraphQLException if the type is NOT a object type */ public GraphQLObjectType getObjectType(String typeName) { GraphQLType graphQLType = typeMap.get(typeName); if (graphQLType != null) { assertTrue(graphQLType instanceof GraphQLObjectType, () -> String.format("You have asked for named object type '%s' but its not an object type but rather a '%s'", typeName, graphQLType.getClass().getName())); } return (GraphQLObjectType) graphQLType; } public Map getTypeMap() { return typeMap; } public List getAllTypesAsList() { return sortTypes(byNameAsc(), typeMap.values()); } /** * This will return the list of {@link graphql.schema.GraphQLObjectType} types that implement the given * interface type. * * @param type interface type to obtain implementations of. * @return list of types implementing provided interface */ public List getImplementations(GraphQLInterfaceType type) { return interfaceNameToObjectTypes.getOrDefault(type.getName(), emptyList()); } /** * Returns true if a specified concrete type is a possible type of a provided abstract type. * If the provided abstract type is: * - an interface, it checks whether the concrete type is one of its implementations. * - a union, it checks whether the concrete type is one of its possible types. * * @param abstractType abstract type either interface or union * @param concreteType concrete type * @return true if possible type, false otherwise. */ public boolean isPossibleType(GraphQLNamedType abstractType, GraphQLObjectType concreteType) { if (abstractType instanceof GraphQLInterfaceType) { ImmutableList objectNames = this.interfaceNameToObjectTypeNames.getOrDefault(abstractType.getName(), emptyList()); return objectNames.contains(concreteType.getName()); } else if (abstractType instanceof GraphQLUnionType) { return ((GraphQLUnionType) abstractType).isPossibleType(concreteType); } return assertShouldNeverHappen("Unsupported abstract type %s. Abstract types supported are Union and Interface.", abstractType.getName()); } public GraphQLObjectType getQueryType() { return queryType; } public GraphQLObjectType getMutationType() { return mutationType; } public GraphQLObjectType getSubscriptionType() { return subscriptionType; } /** * @return the field visibility * @deprecated use {@link GraphQLCodeRegistry#getFieldVisibility()} instead */ @Deprecated public GraphqlFieldVisibility getFieldVisibility() { return codeRegistry.getFieldVisibility(); } /** * This returns the list of directives that are associated with this schema object including * built in ones. * * @return a list of directives */ public List getDirectives() { return ImmutableList.copyOf(directives); } /** * This returns a map of directives that are associated with this schema object including * built in ones. * * @return a map of directives */ public Map getDirectiveByName() { return directivesByName(getDirectives()); } public GraphQLDirective getDirective(String name) { for (GraphQLDirective directive : getDirectives()) { if (directive.getName().equals(name)) { return directive; } } return null; } /** * This returns the list of directives that have been explicitly put on the * schema object. Note that {@link #getDirectives()} will return * directives for all schema elements, whereas this is just for the schema * element itself * * @return a list of directives */ public List getSchemaDirectives() { return new ArrayList<>(schemaDirectives.values()); } /** * This returns a map of directives that have been explicitly put on the * schema object. Note that {@link #getDirectives()} will return * directives for all schema elements, whereas this is just for the schema * element itself * * @return a list of directives */ public Map getSchemaDirectiveByName() { return directivesByName(getSchemaDirectives()); } /** * This returns the named directive that have been explicitly put on the * schema object. Note that {@link #getDirective(String)} will return * directives for all schema elements, whereas this is just for the schema * element itself * * @param name the name of the schema directive to return * @return a named directive */ public GraphQLDirective getSchemaDirective(String name) { return schemaDirectives.get(name); } public SchemaDefinition getDefinition() { return definition; } public List getExtensionDefinitions() { return extensionDefinitions; } public boolean isSupportingMutations() { return mutationType != null; } public boolean isSupportingSubscriptions() { return subscriptionType != null; } public String getDescription() { return description; } /** * This helps you transform the current GraphQLSchema object into another one by starting a builder with all * the current values and allows you to transform it how you want. * * @param builderConsumer the consumer code that will be given a builder to transform * @return a new GraphQLSchema object based on calling build on that builder */ public GraphQLSchema transform(Consumer builderConsumer) { Builder builder = newSchema(this); builderConsumer.accept(builder); return builder.build(); } /** * @return a new schema builder */ public static Builder newSchema() { return new Builder(); } /** * This allows you to build a schema from an existing schema. It copies everything from the existing * schema and then allows you to replace them. * * @param existingSchema the existing schema * @return a new schema builder */ public static Builder newSchema(GraphQLSchema existingSchema) { return new Builder() .query(existingSchema.getQueryType()) .mutation(existingSchema.getMutationType()) .subscription(existingSchema.getSubscriptionType()) .codeRegistry(existingSchema.getCodeRegistry()) .clearAdditionalTypes() .clearDirectives() .additionalDirectives(existingSchema.directives) .clearSchemaDirectives() .withSchemaDirectives(schemaDirectivesArray(existingSchema)) .additionalTypes(existingSchema.additionalTypes) .description(existingSchema.getDescription()); } private static GraphQLDirective[] schemaDirectivesArray(GraphQLSchema existingSchema) { return existingSchema.schemaDirectives.values() .toArray(new GraphQLDirective[0]); } public static class Builder { private GraphQLObjectType queryType; private GraphQLObjectType mutationType; private GraphQLObjectType subscriptionType; private GraphQLCodeRegistry codeRegistry = GraphQLCodeRegistry.newCodeRegistry().build(); private Set additionalTypes = new LinkedHashSet<>(); private SchemaDefinition definition; private List extensionDefinitions; private String description; // we default these in private Set additionalDirectives = new LinkedHashSet<>( asList(Directives.IncludeDirective, Directives.SkipDirective) ); private Map schemaDirectives = new LinkedHashMap<>(); private SchemaUtil schemaUtil = new SchemaUtil(); public Builder query(GraphQLObjectType.Builder builder) { return query(builder.build()); } public Builder query(GraphQLObjectType queryType) { this.queryType = queryType; return this; } public Builder mutation(GraphQLObjectType.Builder builder) { return mutation(builder.build()); } public Builder mutation(GraphQLObjectType mutationType) { this.mutationType = mutationType; return this; } public Builder subscription(GraphQLObjectType.Builder builder) { return subscription(builder.build()); } public Builder subscription(GraphQLObjectType subscriptionType) { this.subscriptionType = subscriptionType; return this; } /** * @param fieldVisibility the field visibility * @return this builder * @deprecated use {@link graphql.schema.GraphQLCodeRegistry.Builder#fieldVisibility(graphql.schema.visibility.GraphqlFieldVisibility)} instead */ @Deprecated public Builder fieldVisibility(GraphqlFieldVisibility fieldVisibility) { this.codeRegistry = this.codeRegistry.transform(builder -> builder.fieldVisibility(fieldVisibility)); return this; } public Builder codeRegistry(GraphQLCodeRegistry codeRegistry) { this.codeRegistry = codeRegistry; return this; } public Builder additionalTypes(Set additionalTypes) { this.additionalTypes.addAll(additionalTypes); return this; } public Builder additionalType(GraphQLType additionalType) { this.additionalTypes.add(additionalType); return this; } public Builder clearAdditionalTypes() { this.additionalTypes.clear(); return this; } public Builder additionalDirectives(Set additionalDirectives) { this.additionalDirectives.addAll(additionalDirectives); return this; } public Builder additionalDirective(GraphQLDirective additionalDirective) { this.additionalDirectives.add(additionalDirective); return this; } public Builder clearDirectives() { this.additionalDirectives.clear(); return this; } public Builder withSchemaDirectives(GraphQLDirective... directives) { for (GraphQLDirective directive : directives) { withSchemaDirective(directive); } return this; } public Builder withSchemaDirective(GraphQLDirective directive) { assertNotNull(directive, () -> "directive can't be null"); schemaDirectives.put(directive.getName(), directive); return this; } public Builder withSchemaDirective(GraphQLDirective.Builder builder) { return withSchemaDirective(builder.build()); } /** * This is used to clear all the directives in the builder so far. * * @return the builder */ public Builder clearSchemaDirectives() { schemaDirectives.clear(); return this; } public Builder definition(SchemaDefinition definition) { this.definition = definition; return this; } public Builder extensionDefinitions(List extensionDefinitions) { this.extensionDefinitions = extensionDefinitions; return this; } public Builder description(String description) { this.description = description; return this; } /** * Builds the schema * * @param additionalTypes - please dont use this any more * @return the built schema * @deprecated - Use the {@link #additionalType(GraphQLType)} methods */ @Deprecated public GraphQLSchema build(Set additionalTypes) { return additionalTypes(additionalTypes).build(); } /** * Builds the schema * * @param additionalTypes - please don't use this any more * @param additionalDirectives - please don't use this any more * @return the built schema * @deprecated - Use the {@link #additionalType(GraphQLType)} and {@link #additionalDirective(GraphQLDirective)} methods */ @Deprecated public GraphQLSchema build(Set additionalTypes, Set additionalDirectives) { return additionalTypes(additionalTypes).additionalDirectives(additionalDirectives).build(); } /** * Builds the schema * * @return the built schema */ public GraphQLSchema build() { return buildImpl(false); } GraphQLSchema buildImpl(boolean afterTransform) { assertNotNull(additionalTypes, () -> "additionalTypes can't be null"); assertNotNull(additionalDirectives, () -> "additionalDirectives can't be null"); // schemas built via the schema generator have the deprecated directive BUT we want it present for hand built // schemas - its inherently part of the spec! if (additionalDirectives.stream().noneMatch(d -> d.getName().equals(Directives.DeprecatedDirective.getName()))) { additionalDirectives.add(Directives.DeprecatedDirective); } if (additionalDirectives.stream().noneMatch(d -> d.getName().equals(Directives.SpecifiedByDirective.getName()))) { additionalDirectives.add(Directives.SpecifiedByDirective); } // grab the legacy code things from types final GraphQLSchema tempSchema = new GraphQLSchema(this, afterTransform); codeRegistry = codeRegistry.transform(codeRegistryBuilder -> schemaUtil.extractCodeFromTypes(codeRegistryBuilder, tempSchema)); GraphQLSchema graphQLSchema = new GraphQLSchema(tempSchema, codeRegistry); schemaUtil.replaceTypeReferences(graphQLSchema); Collection errors = new SchemaValidator().validateSchema(graphQLSchema); if (errors.size() > 0) { throw new InvalidSchemaException(errors); } return graphQLSchema; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy