package graphql.schema; import graphql.AssertException; import graphql.Internal; import graphql.PublicApi; import graphql.language.ObjectTypeDefinition; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.UnaryOperator; import java.util.stream.Collectors; import static graphql.Assert.assertNotNull; import static graphql.Assert.assertValidName; @PublicApi public class GraphQLObjectType implements GraphQLType, GraphQLOutputType, GraphQLFieldsContainer, GraphQLCompositeType, GraphQLUnmodifiedType, GraphQLNullableType { private final String name; private final String description; private final Map<String, GraphQLFieldDefinition> fieldDefinitionsByName = new LinkedHashMap<>(); private List<GraphQLOutputType> interfaces = new ArrayList<>(); private final ObjectTypeDefinition definition; @Internal public GraphQLObjectType(String name, String description, List<GraphQLFieldDefinition> fieldDefinitions, List<GraphQLOutputType> interfaces) { this(name, description, fieldDefinitions, interfaces, null); } @Internal public GraphQLObjectType(String name, String description, List<GraphQLFieldDefinition> fieldDefinitions, List<GraphQLOutputType> interfaces, ObjectTypeDefinition definition) { assertValidName(name); assertNotNull(fieldDefinitions, "fieldDefinitions can't be null"); assertNotNull(interfaces, "interfaces can't be null"); this.name = name; this.description = description; this.interfaces = interfaces; this.definition = definition; buildDefinitionMap(fieldDefinitions); } void replaceTypeReferences(Map<String, GraphQLType> typeMap) { this.interfaces = this.interfaces.stream() .map(type -> (GraphQLOutputType) new SchemaUtil().resolveTypeReference(type, typeMap)) .collect(Collectors.toList()); } private void buildDefinitionMap(List<GraphQLFieldDefinition> fieldDefinitions) { for (GraphQLFieldDefinition fieldDefinition : fieldDefinitions) { String name = fieldDefinition.getName(); if (fieldDefinitionsByName.containsKey(name)) throw new AssertException("field " + name + " redefined"); fieldDefinitionsByName.put(name, fieldDefinition); } } public GraphQLFieldDefinition getFieldDefinition(String name) { return fieldDefinitionsByName.get(name); } public List<GraphQLFieldDefinition> getFieldDefinitions() { return new ArrayList<>(fieldDefinitionsByName.values()); } /** * @return This returns GraphQLInterface or GraphQLTypeReference instances, if the type * references are not resolved yet. After they are resolved it contains only GraphQLInterface. * Reference resolving happens when a full schema is built. */ public List<GraphQLOutputType> getInterfaces() { return new ArrayList<>(interfaces); } public String getDescription() { return description; } public String getName() { return name; } public ObjectTypeDefinition getDefinition() { return definition; } @Override public String toString() { return "GraphQLObjectType{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", fieldDefinitionsByName=" + fieldDefinitionsByName + ", interfaces=" + interfaces + '}'; } public static Builder newObject() { return new Builder(); } @PublicApi public static class Builder { private String name; private String description; private List<GraphQLFieldDefinition> fieldDefinitions = new ArrayList<>(); private List<GraphQLOutputType> interfaces = new ArrayList<>(); private ObjectTypeDefinition definition; public Builder name(String name) { this.name = name; return this; } public Builder description(String description) { this.description = description; return this; } public Builder definition(ObjectTypeDefinition definition) { this.definition = definition; return this; } public Builder field(GraphQLFieldDefinition fieldDefinition) { assertNotNull(fieldDefinition, "fieldDefinition can't be null"); this.fieldDefinitions.add(fieldDefinition); return this; } /** * Take a field builder in a function definition and apply. Can be used in a jdk8 lambda * e.g.: * <pre> * {@code * field(f -> f.name("fieldName")) * } * </pre> * * @param builderFunction a supplier for the builder impl * @return this */ public Builder field(UnaryOperator<GraphQLFieldDefinition.Builder> builderFunction) { assertNotNull(builderFunction, "builderFunction can't be null"); GraphQLFieldDefinition.Builder builder = GraphQLFieldDefinition.newFieldDefinition(); builder = builderFunction.apply(builder); return field(builder.build()); } /** * Same effect as the field(GraphQLFieldDefinition). Builder.build() is called * from within * * @param builder an un-built/incomplete GraphQLFieldDefinition * @return this */ public Builder field(GraphQLFieldDefinition.Builder builder) { this.fieldDefinitions.add(builder.build()); return this; } public Builder fields(List<GraphQLFieldDefinition> fieldDefinitions) { assertNotNull(fieldDefinitions, "fieldDefinitions can't be null"); this.fieldDefinitions.addAll(fieldDefinitions); return this; } public Builder withInterface(GraphQLInterfaceType interfaceType) { assertNotNull(interfaceType, "interfaceType can't be null"); this.interfaces.add(interfaceType); return this; } public Builder withInterface(GraphQLTypeReference reference) { assertNotNull(reference, "reference can't be null"); this.interfaces.add(reference); return this; } public Builder withInterfaces(GraphQLInterfaceType... interfaceType) { for (GraphQLInterfaceType type : interfaceType) { withInterface(type); } return this; } public Builder withInterfaces(GraphQLTypeReference... references) { for (GraphQLTypeReference reference : references) { withInterface(reference); } return this; } public GraphQLObjectType build() { return new GraphQLObjectType(name, description, fieldDefinitions, interfaces, definition); } } }