Skip to content
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

Add field and rule value to violations #215

Merged
merged 11 commits into from
Dec 12, 2024
Merged

Conversation

jchadwick-buf
Copy link
Member

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.

  • The first commit adds the Violation wrapper class and plumbs it through all uses of Violation.
  • The second commit makes Value non-internal so we can use it to expose protobuf values in a cleaner fashion.
  • The third commit actually implements filling the fieldValue and ruleValuefields.

This is a breaking change. The API changes in the following ways:

  • ValidationResult now provides a new wrapper Violation type instead of the buf.validate.Violation message. This new wrapper has a getProto() method to return a buf.validate.Violation message, and ValidationResult now has a toProto() method to return a buf.validate.Violations message.

src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Value.java Outdated Show resolved Hide resolved
src/main/java/build/buf/protovalidate/Violation.java Outdated Show resolved Hide resolved
@jchadwick-buf
Copy link
Member Author

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.

@jchadwick-buf
Copy link
Member Author

jchadwick-buf commented Dec 6, 2024

I did a bit of refactorings:

  • Now Violation is an interface. There's an internal ConstraintViolation that implements it, and we stay in Builders until the end.
  • Value is still private, but there's a Violation.FieldValue interface to encapsulate the Object and FieldDescriptor for the field and rule values. This seems a bit less awkward.
  • Wrapper interfaces, introduced just recently for structured field paths, are removed. Instead, I just mirrored what I started doing in the pv-go PR instead: carrying the necessary data on the Value. A bit tricky, but far simpler.
  • (Also, no more need to awkwardly re-build violation/rule paths everywhere anymore. We can build them incrementally.)

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.

src/main/java/build/buf/protovalidate/Validator.java Outdated Show resolved Hide resolved
@Nullable Value fieldValue;
@Nullable Value ruleValue;

public interface Violation {
Copy link
Member

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 {
Copy link
Member

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...

Copy link
Member Author

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.

import java.util.List;
import javax.annotation.Nullable;

class EvaluatorBase {
Copy link
Member

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.

Copy link
Member Author

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.

Comment on lines 89 to 97
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));
Copy link
Member

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...

Copy link
Member Author

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.

Copy link
Member

@jhump jhump left a 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.

@@ -34,7 +34,8 @@
* com.google.protobuf.Any}'s within an expression, breaking evaluation if the type is unknown at
* runtime.
*/
class AnyEvaluator extends EvaluatorBase implements Evaluator {
class AnyEvaluator implements Evaluator {
private final ConstraintViolationHelper constraintViolationHelper;
Copy link
Member

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.

Copy link
Member Author

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.

@jchadwick-buf jchadwick-buf merged commit df11bf2 into main Dec 12, 2024
4 checks passed
@jchadwick-buf jchadwick-buf deleted the jchadwick/rule-field-value branch December 12, 2024 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants