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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import build.buf.protovalidate.exceptions.CompilationException;
import build.buf.protovalidate.exceptions.ExecutionException;
import build.buf.validate.ValidateProto;
import build.buf.validate.Violation;
import build.buf.validate.Violations;
import build.buf.validate.conformance.harness.TestConformanceRequest;
import build.buf.validate.conformance.harness.TestConformanceResponse;
Expand Down Expand Up @@ -100,11 +99,11 @@ static TestResult testCase(
private static TestResult validate(Validator validator, DynamicMessage dynamicMessage) {
try {
ValidationResult result = validator.validate(dynamicMessage);
List<Violation> violations = result.getViolations();
if (violations.isEmpty()) {
if (result.isSuccess()) {
return TestResult.newBuilder().setSuccess(true).build();
}
Violations error = Violations.newBuilder().addAllViolations(violations).build();
Violations error =
Violations.newBuilder().addAllViolations(result.toProto().getViolationsList()).build();
return TestResult.newBuilder().setValidationError(error).build();
} catch (CompilationException e) {
return TestResult.newBuilder().setCompilationError(e.getMessage()).build();
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/build/buf/protovalidate/ValidationResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

package build.buf.protovalidate;

import build.buf.validate.Violation;
import build.buf.validate.Violations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -71,12 +72,27 @@ public String toString() {
builder.append("Validation error:");
for (Violation violation : violations) {
builder.append("\n - ");
if (!violation.getFieldPath().isEmpty()) {
builder.append(violation.getFieldPath());
if (!violation.toProto().getFieldPath().isEmpty()) {
builder.append(violation.toProto().getFieldPath());
builder.append(": ");
}
builder.append(String.format("%s [%s]", violation.getMessage(), violation.getConstraintId()));
builder.append(
String.format(
"%s [%s]", violation.toProto().getMessage(), violation.toProto().getConstraintId()));
}
return builder.toString();
}

/**
* Converts the validation result to its equivalent protobuf form.
*
* @return The protobuf form of this validation result.
*/
public build.buf.validate.Violations toProto() {
List<build.buf.validate.Violation> protoViolations = new ArrayList<>();
for (Violation violation : violations) {
protoViolations.add(violation.toProto());
}
return Violations.newBuilder().addAllViolations(protoViolations).build();
}
}
16 changes: 11 additions & 5 deletions src/main/java/build/buf/protovalidate/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
import build.buf.protovalidate.exceptions.CompilationException;
import build.buf.protovalidate.exceptions.ValidationException;
import build.buf.protovalidate.internal.celext.ValidateLibrary;
import build.buf.protovalidate.internal.errors.FieldPathUtils;
import build.buf.protovalidate.internal.errors.ConstraintViolation;
import build.buf.protovalidate.internal.evaluator.Evaluator;
import build.buf.protovalidate.internal.evaluator.EvaluatorBuilder;
import build.buf.protovalidate.internal.evaluator.MessageValue;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.List;
import org.projectnessie.cel.Env;
import org.projectnessie.cel.Library;

Expand Down Expand Up @@ -75,11 +77,15 @@ public ValidationResult validate(Message msg) throws ValidationException {
}
Descriptor descriptor = msg.getDescriptorForType();
Evaluator evaluator = evaluatorBuilder.load(descriptor);
ValidationResult result = evaluator.evaluate(new MessageValue(msg), failFast);
if (result.isSuccess()) {
return result;
List<ConstraintViolation.Builder> result = evaluator.evaluate(new MessageValue(msg), failFast);
if (result.isEmpty()) {
return ValidationResult.EMPTY;
}
List<Violation> violations = new ArrayList<>();
jchadwick-buf marked this conversation as resolved.
Show resolved Hide resolved
for (ConstraintViolation.Builder builder : result) {
violations.add(builder.build());
}
return new ValidationResult(FieldPathUtils.calculateFieldPathStrings(result.getViolations()));
return new ValidationResult(violations);
}

/**
Expand Down
65 changes: 65 additions & 0 deletions src/main/java/build/buf/protovalidate/Violation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2023-2024 Buf Technologies, Inc.
//
// Licensed 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
//
// http://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 build.buf.protovalidate;

import com.google.protobuf.Descriptors;
import javax.annotation.Nullable;

/**
* {@link Violation} provides all of the collected information about an individual constraint
* violation.
*/
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 FieldValue} represents a Protobuf field value inside a Protobuf message. */
interface FieldValue {
/**
* Gets the value of the field, which may be null, a primitive, a Map or a List.
*
* @return The value of the protobuf field.
*/
@Nullable
Object getValue();

/**
* Gets the field descriptor of the field this value is from.
*
* @return A FieldDescriptor pertaining to this field.
*/
Descriptors.FieldDescriptor getDescriptor();
}

/**
* Gets the protobuf form of this violation.
*
* @return The protobuf form of this violation.
*/
build.buf.validate.Violation toProto();

/**
* Gets the value of the field this violation pertains to, or null if there is none.
*
* @return Value of the field associated with the violation, or null if there is none.
*/
@Nullable
FieldValue getFieldValue();

/**
* Gets the value of the rule this violation pertains to, or null if there is none.
*
* @return Value of the rule associated with the violation, or null if there is none.
*/
@Nullable
FieldValue getRuleValue();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import build.buf.protovalidate.Config;
import build.buf.protovalidate.exceptions.CompilationException;
import build.buf.protovalidate.internal.errors.FieldPathUtils;
import build.buf.protovalidate.internal.evaluator.ObjectValue;
import build.buf.protovalidate.internal.evaluator.Value;
import build.buf.protovalidate.internal.expression.AstExpression;
import build.buf.protovalidate.internal.expression.CompiledProgram;
import build.buf.protovalidate.internal.expression.Expression;
Expand Down Expand Up @@ -142,6 +144,7 @@ public List<CompiledProgram> compile(
Env ruleEnv = getRuleEnv(fieldDescriptor, message, rule.field, forItems);
Variable ruleVar = Variable.newRuleVariable(message, message.getField(rule.field));
ProgramOption globals = ProgramOption.globals(ruleVar);
Value ruleValue = new ObjectValue(rule.field, message.getField(rule.field));
try {
Program program = ruleEnv.program(rule.astExpression.ast, globals, PARTIAL_EVAL_OPTIONS);
Program.EvalResult evalResult = program.eval(Activation.emptyActivation());
Expand All @@ -158,13 +161,17 @@ public List<CompiledProgram> compile(
Ast residual = ruleEnv.residualAst(rule.astExpression.ast, evalResult.getEvalDetails());
programs.add(
new CompiledProgram(
ruleEnv.program(residual, globals), rule.astExpression.source, rule.rulePath));
ruleEnv.program(residual, globals),
rule.astExpression.source,
rule.rulePath,
ruleValue));
} catch (Exception e) {
programs.add(
new CompiledProgram(
ruleEnv.program(rule.astExpression.ast, globals),
rule.astExpression.source,
rule.rulePath));
rule.rulePath,
ruleValue));
}
}
return Collections.unmodifiableList(programs);
Expand Down
Loading
Loading