Skip to content

Commit ba67fcf

Browse files
committed
Add protovalidate rules to remaining messages
While protovalidate rules are mainly useful for validating gRPC _request_ messages, adding validation annotations to _response_ messages has several advantages. Namely, doing so provides implicit documentation to API consumers on how the API behaves and what its contract is. It also ensures we think twice before making a change which isn't technically breaking to the message wire format but could be breaking to how clients are using API responses.
1 parent 680bb46 commit ba67fcf

10 files changed

Lines changed: 58 additions & 17 deletions

accrescent/directory/v1/app_download_info.proto

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/package_info.proto";
1010
import "accrescent/directory/v1/split_download_info.proto";
11+
import "buf/validate/validate.proto";
1112

1213
option java_multiple_files = true;
1314
option java_package = "app.accrescent.directory.v1";
1415

1516
// App download information for a given app.
1617
message AppDownloadInfo {
1718
// Download info for each individual split APK.
18-
repeated SplitDownloadInfo split_download_info = 1;
19+
repeated SplitDownloadInfo split_download_info = 1 [(buf.validate.field).repeated.min_items = 1];
1920

2021
// Package information for this app.
21-
PackageInfo package_info = 2;
22+
PackageInfo package_info = 2 [(buf.validate.field).required = true];
2223
}

accrescent/directory/v1/app_listing.proto

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,27 @@ syntax = "proto3";
77
package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/image.proto";
10+
import "buf/validate/validate.proto";
1011

1112
option java_multiple_files = true;
1213
option java_package = "app.accrescent.directory.v1";
1314

1415
// A localized store listing for a given app.
1516
message AppListing {
1617
// The application ID this listing is associated with.
17-
optional string app_id = 1;
18+
optional string app_id = 1 [(buf.validate.field).required = true];
1819

1920
// The language of this listing's fields as a BCP-47 tag.
20-
optional string language = 2;
21+
optional string language = 2 [(buf.validate.field).required = true];
2122

2223
// The proper name of the app, possibly including very short descriptive text
2324
// (e.g. "SecureChat - Secure Texting").
24-
optional string name = 3;
25+
optional string name = 3 [(buf.validate.field).required = true];
2526

2627
// A short description of the app to be shown in headers and small screen
2728
// spaces.
28-
optional string short_description = 4;
29+
optional string short_description = 4 [(buf.validate.field).required = true];
2930

3031
// The app's primary icon, usually the same as the launcher icon.
31-
Image icon = 5;
32+
Image icon = 5 [(buf.validate.field).required = true];
3233
}

accrescent/directory/v1/compatibility.proto

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ syntax = "proto3";
77
package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/compatibility_level.proto";
10+
import "buf/validate/validate.proto";
1011

1112
option java_multiple_files = true;
1213
option java_package = "app.accrescent.directory.v1";
1314

1415
// Compatibility of an app with a given device specification.
1516
message Compatibility {
1617
// The level of compatibility.
17-
optional CompatibilityLevel level = 1;
18+
optional CompatibilityLevel level = 1 [
19+
(buf.validate.field).required = true,
20+
(buf.validate.field).enum = {
21+
defined_only: true
22+
not_in: [0]
23+
}
24+
];
1825
}

accrescent/directory/v1/get_app_download_info_response.proto

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/app_download_info.proto";
1010
import "accrescent/directory/v1/compatibility.proto";
11+
import "buf/validate/validate.proto";
1112

