Skip to content

Commit

Permalink
Merge pull request #19 from rmorrise/vdog-merge
Browse files Browse the repository at this point in the history
Merged in virtualdogbert's changes from grails consortium; tested with grails 3.3.4
  • Loading branch information
rmorrise authored Jan 11, 2019
2 parents 11cd68c + 7ae8a55 commit d5671c3
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 86 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repositories {
dependencies {
//CSC custom plugin for 'cascade' constraint
compile ":cascade-validation:3.0.0"
compile "org.grails.plugins:cascade-validation:3.0.1"
}
```

Expand Down
8 changes: 5 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ buildscript {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
}
}
version "3.0.0"
version "3.0.1"
group "org.grails.plugins"

apply plugin: 'eclipse'
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()
Expand All @@ -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 {
Expand Down
Binary file added grails-wrapper.jar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.cscinfo.platform.constraint

import org.grails.datastore.gorm.validation.constraints.AbstractVetoingConstraint
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.grails.datastore.gorm.validation.constraints.AbstractConstraint
import org.springframework.context.MessageSource
import org.springframework.validation.Errors
import org.springframework.validation.FieldError
Expand All @@ -16,67 +18,84 @@ import org.springframework.validation.FieldError
* @author Eric Kelm
* @author Russell Morrisey
*/
class CascadeValidationConstraint extends AbstractVetoingConstraint {
public static final String NAME = "cascadeValidate"
@CompileStatic
class CascadeConstraint extends AbstractConstraint {
static final String CASCADE_CONSTRAINT = "cascade"

private final boolean 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

if (!(constraintParameter instanceof Boolean)) {
throw new IllegalArgumentException("Parameter for constraint [$CASCADE_CONSTRAINT] of property [$constraintPropertyName] of class [$constraintOwningClass] must be a boolean")
}
}

String getName() { NAME }
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) {

@Override
protected boolean processValidateWithVetoing(target, 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
}

private boolean validateValue(target, value, errors, index = null) {
/**
* 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 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 (!getParameter()) {
return
}

if (value.validate()) {
return false
return
}

String objectName = target.errors.objectName
Errors childErrors = value.errors
List<FieldError> 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) {
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")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package grails.cascade.validation

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 CascadeValidationGrailsPlugin extends Plugin {

def grailsVersion = "3.3.0 > *"
def title = "Cascade Validation Plugin"
def author = "Russell Morrisey"
def authorEmail = "rmorrise@cscinfo.com"
def description = '''\
Establishes a 'cascade' constraint property for validateable objects. If "cascade:true" is set
on a nested object, the nested object's validate() method will be invoked and the results will
be reported as part of the parent object's validation.
Based on a blog post by Eric Kelm:
http://asoftwareguy.com/2013/07/01/grails-cascade-validation-for-pogos/
Used with permission.
'''
def documentation = "https://github.com/rmorrise/grails-cascade-validation/wiki/How-to-use-cascade-validation"
def license = "APACHE"
def organization = [name: "CSC", url: "http://www.cscglobal.com/"]
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: "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)
}

private void registerCustomConstraints(ApplicationContext ctx) {
//This method for registering constraints came from longwa
List<ConstraintRegistry> 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)
}
}

}

This file was deleted.

Loading

0 comments on commit d5671c3

Please sign in to comment.