TypeRuntimeWiring.java
package graphql.schema.idl;
import graphql.PublicApi;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLSchema;
import graphql.schema.TypeResolver;
import graphql.schema.idl.errors.StrictModeWiringException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import static graphql.Assert.assertNotNull;
import static java.lang.String.format;
/**
* A type runtime wiring is a specification of the data fetchers and possible type resolver for a given type name.
* <br>
* This is used by {@link RuntimeWiring} to wire together a functional {@link GraphQLSchema}
*/
@PublicApi
public class TypeRuntimeWiring {
private final static AtomicBoolean DEFAULT_STRICT_MODE = new AtomicBoolean(true);
/**
* By default {@link TypeRuntimeWiring} builders are in strict mode, but you can set a JVM wide value too
*
* @param strictMode the desired strict mode state
*
* @see Builder#strictMode(boolean)
*/
public static void setStrictModeJvmWide(boolean strictMode) {
DEFAULT_STRICT_MODE.set(strictMode);
}
/**
* @return the current JVM wide state of strict mode
*/
public static boolean getStrictModeJvmWide() {
return DEFAULT_STRICT_MODE.get();
}
private final String typeName;
private final DataFetcher defaultDataFetcher;
private final Map<String, DataFetcher> fieldDataFetchers;
private final TypeResolver typeResolver;
private final EnumValuesProvider enumValuesProvider;
private TypeRuntimeWiring(String typeName, DataFetcher defaultDataFetcher, Map<String, DataFetcher> fieldDataFetchers, TypeResolver typeResolver, EnumValuesProvider enumValuesProvider) {
this.typeName = typeName;
this.defaultDataFetcher = defaultDataFetcher;
this.fieldDataFetchers = fieldDataFetchers;
this.typeResolver = typeResolver;
this.enumValuesProvider = enumValuesProvider;
}
/**
* Creates a new type wiring builder
*
* @param typeName the name of the type to wire
*
* @return the builder
*/
public static Builder newTypeWiring(String typeName) {
assertNotNull(typeName, () -> "You must provide a type name");
return new Builder().typeName(typeName);
}
/**
* This form allows a lambda to be used as the builder
*
* @param typeName the name of the type to wire
* @param builderFunction a function that will be given the builder to use
*
* @return the same builder back please
*/
public static TypeRuntimeWiring newTypeWiring(String typeName, UnaryOperator<Builder> builderFunction) {
return builderFunction.apply(newTypeWiring(typeName)).build();
}
public String getTypeName() {
return typeName;
}
public Map<String, DataFetcher> getFieldDataFetchers() {
return fieldDataFetchers;
}
public DataFetcher getDefaultDataFetcher() {
return defaultDataFetcher;
}
public TypeResolver getTypeResolver() {
return typeResolver;
}
public EnumValuesProvider getEnumValuesProvider() {
return enumValuesProvider;
}
public static class Builder {
private final Map<String, DataFetcher> fieldDataFetchers = new LinkedHashMap<>();
private String typeName;
private DataFetcher defaultDataFetcher;
private TypeResolver typeResolver;
private EnumValuesProvider enumValuesProvider;
private boolean strictMode = DEFAULT_STRICT_MODE.get();
/**
* Sets the type name for this type wiring. You MUST set this.
*
* @param typeName the name of the type
*
* @return the current type wiring
*/
public Builder typeName(String typeName) {
this.typeName = typeName;
return this;
}
/**
* This puts the builder into strict mode, so if things get defined twice, for example, it
* will throw a {@link StrictModeWiringException}.
*
* @return this builder
*/
public Builder strictMode(boolean strictMode) {
this.strictMode = strictMode;
return this;
}
/**
* This puts the builder into strict mode, so if things get defined twice, for example, it
* will throw a {@link StrictModeWiringException}.
*
* @return this builder
*
* @deprecated use {@link #strictMode(boolean)} instead
*/
@Deprecated(since = "2025-03-22", forRemoval = true)
public Builder strictMode() {
this.strictMode = true;
return this;
}
/**
* Adds a data fetcher for the current type to the specified field
*
* @param fieldName the field that data fetcher should apply to
* @param dataFetcher the new data Fetcher
*
* @return the current type wiring
*/
public Builder dataFetcher(String fieldName, DataFetcher dataFetcher) {
assertNotNull(dataFetcher, () -> "you must provide a data fetcher");
assertNotNull(fieldName, () -> "you must tell us what field");
if (strictMode) {
assertFieldStrictly(fieldName);
}
fieldDataFetchers.put(fieldName, dataFetcher);
return this;
}
/**
* Adds data fetchers for the current type to the specified field
*
* @param dataFetchersMap a map of fields to data fetchers
*
* @return the current type wiring
*/
public Builder dataFetchers(Map<String, DataFetcher> dataFetchersMap) {
assertNotNull(dataFetchersMap, () -> "you must provide a data fetchers map");
if (strictMode) {
dataFetchersMap.forEach((fieldName, df) -> {
assertFieldStrictly(fieldName);
});
}
fieldDataFetchers.putAll(dataFetchersMap);
return this;
}
private void assertFieldStrictly(String fieldName) {
if (fieldDataFetchers.containsKey(fieldName)) {
throw new StrictModeWiringException(format("The field %s already has a data fetcher defined", fieldName));
}
}
/**
* All fields in a type need a data fetcher of some sort and this method is called to provide the default data fetcher
* that will be used for this type if no specific one has been provided per field.
*
* @param dataFetcher the default data fetcher to use for this type
*
* @return the current type wiring
*/
public Builder defaultDataFetcher(DataFetcher dataFetcher) {
assertNotNull(dataFetcher);
if (strictMode && defaultDataFetcher != null) {
throw new StrictModeWiringException(format("The type %s has already has a default data fetcher defined", typeName));
}
defaultDataFetcher = dataFetcher;
return this;
}
/**
* Adds a {@link TypeResolver} to the current type. This MUST be specified for Interface
* and Union types.
*
* @param typeResolver the type resolver in play
*
* @return the current type wiring
*/
public Builder typeResolver(TypeResolver typeResolver) {
assertNotNull(typeResolver, () -> "you must provide a type resolver");
this.typeResolver = typeResolver;
return this;
}
public Builder enumValues(EnumValuesProvider enumValuesProvider) {
assertNotNull(enumValuesProvider, () -> "you must provide an enum values provider");
this.enumValuesProvider = enumValuesProvider;
return this;
}
/**
* @return the built type wiring
*/
public TypeRuntimeWiring build() {
assertNotNull(typeName, () -> "you must provide a type name");
return new TypeRuntimeWiring(typeName, defaultDataFetcher, fieldDataFetchers, typeResolver, enumValuesProvider);
}
}
}