- Notifications
You must be signed in to change notification settings - Fork35
Validation library for graphql-java input
License
graphql-java/graphql-java-extended-validation
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This library provides extended validation of fields and field arguments forgraphql-java
<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-extended-validation</artifactId> <version>24.0</version></dependency>
implementation'com.graphql-java:graphql-java-extended-validation:24.0'Note:
use 24.0 or above for graphql-java 24.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
The library is currently available on Maven Central.
This library provides a series of directives that can be applied to field arguments and input type fields which willconstrain their allowable values.
These names and semantics are inspired from the javax.validation annotations
https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/package-summary.html
You can add these onto arguments or input types in your graphql SDL.
For example
# # this declares the directive as being possible on arguments and input fields #directive@Size(min : Int = 0, max : Int = 2147483647, message : String ="graphql.validation.Size.message")onARGUMENT_DEFINITION |INPUT_FIELD_DEFINITIONinputApplication { name : String @Size(min : 3, max : 100) }typeQuery {hired (applications : [Application!] @Size(max : 10)) : [Boolean] }
In the example above, we have aapplications argument that takes at most 10 applications and within eachApplication input object,thename field must be at least 3 characters long and no more than 100 characters long to be considered valid.
The@Expression validation directive allows Java EL to be used to help build validation rules.
Java EL is a powerful expression syntax for expressing validation conditions.
Some simple sample Java EL expressions might be :
| EL Expression | Result |
|---|---|
${1> (4/2)} | false |
${4.0>= 3} | true |
${100.0 == 100} | true |
${(10*10) ne 100} | false |
${'a' > 'b'} | false |
${'hip' lt 'hit'} | true |
${4> 3} | true |
${1.2E4 + 1.4} | 12001.4 |
${3 div 4} | 0.75 |
${10 mod 4} | 2 |
${((x, y) → x + y)(3, 5.5)} | 8.5 |
[1,2,3,4].stream().sum() | 10 |
[1,3,5,2].stream().sorted().toList() | [1, 2, 3, 5] |
The following validation variables are made available to you :
| Name | Value |
|---|---|
validatedValue | The value being validated |
gqlField | TheGraphQLFieldDefinition being validated |
gqlFieldContainer | TheGraphQLFieldsContainer parent type containing that field |
gqlArgument | TheGraphQLArgument being validated. This can be null for field level validation |
arguments | The map of all argument values for the current field |
args | A short hand name for the map of all argument values for the current field |
The Java EL expression MUST evaluate to a boolean value to be useful in the@Expresion directive.
See here fora more complete overview of Java EL
The validation code uses agraphql.validation.interpolation.MessageInterpolator interface to build out error messages. A defaultResourceBundleMessageInterpolator class is provided to load error messages from Java resource bundles to allow internationalised messages (I18N)
You can use Java EL syntax in the message templates to format even more powerful error messages.
The field ${gqlField.name} has the following invalid value : ${formatter.format('%1$.2f', validatedValue)}If you use directive arguments likemessage : String = "graphql.validation.Size.message" then theResourceBundleMessageInterpolator classwill use that as a resource bundle lookup key. This too is inspired by the javax.validation annotations and how they work.
Like javax.validation, this library ships with some default error message templates but you can override them.
The validation library aims to offer Internationalisation (18N) of the error messages. When the validation rulesrun they are passed in ajava.util.Locale. AResourceBundleMessageInterpolator can then be used to build up messagesthat come from I18N bundles.
ALocale should be created per graphql execution, and can be passed toExecutionInput. More i18n is being added to graphql-javaand later this library will then be updated to to take advantage of i18n.
In the meantime you can work around this by having thecontext,source orroot implementgraphql.validation.locale.LocaleProvider andthe library will extract aLocale from that.
If you are using graphql SDL to build your graphql schema then you can use aValidationSchemaWiring class to automaticallychange your field data fetchers so that validation rules are called before the field data is fetched.
This allows you to automatically enhance your schema with validation by directives alone.
The following shows how to setup the SDL generation so that the build schema will have validation in place.
//// This contains by default the standard library provided @Directive constraints//ValidationRulesvalidationRules =ValidationRules.newValidationRules() .onValidationErrorStrategy(OnValidationErrorStrategy.RETURN_NULL) .build();//// This will rewrite your data fetchers when rules apply to them so that validationValidationSchemaWiringschemaWiring =newValidationSchemaWiring(validationRules);//// we add this schema wiring to the graphql runtimeRuntimeWiringruntimeWiring =RuntimeWiring.newRuntimeWiring().directiveWiring(schemaWiring).build();//// then pretty much standard graphql-java code to build a graphql schemaReadersdl =buildSDL();TypeDefinitionRegistrytypeDefinitionRegistry =newSchemaParser().parse(sdl);GraphQLSchemagraphQLSchema =newSchemaGenerator().makeExecutableSchema(typeDefinitionRegistry,runtimeWiring);
Under the coversValidationSchemaWiring asks each possible rule if it applies to a field as they are encountered (at schema build time).
If they do apply then it rewrites the DataFetcher so that it first calls the validation code and produces errors if the field input is notconsidered valid.
The default strategyOnValidationErrorStrategy.RETURN_NULL will return null for the field input if it is not considered valid. You canwrite your own strategy if you want.
We recommend that you use the SDL schema directive wiring and @directives for the easiest way to get input type validation.
However there can be reasons why you cant use this approach and you have use the API directly in your data fetching code.
//// an example of writing your own custom validation rule//ValidationRulemyCustomValidationRule =newValidationRule() {@OverridepublicbooleanappliesTo(GraphQLFieldDefinitionfieldDefinition,GraphQLFieldsContainerfieldsContainer) {returnfieldDefinition.getName().equals("decide whether this rule applies here"); }@OverridepublicbooleanappliesTo(GraphQLArgumentargument,GraphQLFieldDefinitionfieldDefinition,GraphQLFieldsContainerfieldsContainer) {returnargument.getName().equals("decide whether this rule applies here to an argument"); }@OverridepublicList<GraphQLError>runValidation(ValidationEnvironmentvalidationEnvironment) {List<GraphQLError>errors =newArrayList<>();Map<String,Object>argumentValues =validationEnvironment.getArgumentValues();for (StringargName :argumentValues.keySet()) {ObjectargValue =argumentValues.get(argName);GraphQLErrorerror =runCodeThatValidatesInputHere(validationEnvironment,argName,argValue);if (error !=null) {errors.add(error); } }returnerrors; } };DataFetcherdataFetcher =newDataFetcher() {@OverridepublicObjectget(DataFetchingEnvironmentenv) {//// By default the ValidationRule contains the SDL @directive rules, but// you can also add your own as we do here.//ValidationRulesvalidationRules =ValidationRules .newValidationRules() .locale(Locale.getDefault()) .addRule(myCustomValidationRule) .build();//// The expected strategy is to return null data and the errors if there are any validation// problems//List<GraphQLError>errors =validationRules.runValidationRules(env);if (!errors.isEmpty()) {returnDataFetcherResult.newResult().errors(errors).data(null).build(); }returnnormalDataFetchingCodeRunsNow(env); } };
The above code shows a custom validation rule (with nonsense logic for demonstration purposes) and then a data fetcherthat uses theValidationRules API to run validation rules.
The boolean value must be false.
Example :
updateDriver( isDrunk : Boolean @AssertFalse) : DriverDetailsApplies to :
Boolean,ListsSDL :
directive @AssertFalse(message : String = "graphql.validation.AssertFalse.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.AssertFalse.message
The boolean value must be true.
Example :
driveCar( hasLicence : Boolean @AssertTrue) : DriverDetailsApplies to :
Boolean,ListsSDL :
directive @AssertTrue(message : String = "graphql.validation.AssertTrue.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.AssertTrue.message
The element must be a number whose value must be less than or equal to the specified maximum.
Example :
driveCar( bloodAlcoholLevel : Float @DecimalMax(value : "0.05") : DriverDetailsApplies to :
String,Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @DecimalMax(value : String!, inclusive : Boolean! = true, message : String = "graphql.validation.DecimalMax.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.DecimalMax.message
The element must be a number whose value must be greater than or equal to the specified minimum.
Example :
driveCar( carHorsePower : Float @DecimalMin(value : "300.50") : DriverDetailsApplies to :
String,Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @DecimalMin(value : String!, inclusive : Boolean! = true, message : String = "graphql.validation.DecimalMin.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.DecimalMin.message
The element must be a number inside the specifiedinteger andfraction range.
Example :
buyCar( carCost : Float @Digits(integer : 5, fraction : 2) : DriverDetailsApplies to :
String,Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Digits(integer : Int!, fraction : Int, message : String = "graphql.validation.Digits.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Digits.message
The provided expression must evaluate to true. The expression language isJava EL and expressions MUST resolve to a boolean value, ie. it is valid or not.
Example :
drivers( first : Int, after : String!, last : Int, before : String) : DriverConnection @Expression(value : "${args.containsOneOf('first','last') }"Applies to :
All Types and ScalarsSDL :
directive @Expression(value : String!, message : String = "graphql.validation.Expression.message") on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Expression.message
The element must be a number whose value must be less than or equal to the specified maximum.
Example :
driveCar( horsePower : Float @Max(value : 1000) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Max(value : Int! = 2147483647, message : String = "graphql.validation.Max.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Max.message
The element must be a number whose value must be greater than or equal to the specified minimum.
Example :
driveCar( age : Int @Min(value : 18) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Min(value : Int! = 0, message : String = "graphql.validation.Min.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Min.message
The element must be a negative number.
Example :
driveCar( lostLicencePoints : Int @Negative) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Negative(message : String = "graphql.validation.Negative.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Negative.message
The element must be a negative number or zero.
Example :
driveCar( lostLicencePoints : Int @NegativeOrZero) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @NegativeOrZero(message : String = "graphql.validation.NegativeOrZero.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.NegativeOrZero.message
The String must contain at least one non-whitespace character, according to Java's Character.isWhitespace().
Example :
updateAccident( accidentNotes : String @NotBlank) : DriverDetailsApplies to :
String,ID,ListsSDL :
directive @NotBlank(message : String = "graphql.validation.NotBlank.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.NotBlank.message
The element must have a non-zero size.
Example :
updateAccident( accidentNotes : String! @NotEmpty) : DriverDetailsApplies to :
String,ID,ListsSDL :
directive @NotEmpty(message : String = "graphql.validation.NotEmpty.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.NotEmpty.message
The list or input object must have a non-zero size.
Example :
updateAccident( accidentNotes : [Notes]! @ContainerNotEmpty) : DriverDetailsApplies to :
Lists,Input ObjectsSDL :
directive @ContainerNotEmpty(message : String = "graphql.validation.ContainerNotEmpty.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.ContainerNotEmpty.message
The String must match the specified regular expression, which follows the Java regular expression conventions.
Example :
updateDriver( licencePlate : String @Pattern(regexp : "[A-Z][A-Z][A-Z]-[0-9][0-9][0-9]") : DriverDetailsApplies to :
String,ID,ListsSDL :
directive @Pattern(regexp : String! =".*", message : String = "graphql.validation.Pattern.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Pattern.message
The element must be a positive number.
Example :
driver( licencePoints : Int @Positive) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Positive(message : String = "graphql.validation.Positive.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Positive.message
The element must be a positive number or zero.
Example :
driver( licencePoints : Int @PositiveOrZero) : DriverDetailsApplies to :
Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @PositiveOrZero(message : String = "graphql.validation.PositiveOrZero.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.PositiveOrZero.message
The element range must be between the specifiedmin andmax boundaries (inclusive). It accepts numbers and strings that represent numerical values.
Example :
driver( milesTravelled : Int @Range( min : 1000, max : 100000)) : DriverDetailsApplies to :
String,Byte,Short,Int,Long,BigDecimal,BigInteger,Float,ListsSDL :
directive @Range(min : Int = 0, max : Int = 2147483647, message : String = "graphql.validation.Range.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Range.message
The string's size must be between the specifiedmin andmax boundaries (inclusive).
Example :
updateDrivingNotes( drivingNote : String @Size( min : 1000, max : 100000)) : DriverDetailsApplies to :
String,ID,ListsSDL :
directive @Size(min : Int = 0, max : Int = 2147483647, message : String = "graphql.validation.Size.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.Size.message
The list's or input object's size must be between the specifiedmin andmax boundaries (inclusive).
Example :
updateDrivingNotes( drivingNote : [String!]! @ContainerSize( min : 10, max : 20)) : DriverDetailsApplies to :
Lists,Input ObjectsSDL :
directive @ContainerSize(min : Int = 0, max : Int = 2147483647, message : String = "graphql.validation.ContainerSize.message") on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITIONMessage :
graphql.validation.ContainerSize.message
About
Validation library for graphql-java input
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.