Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class GormValidatorAdapter extends SpringValidatorAdapter {
}

@Override
def <T> Set<ConstraintViolation<T>> validate(T object, Class<?>[] groups) {
<T> Set<ConstraintViolation<T>> validate(T object, Class<?>[] groups) {
def constraintViolations = super.validate(object, groups)
if (object instanceof GormValidateable) {
def errors = ((GormValidateable) object).getErrors()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class GormValidatorFactoryAdapter implements ValidatorFactory {
}

@Override
def <T> T unwrap(Class<T> type) {
<T> T unwrap(Class<T> type) {
return factory.unwrap(type)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class JakartaValidatorRegistry extends DefaultValidatorRegistry implements Valid
*
* @return The configuration
*/
protected Configuration<?> buildConfiguration() {
protected Configuration buildConfiguration() {
MappingContext context = this.mappingContext
MessageSource ms = messageSource
return buildConfigurationFor(context, ms)
Expand All @@ -86,7 +86,7 @@ class JakartaValidatorRegistry extends DefaultValidatorRegistry implements Valid
* @return The configuration
*/
static Configuration buildConfigurationFor(MappingContext context, MessageSource messageSource) {
Configuration<? extends Configuration> validatorConfiguration = Validation.byDefaultProvider()
Configuration validatorConfiguration = Validation.byDefaultProvider()
.configure()
validatorConfiguration = validatorConfiguration.ignoreXmlConfiguration()
validatorConfiguration = validatorConfiguration.traversableResolver(new MappingContextTraversableResolver(context))
Expand Down Expand Up @@ -164,7 +164,7 @@ class JakartaValidatorRegistry extends DefaultValidatorRegistry implements Valid
}

@Override
def <T> T unwrap(Class<T> aClass) {
<T> T unwrap(Class<T> aClass) {
return validatorFactory.unwrap(aClass)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.grails.datastore.gorm.validation.jakarta;

import java.util.Arrays;
import java.util.Objects;

/**
* A method key used to store information about a method
Expand All @@ -29,9 +30,9 @@
*/
class MethodKey {
private final String name;
private final Class[] parameterTypes;
private final Class<?>[] parameterTypes;

public MethodKey(String name, Class[] parameterTypes) {
public MethodKey(String name, Class<?>[] parameterTypes) {
this.name = name;
this.parameterTypes = parameterTypes;
}
Expand All @@ -43,8 +44,7 @@ public boolean equals(Object o) {

MethodKey methodKey = (MethodKey) o;

if (name != null ? !name.equals(methodKey.name) : methodKey.name != null) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Objects.equals(name, methodKey.name)) return false;
return Arrays.equals(parameterTypes, methodKey.parameterTypes);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ class MethodValidationImplementer implements ServiceEnhancer {
}

@Override
@SuppressWarnings('unused')
void implement(ClassNode domainClassNode, MethodNode abstractMethodNode, MethodNode newMethodNode, ClassNode targetClassNode) {
// no-op
// no-op: doesImplement() always returns false, so the framework never invokes this
}

@Override
Expand All @@ -108,7 +109,7 @@ class MethodValidationImplementer implements ServiceEnhancer {
Statement body = (Statement) newMethodNode.code

// add parameter name data for the service
weaveParameterNameData(domainClassNode, newMethodNode, abstractMethodNode)
weaveParameterNameData(newMethodNode, abstractMethodNode)

// weave the ValidatedService trait
AbstractTraitApplyingGormASTTransformation.weaveTraitWithGenerics(
Expand Down Expand Up @@ -142,7 +143,9 @@ class MethodValidationImplementer implements ServiceEnhancer {

// add a first line to the method body that validates the method
ArrayExpression argArray = new ArrayExpression(OBJECT_TYPE, validateArgsList)
String validateMethodName = abstractMethodNode.exceptions?.contains(make(ConstraintViolationException)) ? 'jakartaValidate' : 'validate'
boolean throwsConstraintViolationException = abstractMethodNode.exceptions != null &&
Arrays.asList(abstractMethodNode.exceptions).contains(make(ConstraintViolationException))
String validateMethodName = throwsConstraintViolationException ? 'jakartaValidate' : 'validate'
MethodCallExpression validateCall = callThisD(ValidatedService, validateMethodName, args(varThis(), varX(methodField), argArray))
if (body instanceof BlockStatement) {
((BlockStatement) body).statements.add(0, stmt(validateCall))
Expand All @@ -157,7 +160,7 @@ class MethodValidationImplementer implements ServiceEnhancer {

}

protected void weaveParameterNameData(ClassNode domainClassNode, MethodNode newMethodNode, MethodNode abstractMethodNode) {
protected void weaveParameterNameData(MethodNode newMethodNode, MethodNode abstractMethodNode) {
ClassNode newClass = newMethodNode.declaringClass
ModuleNode module = abstractMethodNode.declaringClass.module
String innerClassName = "${newClass.name}\$${ParameterNameProvider.simpleName}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ class ValidationEventListener extends AbstractPersistenceEventListener {
FlushModeType previousFlushMode = currentSession.flushMode
try {
currentSession.setFlushMode(FlushModeType.COMMIT)
boolean hasErrors = false
boolean hasErrors
if (source instanceof ConnectionSourcesProvider) {
def connectionSourceName = ((ConnectionSourcesProvider) source).connectionSources.defaultConnectionSource.name
GormValidationApi validationApi = GormEnhancer.findValidationApi((Class<Object>) entityObject.getClass(), connectionSourceName)
GormValidationApi validationApi = GormEnhancer.findValidationApi((Class<Object>) (Class) entityObject.getClass(), connectionSourceName)
hasErrors = !validationApi.validate((Object) entityObject)
}
else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.grails.datastore.gorm.validation.constraints

import grails.gorm.annotation.Entity
import org.grails.datastore.gorm.validation.constraints.builtin.UniqueConstraint
import org.grails.datastore.mapping.keyvalue.mapping.config.KeyValueMappingContext
import org.grails.datastore.mapping.model.MappingContext
import org.springframework.context.support.StaticMessageSource
import spock.lang.Specification

class MappingContextAwareConstraintFactorySpec extends Specification {

MappingContext mappingContext = new KeyValueMappingContext("test")
MappingContextAwareConstraintFactory factory =
new MappingContextAwareConstraintFactory(UniqueConstraint, new StaticMessageSource(), mappingContext)

void "builds a constraint when the owning class is a registered persistent entity"() {
given:
mappingContext.addPersistentEntities(FactoryBook)
mappingContext.initialize()

when:
def constraint = factory.build(FactoryBook, 'title', true)

then:
constraint instanceof UniqueConstraint
}

void "returns null when the owning class is not a registered persistent entity"() {
expect:
factory.build(String, 'title', true) == null
}
}

@Entity
class FactoryBook {
String title
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.grails.datastore.gorm.validation.jakarta

import spock.lang.Specification

class ConfigurableParameterNameProviderSpec extends Specification {

ConfigurableParameterNameProvider provider = new ConfigurableParameterNameProvider()

void "returns registered parameter names for a method"() {
given:
def method = Sample.getMethod('greet', String, Integer)
provider.addParameterNames('greet', [String, Integer] as Class[], ['name', 'times'])

expect:
provider.getParameterNames(method) == ['name', 'times']
}

void "returns default arg-prefixed names for an unregistered method"() {
given:
def method = Sample.getMethod('greet', String, Integer)

expect:
provider.getParameterNames(method) == ['arg0', 'arg1']
}

void "returns registered parameter names for a constructor"() {
given:
def constructor = Sample.getConstructor(String)
provider.addParameterNames('<init>', [String] as Class[], ['name'])

expect:
provider.getParameterNames(constructor) == ['name']
}

void "returns default arg-prefixed names for an unregistered constructor"() {
given:
def constructor = Sample.getConstructor(String)

expect:
provider.getParameterNames(constructor) == ['arg0']
}

void "does not register names when any argument is null"() {
when:
provider.addParameterNames(null, [String] as Class[], ['name'])
provider.addParameterNames('greet', null, ['name'])
provider.addParameterNames('greet', [String, Integer] as Class[], null)

then:
provider.getParameterNames(Sample.getMethod('greet', String, Integer)) == ['arg0', 'arg1']
}
}

class Sample {

Sample(String name) {
}

void greet(String name, Integer times) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.grails.datastore.gorm.validation.jakarta

import jakarta.validation.ConstraintViolation
import jakarta.validation.ConstraintViolationException
import jakarta.validation.Validation
import jakarta.validation.Validator
import jakarta.validation.constraints.NotBlank

import org.springframework.validation.Errors

import spock.lang.Specification

class ConstraintViolationUtilsSpec extends Specification {

Validator validator = Validation.byDefaultProvider().configure().buildValidatorFactory().getValidator()

void "converts a ConstraintViolationException to Errors using the target's simple class name"() {
given:
def target = new Product(name: '')
Set<ConstraintViolation<Product>> violations = validator.validate(target)
def exception = new ConstraintViolationException(violations)

when:
Errors errors = ConstraintViolationUtils.asErrors(target, exception)

then:
errors.objectName == 'Product'
errors.hasFieldErrors('name')
errors.getFieldError('name').rejectedValue == ''
}

void "converts a set of ConstraintViolation instances to Errors"() {
given:
def target = new Product(name: '')
Set<ConstraintViolation> violations = validator.validate(target) as Set<ConstraintViolation>

when:
Errors errors = ConstraintViolationUtils.asErrors(target, violations)

then:
errors.objectName == 'Product'
errors.hasFieldErrors('name')
errors.getFieldError('name').rejectedValue == ''
}
}

class Product {
@NotBlank
String name
}
Loading
Loading