jamesfredley commented on code in PR #16624:
URL: https://github.com/apache/grails-core/pull/16624#discussion_r4417434024
##########
grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/validation/jakarta/GormValidatorAdapter.groovy:
##########
@@ -60,6 +67,58 @@ class GormValidatorAdapter extends SpringValidatorAdapter
implements CascadingVa
}
}
+ /**
+ * Validates the object in the given groups, or in the default group when
there are none
+ *
+ * @param obj The object to validate
+ * @param errors The errors to report the violations to
+ * @param cascade Whether validation cascades to the persistent
associations of the entity through the
+ * properties marked {@code @Valid}; it cascades through the other
properties marked {@code @Valid} either way
+ * @param groups The groups to validate
+ * @since 8.0.1
+ */
+ void validate(Object obj, Errors errors, boolean cascade, Class<?>[]
groups) {
+ CASCADE_VALIDATION.set(cascade)
+ try {
+ validate(obj, errors, (Object[]) groups)
+ } finally {
+ CASCADE_VALIDATION.remove()
+ }
+ }
+
+ /**
+ * Validates the object in the given groups as {@link #validate(Object,
Errors, boolean, Class[])} does,
+ * reporting only the violations of the constraints declared in one of the
reported groups. The provider
+ * validates a group together with the groups it extends; when validation
cascades from an object validated in
+ * a group whose extended groups a {@code @ConvertGroup} converts, the
associated object is validated in the
+ * group alone, so the violations of the converted groups are left out.
+ *
+ * @param obj The object to validate
+ * @param errors The errors to report the violations to
+ * @param cascade Whether validation cascades to the persistent
associations of the entity
+ * @param groups The groups to validate
+ * @param reportedGroups The groups whose violations to report, null to
report every violation
+ * @since 8.0.1
+ */
+ void validate(Object obj, Errors errors, boolean cascade, Class<?>[]
groups, Set<Class<?>> reportedGroups) {
+ if (reportedGroups == null) {
+ validate(obj, errors, cascade, groups)
+ return
+ }
+ CASCADE_VALIDATION.set(cascade)
+ try {
+ Set<ConstraintViolation<Object>> reported = new LinkedHashSet<>()
+ for (ConstraintViolation<Object> violation :
thisValidator.validate(obj, groups)) {
+ if
(!Collections.disjoint(violation.constraintDescriptor.groups, reportedGroups)) {
+ reported.add(violation)
Review Comment:
This keeps a violation only when its groups intersect the persistent
entity's reported groups. A transient `@Valid` child can convert `Extended` to
`DetailsChecks`. Those violations are real and then discarded, so the parent is
accepted with invalid details. Filter each cascaded bean with the groups in
effect for that bean.
##########
grails-datamapping-validation/src/main/groovy/grails/gorm/validation/PersistentEntityValidator.groovy:
##########
@@ -232,63 +427,37 @@ class PersistentEntityValidator implements
CascadingValidator, ConstrainedEntity
MappingContext mappingContext = associatedEntity.getMappingContext()
EntityReflector associatedReflector =
mappingContext.getEntityReflector(associatedEntity)
- Association otherSide = null
- if (association.isBidirectional()) {
- otherSide = association.getInverseSide()
- }
-
- Map associatedConstrainedProperties
+ PersistentEntityValidator associatedValidator = null
+ Map<String, ConstrainedProperty> associatedConstrainedProperties =
Collections.<String, ConstrainedProperty>emptyMap()
def validator = mappingContext.getEntityValidator(associatedEntity)
if (validator instanceof PersistentEntityValidator) {
- associatedConstrainedProperties = ((PersistentEntityValidator)
validator).getConstrainedProperties()
- }
- else {
- associatedConstrainedProperties = Collections.<String,
ConstrainedProperty>emptyMap()
+ associatedValidator = (PersistentEntityValidator) validator
+ associatedConstrainedProperties =
associatedValidator.getValidatedConstrainedProperties(associatedAllHints)
}
- // Invoke any beforeValidate callbacks on the associated object before
validating
- validateHelper.invokeBeforeValidate(associatedObject,
associatedConstrainedProperties.keySet() as List<String>)
-
- List<PersistentProperty> associatedPersistentProperties =
associatedEntity.getPersistentProperties()
String nestedPath = errors.getNestedPath()
try {
errors.setNestedPath(buildNestedPath(nestedPath, propertyName,
indexOrKey))
+ CASCADE.set(new Cascade(associatedHints, associatedAllHints,
association))
- for (PersistentProperty associatedPersistentProperty :
associatedPersistentProperties) {
- if (association.isEmbedded() &&
EMBEDDED_EXCLUDES.contains(associatedPersistentProperty.getName())) {
- continue
- }
-
- String associatedPropertyName =
associatedPersistentProperty.getName()
- if
(associatedConstrainedProperties.containsKey(associatedPropertyName)) {
- ConstrainedProperty associatedConstrainedProperty =
associatedConstrainedProperties.get(associatedPropertyName)
- validatePropertyWithConstraint(associatedObject,
errors.getNestedPath() + associatedPropertyName, associatedReflector, errors,
associatedConstrainedProperty, associatedPersistentProperty)
- }
-
- // Don't continue cascade if the the other side is equal to
avoid stack overflow
- if (associatedPersistentProperty == otherSide) {
- continue
- }
-
- if (associatedPersistentProperty instanceof Association) {
- if (association.isBidirectional() &&
associatedPersistentProperty == association.inverseSide) {
- // If this property is the inverse side of the
currently processed association then
- // we don't want to process it
- continue
- }
+ // the constraints do not depend on the hints of a step, so they
are validated once for all of them
+ if (validatedObjects.add(new
ValidatedConstraints(associatedObject, associatedAllHints))) {
+ // Invoke any beforeValidate callbacks on the associated
object before validating
+ validateHelper.invokeBeforeValidate(associatedObject,
associatedConstrainedProperties.keySet() as List<String>)
+ validateConstraints(associatedObject, associatedEntity,
errors, associatedReflector, associatedConstrainedProperties, association,
false)
+ }
- cascadeToAssociativeProperty(
- associatedObject,
- errors,
- associatedReflector,
- (Association) associatedPersistentProperty,
- validatedObjects)
- }
+ if (associatedValidator != null) {
+
associatedValidator.validateAssociationsAndBeyond(associatedObject, errors,
true, associatedHints, validatedObjects)
Review Comment:
This passes the associated object through without the unwrap the root path
does. An initialized proxy whose target has a valid field-annotated `label` is
then read by Bean Validation as the proxy, so `NotBlank` fails with a null
rejected value. Unwrap an initialized association before cascading, the same
way the root object is unwrapped.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]