1213
option java_multiple_files = true;
1314
option java_package = "app.accrescent.directory.v1";
@@ -16,7 +17,7 @@ option java_package = "app.accrescent.directory.v1";
1617
message GetAppDownloadInfoResponse {
1718
// Compatibility information between the app and the device attributes
1819
// specified in the request.
19-
Compatibility compatibility = 1;
20+
Compatibility compatibility = 1 [(buf.validate.field).required = true];
2021

2122
// The app's download info. This field will be populated if and only if all of
2223
// the following statements apply:
@@ -29,4 +30,13 @@ message GetAppDownloadInfoResponse {
2930
// In other words, it will only be populated if there are APKs available for
3031
// download which are newer than those specified in the request.
3132
AppDownloadInfo app_download_info = 2;
33+
34+
option (buf.validate.message).cel = {
35+
id: "app_download_info.not_present_if_incompatible"
36+
message: "app download info must not be present if the app is incompatible"
37+
expression:
38+
"this.compatibility.level"
39+
"== accrescent.directory.v1.CompatibilityLevel.COMPATIBILITY_LEVEL_COMPATIBLE
40+
"|| !has(this.app_download_info)"
41+
};
3242
}

accrescent/directory/v1/get_app_listing_response.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ syntax = "proto3";
77
package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/app_listing.proto";
10+
import "buf/validate/validate.proto";
1011

1112
option java_multiple_files = true;
1213
option java_package = "app.accrescent.directory.v1";
1314

1415
// Response to requesting an app listing.
1516
message GetAppListingResponse {
1617
// The listing provided by the API.
17-
AppListing listing = 1;
18+
AppListing listing = 1 [(buf.validate.field).required = true];
1819
}

accrescent/directory/v1/get_app_package_info_response.proto

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ syntax = "proto3";
77
package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/package_info.proto";
10+
import "buf/validate/validate.proto";
1011

1112
option java_multiple_files = true;
1213
option java_package = "app.accrescent.directory.v1";
1314

1415
// Response to requesting an app's package info.
1516
message GetAppPackageInfoResponse {
1617
// The package info matching the request parameters.
17-
PackageInfo package_info = 1;
18+
PackageInfo package_info = 1 [(buf.validate.field).required = true];
1819
}

accrescent/directory/v1/image.proto

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ syntax = "proto3";
66

77
package accrescent.directory.v1;
88

9+
import "buf/validate/validate.proto";
10+
911
option java_multiple_files = true;
1012
option java_package = "app.accrescent.directory.v1";
1113

1214
// Graphical image to be displayed in client UI.
1315
message Image {
1416
// The URL of the image file which can be retrieved with a simple HTTP GET
1517
// request.
16-
optional string url = 1;
18+
optional string url = 1 [
19+
(buf.validate.field).required = true,
20+
(buf.validate.field).string.uri = true
21+
];
1722
}

accrescent/directory/v1/list_app_listings_response.proto

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ syntax = "proto3";
77
package accrescent.directory.v1;
88

99
import "accrescent/directory/v1/app_listing.proto";
10+
import "buf/validate/validate.proto";
1011

1112
option java_multiple_files = true;
1213
option java_package = "app.accrescent.directory.v1";
1314

1415
// Response to requesting a list of app listings.
1516
message ListAppListingsResponse {
1617
// The app listings matching the request parameters.
17-
repeated AppListing listings = 1;
18+
repeated AppListing listings = 1 [(buf.validate.field).cel = {
19+
id: "listings.have_unique_app_ids"
20+
message: "listings must have unique app IDs"
21+
expression: "this.map(l, l.app_id).unique()"
22+
}];
1823

1924
// An opaque token which, if passed to another invocation of ListAppListings,
2025
// will return the next page of app listings.

accrescent/directory/v1/package_info.proto

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ syntax = "proto3";
66

77
package accrescent.directory.v1;
88

9+
import "buf/validate/validate.proto";
10+
911
option java_multiple_files = true;
1012
option java_package = "app.accrescent.directory.v1";
1113

1214
// Package information for a given app.
1315
message PackageInfo {
1416
// The app's version code.
15-
optional uint64 version_code = 1;
17+
optional uint64 version_code = 1 [
18+
(buf.validate.field).required = true,
19+
(buf.validate.field).uint64.gt = 0
20+
];
1621

1722
// The app's human-readable version name.
18-
optional string version_name = 2;
23+
optional string version_name = 2 [(buf.validate.field).required = true];
1924
}

accrescent/directory/v1/split_download_info.proto

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ syntax = "proto3";
66

77
package accrescent.directory.v1;
88

9+
import "buf/validate/validate.proto";
10+
911
option java_multiple_files = true;
1012
option java_package = "app.accrescent.directory.v1";
1113

1214
// Download info for an individual split APK.
1315
message SplitDownloadInfo {
1416
// The download size in bytes of this individual split APK.
15-
optional uint32 download_size = 1;
17+
optional uint32 download_size = 1 [(buf.validate.field).required = true];
1618

1719
// The URL of the APK which can be retrieved with a simple HTTP GET.
18-
optional string url = 2;
20+
optional string url = 2 [
21+
(buf.validate.field).required = true,
22+
(buf.validate.field).string.uri = true
23+
];
1924
}

0 commit comments

Comments
 (0)