Skip to content

Commit a69e401

Browse files
author
Soojung Ha
committed
Add option to specify a validator class map in RestLiDataValidator.
RB=631279 G=si-dev R=nshankar,kvidhani A=nshankar,kvidhani
1 parent 1121028 commit a69e401

File tree

5 files changed

+113
-5
lines changed

5 files changed

+113
-5
lines changed

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
5.0.7
2+
-----
3+
14
5.0.6
25
-----
36
(RB=637091)
47
Modify multipart mime streaming test to avoid transient failures
58

9+
(RB=631279)
10+
Add option to specify a validator class map in RestLiDataValidator
611

712
5.0.5
813
-----

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=5.0.5
1+
version=5.0.6
22
sonatypeUsername=please_set_in_home_dir_if_uploading_to_maven_central
33
sonatypePassword=please_set_in_home_dir_if_uploading_to_maven_central
44

restli-common/src/main/java/com/linkedin/restli/common/validation/RestLiDataValidator.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.linkedin.data.schema.validation.ValidationOptions;
3636
import com.linkedin.data.schema.validation.ValidationResult;
3737
import com.linkedin.data.schema.validator.DataSchemaAnnotationValidator;
38+
import com.linkedin.data.schema.validator.Validator;
3839
import com.linkedin.data.schema.validator.ValidatorContext;
3940
import com.linkedin.data.template.DataTemplate;
4041
import com.linkedin.data.template.DataTemplateUtil;
@@ -52,6 +53,7 @@
5253
import java.util.ArrayList;
5354
import java.util.Arrays;
5455
import java.util.Collection;
56+
import java.util.Collections;
5557
import java.util.HashMap;
5658
import java.util.HashSet;
5759
import java.util.List;
@@ -112,6 +114,8 @@ public class RestLiDataValidator
112114
private final Predicate _createOnlyDescendantPredicate;
113115
private final Class<? extends RecordTemplate> _valueClass;
114116
private final ResourceMethod _resourceMethod;
117+
// To be passed into DataSchemaAnnotationValidator.
118+
private final Map<String, Class<? extends Validator>> _validatorClassMap;
115119

116120
private static final String INSTANTIATION_ERROR = "InstantiationException while trying to instantiate the record template class";
117121
private static final String ILLEGAL_ACCESS_ERROR = "IllegalAccessException while trying to instantiate the record template class";
@@ -177,7 +181,37 @@ else if (annotation.annotationType() == CreateOnly.class)
177181
*/
178182
public RestLiDataValidator(Annotation[] annotations, Class<? extends RecordTemplate> valueClass, ResourceMethod resourceMethod)
179183
{
180-
this(annotationsToMap(annotations), valueClass, resourceMethod);
184+
this(annotations, valueClass, resourceMethod, Collections.emptyMap());
185+
}
186+
187+
/**
188+
* Constructor.
189+
*
190+
* @param annotations annotations on the resource class
191+
* @param valueClass class of the record template
192+
* @param resourceMethod resource method type
193+
* @param validatorClassMap custom validator class map (see {@link #RestLiDataValidator(Map, Class, ResourceMethod, Map)} for explanation)
194+
*/
195+
public RestLiDataValidator(Annotation[] annotations,
196+
Class<? extends RecordTemplate> valueClass,
197+
ResourceMethod resourceMethod,
198+
Map<String, Class<? extends Validator>> validatorClassMap)
199+
{
200+
this(annotationsToMap(annotations), valueClass, resourceMethod, validatorClassMap);
201+
}
202+
203+
/**
204+
* Constructor.
205+
*
206+
* @param annotations map from annotation name to annotation values
207+
* @param valueClass class of the record template
208+
* @param resourceMethod resource method type
209+
*/
210+
public RestLiDataValidator(Map<String, List<String>> annotations,
211+
Class<? extends RecordTemplate> valueClass,
212+
ResourceMethod resourceMethod)
213+
{
214+
this(annotations, valueClass, resourceMethod, Collections.emptyMap());
181215
}
182216

