Skip to content

Commit 261b5b1

Browse files
author
jchadwick-buf
authored
Implement structured field and rule paths for violations (#63)
Implements the changes needed to pass conformance after bufbuild/protovalidate#265. DO NOT MERGE until the following is done: - [x] bufbuild/protovalidate#265 is merged - [x] Dependencies updated to stable version release
1 parent b03f050 commit 261b5b1

File tree

12 files changed

+267
-84
lines changed

12 files changed

+267
-84
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ COPYRIGHT_YEARS := 2023
1111
LICENSE_IGNORE := -e internal/testdata/
1212
LICENSE_HEADER_VERSION := 0294fdbe1ce8649ebaf5e87e8cdd588e33730bbb
1313
# NOTE: Keep this version in sync with the version in `/bazel/deps.bzl`.
14-
PROTOVALIDATE_VERSION ?= v0.8.1
14+
PROTOVALIDATE_VERSION ?= v0.9.0
1515

1616
# Set to use a different compiler. For example, `GO=go1.18rc1 make test`.
1717
GO ?= go

bazel/deps.bzl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ _dependencies = {
6565
},
6666
# NOTE: Keep Version in sync with `/Makefile`.
6767
"com_github_bufbuild_protovalidate": {
68-
"sha256": "c637c8cbaf71b6dc38171e47c2c736581b4cfef385984083561480367659d14f",
69-
"strip_prefix": "protovalidate-0.8.1",
68+
"sha256": "fca6143d820e9575f3aec328918fa25acc8eeb6e706050127d3a36cfdede4610",
69+
"strip_prefix": "protovalidate-0.9.0",
7070
"urls": [
71-
"https://github.com/bufbuild/protovalidate/archive/v0.8.1.tar.gz",
71+
"https://github.com/bufbuild/protovalidate/archive/v0.9.0.tar.gz",
7272
],
7373
},
7474
}

