Skip to content

Commit

Permalink
Protocol buffer AnyToJsonConverter implementation
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 612491506
  • Loading branch information
jcking authored and copybara-github committed Mar 4, 2024
1 parent c49f5ce commit 04fa70d
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 29 deletions.
19 changes: 19 additions & 0 deletions extensions/protobuf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ package(

licenses(["notice"])

cc_library(
name = "json",
srcs = ["json.cc"],
hdrs = ["json.h"],
deps = [
"//common:any",
"//common:json",
"//extensions/protobuf/internal:json",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:cord",
"@com_google_absl//absl/strings:string_view",
"@com_google_protobuf//:protobuf",
],
)

cc_library(
name = "memory_manager",
srcs = ["memory_manager.cc"],
Expand Down
47 changes: 20 additions & 27 deletions extensions/protobuf/internal/json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#include "absl/types/variant.h"
#include "common/any.h"
#include "common/json.h"
#include "common/value_manager.h"
#include "extensions/protobuf/internal/any.h"
#include "extensions/protobuf/internal/duration.h"
#include "extensions/protobuf/internal/field_mask.h"
Expand All @@ -50,7 +49,7 @@ namespace cel::extensions::protobuf_internal {
namespace {

absl::StatusOr<Json> ProtoSingularFieldToJson(
ValueManager& value_manager, const google::protobuf::Message& message,
AnyToJsonConverter& converter, const google::protobuf::Message& message,
absl::Nonnull<const google::protobuf::Reflection*> reflection,
absl::Nonnull<const google::protobuf::FieldDescriptor*> field) {
switch (field->cpp_type()) {
Expand Down Expand Up @@ -81,7 +80,7 @@ absl::StatusOr<Json> ProtoSingularFieldToJson(
return reflection->GetCord(message, field);
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
return ProtoMessageToJson(value_manager,
return ProtoMessageToJson(converter,
reflection->GetMessage(message, field));
default:
return absl::InvalidArgumentError(absl::StrCat(
Expand Down Expand Up @@ -112,7 +111,7 @@ absl::StatusOr<JsonString> ProtoMapKeyToJsonString(const google::protobuf::MapKe
}

absl::StatusOr<Json> ProtoMapValueToJson(
ValueManager& value_manager,
AnyToJsonConverter& converter,
absl::Nonnull<const google::protobuf::FieldDescriptor*> field,
const google::protobuf::MapValueRef& value) {
switch (field->cpp_type()) {
Expand All @@ -139,7 +138,7 @@ absl::StatusOr<Json> ProtoMapValueToJson(
return JsonString(value.GetStringValue());
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
return ProtoMessageToJson(value_manager, value.GetMessageValue());
return ProtoMessageToJson(converter, value.GetMessageValue());
default:
return absl::InvalidArgumentError(absl::StrCat(
"unexpected protocol buffer field type: ",
Expand All @@ -148,7 +147,7 @@ absl::StatusOr<Json> ProtoMapValueToJson(
}

absl::StatusOr<Json> ProtoMapFieldToJson(
ValueManager& value_manager, const google::protobuf::Message& message,
AnyToJsonConverter& converter, const google::protobuf::Message& message,
absl::Nonnull<const google::protobuf::Reflection*> reflection,
absl::Nonnull<const google::protobuf::FieldDescriptor*> field) {
JsonObjectBuilder builder;
Expand All @@ -160,7 +159,7 @@ absl::StatusOr<Json> ProtoMapFieldToJson(
CEL_ASSIGN_OR_RETURN(auto key, ProtoMapKeyToJsonString(begin.GetKey()));
CEL_ASSIGN_OR_RETURN(
auto value,
ProtoMapValueToJson(value_manager, field->message_type()->map_value(),
ProtoMapValueToJson(converter, field->message_type()->map_value(),
begin.GetValueRef()));
builder.insert_or_assign(std::move(key), std::move(value));
++begin;
Expand All @@ -169,7 +168,7 @@ absl::StatusOr<Json> ProtoMapFieldToJson(
}

absl::StatusOr<Json> ProtoRepeatedFieldToJson(
ValueManager& value_manager, const google::protobuf::Message& message,
AnyToJsonConverter& converter, const google::protobuf::Message& message,
absl::Nonnull<const google::protobuf::Reflection*> reflection,
absl::Nonnull<const google::protobuf::FieldDescriptor*> field) {
JsonArrayBuilder builder;
Expand Down Expand Up @@ -248,8 +247,8 @@ absl::StatusOr<Json> ProtoRepeatedFieldToJson(
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
for (int field_index = 0; field_index < field_size; ++field_index) {
CEL_ASSIGN_OR_RETURN(
auto json, ProtoMessageToJson(value_manager,
reflection->GetRepeatedMessage(
auto json,
ProtoMessageToJson(converter, reflection->GetRepeatedMessage(
message, field, field_index)));
builder.push_back(std::move(json));
}
Expand All @@ -263,19 +262,19 @@ absl::StatusOr<Json> ProtoRepeatedFieldToJson(
}

absl::StatusOr<Json> ProtoFieldToJson(
ValueManager& value_manager, const google::protobuf::Message& message,
AnyToJsonConverter& converter, const google::protobuf::Message& message,
absl::Nonnull<const google::protobuf::Reflection*> reflection,
absl::Nonnull<const google::protobuf::FieldDescriptor*> field) {
if (field->is_map()) {
return ProtoMapFieldToJson(value_manager, message, reflection, field);
return ProtoMapFieldToJson(converter, message, reflection, field);
}
if (field->is_repeated()) {
return ProtoRepeatedFieldToJson(value_manager, message, reflection, field);
return ProtoRepeatedFieldToJson(converter, message, reflection, field);
}
return ProtoSingularFieldToJson(value_manager, message, reflection, field);
return ProtoSingularFieldToJson(converter, message, reflection, field);
}

absl::StatusOr<Json> ProtoAnyToJson(ValueManager& value_manager,
absl::StatusOr<Json> ProtoAnyToJson(AnyToJsonConverter& converter,
const google::protobuf::Message& message) {
CEL_ASSIGN_OR_RETURN(auto any, UnwrapDynamicAnyProto(message));
absl::string_view type_name;
Expand Down Expand Up @@ -459,13 +458,8 @@ absl::StatusOr<Json> ProtoAnyToJson(ValueManager& value_manager,
return absl::InvalidArgumentError(
"refusing to convert recursive `google.protobuf.Any` to JSON");
}
CEL_ASSIGN_OR_RETURN(
auto value, value_manager.DeserializeValue(any.type_url(), any.value()));
if (!value.has_value()) {
return absl::NotFoundError(
absl::StrCat("deserializer missing for `", any.type_url(), "`"));
}
CEL_ASSIGN_OR_RETURN(auto json_value, value->ConvertToJson(value_manager));
CEL_ASSIGN_OR_RETURN(auto json_value,
converter.ConvertToJson(any.type_url(), any.value()));
if (!absl::holds_alternative<JsonObject>(json_value)) {
return absl::InternalError("expected JSON object");
}
Expand All @@ -479,7 +473,7 @@ absl::StatusOr<Json> ProtoAnyToJson(ValueManager& value_manager,

} // namespace

absl::StatusOr<Json> ProtoMessageToJson(ValueManager& value_manager,
absl::StatusOr<Json> ProtoMessageToJson(AnyToJsonConverter& converter,
const google::protobuf::Message& message) {
const auto* descriptor = message.GetDescriptor();
if (ABSL_PREDICT_FALSE(descriptor == nullptr)) {
Expand Down Expand Up @@ -529,7 +523,7 @@ absl::StatusOr<Json> ProtoMessageToJson(ValueManager& value_manager,
return value;
}
case google::protobuf::Descriptor::WELLKNOWNTYPE_ANY:
return ProtoAnyToJson(value_manager, message);
return ProtoAnyToJson(converter, message);
case google::protobuf::Descriptor::WELLKNOWNTYPE_FIELDMASK: {
CEL_ASSIGN_OR_RETURN(auto value,
DynamicFieldMaskProtoToJsonString(message));
Expand Down Expand Up @@ -562,9 +556,8 @@ absl::StatusOr<Json> ProtoMessageToJson(ValueManager& value_manager,
reflection->ListFields(message, &fields);
builder.reserve(fields.size());
for (const auto* field : fields) {
CEL_ASSIGN_OR_RETURN(
auto field_value,
ProtoFieldToJson(value_manager, message, reflection, field));
CEL_ASSIGN_OR_RETURN(auto field_value, ProtoFieldToJson(converter, message,
reflection, field));
builder.insert_or_assign(JsonString(field->json_name()),
std::move(field_value));
}
Expand Down
3 changes: 1 addition & 2 deletions extensions/protobuf/internal/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@
#include "absl/base/nullability.h"
#include "absl/status/statusor.h"
#include "common/json.h"
#include "common/value_manager.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

namespace cel::extensions::protobuf_internal {

// `value_manager` is used if `message` is `google.protobuf.Any`.
absl::StatusOr<Json> ProtoMessageToJson(ValueManager& value_manager,
absl::StatusOr<Json> ProtoMessageToJson(AnyToJsonConverter& converter,
const google::protobuf::Message& message);

// Convert a protocol buffer enum to JSON. Prefers the name, but will fallback
Expand Down
56 changes: 56 additions & 0 deletions extensions/protobuf/json.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "extensions/protobuf/json.h"

#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/any.h"
#include "common/json.h"
#include "extensions/protobuf/internal/json.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

namespace cel::extensions {

absl::StatusOr<Json> ProtoAnyToJsonConverter::ConvertToJson(
absl::string_view type_url, const absl::Cord& value) {
absl::string_view type_name;
if (!ParseTypeUrl(type_url, &type_name)) {
return absl::InvalidArgumentError(
absl::StrCat("invalid type URL: ", type_url));
}
const auto* descriptor = pool_->FindMessageTypeByName(type_name);
if (descriptor == nullptr) {
return absl::NotFoundError(
absl::StrCat("unable to locate descriptor for `", type_name, "`"));
}
const auto* prototype = factory_->GetPrototype(descriptor);
if (prototype == nullptr) {
return absl::NotFoundError(
absl::StrCat("unable to create prototype for `", type_name, "`"));
}
auto message = absl::WrapUnique(prototype->New());
if (!message->ParsePartialFromCord(value)) {
return absl::InvalidArgumentError(
absl::StrCat("failed to parse `", type_name, "`"));
}
return protobuf_internal::ProtoMessageToJson(*this, *message);
}

} // namespace cel::extensions
44 changes: 44 additions & 0 deletions extensions/protobuf/json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_JSON_H_
#define THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_JSON_H_

#include "absl/base/nullability.h"
#include "absl/status/statusor.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "common/json.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

namespace cel::extensions {

class ProtoAnyToJsonConverter final : public AnyToJsonConverter {
public:
ProtoAnyToJsonConverter(absl::Nonnull<const google::protobuf::DescriptorPool*> pool,
absl::Nonnull<google::protobuf::MessageFactory*> factory)
: pool_(pool), factory_(factory) {}

absl::StatusOr<Json> ConvertToJson(absl::string_view type_url,
const absl::Cord& value) override;

private:
const google::protobuf::DescriptorPool* const pool_;
google::protobuf::MessageFactory* const factory_;
};

} // namespace cel::extensions

#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_JSON_H_

0 comments on commit 04fa70d

Please # to comment.