Using Custom Validator on List - grails

I have a Command Object as follows :
class TestCreationCommand {
String title
List testItems = [].withLazyDefault {new VocabQuestion()}
static constraints = {
title(blank:false,maxLength:150)
testItems( validator: { ti ->
ti.each {it.validate()}
} )
}
}
Test Items is a list of VocabQuestion objects. VocabQuestion is as follows :
class VocabQuestion {
static constraints = {
question(blank:false,maxLength:150)
answer(blank:false,maxLength:40)
}
static belongsTo = [vocabularyTest: VocabularyTest]
String question
String answer
}
I'm attempting to validate the constraints on the VocabQuestion using a custom vaildator ( in the constraints of the Command class above ), but I keep getting the following error message.
Return value from validation closure [validator] of property [testItems] of class [class vocabularytest.TestCreationCommand] is returning a list but the first element must be a string containing the error message code
I have had many different attempts at this .....
I am not sure what the message is telling me or how to go about debugging what the return value from the closure is.
Can anyone provide me any pointers?

You are not returning what Grails understands. You can't expect to just return anything and have Grails know what to do with it. Return a boolean or an error string.
static constraints = {
title(blank:false,maxLength:150)
testItems( validator: { ti ->
Boolean errorExists = false
ti.each {
if (!it.validate()) {
errorExists = true
}
}
errorExists
})
}

Check out this SO question It might be the format of the validator you need.
The .every will return a boolean.

Related

How to map Grails domain class properties to non-matching json string?

Is there a built-in / easy way to set mappings between domain class properties and JSON strings that don't have exact matches for the property names?
For example, when I have a domain class:
class Person {
String jobTitle
String favoriteColor
static constraints = {
jobTitle(blank: false)
favoriteColor(blank: false)
}
}
And someone's giving me the following JSON:
{ "currentJob" : "secret agent", "the-color" : "red" }
I'd like to be able to still do this:
new Person(request.JSON).save()
Is there a way in groovy/grails for me to map currentJob -> jobTitle and the-color -> favorite color?
EDIT:
I've done a little experimenting, but I still haven't gotten it working. But I have found out a couple interesting things...
At first I tried overwriting the setProperty method:
#Override
setProperty(String name, Object value) {
if(this.hasProperty(name)) this[name] = value
else {
switch(name) {
'currentJob': this.jobTitle = value; break;
'the-color': this.favoriteColor = value; break;
}
}
}
But this doesn't work for two reasons: 1) setProperty is only called if there is a property that matches name and 2) "this[name] = value" calls setProperty, leading to an infinite recursive loop.
So then I thought, well screw it, I know what the incoming json string looks like (If only I could control it), I'll just get rid of the line that handles the scenario where the names match and I'll override hasProperty, maybe that will work:
#Override
void setProperty(String name, Object value) {
switch(name) {
'currentJob': this.jobTitle = value; break;
'the-color': this.favoriteColor = value; break;
}
}
#Override
boolean hasProperty(String name) {
if(name == "currentJob" || name == "the-color") return true
return false
}
But no, that didn't work either. By a random stroke of luck I discovered, that not only did I have to overwrite hasProperty(), but I also had to have an empty setter for the property.
void setCurrentJob(){ }
That hack worked for currentJob - I guess setProperty only gets called if hasProperty returns true and there is a setter for the property (Even if that setter is auto generated under the covers in grails). Unfortunately I can't make a function "setThe-Color" because of the dash, so this solution doesn't work for me.
Still stuck on this, any help would definitely be appreciated.
EDIT:
Overriding the void propertyMissing(String name, Object value){} method is called by this:
Person person = new Person()
person["currentJob"] = "programmer"
person["the-color"] = "red"
But not by this:
Person person = new Person(["currentJob":"programmer", "the-color":"red"])

Inherited grails domain classes missing dynamic properties

I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.

Import domain in constraints

I have 2 domain classes
class a {
String name
static constraints = {
name unique:true
}
}
class b {
String description
}
and in domain class b I want to call class a
import a
class b {
String description
static constraints = {
description unique:'a.name'
}
}
and get error
Scope for constraint [unique] of property [description] of class [b] must be a valid property name of same class
How do I get a property from class a to b?
Assuming you try to do this in Grails 2+
You can't use validation that way. In your example you need to reference to a property of the same domain class. To correct the constraint in class B you can write:
class B {
String description
static contraints = {
description unique:true
}
}
But I think you want to import the constraints from class a which is done like this.
class B {
String description
static contraints = {
importFrom A
}
}
See http://grails.org/doc/latest/guide/validation.html#sharingConstraints
This will import all constraints on properties that the two classes share. Which in your case is none.
UPDATE
I got a similar question and found a solution for it. So I thought to share it here with you.
The problem can be solved with a custom validator. In your case constraints for class B:
static constraints = {
description(validator: {
if (!it) {
// validates to TRUE if the collection is empty
// prevents NULL exception
return true
}
def names = A.findAll()*.name
return names == names.unique()
})
}
It's difficult to answer your question correctly since the requirements are a bit odd. But maybe it will help.
You need to write a custom validator to check the uniqueness since it relies on more information than the single instance of b will have.
Something like
static constraints {
description validator: { val ->
!a.findByName(val)
}
}
might do the trick.

Grails custom validater, getting a reference to the field name

I am trying to write a generic custom validator for a property, to make it generic i need a reference to the field name within the closure, the code is as follows
In config.groovy
grails.gorm.default.constraints = {
nameShared(validator: {val, obj, errors->
Pattern p = Pattern.compile("[a-zA-Z0-9-]{1,15}")
Matcher m = p.matcher(val)
boolean b = m.matches()
if(!b)
errors.rejectValue('name', "${obj.class}.name.invalid", "Name is invalid")
})
in My domain class
class Student{
String name
static constraints = {
name(shared:'nameShared')
}
}
class Teacher{
String firstName
String lastName
static constraints = {
firstName(shared:'nameShared')
}
}
I want to use the same validator for both name and firstName, but since i am hardcoding the fieldName in the validator, it will always work for name and not firstName, so i want to know if there is anyway i can get a reference to the fieldname and make the validator generic, please help
You could use the variable propertyName to get the name of the validated property.
From the grails docs:
The Closure also has access to the name of the field that the constraint applies to using propertyName
myField validator: { val, obj -> return propertyName == "myField" }
You could wrap your validator-closure inside another function like this:
def getValidator(myValue) {
return { val, obj, errors ->
// make use of myValue
}
}
myField validator: getValidator('foo')

How to refer to another property in a custom Grails validator?

I have a property that can be nullable or required depending on the status of another variable.
class Person{
name()
civilStatus(inList:['Single','Married','Divorced','Widowed'])
partnerOrSpouse()
}
the partnerOrSpouse property is nullable or not depending on the value of the civilStatus property.
You can use a custom validator. Using the two-parameter version, the first is the value being validated and the second is the domain class instance. You can refer to other properties via the 'obj' parameter:
class Person {
...
static constraints = {
name()
civilStatus inList:['Single','Married','Divorced','Widowed']
partnerOrSpouse validator: { val, obj ->
if (obj.civilStatus == 'Single') {
return 'some.error.code'
}
}
}
}

Resources