Grails validation on an associated 'hasMany' object - validation

I'm having a validation issue very similar to what is described here
https://schneide.wordpress.com/2010/09/20/gorm-gotchas-validation-and-hasmany/
but with an important difference that I don't have (or want) a List<Element> elements field in my domain. My code is
class Location {
static hasMany = [pocs: LocationPoc]
Integer id
String address
String city
State state
String zip
...
static mapping = {
...
}
static constraints = {
def regEx = new RegEx()
address blank: true, nullable: true, matches: regEx.VALID_ADDRESS_REGEX
city blank: true, nullable: true
state blank: true, nullable: true
zip blank: true, nullable: true
...
}
}
however, if I save/update a location with a bunk POC (point of contact), I get some wild errors. I would like to validate the POC's when I save/update a location, but I'm not exactly sure how. I've tried a few variations of
pocs validator: {
obj -> obj?.pocs?.each {
if (!it.validate()) {
return false
}
}
return true
}
to no avail. Is this possbile without creating a new field on my domain, List<LocationPoc> pocs?

You're close. The issue is you need to target the property you want to validate instead of using the object reference. It should look like this:
pocs validator: { val, obj, err ->
val?.each {
if (!it.validate()) return false
}
}

Validation doesn't automatically cascade through hasMany associations. In order to get free validation, the other side of the relationship needs to belong to Location.
You didn't include your LocationPOC class, but if you modify
Location location
to
static belongsTo = [location: Location]
Then you will get cascading validation when you save your Location object.
If you can't set the belongsTo property on LocationPoc, and need to use the custom validator, the syntax is a bit different than the answer above.
pocs validator: {val, obj ->
val?.inject true, {acc,item -> acc && item.validate()}
}
the three arguement version of validate expects you to add errors to the errorCollection. https://grails.github.io/grails2-doc/2.5.6/ref/Constraints/validator.html
Plus using a return statement inside of .each doesn't work like the above example. It just exits the closure and starts the next iteration. The validator from the other answer was just returning val (the result of val.each is just val)
You need to spin through the entire collection looking for non valid options.

Related

Validate nested domain class instance in command object

I try to validate a nested domain class instance on a command object.
Having the following command object
package demo
import grails.databinding.BindingFormat
class SaveEventCommand {
#BindingFormat('yyyy-MM-dd')
Date date
Refreshment refreshment
static constraints = {
date validator: { date -> date > new Date() + 3}
refreshment nullable: true
}
}
And having the following domain class with its own constraints
package demo
class Refreshment {
String food
String drink
Integer quantity
static constraints = {
food inList: ['food1', 'food2', 'food3']
drink nullable: true, inList: ['drink1', 'drink2', 'drink3']
quantity: min: 1
}
}
I need when refreshment is not nullable the command object validates the date property and check the corresponding restrictions in refreshment instance
For now try with this code in the controller:
def save(SaveEventCommand command) {
if (command.hasErrors() || !command.refreshment.validate()) {
respond ([errors: command.errors], view: 'create')
return
}
// Store logic goes here
}
Here through !command.refreshment.validate() I try to validate the refresh instance but I get the result that there are no errors, even when passing data that is not correct.
Thank you any guide and thank you for your time
I typically just include some code that will use a custom validator to kick off validation for any property that is composed of another command object. For example:
thePropertyInQuestion(nullable: true, validator: {val, obj, err ->
if (val == null) return
if (!val.validate()) {
val.errors.allErrors.each { e ->
err.rejectValue(
"thePropertyInQuestion.${e.arguments[0]}",
"${e.objectName}.${e.arguments[0]}.${e.code}",
e.arguments,
"${e.objectName}.${e.arguments[0]}.${e.code}"
)
}
}
})
This way it's pretty clear that I want validation to occur. Plus it moves all the errors up into the root errors collection which makes things super easy for me.
Two things I could think of:
Implement grails.validation.Validateable on your command object
What happens when you provide an invalid date? Can you see errors while validating?

Grails: Sorting Nested Domain Objects

