diff --git a/README.md b/README.md
index cbe7c3d..35aa365 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,8 @@
[](https://github.com/graphql-java/graphql-java-extended-validation/actions/workflows/master.yml)
-[](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-validation/)
-[](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-validation/)
-[](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-validation/)
+[](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-validation/)
+[](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-extended-validation/)
[](https://github.com/graphql-java/graphql-java-extended-validation/blob/master/LICENSE.md)
@@ -19,25 +18,25 @@ This library provides extended validation of fields and field arguments for [gra
com.graphql-java
graphql-java-extended-validation
- 20.0
+ 22.0
```
```groovy
-implementation 'com.graphql-java:graphql-java-extended-validation:20.0'
+implementation 'com.graphql-java:graphql-java-extended-validation:22.0'
```
> Note:
->
-> use 19.1 or above for graphql-java 19.x and above
->
-> use 19.1-hibernate-validator-6.2.0.Final for graphql-java 19.x and SpringBoot 2.x support
->
-> use 20.0 or above for graphql-java 20.x and above
+>
+> use 22.0 or above for graphql-java 22.x and above
+>
+> use 21.0 or above for graphql-java 21.x and above
+>
+> use 20.0 for graphql-java 20.x and above
>
> use 20.0-hibernate-validator-6.2.0.Final for graphql-java 20.x and SpringBoot 2.x support
-It's currently available from Maven central.
+The library is currently available on Maven Central.
# SDL @Directive constraints
@@ -312,7 +311,7 @@ The element must be a number inside the specified `integer` and `fraction` range
- Applies to : `String`, `Byte`, `Short`, `Int`, `Long`, `BigDecimal`, `BigInteger`, `Float`, `Lists`
-- SDL : `directive @Digits(integer : Int!, fraction : Int!, message : String = "graphql.validation.Digits.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION`
+- SDL : `directive @Digits(integer : Int!, fraction : Int, message : String = "graphql.validation.Digits.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION`
- Message : `graphql.validation.Digits.message`
diff --git a/build.gradle b/build.gradle
index da5f461..86d43e5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -40,8 +40,8 @@ repositories {
}
dependencies {
- api "com.graphql-java:graphql-java:21.0"
- api "com.graphql-java:graphql-java-extended-scalars:21.0"
+ api "com.graphql-java:graphql-java:22.0"
+ api "com.graphql-java:graphql-java-extended-scalars:22.0"
api "org.hibernate.validator:hibernate-validator:7.0.1.Final"
api "org.glassfish:jakarta.el:4.0.2"
diff --git a/src/main/java/graphql/validation/constraints/AbstractDirectiveConstraint.java b/src/main/java/graphql/validation/constraints/AbstractDirectiveConstraint.java
index ddb7a7f..3cfd0e9 100644
--- a/src/main/java/graphql/validation/constraints/AbstractDirectiveConstraint.java
+++ b/src/main/java/graphql/validation/constraints/AbstractDirectiveConstraint.java
@@ -27,6 +27,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import static graphql.schema.GraphQLTypeUtil.isList;
import static graphql.validation.rules.ValidationEnvironment.ValidatedElement.FIELD;
@@ -198,23 +199,28 @@ protected boolean isOneOfTheseTypes(GraphQLInputType inputType, Collection assertExpectedArgType(argName, "Int"));
+ }
- Number value = argument.getValue();
- if (value == null) {
- return assertExpectedArgType(argName, "Int");
- }
- return value.intValue();
+ /**
+ * Returns an optional integer argument from a directive (or its default), or empty Optional if the argument is null.
+ *
+ * @param directive the directive to check
+ * @param argName the argument name
+ * @return an optional null value
+ */
+ protected Optional getIntArgOpt(GraphQLAppliedDirective directive, String argName) {
+ return Optional.ofNullable(directive.getArgument(argName))
+ .map(GraphQLAppliedDirectiveArgument::getValue)
+ .map(Number::intValue);
}
/**
diff --git a/src/main/java/graphql/validation/constraints/standard/DigitsConstraint.java b/src/main/java/graphql/validation/constraints/standard/DigitsConstraint.java
index fe94bbd..a8f963e 100644
--- a/src/main/java/graphql/validation/constraints/standard/DigitsConstraint.java
+++ b/src/main/java/graphql/validation/constraints/standard/DigitsConstraint.java
@@ -10,6 +10,7 @@
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import static graphql.validation.constraints.GraphQLScalars.GRAPHQL_NUMBER_AND_STRING_TYPES;
@@ -21,14 +22,14 @@ public DigitsConstraint() {
@Override
public Documentation getDocumentation() {
return Documentation.newDocumentation()
- .messageTemplate(getMessageTemplate())
- .description("The element must be a number inside the specified `integer` and `fraction` range.")
- .example("buyCar( carCost : Float @Digits(integer : 5, fraction : 2) : DriverDetails")
- .applicableTypes(GRAPHQL_NUMBER_AND_STRING_TYPES)
- .directiveSDL("directive @Digits(integer : Int!, fraction : Int!, message : String = \"%s\") " +
- "on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION",
- getMessageTemplate())
- .build();
+ .messageTemplate(getMessageTemplate())
+ .description("The element must be a number inside the specified `integer` and optionally inside `fraction` range.")
+ .example("buyCar( carCost : Float @Digits(integer : 5, fraction : 2) : DriverDetails")
+ .applicableTypes(GRAPHQL_NUMBER_AND_STRING_TYPES)
+ .directiveSDL("directive @Digits(integer : Int!, fraction : Int, message : String = \"%s\") " +
+ "on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION",
+ getMessageTemplate())
+ .build();
}
@Override
@@ -43,28 +44,40 @@ protected List runConstraint(ValidationEnvironment validationEnvir
GraphQLAppliedDirective directive = validationEnvironment.getContextObject(GraphQLAppliedDirective.class);
int maxIntegerLength = getIntArg(directive, "integer");
- int maxFractionLength = getIntArg(directive, "fraction");
+ Optional maxFractionLengthOpt = getIntArgOpt(directive, "fraction");
boolean isOk;
try {
BigDecimal bigNum = asBigDecimal(validatedValue);
- isOk = isOk(bigNum, maxIntegerLength, maxFractionLength);
+ boolean isFractionPartOk = maxFractionLengthOpt
+ .map(maxFractionLength -> isFractionPartOk(bigNum, maxFractionLength))
+ .orElse(true);
+
+ isOk = isFractionPartOk && isIntegerPartOk(bigNum, maxIntegerLength);
} catch (NumberFormatException e) {
isOk = false;
}
if (!isOk) {
- return mkError(validationEnvironment, "integer", maxIntegerLength, "fraction", maxFractionLength);
+ return mkError(
+ validationEnvironment,
+ "integer",
+ maxIntegerLength, "fraction",
+ maxFractionLengthOpt.map(Object::toString).orElse("unlimited")
+ );
}
return Collections.emptyList();
}
- private boolean isOk(BigDecimal bigNum, int maxIntegerLength, int maxFractionLength) {
- int integerPartLength = bigNum.precision() - bigNum.scale();
- int fractionPartLength = Math.max(bigNum.scale(), 0);
+ private static boolean isIntegerPartOk(BigDecimal bigNum, int maxIntegerLength) {
+ final int integerPartLength = bigNum.precision() - bigNum.scale();
+ return maxIntegerLength >= integerPartLength;
+ }
- return maxIntegerLength >= integerPartLength && maxFractionLength >= fractionPartLength;
+ private static boolean isFractionPartOk(BigDecimal bigNum, int maxFractionLength) {
+ final int fractionPartLength = Math.max(bigNum.scale(), 0);
+ return maxFractionLength >= fractionPartLength;
}
@Override
diff --git a/src/main/java/graphql/validation/schemawiring/ValidationSchemaWiring.java b/src/main/java/graphql/validation/schemawiring/ValidationSchemaWiring.java
index 30867b9..65d0a23 100644
--- a/src/main/java/graphql/validation/schemawiring/ValidationSchemaWiring.java
+++ b/src/main/java/graphql/validation/schemawiring/ValidationSchemaWiring.java
@@ -5,10 +5,12 @@
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLFieldsContainer;
+import graphql.schema.GraphQLObjectType;
import graphql.schema.idl.SchemaDirectiveWiring;
import graphql.schema.idl.SchemaDirectiveWiringEnvironment;
import graphql.validation.interpolation.MessageInterpolator;
import graphql.validation.rules.OnValidationErrorStrategy;
+import graphql.validation.rules.TargetedValidationRules;
import graphql.validation.rules.ValidationRules;
import java.util.Locale;
@@ -34,15 +36,23 @@ public ValidationSchemaWiring(ValidationRules ruleCandidates) {
public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment env) {
GraphQLFieldsContainer fieldsContainer = env.getFieldsContainer();
GraphQLFieldDefinition fieldDefinition = env.getFieldDefinition();
-
+ TargetedValidationRules rules = ruleCandidates.buildRulesFor(fieldDefinition, fieldsContainer);
+ if (rules.isEmpty()) {
+ return fieldDefinition;
+ }
+ if (! (fieldsContainer instanceof GraphQLObjectType)) {
+ // only object type fields can have data fetchers
+ return fieldDefinition;
+ }
+ GraphQLObjectType graphQLObjectType = (GraphQLObjectType) fieldsContainer;
OnValidationErrorStrategy errorStrategy = ruleCandidates.getOnValidationErrorStrategy();
MessageInterpolator messageInterpolator = ruleCandidates.getMessageInterpolator();
Locale locale = ruleCandidates.getLocale();
- final DataFetcher> currentDF = env.getCodeRegistry().getDataFetcher(fieldsContainer, fieldDefinition);
+ final DataFetcher> currentDF = env.getCodeRegistry().getDataFetcher(graphQLObjectType, fieldDefinition);
final DataFetcher> newDF = buildValidatingDataFetcher(errorStrategy, messageInterpolator, currentDF, locale);
- env.getCodeRegistry().dataFetcher(fieldsContainer, fieldDefinition, newDF);
+ env.getCodeRegistry().dataFetcher(graphQLObjectType, fieldDefinition, newDF);
return fieldDefinition;
}
diff --git a/src/test/groovy/graphql/validation/constraints/standard/DigitsConstraintTest.groovy b/src/test/groovy/graphql/validation/constraints/standard/DigitsConstraintTest.groovy
index 772ec36..701746c 100644
--- a/src/test/groovy/graphql/validation/constraints/standard/DigitsConstraintTest.groovy
+++ b/src/test/groovy/graphql/validation/constraints/standard/DigitsConstraintTest.groovy
@@ -23,6 +23,7 @@ class DigitsConstraintTest extends BaseConstraintTestSupport {
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | null | ''
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | Byte.valueOf("0") | ''
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | Double.valueOf("500.2") | ''
+ 'field( arg : String @Digits(integer : 5) ) : ID' | Double.valueOf("500.2345678") | ''
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | new BigDecimal("-12345.12") | ''
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | new BigDecimal("-123456.12") | 'Digits;path=/arg;val:-123456.12;\t'