Skip to content

Commit

Permalink
feat(mixin): add mixin support in http option utils (#14707)
Browse files Browse the repository at this point in the history
* feat(mixin): add mixin support in http option utils

* add more test cases
  • Loading branch information
cuiy0006 committed Sep 16, 2024
1 parent fe160bb commit 43f595c
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 58 deletions.
111 changes: 70 additions & 41 deletions generator/internal/http_option_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "generator/internal/http_option_utils.h"
#include "generator/internal/http_annotation_parser.h"
#include "generator/internal/longrunning.h"
#include "generator/internal/mixin_utils.h"
#include "generator/internal/printer.h"
#include "google/cloud/internal/absl_str_join_quiet.h"
#include "google/cloud/internal/absl_str_replace_quiet.h"
Expand Down Expand Up @@ -143,6 +144,15 @@ std::string FormatQueryParameterCode(
return code;
}

std::string FormatApiVersionFromPackageNameString(
std::string const& package_name, std::string const& file_name) {
std::vector<std::string> parts = absl::StrSplit(package_name, '.');
if (absl::StartsWith(parts.back(), "v")) return parts.back();
GCP_LOG(FATAL) << "Unrecognized API version in file: " << file_name
<< ", package: " << package_name;
return {}; // Suppress clang-tidy warnings
}

} // namespace

