Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ tasks.withType(PublishToMavenRepository) {
dependsOn build
}

// Only publish Maven POM, disable default Gradle modules file
tasks.withType(GenerateModuleMetadata) {
enabled = false
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public void visitField(QueryVisitorFieldEnvironment env) {
if (totalComplexity > maxComplexity) {
QueryComplexityInfo queryComplexityInfo = QueryComplexityInfo.newQueryComplexityInfo()
.complexity(totalComplexity)
.instrumentationValidationParameters(parameters)
.build();
boolean throwAbortException = maxQueryComplexityExceededFunction.apply(queryComplexityInfo);
if (throwAbortException) {
Expand Down
28 changes: 26 additions & 2 deletions src/main/java/graphql/analysis/QueryComplexityInfo.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphql.analysis;

import graphql.PublicApi;
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;

/**
* The query complexity info.
Expand All @@ -9,9 +10,11 @@
public class QueryComplexityInfo {

private final int complexity;
private InstrumentationValidationParameters instrumentationValidationParameters;

private QueryComplexityInfo(int complexity) {
private QueryComplexityInfo(int complexity, InstrumentationValidationParameters parameters) {
this.complexity = complexity;
this.instrumentationValidationParameters = parameters;
}

/**
Expand All @@ -23,6 +26,15 @@ public int getComplexity() {
return complexity;
}

/**
* This returns the instrumentation validation parameters.
*
* @return the instrumentation validation parameters.
*/
public InstrumentationValidationParameters getInstrumentationValidationParameters() {
return instrumentationValidationParameters;
}

@Override
public String toString() {
return "QueryComplexityInfo{" +
Expand All @@ -41,6 +53,7 @@ public static Builder newQueryComplexityInfo() {
public static class Builder {

private int complexity;
private InstrumentationValidationParameters instrumentationValidationParameters;

private Builder() {
}
Expand All @@ -56,11 +69,22 @@ public Builder complexity(int complexity) {
return this;
}

/**
* The instrumentation validation parameters.
*
* @param parameters the instrumentation validation parameters.
* @return this builder
*/
public Builder instrumentationValidationParameters(InstrumentationValidationParameters parameters) {
this.instrumentationValidationParameters = parameters;
return this;
}

/**
* @return a built {@link QueryComplexityInfo} object
*/
public QueryComplexityInfo build() {
return new QueryComplexityInfo(complexity);
return new QueryComplexityInfo(complexity, instrumentationValidationParameters);
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/graphql/collect/ImmutableMapWithNullValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,9 @@ public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingF
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}

@Override
public String toString() {
return delegate.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import graphql.PublicApi;
import graphql.language.SourceLocation;
import graphql.language.VariableDefinition;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLInputObjectField;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeUtil;
Expand Down Expand Up @@ -59,6 +60,10 @@ public NonNullableValueCoercedAsNullException(GraphQLInputObjectField inputTypeF
this.path = path;
}

public NonNullableValueCoercedAsNullException(GraphQLArgument graphQLArgument) {
super(format("Argument '%s' has coerced Null value for NonNull type '%s'", graphQLArgument.getName(), graphQLArgument.getType()));
}

@Override
public List<SourceLocation> getLocations() {
return sourceLocations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,20 @@ public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandler
ResultPath path = handlerParameters.getPath();

ExceptionWhileDataFetching error = new ExceptionWhileDataFetching(path, exception, sourceLocation);
logNotSafe.warn(error.getMessage(), exception);
logException(error, exception);

return DataFetcherExceptionHandlerResult.newResult().error(error).build();
}

/**
* Called to log the exception - a subclass could choose to something different in logging terms
* @param error the graphql error
* @param exception the exception that happened
*/
protected void logException(ExceptionWhileDataFetching error, Throwable exception) {
logNotSafe.warn(error.getMessage(), exception);
}

/**
* Called to unwrap an exception to a more suitable cause if required.
*
Expand Down
113 changes: 54 additions & 59 deletions src/main/java/graphql/execution/ValuesResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -388,30 +388,38 @@ private Object externalValueToLiteralForObject(GraphqlFieldVisibility fieldVisib
*/
private Map<String, Object> externalValueToInternalValueForVariables(GraphQLSchema schema,
List<VariableDefinition> variableDefinitions,
Map<String, Object> rawVariables
) {
Map<String, Object> rawVariables) {
GraphqlFieldVisibility fieldVisibility = schema.getCodeRegistry().getFieldVisibility();
Map<String, Object> coercedValues = new LinkedHashMap<>();
for (VariableDefinition variableDefinition : variableDefinitions) {
String variableName = variableDefinition.getName();
GraphQLType variableType = TypeFromAST.getTypeFromAST(schema, variableDefinition.getType());
assertTrue(variableType instanceof GraphQLInputType);
// can be NullValue
Value defaultValue = variableDefinition.getDefaultValue();
boolean hasValue = rawVariables.containsKey(variableName);
Object value = rawVariables.get(variableName);
if (!hasValue && defaultValue != null) {
Object coercedDefaultValue = literalToInternalValue(fieldVisibility, variableType, defaultValue, Collections.emptyMap());
coercedValues.put(variableName, coercedDefaultValue);
} else if (isNonNull(variableType) && (!hasValue || value == null)) {
throw new NonNullableValueCoercedAsNullException(variableDefinition, variableType);
} else if (hasValue) {
if (value == null) {
coercedValues.put(variableName, null);
} else {
Object coercedValue = externalValueToInternalValue(fieldVisibility, variableType, value);
coercedValues.put(variableName, coercedValue);
try {
String variableName = variableDefinition.getName();
GraphQLType variableType = TypeFromAST.getTypeFromAST(schema, variableDefinition.getType());
assertTrue(variableType instanceof GraphQLInputType);
// can be NullValue
Value defaultValue = variableDefinition.getDefaultValue();
boolean hasValue = rawVariables.containsKey(variableName);
Object value = rawVariables.get(variableName);
if (!hasValue && defaultValue != null) {
Object coercedDefaultValue = literalToInternalValue(fieldVisibility, variableType, defaultValue, Collections.emptyMap());
coercedValues.put(variableName, coercedDefaultValue);
} else if (isNonNull(variableType) && (!hasValue || value == null)) {
throw new NonNullableValueCoercedAsNullException(variableDefinition, variableType);
} else if (hasValue) {
if (value == null) {
coercedValues.put(variableName, null);
} else {
Object coercedValue = externalValueToInternalValue(fieldVisibility, variableType, value);
coercedValues.put(variableName, coercedValue);
}
}
} catch (CoercingParseValueException e) {
throw CoercingParseValueException.newCoercingParseValueException()
.message(String.format("Variable '%s' has an invalid value: %s", variableDefinition.getName(), e.getMessage()))
.extensions(e.getExtensions())
.cause(e.getCause())
.sourceLocation(variableDefinition.getSourceLocation())
.build();
}
}

Expand Down Expand Up @@ -452,7 +460,7 @@ private Map<String, Object> getArgumentValuesImpl(GraphQLCodeRegistry codeRegist
argumentType);
coercedValues.put(argumentName, coercedDefaultValue);
} else if (isNonNull(argumentType) && (!hasValue || isNullValue(value))) {
throw new RuntimeException();
throw new NonNullableValueCoercedAsNullException(argumentDefinition);
} else if (hasValue) {
if (isNullValue(value)) {
coercedValues.put(argumentName, value);
Expand Down Expand Up @@ -485,50 +493,37 @@ private Map<String, Argument> argumentMap(List<Argument> arguments) {
private Object externalValueToInternalValue(GraphqlFieldVisibility fieldVisibility,
GraphQLType graphQLType,
Object value) throws NonNullableValueCoercedAsNullException, CoercingParseValueException {
try {
if (isNonNull(graphQLType)) {
Object returnValue =
externalValueToInternalValue(fieldVisibility, unwrapOne(graphQLType), value);
if (returnValue == null) {
throw new NonNullableValueCoercedAsNullException("", emptyList(), graphQLType);
}
return returnValue;
if (isNonNull(graphQLType)) {
Object returnValue =
externalValueToInternalValue(fieldVisibility, unwrapOne(graphQLType), value);
if (returnValue == null) {
throw new NonNullableValueCoercedAsNullException("", emptyList(), graphQLType);
}
return returnValue;
}

if (value == null) {
return null;
}
if (value == null) {
return null;
}

if (graphQLType instanceof GraphQLScalarType) {
return externalValueToInternalValueForScalar((GraphQLScalarType) graphQLType, value);
} else if (graphQLType instanceof GraphQLEnumType) {
return externalValueToInternalValueForEnum((GraphQLEnumType) graphQLType, value);
} else if (graphQLType instanceof GraphQLList) {
return externalValueToInternalValueForList(fieldVisibility, (GraphQLList) graphQLType, value);
} else if (graphQLType instanceof GraphQLInputObjectType) {
if (value instanceof Map) {
return externalValueToInternalValueForObject(fieldVisibility, (GraphQLInputObjectType) graphQLType, (Map<String, Object>) value);
} else {
throw CoercingParseValueException.newCoercingParseValueException()
.message("Expected type 'Map' but was '" + value.getClass().getSimpleName() +
"'. Variables for input objects must be an instance of type 'Map'.")
.build();
}
if (graphQLType instanceof GraphQLScalarType) {
return externalValueToInternalValueForScalar((GraphQLScalarType) graphQLType, value);
} else if (graphQLType instanceof GraphQLEnumType) {
return externalValueToInternalValueForEnum((GraphQLEnumType) graphQLType, value);
} else if (graphQLType instanceof GraphQLList) {
return externalValueToInternalValueForList(fieldVisibility, (GraphQLList) graphQLType, value);
} else if (graphQLType instanceof GraphQLInputObjectType) {
if (value instanceof Map) {
return externalValueToInternalValueForObject(fieldVisibility, (GraphQLInputObjectType) graphQLType, (Map<String, Object>) value);
} else {
return assertShouldNeverHappen("unhandled type %s", graphQLType);
throw CoercingParseValueException.newCoercingParseValueException()
.message("Expected type 'Map' but was '" + value.getClass().getSimpleName() +
"'. Variables for input objects must be an instance of type 'Map'.")
.build();
}
} catch (CoercingParseValueException e) {
if (e.getLocations() != null) {
throw e;
}
CoercingParseValueException.Builder builder = CoercingParseValueException.newCoercingParseValueException();
throw builder
.message("invalid value : " + e.getMessage())
.extensions(e.getExtensions())
.cause(e.getCause())
.build();
} else {
return assertShouldNeverHappen("unhandled type %s", graphQLType);
}

}

/**
Expand Down
74 changes: 64 additions & 10 deletions src/main/java/graphql/normalized/NormalizedInputValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import java.util.Objects;

import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertTrue;
import static graphql.Assert.assertValidName;
import static graphql.language.AstPrinter.printAst;

/**
Expand All @@ -14,10 +17,23 @@ public class NormalizedInputValue {
private final Object value;

public NormalizedInputValue(String typeName, Object value) {
this.typeName = typeName;
this.typeName = assertValidTypeName(typeName);
this.value = value;
}

private String assertValidTypeName(String typeName) {
assertValidName(unwrapAll(typeName));
return typeName;
}

private String unwrapAll(String typeName) {
String result = unwrapOne(typeName);
while (isWrapped(result)) {
result = unwrapOne(result);
}
return result;
}

/**
* This can be a wrapped type: e.g. [String!]!
*
Expand All @@ -27,6 +43,13 @@ public String getTypeName() {
return typeName;
}

/**
* @return the type name unwrapped of all list and non-null type wrapping
*/
public String getUnwrappedTypeName() {
return unwrapAll(typeName);
}

/**
* Depending on the type it returns:
* Scalar or Enum: the ast literal of the Scalar.
Expand All @@ -40,21 +63,52 @@ public Object getValue() {
}


public boolean isList() {
/**
* @return true if the input value type is a list or a non-nullable list
*/
public boolean isListLike() {
return typeName.startsWith("[");
}

public String getUnwrappedTypeName() {
String result = unwrapNonNull(typeName);
while (result.startsWith("[")) {
result = result.substring(1, result.length() - 2);
result = unwrapNonNull(result);
/**
* @return true if the input value type is non-nullable
*/
public boolean isNonNullable() {
return typeName.endsWith("!");
}

/**
* @return true if the input value type is nullable
*/
public boolean isNullable() {
return !isNonNullable();
}

private boolean isWrapped(String typeName) {
return typeName.endsWith("!") || isListOnly(typeName);
}

private boolean isListOnly(String typeName) {
if (typeName.endsWith("!")) {
return false;
}
return result;
return typeName.startsWith("[") || typeName.endsWith("]");
}

private String unwrapNonNull(String string) {
return string.endsWith("!") ? string.substring(0, string.length() - 2) : string;
private String unwrapOne(String typeName) {
assertNotNull(typeName);
assertTrue(typeName.trim().length() > 0, () -> "We have an empty type name unwrapped");
if (typeName.endsWith("!")) {
return typeName.substring(0, typeName.length() - 1);
}
if (isListOnly(typeName)) {
// nominally this will never be true - but better to be safe than sorry
assertTrue(typeName.startsWith("["), () -> String.format("We have a unbalanced list type string '%s'", typeName));
assertTrue(typeName.endsWith("]"), () -> String.format("We have a unbalanced list type string '%s'", typeName));

return typeName.substring(1, typeName.length() - 1);
}
return typeName;
}

@Override
Expand Down
Loading