Skip to content

Commit c22f5ec

Browse files
authored
make fraction part in DigitsConstraint optional (#92)
* make fragment part in DigitsConstraint optional * make fragment part in DigitsConstraint optional * fix tests * update docu to make fraction optional
1 parent 02e006a commit c22f5ec

File tree

4 files changed

+46
-26
lines changed

4 files changed

+46
-26
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ The element must be a number inside the specified `integer` and `fraction` range
313313

314314
- Applies to : `String`, `Byte`, `Short`, `Int`, `Long`, `BigDecimal`, `BigInteger`, `Float`, `Lists`
315315

316-
- SDL : `directive @Digits(integer : Int!, fraction : Int!, message : String = "graphql.validation.Digits.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION`
316+
- SDL : `directive @Digits(integer : Int!, fraction : Int, message : String = "graphql.validation.Digits.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION`
317317

318318
- Message : `graphql.validation.Digits.message`
319319

src/main/java/graphql/validation/constraints/AbstractDirectiveConstraint.java

+16-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.LinkedHashMap;
2828
import java.util.List;
2929
import java.util.Map;
30+
import java.util.Optional;
3031

3132
import static graphql.schema.GraphQLTypeUtil.isList;
3233
import static graphql.validation.rules.ValidationEnvironment.ValidatedElement.FIELD;
@@ -198,23 +199,28 @@ protected boolean isOneOfTheseTypes(GraphQLInputType inputType, Collection<Graph
198199
}
199200

200201
/**
201-
* Returns an integer argument from a directive (or its default) and throws an assertion of the argument is null
202+
* Returns an integer argument from a directive (or its default) and throws an assertion if the argument is null
202203
*
203204
* @param directive the directive to check
204205
* @param argName the argument name
205206
* @return a non null value
206207
*/
207208
protected int getIntArg(GraphQLAppliedDirective directive, String argName) {
208-
GraphQLAppliedDirectiveArgument argument = directive.getArgument(argName);
209-
if (argument == null) {
210-
return assertExpectedArgType(argName, "Int");
211-
}
209+
return getIntArgOpt(directive, argName)
210+
.orElseGet(() -> assertExpectedArgType(argName, "Int"));
211+
}
212212

213-
Number value = argument.getValue();
214-
if (value == null) {
215-
return assertExpectedArgType(argName, "Int");
216-
}
217-
return value.intValue();
213+
/**
214+
* Returns an optional integer argument from a directive (or its default), or empty Optional if the argument is null.
215+
*
216+
* @param directive the directive to check
217+
* @param argName the argument name
218+
* @return an optional null value
219+
*/
220+
protected Optional<Integer> getIntArgOpt(GraphQLAppliedDirective directive, String argName) {
221+
return Optional.ofNullable(directive.getArgument(argName))
222+
.map(GraphQLAppliedDirectiveArgument::<Number>getValue)
223+
.map(Number::intValue);
218224
}
219225

220226
/**

src/main/java/graphql/validation/constraints/standard/DigitsConstraint.java

+28-15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.math.BigDecimal;
1111
import java.util.Collections;
1212
import java.util.List;
13+
import java.util.Optional;
1314

1415
import static graphql.validation.constraints.GraphQLScalars.GRAPHQL_NUMBER_AND_STRING_TYPES;
1516

@@ -21,14 +22,14 @@ public DigitsConstraint() {
2122
@Override
2223
public Documentation getDocumentation() {
2324
return Documentation.newDocumentation()
24-
.messageTemplate(getMessageTemplate())
25-
.description("The element must be a number inside the specified `integer` and `fraction` range.")
26-
.example("buyCar( carCost : Float @Digits(integer : 5, fraction : 2) : DriverDetails")
27-
.applicableTypes(GRAPHQL_NUMBER_AND_STRING_TYPES)
28-
.directiveSDL("directive @Digits(integer : Int!, fraction : Int!, message : String = \"%s\") " +
29-
"on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION",
30-
getMessageTemplate())
31-
.build();
25+
.messageTemplate(getMessageTemplate())
26+
.description("The element must be a number inside the specified `integer` and optionally inside `fraction` range.")
27+
.example("buyCar( carCost : Float @Digits(integer : 5, fraction : 2) : DriverDetails")
28+
.applicableTypes(GRAPHQL_NUMBER_AND_STRING_TYPES)
29+
.directiveSDL("directive @Digits(integer : Int!, fraction : Int, message : String = \"%s\") " +
30+
"on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION",
31+
getMessageTemplate())
32+
.build();
3233
}
3334

3435
@Override
@@ -43,28 +44,40 @@ protected List<GraphQLError> runConstraint(ValidationEnvironment validationEnvir
4344

4445
GraphQLAppliedDirective directive = validationEnvironment.getContextObject(GraphQLAppliedDirective.class);
4546
int maxIntegerLength = getIntArg(directive, "integer");
46-
int maxFractionLength = getIntArg(directive, "fraction");
47+
Optional<Integer> maxFractionLengthOpt = getIntArgOpt(directive, "fraction");
4748

4849
boolean isOk;
4950
try {
5051
BigDecimal bigNum = asBigDecimal(validatedValue);
51-
isOk = isOk(bigNum, maxIntegerLength, maxFractionLength);
52+
boolean isFractionPartOk = maxFractionLengthOpt
53+
.map(maxFractionLength -> isFractionPartOk(bigNum, maxFractionLength))
54+
.orElse(true);
55+
56+
isOk = isFractionPartOk && isIntegerPartOk(bigNum, maxIntegerLength);
5257
} catch (NumberFormatException e) {
5358
isOk = false;
5459
}
5560

5661
if (!isOk) {
57-
return mkError(validationEnvironment, "integer", maxIntegerLength, "fraction", maxFractionLength);
62+
return mkError(
63+
validationEnvironment,
64+
"integer",
65+
maxIntegerLength, "fraction",
66+
maxFractionLengthOpt.map(Object::toString).orElse("unlimited")
67+
);
5868
}
5969

6070
return Collections.emptyList();
6171
}
6272

63-
private boolean isOk(BigDecimal bigNum, int maxIntegerLength, int maxFractionLength) {
64-
int integerPartLength = bigNum.precision() - bigNum.scale();
65-
int fractionPartLength = Math.max(bigNum.scale(), 0);
73+
private static boolean isIntegerPartOk(BigDecimal bigNum, int maxIntegerLength) {
74+
final int integerPartLength = bigNum.precision() - bigNum.scale();
75+
return maxIntegerLength >= integerPartLength;
76+
}
6677

67-
return maxIntegerLength >= integerPartLength && maxFractionLength >= fractionPartLength;
78+
private static boolean isFractionPartOk(BigDecimal bigNum, int maxFractionLength) {
79+
final int fractionPartLength = Math.max(bigNum.scale(), 0);
80+
return maxFractionLength >= fractionPartLength;
6881
}
6982

7083
@Override

src/test/groovy/graphql/validation/constraints/standard/DigitsConstraintTest.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class DigitsConstraintTest extends BaseConstraintTestSupport {
2323
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | null | ''
2424
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | Byte.valueOf("0") | ''
2525
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | Double.valueOf("500.2") | ''
26+
'field( arg : String @Digits(integer : 5) ) : ID' | Double.valueOf("500.2345678") | ''
2627

2728
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | new BigDecimal("-12345.12") | ''
2829
'field( arg : String @Digits(integer : 5, fraction : 2) ) : ID' | new BigDecimal("-123456.12") | 'Digits;path=/arg;val:-123456.12;\t'

0 commit comments

Comments
 (0)