I've got a one to many relationship in.
I have an Application which has multiple files which can be uploaded into it. The Application view contains the Uploaded file list (the files in the list just contain meta data about the file such as file name, file size etc.). I am having trouble with the ability to sort the nested list.
I am using the sortableColumn to call a sort action in my controller.
How can I retrieve an application and specify that the nested list of uploaded files be sorted by the requested attribute (ex. File Name, File Size etc.)
EDIT:
here are some snippets:
Here is the Application Domain Object
class Application implements Serializable {
private static final long serialVersionUID = 1
Firm firm
static hasMany = [applicationDocumentHolders:ApplicationDocumentHolder]
static belongsTo = [firm:Firm]
static mapping = {firm lazy: false}
}
And here is the Application Document Holder (I am storing the actual documents in MongoDB and the meta data in SQLServer in an applicationDocumentHolder object, so for the sake of simplicity I am leaving out the document file as it does not pertain to this question)
import java.sql.Time
class ApplicationDocumentHolder implements Serializable {
private static final long serialVersionUID = 1
static belongsTo = [application:Application]
static mapping = {application lazy: false}
Application application
ApplicationDocumentType type
ApplicationDocumentStatus status
Date verfiedDate
Long uploadFileId
//Following fields will be null until a file is uploaded
String fileName
Date uploadDate
String uploadUserId
long fileSize
static constraints = {
type blank: false
status blank: false
verfiedDate nullable: true
uploadFileId nullable: true
uploadFile nullable: true
fileName nullable: true
uploadDate nullable: true
uploadUserId nullable: true
fileSize nullable: true
}
}
Here is a snippet from the GSP where I am using the sortableColumn tag to call the sort action in my controller
<g:sortableColumn action="sort" property="type" title="${message(code: 'applicationDocumentHolder.type.label', default: 'Type')}" />
<g:sortableColumn action="sort" property="status" title="${message(code: 'applicationDocumentHolder.status.label', default: 'Status')}" />
<g:sortableColumn action="sort" property="verfiedDate" title="${message(code: 'applicationDocumentHolder.verfiedDate.label', default: 'Verfied Date')}" />
<g:sortableColumn action="sort" property="fileName" title="${message(code: 'applicationDocumentHolder.uploadFileName.label', default: 'Upload File Name')}" />
I am wondering what the best way to do this is.
I can sort the collection of ApplicationDocumentHolders in the controller but I keep getting violations because these are attached objects to the DB.
I can also re-retrieve the list sorted but I am not sure how you specify that you want a dynamically sorted nested object when it is retrieved from the DB (I know there must be a simple way to do this but I am pretty new to grails).
I would like to know how to do it both way just for future reference.
You can create a simple Closure takes the attribute you want to sort by and the file. You can then Closure.curry(attributeName) that Closure to get a new Closure which sorts by that attribute. Then, pass the Closure to Collection.toSorted() to sort the list of files.
def files = [
[name: 'foo.txt', size: 10],
[name: 'bar.txt', size: 30],
[name: 'hello.txt', size: 20]
]
def sorter = { attr, file -> file[attr] }
files.toSorted sorter.curry('size') // sort by size
files.toSorted sorter.curry('name') // sort by name

Grails default nullable constraints

In my Grails app I have the following command object
#Validateable
class CalendarEventCommand {
#BindingFormat('FestivalType')
Collection<FestivalType> types
Date start
Date end
MapFocalPoint location
boolean freeOnly = false
}
which is used as the argument to a controller action
def getCalendarEvents(CalendarEventCommand calendarEventCommand) {
if (calendarEventCommand.validate()) {
log.error "Command errors $calendarEventCommand.errors"
} else {
log.warn "Everything is fine"
}
}
In Config.groovy I've specified the following as the default constraints
grails.gorm.default.constraints = {
// apply a max size of 191 chars to String columns to support utf8mb4
// http://mathiasbynens.be/notes/mysql-utf8mb4
'*'(maxSize: 191)
// this shared constraint provides a way to override the default above for long text properties
unlimitedSize(maxSize: Integer.MAX_VALUE)
}
If an instance is created with a null value for start and end validation passes, but I wouldn't expect it to because AFAIK a default constraint of nullable: false should be applied to all properties. I've tried adding this explicitly, by changing the first default constraint to
'*'(maxSize: 191, nullable: false)
But validate() still returns true when start and/or end are null. If I add these constraints to CalendarEventCommand
static constraints = {
start nullable: false
end nullable: false
}
then validate() returns false, but AFAIK it shouldn't be necessary for me to add these constraints.
I think this is an expected behavior. There are couple of JIRA defects regarding this functionality out of which GRAILS-7431 and GRAILS-8583 seems more focused towards the behavior.
I was going through DefaultConstraintEvaluator.java which takes care of global constraints only for domain classes. I think we have to end up using the way mentioned later.
static constraints = {
start nullable: false
end nullable: false
}

