GroupSequence and ordered evaluation in JSR 303 - validation

In our application we have such a case:
Constraints should be evaluated in particular order. (cheap to expensive)
Constraints should not be evaluated after a violation per field.
All fields should be validated.
For first two, groupsequence is fitting very good. However for my 3rd requirement I could not find a way to solve.
public class AccountBean {
#CheepValidation
#ExpensiveValidation
#VeryExpensiveValidation
private String name;
#CheepValidation
#ExpensiveValidation
#VeryExpensiveValidation
private String surname
}
For example,
Let's say that, for name field VeryExpensiveValidationconstraint is violated and for surname field ExpensiveValidation constraint is violated.
For this case I should display:
For field name: Only VeryExpensiveValidation error message
For field surname: Only ExpensiveValidation error message
Note that for field surname we did not evaluate VeryExpensiveValidation constraint.
Is there a way to implement it with JSR 303?
Thanks

You can use groups and #GroupSequence, but it's a bit unwieldy.
public class AccountBean {
#CheapValidation(groups=Name1.class)
#ExpensiveValidation(groups=Name2.class)
#VeryExpensiveValidation(groups=Name3.class)
String name;
#CheapValidation(groups=Surname1.class)
#ExpensiveValidation(groups=Surname2.class)
#VeryExpensiveValidation(groups=Surname3.class)
String surname;
public interface Name1 {}
public interface Name2 {}
public interface Name3 {}
#GroupSequence({Name1.class, Name2.class, Name3.class})
public interface Name {}
public interface Surname1 {}
public interface Surname2 {}
public interface Surname3 {}
#GroupSequence({Surname1.class, Surname2.class, Surname3.class})
public interface Surname {}
}
Then validate with:
validator.validate(myAccountBean,
AccountBean.Name.class, AccountBean.Surname.class)
The key is to have two entirely independent group sequences.
Unfortunately, it seems you must explicitly list the groups for all the fields you want to validate. I wasn't able to get it working with a 'default' #GroupSequence. Can anyone improve on this?

I've implemented ordered validation with GroupSequence but, generally speaking, GroupSequence beans validation implementation is not transparent.
Meaning, untill first group is fully validated, you can not trigger the validation of the second group.
E.g.
I have 3 validated fields with custom validators. The idea is pretty straightforward: every field should be validated with the set of validators from top to bottom independently (descending cardinality).
#StringPropertyNotNullOrEmptyConstraint(message = "Group name is required", groups = {ValidationStep1.class})
private final StringProperty groupName;
#StringPropertyNotNullOrEmptyConstraint(message = "Group password is required", groups = {ValidationStep1.class})
#StringPropertyMatchConstraint(message = "The given password phrases do not match", dependentProperties = {"groupPasswordMatch"}, groups = {ValidationStep2.class})
private final StringProperty groupPassword;
#StringPropertyNotNullOrEmptyConstraint(message = "Group password match is required", groups = {ValidationStep1.class})
#StringPropertyMatchConstraint(message = "The given passwords phrases do not match", dependentProperties = {"groupPassword"}, groups = {ValidationStep2.class})
private final StringProperty groupPasswordMatch;
public interface ValidationStep1 {
}
public interface ValidationStep2 {
}
#GroupSequence({GroupDialogModel.class, ValidationStep1.class, ValidationStep2.class})
public interface GroupDialogModelValidationSequence {
}
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<GroupDialogModel>> constraintViolations = validator.validate(this, GroupDialogModelValidationSequence.class);
The caveat of this approach is that each field should go through ValidationStep1 first and only after each validation of step 1 succeeds it goes to step 2. For example, even if password fields are not empty, but contain different values, validation for them succeeds if group name field does not contain any value. And only after I enter some value to the group name, ValidationStep1 group succeeds and then it displays validation result of ValidationStep2 (passwords do not match).
Making each group for each field in every sequence is bad practice IMHO, but it seems like there is no other choice.
Any other solution is much appreciated.