183217
/**
@@ -186,8 +220,14 @@ public RestLiDataValidator(Annotation[] annotations, Class<? extends RecordTempl
186220
* @param annotations map from annotation name to annotation values
187221
* @param valueClass class of the record template
188222
* @param resourceMethod resource method type
223+
* @param validatorClassMap custom validator class map, with keys as "validate" property keys
224+
* (See {@link DataSchemaAnnotationValidator}) and values as validator class types
225+
* (e.g. "strlen" as key and StrlenValidator.class as value)
189226
*/
190-
public RestLiDataValidator(Map<String, List<String>> annotations, Class<? extends RecordTemplate> valueClass, ResourceMethod resourceMethod)
227+
public RestLiDataValidator(Map<String, List<String>> annotations,
228+
Class<? extends RecordTemplate> valueClass,
229+
ResourceMethod resourceMethod,
230+
Map<String, Class<? extends Validator>> validatorClassMap)
191231
{
192232
List<Predicate> readOnly = new ArrayList<Predicate>();
193233
List<Predicate> createOnly = new ArrayList<Predicate>();
@@ -224,13 +264,14 @@ else if (annotationName.equals(CreateOnly.class.getAnnotation(RestSpecAnnotation
224264
_createOnlyDescendantPredicate = Predicates.or(createOnlyDescendant);
225265
_valueClass = valueClass;
226266
_resourceMethod = resourceMethod;
267+
_validatorClassMap = Collections.unmodifiableMap(validatorClassMap);
227268
}
228269

229270
private class DataValidator extends DataSchemaAnnotationValidator
230271
{
231272
private DataValidator(DataSchema schema)
232273
{
233-
super(schema);
274+
super(schema, _validatorClassMap);
234275
}
235276

236277
@Override

restli-int-test-api/src/main/pegasus/com/linkedin/restli/examples/greetings/api/ValidationDemo.pdsc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
{
2626
"name": "intB",
2727
"type": "int",
28-
"optional": true
28+
"optional": true,
29+
"validate": {
30+
"seven": {}
31+
}
2932
},
3033
{
3134
"name": "UnionFieldWithInlineRecord",

restli-int-test/src/test/java/com/linkedin/restli/examples/TestRestLiValidation.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
package com.linkedin.restli.examples;
1818

1919

20+
import com.linkedin.data.DataMap;
21+
import com.linkedin.data.element.DataElement;
22+
import com.linkedin.data.message.Message;
23+
import com.linkedin.data.schema.validation.ValidationResult;
24+
import com.linkedin.data.schema.validator.AbstractValidator;
25+
import com.linkedin.data.schema.validator.Validator;
26+
import com.linkedin.data.schema.validator.ValidatorContext;
2027
import com.linkedin.data.transform.DataProcessingException;
2128
import com.linkedin.r2.RemoteInvocationException;
2229
import com.linkedin.restli.client.BatchGetEntityRequest;
@@ -37,7 +44,9 @@
3744
import com.linkedin.restli.common.HttpStatus;
3845
import com.linkedin.restli.common.IdResponse;
3946
import com.linkedin.restli.common.PatchRequest;
47+
import com.linkedin.restli.common.ResourceMethod;
4048
import com.linkedin.restli.common.UpdateStatus;
49+
import com.linkedin.restli.common.validation.RestLiDataValidator;
4150
import com.linkedin.restli.examples.greetings.api.Greeting;
4251
import com.linkedin.restli.examples.greetings.api.GreetingMap;
4352
import com.linkedin.restli.examples.greetings.api.MyItemArray;
@@ -755,4 +764,54 @@ public void testFinderWithException(Object builder) throws RemoteInvocationExcep
755764
Assert.assertEquals(e.getStatus(), HttpStatus.S_400_BAD_REQUEST.getCode());
756765
}
757766
}
767+
768+
@Test
769+
public void testCustomValidatorMap()
770+
{
771+
// Provide Rest.li annotations manually since the validator is not called from the server or through generated request builders.
772+
Map<String, List<String>> annotations = new HashMap<>();
773+
annotations.put("createOnly", Arrays.asList("stringB", "intB", "UnionFieldWithInlineRecord/com.linkedin.restli.examples.greetings.api.myRecord/foo2", "MapWithTyperefs/*/id"));
774+
annotations.put("readOnly", Arrays.asList("stringA", "intA", "UnionFieldWithInlineRecord/com.linkedin.restli.examples.greetings.api.myRecord/foo1", "ArrayWithInlineRecord/*/bar1", "validationDemoNext/stringB", "validationDemoNext/UnionFieldWithInlineRecord"));
775+
776+
// Invalid entity, because intB is not a multiple of seven.
777+
ValidationDemo.UnionFieldWithInlineRecord unionField1 = new ValidationDemo.UnionFieldWithInlineRecord();
778+
unionField1.setMyEnum(myEnum.FOOFOO);
779+
ValidationDemo entity = new ValidationDemo().setIntB(24).setStringB("some string").setUnionFieldWithInlineRecord(unionField1);
780+
781+
// Validate without the class map
782+
RestLiDataValidator validator = new RestLiDataValidator(annotations, ValidationDemo.class, ResourceMethod.CREATE);
783+
ValidationResult result = validator.validateInput(entity);
784+
Assert.assertTrue(result.isValid());
785+
786+
// Validate with the class map
787+
Map<String, Class<? extends Validator>> validatorClassMap = new HashMap<>();
788+
validatorClassMap.put("seven", SevenValidator.class);
789+
validator = new RestLiDataValidator(annotations, ValidationDemo.class, ResourceMethod.CREATE, validatorClassMap);
790+
result = validator.validateInput(entity);
791+
Assert.assertFalse(result.isValid());
792+
Assert.assertEquals(result.getMessages().size(), 1);
793+
for (Message message : result.getMessages())
794+
{
795+
Assert.assertTrue(message.toString().contains("24 is not a multiple of seven"));
796+
}
797+
}
798+
799+
public static class SevenValidator extends AbstractValidator
800+
{
801+
public SevenValidator(DataMap config)
802+
{
803+
super(config);
804+
}
805+
806+
@Override
807+
public void validate(ValidatorContext context)
808+
{
809+
DataElement element = context.dataElement();
810+
Integer value = (Integer) element.getValue();
811+
if (value % 7 != 0)
812+
{
813+
context.addResult(new Message(element.path(), "%d is not a multiple of seven", value));
814+
}
815+
}
816+
}
758817
}

0 commit comments

Comments
 (0)