Grails command object validation gives exception instead of errors - validation

I have command object like this:
#Validateable
class TaCustomerBoardActionCommand {
TaCustomerBoardAction action
static constraints = {
action casecade: true
}
}
and classes in command object below:
class TaCustomerBoardAction {
TaCustomerBoard taCustomerBoard
TaapAction taapAction
Date dateCreated // updated by grails
Date lastUpdated // updated by grails
User createdBy
OrgUnit orgUnit
Client client
static belongsTo = [Client]
static constraints = {
}
}
and
TaapAction {
int id
User createdUser
User responsibleUser
Brand brand
BusinessType businessType
Topic topic
Topic subTopic
String subject
String description
Date targetDate
int progress
String responsible
Client client
static belongsTo = [Client]
OrgUnit orgUnit
Date dateCreated // updated by grails
Date lastUpdated // updated by grails
TaapActionState taapActionState
static constraints = {
subject nullable: false, size: 1..64
description nullable: false, size: 1..4000
responsible nullable: false, size: 1..512
progress nullable: false
responsibleUser nullable:false
brand nullable:false
businessType nullable:false
topic nullable:false
subTopic nullable:false
targetDate nullable:false
}
TaCustomerBoard has similar constraints as above class.
but it gives exception instead of error codes.
Below is controller Post method:
def saveTaCustomerBoardAction(TaCustomerBoardActionCommand cmd){
if(cmd.validate()){
taActionPlanningService.saveAction(cmd.action.taapAction)
cmd.action.save(flush: true, failOnError: true)
}
[cmd:cmd]
}
Stack trace:
grails.validation.ValidationException: Validation Error(s) occurred
during save():
- Field error in object 'de.idare.move.taap.TaapAction' on field 'progress': rejected value [null]; codes
[de.idare.move.taap.TaapAction.progress.typeMismatch.error,de.idare.move.taap.TaapAction.progress.typeMismatch,taapAction.progress.typeMismatch.error,taapAction.progress.typeMismatch,typeMismatch.de.idare.move.taap.TaapAction.progress,typeMismatch.progress,typeMismatch.int,typeMismatch];
arguments [progress]; default message [Data Binding Failed]
- Field error in object 'de.idare.move.taap.TaapAction' on field 'description': rejected value [null]; codes
[de.idare.move.taap.TaapAction.description.nullable.error.de.idare.move.taap.TaapAction.description,de.idare.move.taap.TaapAction.description.nullable.error.description,de.idare.move.taap.TaapAction.description.nullable.error.java.lang.String,de.idare.move.taap.TaapAction.description.nullable.error,taapAction.description.nullable.error.de.idare.move.taap.TaapAction.description,taapAction.description.nullable.error.description,taapAction.description.nullable.error.java.lang.String,taapAction.description.nullable.error,de.idare.move.taap.TaapAction.description.nullable.de.idare.move.taap.TaapAction.description,de.idare.move.taap.TaapAction.description.nullable.description,de.idare.move.taap.TaapAction.description.nullable.java.lang.String,de.idare.move.taap.TaapAction.description.nullable,taapAction.description.nullable.de.idare.move.taap.TaapAction.description,taapAction.description.nullable.description,taapAction.description.nullable.java.lang.String,taapAction.description.nullable,nullable.de.idare.move.taap.TaapAction.description,nullable.description,nullable.java.lang.String,nullable];
arguments [description,class de.idare.move.taap.TaapAction]; default
message [Property [{0}] of class [{1}] can not be null]
Kindly help me I am stuck with this problem.

Your problem is rather straight forward. Well it would seem, you have provided how things work but not actually provided what is sent. My suggestion is to do a println params in the controller action using validation method to see what it is sent / and validated.
You have declared progress as int and not Integer. This means it can't be nullable. Always use Boolean Integer or whatever the case maybe if something is meant to be nullable. Secondly you have also declared description and progress as nullable false meaning they have to be provided. The error message suggests command sent does not have a progress or description sent to it as part of the validation. This is something you need to investigate further by simple debugging such as println at your end to figure out why that is the case.
int progress
...
static constraints = {
progress nullable: false
description nullable: false, size: 1..4000
}

Just remove the failOnError: true. You'll be able to process error objects instead of catching exception.
Documentation

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 validation on an associated 'hasMany' object

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.

Grails: in domain, property declared as Long, should still add nullable:false in constraints

In Grails, it is said that by default, nullable is already set to false. But when I try to run student.validate() and not set studentId to nullable:false; validate cannot catch the error hence I get a MySQLIntegrityConstraintViolationException.
Is my observation correct? or do you think I've messed up some code that's why validate cannot catch the error?
And if my observation is right. what is the reason behind it?
#Validateable
class Student {
Long studentId
.
.
static constraints = {
studentId nullable:false
.
.
}
}
def saveCreate(Student student){
if (!student.validate()) {
List errorList = buildErrorMessages(ErrorBean.getErrorBeans(student.errors), []);
render([isSuccess: false, errors: errorList] as JSON);
return;
}
student.save(flush: true);
def Map studentInformation = [id: student.id, name: student.name];
render([isSuccess: true, student: studentInformation] as JSON);
}
If i add this in static constraints
studentId nullable: false
when I run student.validate, validate can catch the error. If I didnt put it, validate will return true and will try to save the data.

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.

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
}

Resources