Related

Bean validation - validate optional fields

Given a class that represents payload submitted from a form, I want to apply bean validation to a field that may or may not be present, for example:
class FormData {
#Pattern(...)
#Size(...)
#Whatever(...)
private String optionalField;
...
}
If optionalField is not sent in the payload, I don't want to apply any of the validators above, but if it is sent, I want to apply all of them. How can it be done?
Thanks.
So usually all of these constraints consider null value as valid. If your optional filed is null when it's not part of the payload all should work just fine as it is.
And for any mandatory fields you can put #NotNull on them.
EDIT
here's an example:
class FormData {
#Pattern(regexp = "\\d+")
#Size(min = 3, max = 3)
private final String optionalField;
#Pattern(regexp = "[a-z]+")
#Size(min = 3, max = 3)
#NotNull
private final String mandatoryField;
}
#Test
public void test() {
Validator validator = getValidator();
// optonal field is null so no violations will rise on it
FormData data = new FormData( null, "abc" );
Set<ConstraintViolation<FormData>> violations = validator.validate( data );
assertThat( violations ).isEmpty();
// optional field is present but it should fail the pattern validation:
data = new FormData( "aaa", "abc" );
violations = validator.validate( data );
assertThat( violations ).containsOnlyViolations(
violationOf( Pattern.class ).withProperty( "optionalField" )
);
}
You can see that in the first case you don't get any violations as the optional field is null. but in the second exmaple you receive a violation of pattern constraint as aaa is not a string of digits.

Dealing with m-to-n relations in #RepositoryRestResource

