-
Notifications
You must be signed in to change notification settings - Fork 17
Add field and rule value to violations #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
I'm going to try to pick up the violation accumulator change today if possible. Please feel free to review what's here though. Particularly interested if there's any way we can clean up the Violation API surface to be a bit nicer. |
|
I did a bit of refactorings:
This necessitated some reshuffling that may not be intuitive and I'm sure there's ugly things in here but I wanted to get this out in some shape before the weekend. |
| @Nullable Value fieldValue; | ||
| @Nullable Value ruleValue; | ||
|
|
||
| public interface Violation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it!!
| * {@link ConstraintViolation} contains all of the collected information about an individual | ||
| * constraint violation. | ||
| */ | ||
| public class ConstraintViolation implements Violation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is the pattern everything else uses -- public access but in an "internal" package.
It's a bit unfortunate that this uses Go idioms instead of Java ones, though I guess the Java idiom (using default/package-private access) would require dumping everything into one package, which is admittedly nasty in other ways...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, same feelings here. I don't love it exactly, but actually it seems like a lot of Java software winds up doing basically this sort of thing, since there's really not much flexibility w.r.t. visibility.
src/main/java/build/buf/protovalidate/internal/evaluator/EmbeddedMessageEvaluator.java
Outdated
Show resolved
Hide resolved
| import java.util.List; | ||
| import javax.annotation.Nullable; | ||
|
|
||
| class EvaluatorBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of extending this in every evaluator type, I think it would be better to use composition and maybe call it a ConstraintViolationHelper -- since it's really just used to get values for building a violation.
There's a lot of places that now call super(evaluator) that IMO don't look intuitive. Also, this doesn't provide any base Evaluator method implementations so it is awkward as a base class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was going to try to extract more common functionality but it didn't work out so it's a bit pointless now. Now it's ConstraintViolationHelper and not being used as a base class.
| ConstraintViolation.Builder violation = | ||
| ConstraintViolation.newBuilder() | ||
| .addAllRulePathElements(getRulePrefixElements()) | ||
| .addAllRulePathElements(IN_RULE_PATH.getElementsList()) | ||
| .addFirstFieldPathElement(getFieldPathElement()) | ||
| .setConstraintId("any.in") | ||
| .setMessage("type URL must be in the allow list") | ||
| .setFieldValue(new ConstraintViolation.FieldValue(val)) | ||
| .setRuleValue(new ConstraintViolation.FieldValue(this.inValue, IN_DESCRIPTOR)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to try to wrap some of this up into a helper in EvaluatorBase, to de-boiler-plate the various calls that do this.
It looks like they all call ConstraintViolation.newBuilder().addAllRulePathElements(getRulePrefixElements()), but the other methods vary slightly. Many also call .addFirstFieldPathElement(getFieldPathElement()), but some do not. Anyhow, maybe not worth looking into since they all seem to vary a little -- moving the builder pattern steps into method arguments would certainly make call sites more concise, but potentially at the cost of readability, so maybe not worth doing more here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am leaning on the other direction (not having EvaluatorBase) instead, since I'm on the side of this not being worth it, at least for now. It does feel like there's likely something more elegant to pull out of this, but with all of the edge cases we have right now it's not currently apparent.
jhump
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. One tiny nit, but not blocking if you disagree with it. If you do make the change, feel free to merge w/out further review.
| */ | ||
| class AnyEvaluator extends EvaluatorBase implements Evaluator { | ||
| class AnyEvaluator implements Evaluator { | ||
| private final ConstraintViolationHelper constraintViolationHelper; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would call the field helper (here and throughout) to make the call sites more readable. They are so verbose, it's a bit of a chore to read or scan the code with this long name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I accidentally made my Java a little too Java. Fixed.
…d nested (#246) `protovalidate-java` cannot use a CEL expression where `this` represents a repeated message. `EvaluatorBuilder`'s `processFieldExpression` is currently directly using `Decls.newObjectType()` to create an object type based on the name of the message type in the field instead of honoring whether or not the field is repeated and returning a `Decls.newListType()`. `provalidate` [recently added](bufbuild/protovalidate#329) conformance tests that illustrate this problem. To fix this, this PR: 1. Adds JUnit tests and `.proto` that demonstrate the issue. 2. Updates `EvaluatorBuilder`'s `processFieldExpression` to delegate the determination of the CEL type to `DescriptorMappings.getCELType()`, which uses the `FieldDescriptor` to determine the correct type to return. This method requires passing a `forItems` flag stating if the type returned should be the list (or map) type for a field or the type of the items in the collection. Using the [new `nestedRule` property](#215 state provides the value of this flag, [similar to how it's used in `processStandardConstraints`](https://github.com/bufbuild/protovalidate-java/blob/b3cd73121d04182dc989b76902e468f94b5f0b99/src/main/java/build/buf/protovalidate/EvaluatorBuilder.java#L421) to pass the `forItems` flag to `constraintCache.compile()`. 3. Because there were now 7 places where `valueEvaluator.getNestedRule() == null` was used to determine behavior, I moved this comparison into a `hasNestedRule()` method on `ValueEvaluator`, using conformance state to check regression. Conformance notes: This PR passes conformance in `protovalidate-java`. It has been tested against the conformance suite at the current head of `protovalidate` (which now includes the standard library, so there are many new failures). Before changes in this PR, there were 137 failures. After, there were 135: ``` BEFORE: FAIL (failed: 137, skipped: 0, passed: 2681, total: 2818) AFTER : FAIL (failed: 135, skipped: 0, passed: 2683, total: 2818) ``` Running `field_expression/repeated/message` shows that the two conformance tests fixed were for the desired issue: ``` BEFORE: FAIL (failed: 2, skipped: 0, passed: 2, total: 4) AFTER: PASS (failed: 0, skipped: 0, passed: 4, total: 4) ```
Adds the ability to access the captured rule and field value from a Violation.
There's a bit of refactoring toil, so I tried to split the commits cleanly to make this easier to review.
Violationwrapper class and plumbs it through all uses ofViolation.Valuenon-internal so we can use it to expose protobuf values in a cleaner fashion.fieldValueandruleValuefields.This is a breaking change. The API changes in the following ways:
ValidationResultnow provides a new wrapperViolationtype instead of thebuf.validate.Violationmessage. This new wrapper has agetProto()method to return abuf.validate.Violationmessage, andValidationResultnow has atoProto()method to return abuf.validate.Violationsmessage.