From 9103d2b8a4b14ff843e5f2151ed940afc7ac4ea7 Mon Sep 17 00:00:00 2001 From: Software Engineering Geek Date: Sat, 23 Jun 2018 14:37:35 -0400 Subject: [PATCH 1/9] (cherry picked from commit 2715628eabbf7cfe16421446e1a98a1a12421fc6) --- build.gradle | 4 ++-- grails-wrapper.jar | Bin 0 -> 5463 bytes .../CascadeValidationConstraint.groovy | 4 ++++ ...GrailsCascadeValidationGrailsPlugin.groovy | 20 ++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 grails-wrapper.jar diff --git a/build.gradle b/build.gradle index 239b33e..4196826 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,8 @@ apply plugin: 'idea' apply plugin: "org.grails.grails-plugin" apply plugin: "org.grails.grails-plugin-publish" -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 repositories { mavenLocal() diff --git a/grails-wrapper.jar b/grails-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..bc85146c130e69264ce20e079987784615201b35 GIT binary patch literal 5463 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 Q1NO~BaT5_U$?e cascadeValidationConstraintFactory(DefaultConstraintFactory, CascadeValidationConstraint, null) }} + + void doWithApplicationContext() { + //This method for registering constraints came from longwa + List registries = [] + DefaultConstraintEvaluator evaluator = applicationContext.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator + + // Register with both the default constraint as well as the gorm registry (it's stupid that it needs both) + // Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it + // so we are forced to invade it's privacy if we want custom constraints for Validateable instances. + registries << evaluator.constraintRegistry + registries << applicationContext.getBean("gormValidatorRegistry", ConstraintRegistry) + + registries.each { ConstraintRegistry registry -> + registry.addConstraint(CascadeValidationConstraint) + } + } + } From dbf56ba16e69dde6d9ec34a357a9fcea899f98d6 Mon Sep 17 00:00:00 2001 From: Software Engineering Geek Date: Sun, 24 Jun 2018 11:22:44 -0400 Subject: [PATCH 2/9] (cherry picked from commit 655e1e0e68fc31a84afd25cb9a2c1be15a3a70f1) --- build.gradle | 2 + ...traint.groovy => CascadeConstraint.groovy} | 58 +++++++++++++------ ...GrailsCascadeValidationGrailsPlugin.groovy | 35 ++++++----- .../CascadeValidationConstraintSpec.groovy | 52 ++++++----------- 4 files changed, 80 insertions(+), 67 deletions(-) rename src/main/groovy/com/cscinfo/platform/constraint/{CascadeValidationConstraint.groovy => CascadeConstraint.groovy} (64%) diff --git a/build.gradle b/build.gradle index 4196826..8547b53 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,8 @@ dependencies { testCompile "cglib:cglib-nodep:3.2.5" testCompile "org.grails:grails-gorm-testing-support" testCompile "org.grails:grails-plugin-testing" + testCompile "org.grails:grails-web-testing-support" + testCompile 'net.bytebuddy:byte-buddy-dep:1.8.12' } bootRun { diff --git a/src/main/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraint.groovy b/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy similarity index 64% rename from src/main/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraint.groovy rename to src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy index 59126cd..12c7473 100644 --- a/src/main/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraint.groovy +++ b/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy @@ -2,7 +2,7 @@ package com.cscinfo.platform.constraint import groovy.transform.CompileDynamic import groovy.transform.CompileStatic -import org.grails.datastore.gorm.validation.constraints.AbstractVetoingConstraint +import org.grails.datastore.gorm.validation.constraints.AbstractConstraint import org.springframework.context.MessageSource import org.springframework.validation.Errors import org.springframework.validation.FieldError @@ -19,57 +19,81 @@ import org.springframework.validation.FieldError * @author Russell Morrisey */ @CompileStatic -class CascadeValidationConstraint extends AbstractVetoingConstraint { - public static final String NAME = "cascadeValidate" +class CascadeConstraint extends AbstractConstraint { + boolean enabled = true - private final boolean cascade + static final String CASCADE_CONSTRAINT = "cascade" - CascadeValidationConstraint(Class constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) { + CascadeConstraint(Class constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) { super(constraintOwningClass, constraintPropertyName, constraintParameter, messageSource) - this.cascade = (Boolean) constraintParameter - } - String getName() { NAME } + if (!(constraintParameter instanceof Boolean)) { + throw new IllegalArgumentException("Parameter for constraint [$CASCADE_CONSTRAINT] of property [$constraintPropertyName] of class [$constraintOwningClass] must be a boolean") + } + + this.enabled = (boolean) constraintParameter + } @Override - protected boolean processValidateWithVetoing(target, propertyValue, Errors errors) { + protected Object validateParameter(Object constraintParameter) { + return constraintParameter + } + + boolean supports(Class type) { + Collection.isAssignableFrom(type) || type.metaClass.respondsTo(type, 'validate') + } + + String getName() { + return CASCADE_CONSTRAINT + } + + protected void processValidate(Object target, Object propertyValue, Errors errors) { + boolean result = false if (propertyValue instanceof Collection) { propertyValue.eachWithIndex { item, pvIdx -> - result = validateValue(target, item, errors, pvIdx) || result + validateValue(target, item, errors, pvIdx) || result } } else { - result = validateValue(target, propertyValue, errors) + validateValue(target, propertyValue, errors) } - - return result } + /** + * Processes the validation of the propertyValue, against the checks patterns set, and setting and calling rejectValue + * if the propertyValue matches any of the patterns in the checks list. + * + * @param target The target field to verify. + * @param propertyValue the property value of the field. + * @param errors Errors to be sent by rejectValues,. + */ @CompileDynamic - private boolean validateValue(target, value, errors, index = null) { + private void validateValue(target, value, errors, index = null) { if (!value.respondsTo('validate')) { throw new NoSuchMethodException("Error validating field [${constraintPropertyName}]. Unable to apply 'cascade' constraint on [${value.class}] because the object does not have a validate() method. If the object is a command object, you may need to add the @Validateable annotation to the class definition.") } if (value.validate()) { - return false + return } String objectName = target.errors.objectName Errors childErrors = value.errors List childFieldErrors = childErrors.fieldErrors + childFieldErrors.each { FieldError childFieldError -> String field - if(index != null) { + + if (index != null) { field = "${propertyName}.${index}.${childFieldError.field}" } else { field = "${propertyName}.${childFieldError.field}" } + FieldError fieldError = new FieldError(objectName, field, childFieldError.rejectedValue, childFieldError.bindingFailure, childFieldError.codes, childFieldError.arguments, childFieldError.defaultMessage) errors.addError(fieldError) } - return true } boolean supports(Class type) { diff --git a/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy b/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy index 10b3975..26b14a3 100644 --- a/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy +++ b/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy @@ -1,11 +1,12 @@ package grails.cascade.validation -import com.cscinfo.platform.constraint.CascadeValidationConstraint +import com.cscinfo.platform.constraint.CascadeConstraint import grails.plugins.Plugin import org.grails.datastore.gorm.validation.constraints.factory.DefaultConstraintFactory import org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator import org.grails.datastore.gorm.validation.constraints.registry.ConstraintRegistry +import org.springframework.context.ApplicationContext class GrailsCascadeValidationGrailsPlugin extends Plugin { @@ -36,19 +37,23 @@ Used with permission. }} void doWithApplicationContext() { - //This method for registering constraints came from longwa - List registries = [] - DefaultConstraintEvaluator evaluator = applicationContext.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator - - // Register with both the default constraint as well as the gorm registry (it's stupid that it needs both) - // Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it - // so we are forced to invade it's privacy if we want custom constraints for Validateable instances. - registries << evaluator.constraintRegistry - registries << applicationContext.getBean("gormValidatorRegistry", ConstraintRegistry) - - registries.each { ConstraintRegistry registry -> - registry.addConstraint(CascadeValidationConstraint) - } - } + registerCustomConstraints(applicationContext) + } + + private void registerCustomConstraints(ApplicationContext ctx) { + //This method for registering constraints came from longwa + List registries = [] + DefaultConstraintEvaluator evaluator = ctx.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator + + // Register with both the default constraint as well as the gorm registry (it's stupid that it needs both) + // Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it + // so we are forced to invade it's privacy if we want custom constraints for Validateable instances. + registries << evaluator.constraintRegistry + registries << ctx.getBean("gormValidatorRegistry", ConstraintRegistry) + + registries.each { ConstraintRegistry registry -> + registry.addConstraint(CascadeConstraint) + } + } } diff --git a/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy b/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy index e7b3333..78c6fd7 100644 --- a/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy +++ b/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy @@ -1,20 +1,18 @@ package com.cscinfo.platform.constraint -import com.cscinfo.platform.constraint.support.ValidateableParentWithChildList -import com.cscinfo.platform.constraint.support.ValidateableProperty import com.cscinfo.platform.constraint.support.ValidateableParent +import com.cscinfo.platform.constraint.support.ValidateableProperty import grails.validation.ValidationErrors import org.springframework.context.MessageSource import org.springframework.validation.Errors import org.springframework.validation.FieldError import spock.lang.Specification - /** * @author: rmorrise * @author Eric Kelm */ class CascadeValidationConstraintSpec extends Specification { - CascadeValidationConstraint constraint + CascadeConstraint constraint ValidateableParent parent ValidationErrors errors MessageSource messageSource @@ -28,25 +26,25 @@ class CascadeValidationConstraintSpec extends Specification { def "constraint name should be cascade"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) expect: - constraint.name == 'cascadeValidate' + constraint.name == 'cascade' } def "validateWithVetoing fails when constraint is set on non-validatable type"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) def target = "Some value" when: - constraint.validateWithVetoing(parent, target, errors) + constraint.validate(parent, target, errors) then: thrown(NoSuchMethodException) @@ -54,23 +52,23 @@ class CascadeValidationConstraintSpec extends Specification { def "validateWithVetoing returns valid when constraint is set to validateable type and constraints pass"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) def target = Mock(ValidateableProperty) when: - def result = constraint.validateWithVetoing(parent, target, errors) + def result = constraint.validate(parent, target, errors) then: 1 * target.validate() >> true - result == false + 0 * errors.addError(_) } def "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) @@ -88,7 +86,7 @@ class CascadeValidationConstraintSpec extends Specification { def parentName = 'foo' when: - def result = constraint.validateWithVetoing(parent, target, errors) + def result = constraint.validate(parent, target, errors) then: 1 * target.validate() >> false @@ -103,12 +101,11 @@ class CascadeValidationConstraintSpec extends Specification { it.arguments == args && it.defaultMessage == defaultMessage }) - result == true } def "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail on list"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParentWithChildList, 'children', true, messageSource) @@ -128,7 +125,7 @@ class CascadeValidationConstraintSpec extends Specification { def parentName = 'foo' when: - def result = constraint.validateWithVetoing(parent, target, errors) + def result = constraint.validate(parent, target, errors) then: 1 * child1.validate() >> false @@ -138,27 +135,12 @@ class CascadeValidationConstraintSpec extends Specification { 1 * child1Errors.fieldErrors >> fieldErrors 1 * child2Errors.fieldErrors >> fieldErrors target.size() * errors.objectName >> parentName - 1 * errors.addError({ - it.objectName == parentName && - it.field == "children.0." + field && - it.bindingFailure == true && - it.codes == codes && - it.arguments == args && - it.defaultMessage == defaultMessage - }) - 1 * errors.addError({ - it.objectName == parentName && it.field == "children.1." + field && - it.bindingFailure == true && - it.codes == codes && - it.arguments == args && - it.defaultMessage == defaultMessage - }) - result == true + 2 * errors.addError(_) } def "constraint does not support non-validateable types"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) @@ -169,7 +151,7 @@ class CascadeValidationConstraintSpec extends Specification { def "constraint supports validateable types"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) @@ -180,7 +162,7 @@ class CascadeValidationConstraintSpec extends Specification { def "constraint supports collection types"() { given: - constraint = new CascadeValidationConstraint( + constraint = new CascadeConstraint( ValidateableParent, 'property', true, messageSource) From 538887483363f82156db36e16bfc807342b2cbae Mon Sep 17 00:00:00 2001 From: rmorrise Date: Thu, 10 Jan 2019 17:04:28 -0500 Subject: [PATCH 3/9] Merge fixes. --- .../constraint/CascadeConstraint.groovy | 19 +++++-------------- ...ec.groovy => CascadeConstraintSpec.groovy} | 15 ++++++++++----- 2 files changed, 15 insertions(+), 19 deletions(-) rename src/test/groovy/com/cscinfo/platform/constraint/{CascadeValidationConstraintSpec.groovy => CascadeConstraintSpec.groovy} (89%) diff --git a/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy b/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy index 12c7473..ff39e34 100644 --- a/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy +++ b/src/main/groovy/com/cscinfo/platform/constraint/CascadeConstraint.groovy @@ -20,8 +20,6 @@ import org.springframework.validation.FieldError */ @CompileStatic class CascadeConstraint extends AbstractConstraint { - boolean enabled = true - static final String CASCADE_CONSTRAINT = "cascade" CascadeConstraint(Class constraintOwningClass, String constraintPropertyName, Object constraintParameter, MessageSource messageSource) { @@ -30,13 +28,6 @@ class CascadeConstraint extends AbstractConstraint { if (!(constraintParameter instanceof Boolean)) { throw new IllegalArgumentException("Parameter for constraint [$CASCADE_CONSTRAINT] of property [$constraintPropertyName] of class [$constraintOwningClass] must be a boolean") } - - this.enabled = (boolean) constraintParameter - } - - @Override - protected Object validateParameter(Object constraintParameter) { - return constraintParameter } boolean supports(Class type) { @@ -74,6 +65,10 @@ class CascadeConstraint extends AbstractConstraint { throw new NoSuchMethodException("Error validating field [${constraintPropertyName}]. Unable to apply 'cascade' constraint on [${value.class}] because the object does not have a validate() method. If the object is a command object, you may need to add the @Validateable annotation to the class definition.") } + if (!getParameter()) { + return + } + if (value.validate()) { return } @@ -96,15 +91,11 @@ class CascadeConstraint extends AbstractConstraint { } } - boolean supports(Class type) { - Collection.isAssignableFrom(type) || type.metaClass.respondsTo(type, 'validate') - } - @Override protected Object validateParameter(Object constraintParameter) { if (!(constraintParameter instanceof Boolean)) { throw new IllegalArgumentException("Parameter for constraint [" + - NAME+ "] of property [" + + CASCADE_CONSTRAINT + "] of property [" + constraintPropertyName + "] of class [" + constraintOwningClass + "] must be a boolean value") } diff --git a/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy similarity index 89% rename from src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy rename to src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy index 78c6fd7..f6acd82 100644 --- a/src/test/groovy/com/cscinfo/platform/constraint/CascadeValidationConstraintSpec.groovy +++ b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy @@ -1,6 +1,7 @@ package com.cscinfo.platform.constraint import com.cscinfo.platform.constraint.support.ValidateableParent +import com.cscinfo.platform.constraint.support.ValidateableParentWithChildList import com.cscinfo.platform.constraint.support.ValidateableProperty import grails.validation.ValidationErrors import org.springframework.context.MessageSource @@ -11,7 +12,7 @@ import spock.lang.Specification * @author: rmorrise * @author Eric Kelm */ -class CascadeValidationConstraintSpec extends Specification { +class CascadeConstraintSpec extends Specification { CascadeConstraint constraint ValidateableParent parent ValidationErrors errors @@ -35,7 +36,7 @@ class CascadeValidationConstraintSpec extends Specification { constraint.name == 'cascade' } - def "validateWithVetoing fails when constraint is set on non-validatable type"() { + def "validate fails when constraint is set on non-validatable type"() { given: constraint = new CascadeConstraint( ValidateableParent, @@ -50,7 +51,7 @@ class CascadeValidationConstraintSpec extends Specification { thrown(NoSuchMethodException) } - def "validateWithVetoing returns valid when constraint is set to validateable type and constraints pass"() { + def "validate returns valid when constraint is set to validateable type and constraints pass"() { given: constraint = new CascadeConstraint( ValidateableParent, @@ -66,7 +67,11 @@ class CascadeValidationConstraintSpec extends Specification { 0 * errors.addError(_) } - def "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail"() { + def "validate returns valid when constraint is not enabled"() { + + } + + def "validate returns invalid when constraint is set to validateable type and constraints fail"() { given: constraint = new CascadeConstraint( ValidateableParent, @@ -103,7 +108,7 @@ class CascadeValidationConstraintSpec extends Specification { }) } - def "validateWithVetoing returns invalid when constraint is set to validateable type and constraints fail on list"() { + def "validate returns invalid when constraint is set to validateable type and constraints fail on list"() { given: constraint = new CascadeConstraint( ValidateableParentWithChildList, From 4e9299d882134b9e067bab03fbaf54febfe1e130 Mon Sep 17 00:00:00 2001 From: rmorrise Date: Thu, 10 Jan 2019 17:09:33 -0500 Subject: [PATCH 4/9] Added test for disabled case. --- .../constraint/CascadeConstraintSpec.groovy | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy index f6acd82..2454241 100644 --- a/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy +++ b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy @@ -9,8 +9,9 @@ import org.springframework.validation.Errors import org.springframework.validation.FieldError import spock.lang.Specification /** - * @author: rmorrise + * @author rmorrise * @author Eric Kelm + * @author virtualdogbert */ class CascadeConstraintSpec extends Specification { CascadeConstraint constraint @@ -68,7 +69,33 @@ class CascadeConstraintSpec extends Specification { } def "validate returns valid when constraint is not enabled"() { + given: + constraint = new CascadeConstraint( + ValidateableParent, + 'property', + false, messageSource) + def target = Mock(ValidateableProperty) + def childErrors = Mock(Errors) + def rejected = Mock(Object) + String[] codes = ['A', 'B'] + def defaultMessage = 'default' + Object[] args = [Mock(Object)] + def field = 'field' + def fieldError = new FieldError('obj', field, rejected, true, codes, + args, defaultMessage) + def fieldErrors = [fieldError] + def parentName = 'foo' + + when: + def result = constraint.validate(parent, target, errors) + + then: + 0 * target.validate() + 0 * target.errors + 0 * childErrors.fieldErrors + 0 * errors.objectName + 0 * errors.addError(_) } def "validate returns invalid when constraint is set to validateable type and constraints fail"() { From cfd0367b5bbbba37e1afee6d77eea15850803bf5 Mon Sep 17 00:00:00 2001 From: rmorrise Date: Fri, 11 Jan 2019 15:21:05 -0500 Subject: [PATCH 5/9] Renamed plugin class; switched to vdog's constraint registration. --- ...y => CascadeValidationGrailsPlugin.groovy} | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) rename src/main/groovy/grails/cascade/validation/{GrailsCascadeValidationGrailsPlugin.groovy => CascadeValidationGrailsPlugin.groovy} (51%) diff --git a/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy b/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy similarity index 51% rename from src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy rename to src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy index 26b14a3..5e6d35e 100644 --- a/src/main/groovy/grails/cascade/validation/GrailsCascadeValidationGrailsPlugin.groovy +++ b/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy @@ -8,7 +8,7 @@ import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEv import org.grails.datastore.gorm.validation.constraints.registry.ConstraintRegistry import org.springframework.context.ApplicationContext -class GrailsCascadeValidationGrailsPlugin extends Plugin { +class CascadeValidationGrailsPlugin extends Plugin { def grailsVersion = "3.3.0 > *" def title = "Cascade Validation Plugin" @@ -29,31 +29,26 @@ Used with permission. def issueManagement = [system: 'GITHUB', url: 'https://github.com/rmorrise/grails-cascade-validation/issues'] def scm = [url: 'https://github.com/rmorrise/grails-cascade-validation'] - def developers = [ [ name: "Soeren Glasius", email: "soeren@glasius.dk" ], [ name: "Russell Morrisey", email: "russell.morrisey@cscglobal.com" ]] - - //Class constraintClass, MessageSource messageSource, List targetTypes = [Object] - Closure doWithSpring() {{ -> - cascadeValidationConstraintFactory(DefaultConstraintFactory, CascadeValidationConstraint, null) - }} + def developers = [[name: "Soeren Glasius", email: "soeren@glasius.dk"], [name: "Russell Morrisey", email: "russell.morrisey@cscglobal.com"]] void doWithApplicationContext() { - registerCustomConstraints(applicationContext) - } - - private void registerCustomConstraints(ApplicationContext ctx) { - //This method for registering constraints came from longwa - List registries = [] - DefaultConstraintEvaluator evaluator = ctx.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator - - // Register with both the default constraint as well as the gorm registry (it's stupid that it needs both) - // Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it - // so we are forced to invade it's privacy if we want custom constraints for Validateable instances. - registries << evaluator.constraintRegistry - registries << ctx.getBean("gormValidatorRegistry", ConstraintRegistry) - - registries.each { ConstraintRegistry registry -> - registry.addConstraint(CascadeConstraint) - } - } + registerCustomConstraints(applicationContext) + } + + private void registerCustomConstraints(ApplicationContext ctx) { + //This method for registering constraints came from longwa + List registries = [] + DefaultConstraintEvaluator evaluator = ctx.getBean(ConstraintsEvaluator) as DefaultConstraintEvaluator + + // Register with both the default constraint as well as the gorm registry (it's stupid that it needs both) + // Also the ConstraintsEvaluator evaluator constructs a new internal registry and doesn't seem to expose it + // so we are forced to invade it's privacy if we want custom constraints for Validateable instances. + registries << evaluator.constraintRegistry + registries << ctx.getBean("gormValidatorRegistry", ConstraintRegistry) + + registries.each { ConstraintRegistry registry -> + registry.addConstraint(CascadeConstraint) + } + } } From 6eae50f1335e4dac0bbe7398e7cdee6c7b85aa88 Mon Sep 17 00:00:00 2001 From: rmorrise Date: Fri, 11 Jan 2019 15:24:11 -0500 Subject: [PATCH 6/9] Updated developers --- .../validation/CascadeValidationGrailsPlugin.groovy | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy b/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy index 5e6d35e..a6ce61a 100644 --- a/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy +++ b/src/main/groovy/grails/cascade/validation/CascadeValidationGrailsPlugin.groovy @@ -29,7 +29,14 @@ Used with permission. def issueManagement = [system: 'GITHUB', url: 'https://github.com/rmorrise/grails-cascade-validation/issues'] def scm = [url: 'https://github.com/rmorrise/grails-cascade-validation'] - def developers = [[name: "Soeren Glasius", email: "soeren@glasius.dk"], [name: "Russell Morrisey", email: "russell.morrisey@cscglobal.com"]] + def developers = [ + [name: "Burt Beckwith"], + [name: "Soeren Glasius", email: "soeren@glasius.dk"], + [name: "Eric Kelm"], + [name: "Russell Morrisey", email: "russell.morrisey@cscglobal.com"], + [name: "Christian Oestreich"], + [name: "Virtualdogbert"] + ] void doWithApplicationContext() { registerCustomConstraints(applicationContext) From f069ea222d51fd9368bad7520fffb0a4aa4291dd Mon Sep 17 00:00:00 2001 From: rmorrise Date: Fri, 11 Jan 2019 15:26:58 -0500 Subject: [PATCH 7/9] Updated version and README. --- README.md | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 765a2cb..a6cbef6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ repositories { dependencies { //CSC custom plugin for 'cascade' constraint - compile ":cascade-validation:3.0.0" + compile "compile 'org.grails.plugins:cascade-validation:3.0.1" } ``` diff --git a/build.gradle b/build.gradle index 8547b53..1a6d281 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { classpath "org.grails:grails-gradle-plugin:$grailsVersion" } } -version "3.0.0" +version "3.0.1" group "org.grails.plugins" apply plugin: 'eclipse' From b0d2d99537f8eab18815e5c6117350c9444c8507 Mon Sep 17 00:00:00 2001 From: rmorrise Date: Fri, 11 Jan 2019 15:28:45 -0500 Subject: [PATCH 8/9] Re-added detailed error condition checks. --- .../constraint/CascadeConstraintSpec.groovy | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy index 2454241..0c073dc 100644 --- a/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy +++ b/src/test/groovy/com/cscinfo/platform/constraint/CascadeConstraintSpec.groovy @@ -167,7 +167,21 @@ class CascadeConstraintSpec extends Specification { 1 * child1Errors.fieldErrors >> fieldErrors 1 * child2Errors.fieldErrors >> fieldErrors target.size() * errors.objectName >> parentName - 2 * errors.addError(_) + 1 * errors.addError({ + it.objectName == parentName && + it.field == "children.0." + field && + it.bindingFailure == true && + it.codes == codes && + it.arguments == args && + it.defaultMessage == defaultMessage + }) + 1 * errors.addError({ + it.objectName == parentName && it.field == "children.1." + field && + it.bindingFailure == true && + it.codes == codes && + it.arguments == args && + it.defaultMessage == defaultMessage + }) } def "constraint does not support non-validateable types"() { From 7ae8a55daf96f9e43fc537f6893be5464a6b96e2 Mon Sep 17 00:00:00 2001 From: rmorrise Date: Fri, 11 Jan 2019 18:21:33 -0500 Subject: [PATCH 9/9] Fixed typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6cbef6..405cd89 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ repositories { dependencies { //CSC custom plugin for 'cascade' constraint - compile "compile 'org.grails.plugins:cascade-validation:3.0.1" + compile "org.grails.plugins:cascade-validation:3.0.1" } ```