Preface
I want to create a sub-resource of another resource in one call. These resources have a #ManyToMany relationship: Users and Groups.
I do not want to create first a user, then the group and after that the relation as it is shown in Working with Relationships in Spring Data REST - simply because I think a resource that cannot exist on its own, such as a group, should only be created if at least one user is also associated with that resource. For this I require a single endpoint like this one (which is not working for me, otherwise I wouldn't be here) that creates a group and also sets the associated "seeding" user in one transaction.
Currently, the only way to make this work for me is to "synchronize" the relation manually like this:
public void setUsers(Set<AppUser> users) {
users.forEach(u -> u.getGroups().add(this));
this.users = users;
}
this would allow me to
POST http://localhost:8080/groups
{
"name": "Group X",
"users": ["http://localhost:8080/users/1"]
}
but my problem with that is that this does not feel right to me - it does seem like a workaround and not the actual Spring-way to make this requirement work. So ..
I'm currently struggling with creating relational resources using Spring's #RepositoryRestResource. I want to create a new group and associate it with the calling user like this:
POST http://localhost:8080/users/1/groups
{
"name": "Group X"
}
but the only result is the response 204 No Content. I have no idea why. This may or may not be related to another question of mine (see here) where I try to achieve the same by setting the relating resource in the JSON payload - that doesn't work either.
Server side I am getting the following error:
tion$ResourceSupportHttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class org.springframework.hateoas.Resources<java.lang.Object>]]: java.lang.NullPointerException
Please let me know in case you need any specific code.
Tried
I added exported = false to the #RepositoryRestResource of UserGroupRepository:
#RepositoryRestResource(collectionResourceRel = "groups", path = "groups", exported = false)
public interface UserGroupRepository extends JpaRepository<UserGroup, Long> {
List<UserGroup> findByName(#Param("name") String name);
}
and I am sending:
PATCH http://localhost:8080/users/1
{
"groups": [
{
"name": "Group X"
}
]
}
However, the result is still just 204 No Content and a ResourceNotFoundException on the server side.
Unit Test
Essentially, the following unit test is supposed to work but I can also live with an answer why this cannot work and which also shows how this is done correctly.
#Autowired
private TestRestTemplate template;
private static String USERS_ENDPOINT = "http://localhost:8080/users/";
private static String GROUPS_ENDPOINT = "http://localhost:8080/groups/";
// ..
#Test
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public void whenCreateUserGroup() {
// Creates a user
whenCreateAppUser();
ResponseEntity<AppUser> appUserResponse = template.getForEntity(USERS_ENDPOINT + "1/", AppUser.class);
AppUser appUser = appUserResponse.getBody();
UserGroup userGroup = new UserGroup();
userGroup.setName("Test Group");
userGroup.setUsers(Collections.singleton(appUser));
template.postForEntity(GROUPS_ENDPOINT, userGroup, UserGroup.class);
ResponseEntity<UserGroup> userGroupResponse = template.getForEntity(GROUPS_ENDPOINT + "2/", UserGroup.class);
Predicate<String> username = other -> appUser.getUsername().equals(other);
assertNotNull("Response must not be null.", userGroupResponse.getBody());
assertTrue("User was not associated with the group he created.",
userGroupResponse.getBody().getUsers().stream()
.map(AppUser::getUsername).anyMatch(username));
}
However, the line
userGroup.setUsers(Collections.singleton(appUser));
will break this test and return a 404 Bad Request.
According to SDR reference:
POST
Only supported for collection associations. Adds a new element to the collection. Supported media types:
text/uri-list - URIs pointing to the resource to add to the association.
So to add group to user try to do this:
POST http://localhost:8080/users/1/groups (with Content-Type:text/uri-list)
http://localhost:8080/groups/1
Additional info.

How are shared/placed the int of the ProtoMember/ProtoInclude in ProtoBuf?

I've several questions on how/where the ID of a [ProtoContract] should be declared.
Imagine the following code:
[ProtoContract]
[ProtoInclude(100, typeof(SomeClassA))]//1) CAN I USE 1 here?
public abstract class RootClass{
[ProtoMember(1)]
public int NodeId {get;set;}
}
[ProtoContract]
[ProtoInclude(200, typeof(SomeClassC)]//2) Should I declare this here or directly on the RootClass?
//3) Can I use the id 100 here?
//4) Can I use the id 1 here? or member + include share the id?
public class SomeClassA : RootClass{
[ProtoMember(1)]//5) CAN I USE 1 here? Since the parent already use it but it's a different class
public String Name{get;set;}
}
[ProtoContract]
public class SomeClassC : SomeClassA {
[ProtoMember(2)]
public int Count{get;set;}
}
[ProtoContract]
public class SomeClassD : SomeClassA {
[ProtoMember(2)] //6) Can I use 2 here? Since SomeClassC already use it and is a sibling?
public int Count{get;set;}
}
I've put several number with questions:
CAN I USE 1 here?
Should I declare this here or directly on the RootClass?
Can I use the id 100 here?
Can I use the id 1 here? or member + include share the id?
CAN I USE 1 here? Since the parent already use it but it's a different class
Can I use 2 here? Since SomeClassC already use it and is a sibling?
The thing is that we have a huge model with a lot of classes, which all herits from the same object, so I'm trying to figure out to which ID I should take care.
Short version:
the set of field numbers for a type is the union of the numbers defined against members (fields and properties), and the numbers defined for immediate subtypes (includes)
the set of field numbers must be unique within that single type - it is not required to consider base types or derived types
Longer version:
The reason for this is that subtypes are essentially mapped as optional fields:
[ProtoContract]
[ProtoInclude(100, typeof(SomeClassA))]
public abstract class RootClass{
[ProtoMember(1)]
public int NodeId {get;set;}
}
[ProtoContract]
[ProtoInclude(200, typeof(SomeClassC)]
public class SomeClassA : RootClass{
[ProtoMember(1)]
public String Name{get;set;}
}
[ProtoContract]
public class SomeClassC : SomeClassA {
[ProtoMember(2)]
public int Count{get;set;}
}
is, in terms of proto2 syntax:
message RootClass {
optional int32 NodeId = 1;
optional SomeClassA _notNamed = 100;
}
message SomeClassA {
optional string Name = 1;
optional SomeClassC _notNamed = 200;
}
message SomeClassC {
optional int32 Count = 2;
}
Note that at most 1 sub-type field will be used, so it can be considered oneof for the purposes of .proto. Any fields relating to the sub-type will be included in message SomeClassA, so there is no conflict with RootClass and they do not need to be unique. The numbers only need to be unique per message in the .proto sense.
To take the specific questions, then:
no, because that would conflict with NodeId
it should be declared on SomeClassA; protobuf-net is only expecting immediate descendants, and it keeps the numbering consistent and conveniently readable, since the field number is only required to not conflict with the members of SomeClassA
yes you can; there is no conflict
no, because that would conflict with Name
yes you can; there is no conflict
yes you can; there is no conflict - although actually protobuf-net won't even think of SomeClassD as a sibling anyway (it isn't advertised anywhere as an include) - but if there was a [ProtoInclude(201, typeof(SomeClassD))] on SomeClassA, then it would be fine. This would change our .proto to add:
optional SomeClassD _alsoNotNamed = 201;
to message SomeClassA, and add:
message SomeClassD {
optional int32 Count = 2;
}
Note that protobuf-net doesn't actually generate the .proto syntax unless you explicitly ask for it (via GetSchema<T> etc) - I'm including it purely for illustrative purposes in terms of the underlying protobuf concepts.

LDAP template search by multiple attributes

Trying to search for users details by using userid,emailid,firstname,lastname,GUID,etc...many more values that need to be added in future
The search should be performed using all the attributes which are not null.
Found this piece of code online *
String filter = "(&(sn=YourName)(mail=*))";
*
Is there any other predefined template or such to do the search, more optimal way without directly specifying values to be Null or using if else statements for each and every attribute? All values must be passed to the method and those not null must be used for search using LDAP. Anything? Please help.
You can effectively use the Filters at run time to specify what to use for search and what not depending on some rules or your NULL validations on attributes. Pls find sample code which fetches person name using filters in ldapTemplate :-
public static final String BASE_DN = "dc=xxx,dc=yyy";
private LdapTemplate ldapTemplate ;
public List getPersonNames() {
String cn = "phil more";
String sn = "more";
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new EqualsFilter("sn", sn));
filter.and(new WhitespaceWildcardsFilter("cn", cn));
return ldapTemplate.search(
BASE_DN,
filter.encode(),
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}
As name suggests the AndFilters joins all individual filters used in lookup like EqualFilter which checks for equality of attributes while WhitespaceWildcardsFilter to perform wildcard search. So here like we got cn = phil more, it in turn uses *phil*more* for search.

ajax dependency selection plugin

Based on a previous post and some suggestions received there I have modified my application to use the ajax dependency selection plugin for some chained select lists that I have.
The select lists are Consultant -> Contract -> Project
I have a create page and edit page for an evaluation that use the same form template. On the create page my chained selects are working correctly and saving to the database. However when I open up an existing evaluation only the select primary select box is populated. Do I need to do something special with this plugin to get the secondary select box values and lists populated as desired on the edit page?
Here is the code from my domain classes
Consultant domain class
class CdeConsultant {
String ccf_consultant_firm
static hasMany=[contracts:Contract]
static mapping = {
table name: 'cde_consultant'
version false
id column: 'ccf_consultant_id', generator: "assigned"
}
}
Here is the code for my contract domain class
class Contract {
String contractName
int conId
String phone
String projectManagerName
CdeConsultant cdeConsultant
static hasMany=[projectLists:ProjectList]
static mapping = {
table name: 'contract'
version false
id column: 'contracts_id', generator: "assigned"
}
}
Here is the code from my ProjectList domain class
class ProjectList {
String project
Contract contract
static mapping = {
table name: 'project_list'
version false
id column: 'dpp_project_phase_id', generator: "assigned"
contract column: 'contracts_id'
}
}
Here is my code for the evaluation class which is where these fields are being saved
import java.util.Date
class CdeEvaluation extends Base {
String consultant
String consultantName
String project
String projectManager
String projectManagerPhone
String evalStatus
String cdeComment
Date evalBeginDate
Date evalEndDate
String submitApproval
int workCategory
int contract
String contractName
List<CdeEvalQuestion> questions
static hasMany = [questions: CdeEvalQuestion]
static constraints = {
consultant(nullable:true)
consultantName(nullable:true)
project(nullable:true)
contract(nullable:true)
contractName(nullable:true)
projectManager(nullable:true)
projectManagerPhone(nullable:true)
evalStatus(nullable:true)
workCategory(nullable:true)
evalEndDate validator: {value, cdeEvaluation -> value >= cdeEvaluation.evalBeginDate}
cdeComment(nullable:true, maxSize:2000)
submitApproval(nullable:true)
evalBeginDate(blank: false, nullable:true)
evalEndDate(blank: false, nullable:true)
createdBy(blank: false, nullable:true, maxSize:13)
dateCreated(blank: false, nullable:true)
lastUpdatedBy(blank: false, nullable:true, maxSize:13)
lastUpdated(blank: false, nullable:true)
}
static mapping = {
table name: 'CDE_EVALUATION'
id column: 'ceval_id_seq'
id generator: 'sequence', params: [sequence: 'ceval_id_seq']
}
#Override
public String toString() {
"${project}"
}
class EvaluationController {
static scaffold=true
}
}
And finally here is the code for my form template that is used in both the create and edit pages.
<g:selectPrimary class="form-select-list" id="consultant" name="consultant" label="Consultant"
domain='gov.mt.mdt.cde.domain.evaluation.CdeConsultant'
searchField="ccf_consultant_firm"
collectField='id'
domain2='gov.mt.mdt.cde.domain.evaluation.Contract'
bindid="cdeConsultant.id"
searchField2='contractName'
collectField2='id'
noSelection="['': 'Select A Consultant']"
setId="contract"
value="${cdeEvaluationInstance?.consultant}"
appendValue=''
appendName='Select a Contract' />
<g:selectSecondary class="form-select-list" id="contract" name="contract"
domain2='gov.mt.mdt.cde.domain.evaluation.ProjectList'
bindid="contract.id"
searchField2='project'
collectField2='project'
noSelection="['': 'Select A Contract']"
setId="project"
appendValue=''
appendName='Select a Project'
value="${cdeEvaluationInstance?.contract}"
required="false"/>
<g:select class="form-control" name="project" id="project" optionKey="project" optionValue="project"
from="[]" noSelection="['': 'Select A Project']" value="${cdeEvaluationInstance?.project}" />
Sorry a little late to answer your question here, edit I don't think has much documentation :). Although current situation gives me ideas to expand on the plugin for sure...
The problem being the secondary selection is reliant on primary being selected, the selection on primary itself triggers the gathered list for secondary box to be populated. Since primary is already defined not selected - well there you see the issue.
Let me have a think, if you had raised it as issue/suggestion on github I would have got to it earlier.
UPDATE
Ok I have rolled out 0.40 which should hopefully address edit mode :
Please take a look at this page :
https://github.com/vahidhedayati/ajaxdependancyselectexample/blob/master/grails-app/views/myContinent/testedit.gsp
In theory assuming the hardcoded values are returned as dynamic variables from the db an additional definition is required per primary/secondary call:
so in the
value='4'
secondaryValue='10'
Value is its own value and secondaryValue is what you expect to set the next selectSecondary
Please note I had to also remove appendName and appendValue from selectPrimary/selectSecondary calls. I think there was some form of a conflict in the edit mode....
Anyhow that testedit appeared to be working.

Resources