buf/validate/internal/cel_constraint_rules.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ namespace {
2828

2929
absl::Status ProcessConstraint(
3030
ConstraintContext& ctx,
31-
absl::string_view fieldPath,
3231
const google::api::expr::runtime::BaseActivation& activation,
3332
const CompiledConstraint& expr) {
3433
auto result_or = expr.expr->Evaluate(activation, ctx.arena);
@@ -40,17 +39,21 @@ absl::Status ProcessConstraint(
4039
if (!result.BoolOrDie()) {
4140
// Add violation with the constraint message.
4241
Violation& violation = *ctx.violations.add_violations();
43-
*violation.mutable_field_path() = fieldPath;
4442
violation.set_message(expr.constraint.message());
4543
violation.set_constraint_id(expr.constraint.id());
44+
if (expr.rulePath.has_value()) {
45+
*violation.mutable_rule() = *expr.rulePath;
46+
}
4647
}
4748
} else if (result.IsString()) {
4849
if (!result.StringOrDie().value().empty()) {
4950
// Add violation with custom message.
5051
Violation& violation = *ctx.violations.add_violations();
51-
*violation.mutable_field_path() = fieldPath;
5252
violation.set_message(std::string(result.StringOrDie().value()));
5353
violation.set_constraint_id(expr.constraint.id());
54+
if (expr.rulePath.has_value()) {
55+
*violation.mutable_rule() = *expr.rulePath;
56+
}
5457
}
5558
} else if (result.IsError()) {
5659
const cel::runtime::CelError& error = *result.ErrorOrDie();
@@ -85,6 +88,7 @@ cel::runtime::CelValue ProtoFieldToCelValue(
8588
absl::Status CelConstraintRules::Add(
8689
google::api::expr::runtime::CelExpressionBuilder& builder,
8790
Constraint constraint,
91+
absl::optional<FieldPath> rulePath,
8892
const google::protobuf::FieldDescriptor* rule) {
8993
auto pexpr_or = cel::parser::Parse(constraint.expression());
9094
if (!pexpr_or.ok()) {
@@ -96,7 +100,7 @@ absl::Status CelConstraintRules::Add(
96100
return expr_or.status();
97101
}
98102
std::unique_ptr<cel::runtime::CelExpression> expr = std::move(expr_or).value();
99-
exprs_.emplace_back(CompiledConstraint{std::move(constraint), std::move(expr), rule});
103+
exprs_.emplace_back(CompiledConstraint{std::move(constraint), std::move(expr), std::move(rulePath), rule});
100104
return absl::OkStatus();
101105
}
102106

@@ -105,17 +109,17 @@ absl::Status CelConstraintRules::Add(
105109
std::string_view id,
106110
std::string_view message,
107111
std::string_view expression,
112+
absl::optional<FieldPath> rulePath,
108113
const google::protobuf::FieldDescriptor* rule) {
109114
Constraint constraint;
110115
*constraint.mutable_id() = id;
111116
*constraint.mutable_message() = message;
112117
*constraint.mutable_expression() = expression;
113-
return Add(builder, constraint, rule);
118+
return Add(builder, constraint, std::move(rulePath), rule);
114119
}
115120

116121
absl::Status CelConstraintRules::ValidateCel(
117122
ConstraintContext& ctx,
118-
std::string_view fieldName,
119123
google::api::expr::runtime::Activation& activation) const {
120124
activation.InsertValue("rules", rules_);
121125
activation.InsertValue("now", cel::runtime::CelValue::CreateTimestamp(absl::Now()));
@@ -126,7 +130,7 @@ absl::Status CelConstraintRules::ValidateCel(
126130
activation.InsertValue(
127131
"rule", ProtoFieldToCelValue(rules_.MessageOrDie(), expr.rule, ctx.arena));
128132
}
129-
status = ProcessConstraint(ctx, fieldName, activation, expr);
133+
status = ProcessConstraint(ctx, activation, expr);
130134
if (ctx.shouldReturn(status)) {
131135
break;
132136
}

buf/validate/internal/cel_constraint_rules.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace buf::validate::internal {
2828
struct CompiledConstraint {
2929
buf::validate::Constraint constraint;
3030
std::unique_ptr<google::api::expr::runtime::CelExpression> expr;
31+
const absl::optional<FieldPath> rulePath;
3132
const google::protobuf::FieldDescriptor* rule;
3233
};
3334

@@ -41,25 +42,23 @@ class CelConstraintRules : public ConstraintRules {
4142
absl::Status Add(
4243
google::api::expr::runtime::CelExpressionBuilder& builder,
4344
Constraint constraint,
45+
absl::optional<FieldPath> rulePath,
4446
const google::protobuf::FieldDescriptor* rule);
4547
absl::Status Add(
4648
google::api::expr::runtime::CelExpressionBuilder& builder,
4749
std::string_view id,
4850
std::string_view message,
4951
std::string_view expression,
52+
absl::optional<FieldPath> rulePath,
5053
const google::protobuf::FieldDescriptor* rule);
51-
[[nodiscard]] const std::vector<CompiledConstraint>& getExprs() const { return exprs_; }
5254

5355
// Validate all the cel rules given the activation that already has 'this' bound.
5456
absl::Status ValidateCel(
5557
ConstraintContext& ctx,
56-
std::string_view fieldName,
5758
google::api::expr::runtime::Activation& activation) const;
5859

5960
void setRules(google::api::expr::runtime::CelValue rules) { rules_ = rules; }
6061
void setRules(const google::protobuf::Message* rules, google::protobuf::Arena* arena);
61-
[[nodiscard]] const google::api::expr::runtime::CelValue& getRules() const { return rules_; }
62-
[[nodiscard]] google::api::expr::runtime::CelValue& getRules() { return rules_; }
6362

6463
protected:
6564
google::api::expr::runtime::CelValue rules_;

buf/validate/internal/cel_rules.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,47 @@
1616

1717
#include "absl/status/status.h"
1818
#include "buf/validate/internal/cel_constraint_rules.h"
19+
#include "buf/validate/internal/constraints.h"
1920
#include "buf/validate/internal/message_factory.h"
2021
#include "buf/validate/validate.pb.h"
2122
#include "google/protobuf/arena.h"
2223
#include "google/protobuf/descriptor.h"
2324

2425
namespace buf::validate::internal {
2526

27+
template <typename T>
28+
constexpr int ruleFieldNumber() = delete;
29+
30+
#define MAP_RULES_TO_FIELD_NUMBER(R, fieldNumber) \
31+
template <> \
32+
constexpr int ruleFieldNumber<R>() { \
33+
return fieldNumber; \
34+
}
35+
36+
MAP_RULES_TO_FIELD_NUMBER(FloatRules, FieldConstraints::kFloatFieldNumber)
37+
MAP_RULES_TO_FIELD_NUMBER(DoubleRules, FieldConstraints::kDoubleFieldNumber)
38+
MAP_RULES_TO_FIELD_NUMBER(Int32Rules, FieldConstraints::kInt32FieldNumber)
39+
MAP_RULES_TO_FIELD_NUMBER(Int64Rules, FieldConstraints::kInt64FieldNumber)
40+
MAP_RULES_TO_FIELD_NUMBER(UInt32Rules, FieldConstraints::kUint32FieldNumber)
41+
MAP_RULES_TO_FIELD_NUMBER(UInt64Rules, FieldConstraints::kUint64FieldNumber)
42+
MAP_RULES_TO_FIELD_NUMBER(SInt32Rules, FieldConstraints::kSint32FieldNumber)
43+
MAP_RULES_TO_FIELD_NUMBER(SInt64Rules, FieldConstraints::kSint64FieldNumber)
44+
MAP_RULES_TO_FIELD_NUMBER(Fixed32Rules, FieldConstraints::kFixed32FieldNumber)
45+
MAP_RULES_TO_FIELD_NUMBER(Fixed64Rules, FieldConstraints::kFixed64FieldNumber)
46+
MAP_RULES_TO_FIELD_NUMBER(SFixed32Rules, FieldConstraints::kSfixed32FieldNumber)
47+
MAP_RULES_TO_FIELD_NUMBER(SFixed64Rules, FieldConstraints::kSfixed64FieldNumber)
48+
MAP_RULES_TO_FIELD_NUMBER(BoolRules, FieldConstraints::kBoolFieldNumber)
49+
MAP_RULES_TO_FIELD_NUMBER(StringRules, FieldConstraints::kStringFieldNumber)
50+
MAP_RULES_TO_FIELD_NUMBER(BytesRules, FieldConstraints::kBytesFieldNumber)
51+
MAP_RULES_TO_FIELD_NUMBER(EnumRules, FieldConstraints::kEnumFieldNumber)
52+
MAP_RULES_TO_FIELD_NUMBER(RepeatedRules, FieldConstraints::kRepeatedFieldNumber)
53+
MAP_RULES_TO_FIELD_NUMBER(MapRules, FieldConstraints::kMapFieldNumber)
54+
MAP_RULES_TO_FIELD_NUMBER(AnyRules, FieldConstraints::kAnyFieldNumber)
55+
MAP_RULES_TO_FIELD_NUMBER(DurationRules, FieldConstraints::kDurationFieldNumber)
56+
MAP_RULES_TO_FIELD_NUMBER(TimestampRules, FieldConstraints::kTimestampFieldNumber)
57+
58+
#undef MAP_RULES_TO_FIELD_NUMBER
59+
2660
template <typename R>
2761
absl::Status BuildCelRules(
2862
std::unique_ptr<MessageFactory>& messageFactory,
@@ -60,13 +94,17 @@ absl::Status BuildCelRules(
6094
R::GetReflection()->ListFields(rules, &fields);
6195
}
6296
for (const auto* field : fields) {
97+
FieldPath rulePath;
98+
*rulePath.mutable_elements()->Add() = fieldPathElement(field);
99+
*rulePath.mutable_elements()->Add() =
100+
staticFieldPathElement<FieldConstraints, ruleFieldNumber<R>()>();
63101
if (!field->options().HasExtension(buf::validate::predefined)) {
64102
continue;
65103
}
66104
const auto& fieldLvl = field->options().GetExtension(buf::validate::predefined);
67105
for (const auto& constraint : fieldLvl.cel()) {
68106
auto status = result.Add(
69-
builder, constraint.id(), constraint.message(), constraint.expression(), field);
107+
builder, constraint.id(), constraint.message(), constraint.expression(), rulePath, field);
70108
if (!status.ok()) {
71109
return status;
72110
}

buf/validate/internal/constraint_rules.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,18 @@ struct ConstraintContext {
3434
return !status.ok() || (failFast && violations.violations_size() > 0);
3535
}
3636

37-
void prefixFieldPath(std::string_view prefix, int start) {
37+
void appendFieldPathElement(const FieldPathElement &element, int start) {
3838
for (int i = start; i < violations.violations_size(); i++) {
3939
auto* violation = violations.mutable_violations(i);
40-
if (violation->field_path().empty()) {
41-
*violation->mutable_field_path() = prefix;
42-
} else {
43-
violation->set_field_path(absl::StrCat(prefix, ".", violation->field_path()));
44-
}
40+
*violation->mutable_field()->mutable_elements()->Add() = element;
41+
}
42+
}
43+
44+
void appendRulePathElement(std::initializer_list<FieldPathElement> suffix, int start) {
45+
for (int i = start; i < violations.violations_size(); i++) {
46+
auto* violation = violations.mutable_violations(i);
47+
auto* elements = violation->mutable_rule()->mutable_elements();
48+
std::copy(suffix.begin(), suffix.end(), RepeatedPtrFieldBackInserter(elements));
4549
}
4650
}
4751
};

0 commit comments

Comments
 (0)