backbone.js model validation isValid() returns true for invalid attribute

I was trying to check the validity of individual attributes using isValid method. It is returning true for an invalid attribute. My code is as follows:
person = Backbone.Model.extend({
defaults:{
name:"default name",
age:0
},
initialize:function(){
this.on("invalid",function(model,errors){
console.log(JSON.stringify(errors));
});
},
validate:function(attrs){
errors=[];
if(attrs.age<0){
errors.push({attribName:"age",errorMsg:"age should be grater than 0"});
}
return errors.length>0?errors:false;
}
});
var person1 = new person();
person1.set({
age:-5
});
console.log("checking validity of model:"+person1.isValid());
console.log("checking for validity of age attribute:"+person1.isValid('age'));
isValid() works fine if used to check the validity of the model as a whole and returns false. But when I try to check the age attribute i.e isValid('age') it returns true when it should return false.
isValid() is an underscore.js function, right? Doesn't it support passing an attribute to check for its validity? What am I missing here?
Short version
Model.isValid doesn't accept an attribute name as argument and has to be used on the whole model. If you don't, you're on undocumented territory and you will get weird behaviors.
To check individual attributes, you will have to set up your own mechanism.
Long version, why you get a different value
Model.isValid does in fact accept an (undocumented) options hash as its first argument and it internally forwards this hash to Model._validate via
this._validate({}, _.extend(options || {}, { validate: true }))
trying to set a validate attribute to true. But at this point, options is a string and won't be modified by _.extend. _validate looks like
_validate: function(attrs, options) {
if (!options.validate || !this.validate) return true;
// ...
}
checking if it indeed has to validate the model, options.validate is undefined and your isValid call gets back a true value.
isValid is a backbone API : http://backbonejs.org/#Model-isValid
The reason it is returning true is, the parameter accepted by isValid is an options paramter. It has to be an object.
One of the scenario you use options is :
validate: function(attrs, options) {
if(options.someSpecialCheck) {
// Perform some special checks here
} else {
// Perform some regular checks here
}
}
myModel.isValid({someSpecialCheck: true});

In a Grails domain object, is it possible to validate a field based on another field?

I have a Grails domain object that looks like this:
class Product {
Boolean isDiscounted = false
Integer discountPercent = 0
static constraints = {
isDiscounted(nullable: false)
discountPercent(range:0..99)
}
I'd like to add a validator to discountPercent that will only validate if isDiscounted is true, something like this:
validator: { val, thisProduct ->
if (thisProduct.isDiscounted) {
// need to run the default validator here
thisProduct.discountPercent.validate() // not actual working code
} else {
thisProduct.discountPercent = null // reset discount percent
}
Does anyone know how I can do this?
This is more or less what you need (on the discountPercent field):
validator: { val, thisProduct ->
if (thisProduct.isDiscounted)
if (val < 0) {
return 'range.toosmall' //default code for this range constraint error
}
if (99 < val) {
return 'range.toobig' //default code for this range constraint error
} else {
return 'invalid.dependency'
}
You can't both have a special validator that relies on something else and have a special validator, as you can't run a single validator on a field (that I know of), only on single properties. But if you run a validation on this property, you'll depend on yourself and go into endless recursion. Therefore I added the range check manually. In your i18n files you can set up something like full.packet.path.FullClassName.invalid.dependency=Product not discounted.
Good luck!

Resources