Skip to content

Commit 4655215

Browse files
authored
Add more examples (#93)
`option_*` cover most features from [the standard options](https://buf.build/bufbuild/protovalidate/docs/main:buf.validate). `cel_*` cover most features from [CEL's feature list](https://github.com/google/cel-spec/blob/master/doc/langdef.md#list-of-standard-definitions). Exception: - From timestamp's `getFooFromBar` family, including `getDate`, `getDayOfMonth`, `getDayOfYear`, `getFullYear`, `getHours`, `getMilliseconds`, `getMinutes`, `getMonth` and `getSeconds`, only `getDayOrWeek` is covered, but there is a comment mentioning all the rest and a link to this feature list. `cel_*` also cover [custom CEL functions and overloads](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md#custom-variables-functions-and-overloads)
1 parent 6760bb5 commit 4655215

File tree

62 files changed

+1991
-62
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1991
-62
lines changed

examples/BUILD.bazel

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,91 @@ load("@rules_buf//buf:defs.bzl", "buf_lint_test")
1818
proto_library(
1919
name = "examples_proto",
2020
srcs = [
21+
"cel_assert_value_is_in_a_list.proto",
22+
"cel_bytes_concatentation.proto",
23+
"cel_bytes_contains.proto",
24+
"cel_bytes_starts_with_ends_with.proto",
2125
"cel_conditional_operator.proto",
26+
"cel_duration_arithmetic.proto",
2227
"cel_duration_from_string.proto",
2328
"cel_enum_comparision.proto",
2429
"cel_field_access.proto",
30+
"cel_field_mask.proto",
2531
"cel_field_presence.proto",
32+
"cel_field_presence_nested.proto",
2633
"cel_field_selection.proto",
27-
"cel_map_key.proto",
34+
"cel_infinity.proto",
35+
"cel_list_concatenation.proto",
36+
"cel_map_all.proto",
37+
"cel_map_exists.proto",
38+
"cel_map_exists_one.proto",
39+
"cel_map_size.proto",
40+
"cel_number_arithmetic.proto",
2841
"cel_repeated_field_all.proto",
2942
"cel_repeated_field_exists_one.proto",
30-
"cel_repeated_field_filter_and_size.proto",
43+
"cel_repeated_field_filter_and_count.proto",
44+
"cel_repeated_field_unique.proto",
45+
"cel_string_concatentation.proto",
3146
"cel_string_contains.proto",
47+
"cel_string_is_email.proto",
48+
"cel_string_is_hostname.proto",
49+
"cel_string_is_ip.proto",
50+
"cel_string_is_uri.proto",
3251
"cel_string_match_pattern.proto",
3352
"cel_string_starts_with_ends_with.proto",
53+
"cel_timestamp_comparsion.proto",
54+
"cel_timestamp_get_attribute.proto",
3455
"cel_timestamp_plus_duration.proto",
3556
"cel_timestamp_subtraction.proto",
36-
"cel_timestamp_to_day_of_week.proto",
57+
"cel_type_conversion.proto",
58+
"cel_value.proto",
59+
"cel_wrapper_type.proto",
60+
"option_any_type_allow_list.proto",
61+
"option_any_type_ban_list.proto",
3762
"option_bool.proto",
38-
"option_bytes.proto",
63+
"option_bytes_ban_values.proto",
64+
"option_bytes_contains.proto",
65+
"option_bytes_equal.proto",
66+
"option_bytes_len.proto",
67+
"option_bytes_pattern.proto",
68+
"option_bytes_prefix_suffix.proto",
69+
"option_duration_allow_values.proto",
70+
"option_duration_disallow_values.proto",
71+
"option_duration_equal.proto",
72+
"option_duration_range.proto",
73+
"option_enum_allow_values.proto",
74+
"option_enum_disallow_values.proto",
75+
"option_field_ignore_empty.proto",
76+
"option_field_presence.proto",
77+
"option_field_skip_validation.proto",
3978
"option_map.proto",
40-
"option_number.proto",
79+
"option_message_disable_validation.proto",
80+
"option_number_allow_values.proto",
81+
"option_number_disallow_values.proto",
82+
"option_number_equal.proto",
83+
"option_number_finite_and_infinite.proto",
84+
"option_number_range.proto",
4185
"option_oneof.proto",
4286
"option_repeated.proto",
43-
"option_require_field_presence.proto",
44-
"option_string.proto",
45-
"option_wkt_any.proto",
46-
"option_wkt_duration.proto",
87+
"option_string_allow_values.proto",
88+
"option_string_ban_values.proto",
89+
"option_string_contains.proto",
90+
"option_string_equal.proto",
91+
"option_string_is_http_header.proto",
92+
"option_string_len.proto",
93+
"option_string_match_pattern.proto",
94+
"option_string_prefix_suffix.proto",
95+
"option_timestamp_range.proto",
96+
"option_timestamp_relative_to_now.proto",
4797
],
4898
strip_import_prefix = "/examples",
4999
visibility = ["//visibility:public"],
50100
deps = [
51101
"//proto/protovalidate/buf/validate:validate_proto",
52102
"@com_google_protobuf//:any_proto",
53103
"@com_google_protobuf//:duration_proto",
104+
"@com_google_protobuf//:field_mask_proto",
105+
"@com_google_protobuf//:struct_proto",
54106
"@com_google_protobuf//:timestamp_proto",
55107
"@com_google_protobuf//:wrappers_proto",
56108
"@go_googleapis//google/type:postaladdress_proto",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
19+
service DivisionService {
20+
// IsDivisible solves whether one number is divisible by another.
21+
rpc IsDivisible(IsDivisibleRequest) returns (IsDivisibleResponse);
22+
}
23+
24+
message IsDivisibleRequest {
25+
// dividend is the dividend in the division.
26+
int32 dividend = 1;
27+
// divisor is the divisor in the division.
28+
int32 divisor = 2 [(buf.validate.field).cel = {
29+
id: "divisor_must_be_a_small_prime",
30+
message: "the divisor must be one of 2, 3 and 5",
31+
// `in` evaluates list membership. The expression evaluates to
32+
// true when the value is in the list.
33+
// Validates that divisor must be one of 2, 3 and 5.
34+
expression: "this in [2, 3, 5]",
35+
// Similarly, you can assert that it's not in a list with `!(this in [2, 3, 5])`.
36+
}];
37+
}
38+
39+
message IsDivisibleResponse {
40+
// is_divisible is the result.
41+
bool is_divisible = 1;
42+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
19+
message CompactDocument {
20+
option (buf.validate.message).cel = {
21+
id: "header_footer_size_limit",
22+
message: "header and footer should be less than 500 bytes in total",
23+
// the `+` operator concatenates two `bytes` together and `size` returns
24+
// the length of a `bytes`.
25+
// This validates that the combined size of header and footer should be < 500.
26+
expression: "size(this.header + this.footer) < 500"
27+
};
28+
bytes header = 1;
29+
bytes footer = 2;
30+
}

examples/cel_bytes_contains.proto

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
19+
// Application is an application.
20+
message Application {
21+
// binary is the application's binary
22+
bytes binary = 1 [(buf.validate.field).cel = {
23+
id: "without_malicious_code",
24+
message: "binary should not contain malicious code",
25+
// `contains` returns if the receiver `bytes` contains the argument `bytes`.
26+
// This validates that the application binary should not contain bytes for
27+
// `'malicious code'`.
28+
// Note that when checking whether bytes for a string is contained, `bytes()`
29+
// must be called to convert the string into bytes.
30+
expression: "!this.contains(bytes('malicious code'))"
31+
}];
32+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
19+
message ScriptFile {
20+
bytes content = 1 [(buf.validate.field).cel = {
21+
id: "script_start_with_shabang_end_with_line_feed",
22+
expression:
23+
// this is a ternary expression in the form of a ? b : c, if the the
24+
// script file doesn't start with '#!', the expression evaluates to the
25+
// first error message, which fails the validation. Otherwise it
26+
// evaluates to the next expression.
27+
"!this.startsWith(bytes('#!')) ? 'must start with #!'"
28+
// If this file does not end with the new line character, it evaluates
29+
// to the second error message.
30+
// Note: the `'\x0A'` is byte string for the new line character, but
31+
// we have two backslashes below. This is because we need to escape the
32+
// backslash.
33+
": !this.endsWith(bytes('\\x0A')) ? 'must end with a new line'"
34+
// If the file passes the two checks above, there is no error.
35+
": ''"
36+
}];
37+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
import "google/protobuf/duration.proto";
19+
20+
message IndirectFlight {
21+
option (buf.validate.message).cel = {
22+
id: "totol_lenght_limit",
23+
message: "the entire trip should be less than 48 hours",
24+
expression:
25+
// This validates that the total duration of an indirect flight should be
26+
// less than 48 hours.
27+
// Adding two duration yields a duration.
28+
// Note that if the CEL expression is too long, you can break it down into
29+
// multiple lines like so:
30+
"this.first_flight_duration + this.second_flight_duration"
31+
// Durations can be compared with `<`, `<=`, `>=`, `>`, `==` and `!=`.
32+
"+ this.layover_duration < duration('48h')"
33+
};
34+
google.protobuf.Duration first_flight_duration = 1;
35+
google.protobuf.Duration layover_duration = 2;
36+
google.protobuf.Duration second_flight_duration = 3;
37+
}

examples/cel_field_mask.proto

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
import "google/protobuf/duration.proto";
19+
import "google/protobuf/field_mask.proto";
20+
import "google/protobuf/timestamp.proto";
21+
22+
service SongService {
23+
rpc UpdateSong(UpdateSongRequest) returns (UpdateSongResponse);
24+
}
25+
26+
message UpdateSongRequest {
27+
// song is the updated song.
28+
Song song = 1;
29+
// field_mask masks the fields to update.
30+
google.protobuf.FieldMask field_mask = 2 [(buf.validate.field).cel = {
31+
id: "valid_field_mask",
32+
message: "a field mask path must be one of name, duration and artist.name",
33+
// `some_list.all()` validates that a predicate is true for all elements
34+
// from that list.
35+
// In this case, `this.paths` is the field paths from.
36+
expression: "this.paths.all(path, path in ['name', 'duration', 'artist.name'])",
37+
}];
38+
}
39+
40+
message Song {
41+
string name = 1;
42+
fixed64 id = 2;
43+
google.protobuf.Timestamp create_time = 3;
44+
google.protobuf.Duration duration = 4;
45+
Artist artist = 5;
46+
}
47+
48+
message Artist {
49+
string name = 1;
50+
// other fields:
51+
// ...
52+
}
53+
54+
message UpdateSongResponse {}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2023 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "buf/validate/validate.proto";
18+
19+
service ContactService {
20+
// CreateContact creates a contact.
21+
rpc CreateContact(CreateContactRequest) returns (CreateContactResponse);
22+
}
23+
24+
message CreateContactRequest {
25+
option (buf.validate.message).cel = {
26+
id: "create_contact_with_first_name",
27+
message: "the contact must have first name",
28+
// This expression validates that all of `this.contact`, `this.contact.full_name`
29+
// and `this.contact.full_name.first_name` are set.
30+
expression: "has(this.contact.full_name.first_name)"
31+
};
32+
// contact is the contact to create.
33+
Contact contact = 1;
34+
}
35+
36+
message Contact {
37+
FullName full_name = 1;
38+
}
39+
40+
message FullName {
41+
string first_name = 1;
42+
string last_name = 2;
43+
}
44+
45+
message CreateContactResponse {}

0 commit comments

Comments
 (0)