void SetHttpDerivedMethodVars(HttpExtensionInfo const& info,
Expand Down Expand Up @@ -263,38 +273,48 @@ void SetHttpQueryParameters(HttpExtensionInfo const& info,
}

HttpExtensionInfo ParseHttpExtension(
google::protobuf::MethodDescriptor const& method) {
google::protobuf::MethodDescriptor const& method,
absl::optional<std::string> package_name_override,
absl::optional<std::string> file_name_override,
absl::optional<MixinMethodOverride> method_override) {
if (!method.options().HasExtension(google::api::http)) return {};

HttpExtensionInfo info;
google::api::HttpRule http_rule =
method.options().GetExtension(google::api::http);

std::string url_pattern;
switch (http_rule.pattern_case()) {
case google::api::HttpRule::kGet:
info.http_verb = "Get";
url_pattern = http_rule.get();
break;
case google::api::HttpRule::kPut:
info.http_verb = "Put";
url_pattern = http_rule.put();
break;
case google::api::HttpRule::kPost:
info.http_verb = "Post";
url_pattern = http_rule.post();
break;
case google::api::HttpRule::kDelete:
info.http_verb = "Delete";
url_pattern = http_rule.delete_();
break;
case google::api::HttpRule::kPatch:
info.http_verb = "Patch";
url_pattern = http_rule.patch();
break;
default:
GCP_LOG(FATAL) << __FILE__ << ":" << __LINE__
<< ": google::api::HttpRule not handled";
if (method_override) {
url_pattern = method_override->http_path;
info.http_verb = method_override->http_verb;
info.body = method_override->http_body ? *method_override->http_body : "";
} else {
switch (http_rule.pattern_case()) {
case google::api::HttpRule::kGet:
info.http_verb = "Get";
url_pattern = http_rule.get();
break;
case google::api::HttpRule::kPut:
info.http_verb = "Put";
url_pattern = http_rule.put();
break;
case google::api::HttpRule::kPost:
info.http_verb = "Post";
url_pattern = http_rule.post();
break;
case google::api::HttpRule::kDelete:
info.http_verb = "Delete";
url_pattern = http_rule.delete_();
break;
case google::api::HttpRule::kPatch:
info.http_verb = "Patch";
url_pattern = http_rule.patch();
break;
default:
GCP_LOG(FATAL) << __FILE__ << ":" << __LINE__
<< ": google::api::HttpRule not handled";
}
info.body = http_rule.body();
}

auto parsed_http_rule = ParsePathTemplate(url_pattern);
Expand All @@ -304,7 +324,6 @@ HttpExtensionInfo ParseHttpExtension(
<< parsed_http_rule.status();
}

info.body = http_rule.body();
info.url_path = url_pattern;

struct SegmentAsStringVisitor {
Expand All @@ -322,8 +341,23 @@ HttpExtensionInfo ParseHttpExtension(
out->append(absl::visit(SegmentAsStringVisitor{}, s->value));
};

auto api_version =
FormatApiVersionFromUrlPattern(url_pattern, method.file()->name());
// parse api version from url, if url doesn't include version info,
// parse api version from package name, if package name doesn't include
// version info, raise fatal error. For non-mixin method, package name is its
// package's name. For mixin method, package name is the target service's
// package's name.
auto file_name =
file_name_override ? *file_name_override : method.file()->name();
auto api_version_opt = FormatApiVersionFromUrlPattern(url_pattern, file_name);
std::string api_version;
if (api_version_opt) {
api_version = *api_version_opt;
} else {
api_version = package_name_override
? FormatApiVersionFromPackageNameString(
*package_name_override, file_name)
: FormatApiVersionFromPackageName(method);
}

auto rest_path_visitor = RestPathVisitor(api_version, info.rest_path);
for (auto const& s : parsed_http_rule->segments) {
Expand Down Expand Up @@ -386,29 +420,24 @@ std::string FormatRequestResource(google::protobuf::Descriptor const& request,
// that mirror their directory path, which ends in the api version as well.
std::string FormatApiVersionFromPackageName(
google::protobuf::MethodDescriptor const& method) {
std::vector<std::string> parts =
absl::StrSplit(method.file()->package(), '.');
if (absl::StartsWith(parts.back(), "v")) return parts.back();
GCP_LOG(FATAL) << "Unrecognized API version in file: "
<< method.file()->name()
<< ", package: " << method.file()->package();
return {}; // Suppress clang-tidy warnings
return FormatApiVersionFromPackageNameString(method.file()->package(),
method.file()->name());
}

// Generate api version by extracting the version from the url pattern.
// In some cases(i.e. location), there is no version in the package name.
std::string FormatApiVersionFromUrlPattern(std::string const& url_pattern,
std::string const& file_name) {
std::vector<std::string> parts = absl::StrSplit(url_pattern, '/');
absl::optional<std::string> FormatApiVersionFromUrlPattern(
std::string const& url_pattern, std::string const& file_name) {
std::vector<std::string> const parts = absl::StrSplit(url_pattern, '/');
static auto const* const kVersion = new std::regex{R"(v\d+)"};
for (auto const& part : parts) {
if (std::regex_match(part, *kVersion)) {
return part;
}
}
GCP_LOG(FATAL) << "Unrecognized API version in file: " << file_name
<< ", url pattern: " << url_pattern;
return {}; // Suppress clang-tidy warnings
GCP_LOG(WARNING) << "Unrecognized API version in file: " << file_name
<< ", url pattern: " << url_pattern;
return absl::nullopt;
}

} // namespace generator_internal
Expand Down
15 changes: 12 additions & 3 deletions generator/internal/http_option_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#define GOOGLE_CLOUD_CPP_GENERATOR_INTERNAL_HTTP_OPTION_UTILS_H

#include "generator/internal/http_annotation_parser.h"
#include "generator/internal/mixin_utils.h"
#include "generator/internal/printer.h"
#include "absl/types/optional.h"
#include <google/protobuf/descriptor.h>
#include <string>

Expand All @@ -39,9 +41,16 @@ struct HttpExtensionInfo {
* Parses the http extension providing resource routing info, if present,
* for the provided method per AIP-4222. Output is also used for gRPC/HTTP
* transcoding and REST transport.
* Mixin method should use the target service's package and file names instead
* of its own.
* Mixin method' url, verb and body are overridden by the definition in target
* service's YAML.
*/
HttpExtensionInfo ParseHttpExtension(
google::protobuf::MethodDescriptor const& method);
google::protobuf::MethodDescriptor const& method,
absl::optional<std::string> package_name_override = absl::nullopt,
absl::optional<std::string> file_name_override = absl::nullopt,
absl::optional<MixinMethodOverride> method_override = absl::nullopt);

/**
* Sets the following method_vars based on the provided parsed_http_info:
Expand Down Expand Up @@ -115,8 +124,8 @@ std::string FormatApiVersionFromPackageName(
/**
* Parses the url pattern of the method and returns its API version.
*/
std::string FormatApiVersionFromUrlPattern(std::string const& url_pattern,
std::string const& file_name);
absl::optional<std::string> FormatApiVersionFromUrlPattern(
std::string const& url_pattern, std::string const& file_name);

} // namespace generator_internal
} // namespace cloud
Expand Down
Loading

0 comments on commit 43f595c

Please sign in to comment.