grails validate nested command object not working
Asked Answered
A

3

7

I'm using grails 2.2.1 and attempting to validate a nested command structure. Here is a simplified version of my command objects:

@Validateable
class SurveyCommand {

    SectionCommand useful
    SectionCommand recommend

    SurveyCommand() {
        useful = new SectionCommand(
                question: 'Did you find this useful?',
                isRequired: true)
        recommend = new SectionCommand(
                question: 'Would you recommend to someone else?',
                isRequired: false)
    }
}

@Validateable
class SectionCommand {
    String question
    String answer
    boolean isRequired

    static constraints = {
        answer(validator: answerNotBlank, nullable: true)
    }

    static answerNotBlank = { String val, SectionCommand obj ->
        if(obj.isRequired) {
            return val != null && !val.isEmpty()
        }
    }
}

When I try to validate an instance of SurveyCommand it always returns true no matter the section values and my custom validator in SectionCommand (answerNotBlank) is never called. From the grails documentation, it seems that this kind of nested structure is supported (deepValidate defaults to true). However, maybe this rule only applies to domain objects and not Command objects? Or am I just missing something here?

Aspergillus answered 24/4, 2013 at 0:50 Comment(0)
B
4

You could add a custom validator to your main command object

@Validateable
class SurveyCommand {

    SectionCommand useful
    SectionCommand recommend

    static subValidator = {val, obj ->
        return val.validate() ?: 'not.valid'
    }

    static constraints = {
        useful(validator: subValidator)
        recommend(validator: subValidator)
    }

    SurveyCommand() {
        useful = new SectionCommand(
            question: 'Did you find this useful?',
            isRequired: true)
        recommend = new SectionCommand(
            question: 'Would you recommend to someone else?',
            isRequired: false)
    }
}
Breastsummer answered 24/4, 2013 at 2:41 Comment(1)
nice! works great, however is there a more DRY way instead of explicitly defining a constraint for each sub property?Aspergillus
P
6

For Grails 2.3 and later I've found that the Cascade Validation Plugin solves this problem nicely. It defines a new validator type called cascade which does exactly what you'd expect. Once installed your example would become:

class SurveyCommand {
    ...

    static constraints = {
        useful(cascade: true)
        recommend(cascade: true)
    }
}
Pragmatism answered 18/1, 2015 at 22:43 Comment(2)
The only available version right now is: compile ":cascade-validation:0.1.4"Regenerative
When running a unit test, the cascade constraint isn't registered with grails. To work around this issue, the following code must be added to the setup() method of the test: ConstrainedProperty.registerNewConstraint(CascadeValidationConstraint.NAME, CascadeValidationConstraint). Source: github.com/rmorrise/grails-cascade-validationRegenerative
B
4

You could add a custom validator to your main command object

@Validateable
class SurveyCommand {

    SectionCommand useful
    SectionCommand recommend

    static subValidator = {val, obj ->
        return val.validate() ?: 'not.valid'
    }

    static constraints = {
        useful(validator: subValidator)
        recommend(validator: subValidator)
    }

    SurveyCommand() {
        useful = new SectionCommand(
            question: 'Did you find this useful?',
            isRequired: true)
        recommend = new SectionCommand(
            question: 'Would you recommend to someone else?',
            isRequired: false)
    }
}
Breastsummer answered 24/4, 2013 at 2:41 Comment(1)
nice! works great, however is there a more DRY way instead of explicitly defining a constraint for each sub property?Aspergillus
P
2

If you are trying to test validation from unit tests using mockForConstraintsTest() then you should register the command objects inConfig.groovy instead of using @Validateable because of an existing Grails Bug. Refer this SO question/answers for details.

You can register the validateable class as below in Config.groovy

grails.validateable.classes = 
           [yourpackage.SurveyCommand, yourpackage.SectionCommand] 
Peeling answered 24/4, 2013 at 5:26 Comment(3)
it seems to work fine (in 2.2.1) to test a @Validatable command class's .validate() method by simply calling mockCommandObject before instantiating it, e.g. mockCommandObject SurveyCommandAspergillus
Agree. That was my approach to the SO question/answer I mentioned earlier. mockCommandObject works but mockForConstraintsTest fails.Peeling
ah, got it, thanks for clarifying. Obviously I didn't read close enoughAspergillus

© 2022 - 2024 — McMap. All rights reserved.