-
Notifications
You must be signed in to change notification settings - Fork 377
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
impl(generator): support protobuf wrapper types as query params #14493
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,24 @@ void SetHttpDerivedMethodVars( | |
google::protobuf::MethodDescriptor const& method, | ||
VarsDictionary& method_vars); | ||
|
||
struct QueryParameterInfo { | ||
protobuf::FieldDescriptor::CppType cpp_type; | ||
// A code fragment the generator emits to access the value of the field. | ||
std::string request_field_accessor; | ||
// Check presence for MESSAGE types as their default values may result in | ||
// undesired behavior. | ||
bool check_presence; | ||
Comment on lines
+76
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL that nested protobuf messages are inherently optional and that: for: message Foo {}
message Bar {
Foo foo = 1;
} |
||
}; | ||
|
||
/** | ||
* Determine if a field is a query parameter candidate, such that it's a | ||
* non-repeated field that is also not an aggregate type. This includes numeric, | ||
* bool, and string native protobuf data types, as well as, protobuf "Well Known | ||
* Types" that wrap those data types. | ||
*/ | ||
absl::optional<QueryParameterInfo> DetermineQueryParameterInfo( | ||
google::protobuf::FieldDescriptor const& field); | ||
|
||
/** | ||
* Sets the "method_http_query_parameters" value in method_vars based on the | ||
* parsed_http_info. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,42 @@ syntax = "proto3"; | |
package google.protobuf; | ||
// Leading comments about message Empty. | ||
message Empty {} | ||
message DoubleValue { | ||
// The double value. | ||
double value = 1; | ||
} | ||
message FloatValue { | ||
// The float value. | ||
float value = 1; | ||
} | ||
message Int64Value { | ||
// The int64 value. | ||
int64 value = 1; | ||
} | ||
message UInt64Value { | ||
// The uint64 value. | ||
uint64 value = 1; | ||
} | ||
message Int32Value { | ||
// The int32 value. | ||
int32 value = 1; | ||
} | ||
message UInt32Value { | ||
// The uint32 value. | ||
uint32 value = 1; | ||
} | ||
message BoolValue { | ||
// The bool value. | ||
bool value = 1; | ||
} | ||
message StringValue { | ||
// The string value. | ||
string value = 1; | ||
} | ||
message BytesValue { | ||
// The bytes value. | ||
bytes value = 1; | ||
} | ||
)"""; | ||
|
||
char const* const kServiceProto = | ||
|
@@ -275,6 +311,65 @@ char const* const kServiceProtoWithoutVersion = | |
" }\n" | ||
"}\n"; | ||
|
||
char const* const kBigQueryServiceProto = R"""( | ||
syntax = "proto3"; | ||
package my.package.v1; | ||
import "google/api/annotations.proto"; | ||
import "google/api/client.proto"; | ||
import "google/api/http.proto"; | ||
import "google/protobuf/well_known.proto"; | ||
|
||
service BigQueryLikeService { | ||
// RPC to get the results of a query job. | ||
rpc GetQueryResults(GetQueryResultsRequest) | ||
returns (GetQueryResultsResponse) { | ||
option (google.api.http) = { | ||
get: "/bigquery/v2/projects/{project_id=*}/queries/{job_id=*}" | ||
}; | ||
} | ||
} | ||
|
||
// Request object of GetQueryResults. | ||
message GetQueryResultsRequest { | ||
// Required. Project ID of the query job. | ||
string project_id = 1; | ||
|
||
// Required. Job ID of the query job. | ||
string job_id = 2; | ||
|
||
// Zero-based index of the starting row. | ||
google.protobuf.UInt64Value start_index = 3; | ||
|
||
// Page token, returned by a previous call, to request the next page of | ||
// results. | ||
google.protobuf.StringValue page_token = 4; | ||
|
||
// Maximum number of results to read. | ||
google.protobuf.UInt32Value max_results = 5; | ||
|
||
// The geographic location of the job. | ||
google.protobuf.BoolValue include_location = 7; | ||
|
||
// Double field. | ||
google.protobuf.DoubleValue double_value = 8; | ||
|
||
// Float field. | ||
google.protobuf.FloatValue float_value = 9; | ||
|
||
// Int32 field. | ||
google.protobuf.Int32Value int32_value = 10; | ||
|
||
// Int64 field. | ||
google.protobuf.Int64Value int64_value = 11; | ||
|
||
// Non supported message type that is not a query param. | ||
google.protobuf.Empty non_supported_type = 12; | ||
} | ||
|
||
// Response object of GetQueryResults. | ||
message GetQueryResultsResponse {} | ||
)"""; | ||
|
||
struct MethodVarsTestValues { | ||
MethodVarsTestValues(std::string m, std::string k, std::string v) | ||
: method(std::move(m)), | ||
|
@@ -298,7 +393,9 @@ class HttpOptionUtilsTest | |
{std::string("google/protobuf/well_known.proto"), kWellKnownProto}, | ||
{std::string("google/foo/v1/service.proto"), kServiceProto}, | ||
{std::string("google/foo/v1/service_without_version.proto"), | ||
kServiceProtoWithoutVersion}}), | ||
kServiceProtoWithoutVersion}, | ||
{std::string("google/foo/v1/big_query_service.proto"), | ||
kBigQueryServiceProto}}), | ||
source_tree_db_(&source_tree_), | ||
merged_db_(&simple_db_, &source_tree_db_), | ||
pool_(&merged_db_, &collector_) { | ||
|
@@ -550,7 +647,26 @@ TEST_F(HttpOptionUtilsTest, SetHttpGetQueryParametersGetPaginated) { | |
rest_internal::TrimEmptyQueryParameters({std::make_pair("page_size", std::to_string(request.page_size())), | ||
std::make_pair("page_token", request.page_token()), | ||
std::make_pair("name", request.name()), | ||
std::make_pair("include_foo", request.include_foo() ? "1" : "0")}))""")); | ||
std::make_pair("include_foo", (request.include_foo() ? "1" : "0"))}))""")); | ||
} | ||
|
||
TEST_F(HttpOptionUtilsTest, | ||
SetHttpGetQueryParametersGetWellKnownTypesPaginated) { | ||
FileDescriptor const* service_file_descriptor = | ||
pool_.FindFileByName("google/foo/v1/big_query_service.proto"); | ||
MethodDescriptor const* method = | ||
service_file_descriptor->service(0)->method(0); | ||
VarsDictionary vars; | ||
SetHttpQueryParameters(ParseHttpExtension(*method), *method, vars); | ||
EXPECT_THAT(vars.at("method_http_query_parameters"), Eq(R"""(, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment: This is nearly a change detector test, but I think it's fine. I don't think saying: |
||
rest_internal::TrimEmptyQueryParameters({std::make_pair("start_index", (request.has_start_index() ? std::to_string(request.start_index().value()) : "")), | ||
std::make_pair("page_token", (request.has_page_token() ? request.page_token().value() : "")), | ||
std::make_pair("max_results", (request.has_max_results() ? std::to_string(request.max_results().value()) : "")), | ||
std::make_pair("include_location", (request.has_include_location() ? (request.include_location().value() ? "1" : "0") : "")), | ||
std::make_pair("double_value", (request.has_double_value() ? std::to_string(request.double_value().value()) : "")), | ||
std::make_pair("float_value", (request.has_float_value() ? std::to_string(request.float_value().value()) : "")), | ||
std::make_pair("int32_value", (request.has_int32_value() ? std::to_string(request.int32_value().value()) : "")), | ||
std::make_pair("int64_value", (request.has_int64_value() ? std::to_string(request.int64_value().value()) : ""))}))""")); | ||
} | ||
|
||
TEST_F(HttpOptionUtilsTest, HasHttpAnnotationRoutingHeaderSuccess) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,9 +104,18 @@ DefaultJobServiceRestStub::ListJobs( | |
rest_internal::DetermineApiVersion("v2", options), "/", | ||
"projects", "/", request.project_id(), "/", "jobs"), | ||
rest_internal::TrimEmptyQueryParameters( | ||
{std::make_pair("all_users", request.all_users() ? "1" : "0"), | ||
{std::make_pair("all_users", (request.all_users() ? "1" : "0")), | ||
std::make_pair("max_results", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI (mostly for myself): I was expecting that we would need to say $ curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" \
https://bigquery.googleapis.com/bigquery/v2/projects/cloud-cpp-testing-resources/jobs?maxResults=1
# response with 1 result
curl -X GET -H "Authorization: Bearer $(gcloud auth print-access-token)" \
https://bigquery.googleapis.com/bigquery/v2/projects/cloud-cpp-testing-resources/jobs?maxResults.value=1
# response with 1 result Strange... 🤷 |
||
(request.has_max_results() | ||
? std::to_string(request.max_results().value()) | ||
: "")), | ||
std::make_pair("min_creation_time", | ||
std::to_string(request.min_creation_time())), | ||
std::make_pair( | ||
"max_creation_time", | ||
(request.has_max_creation_time() | ||
? std::to_string(request.max_creation_time().value()) | ||
: "")), | ||
std::make_pair("page_token", request.page_token()), | ||
std::make_pair("projection", std::to_string(request.projection())), | ||
std::make_pair("parent_job_id", request.parent_job_id())})); | ||
|
@@ -125,7 +134,19 @@ DefaultJobServiceRestStub::GetQueryResults( | |
"projects", "/", request.project_id(), "/", "queries", "/", | ||
request.job_id()), | ||
rest_internal::TrimEmptyQueryParameters( | ||
{std::make_pair("page_token", request.page_token()), | ||
{std::make_pair("start_index", | ||
(request.has_start_index() | ||
? std::to_string(request.start_index().value()) | ||
: "")), | ||
std::make_pair("page_token", request.page_token()), | ||
std::make_pair("max_results", | ||
(request.has_max_results() | ||
? std::to_string(request.max_results().value()) | ||
: "")), | ||
std::make_pair("timeout_ms", | ||
(request.has_timeout_ms() | ||
? std::to_string(request.timeout_ms().value()) | ||
: "")), | ||
std::make_pair("location", request.location())})); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and below, I think we might need to look up the accessor name. (in case the field is called something like
delete
ornamespace
and the accessor isdelete_()
).I'd guess there are 0 such cases in practice, so we can defer.