Skip to content

Commit 147d67f

Browse files
maskri17copybara-github
authored andcommitted
Implement strings.format in CEL string extensions.
PiperOrigin-RevId: 958621592
1 parent 5a38e1c commit 147d67f

8 files changed

Lines changed: 278 additions & 32 deletions

File tree

common/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,7 @@ cc_test(
887887
"@com_google_absl//absl/types:optional",
888888
"@com_google_cel_spec//proto/cel/expr/conformance/proto2:test_all_types_cc_proto",
889889
"@com_google_cel_spec//proto/cel/expr/conformance/proto3:test_all_types_cc_proto",
890+
"@com_google_protobuf//:field_mask_cc_proto",
890891
"@com_google_protobuf//:protobuf",
891892
"@com_google_protobuf//:struct_cc_proto",
892893
"@com_google_protobuf//:type_cc_proto",

common/values/parsed_message_value.cc

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "base/attribute.h"
3535
#include "common/memory.h"
3636
#include "common/value.h"
37+
#include "common/values/values.h"
3738
#include "extensions/protobuf/internal/qualify.h"
3839
#include "internal/empty_descriptors.h"
3940
#include "internal/json.h"
@@ -51,8 +52,6 @@ namespace cel {
5152

5253
namespace {
5354

54-
using ::cel::well_known_types::ValueReflection;
55-
5655
template <typename T>
5756
std::enable_if_t<std::is_base_of_v<google::protobuf::Message, T>,
5857
const google::protobuf::Message* absl_nonnull>
@@ -114,12 +113,8 @@ absl::Status ParsedMessageValue::ConvertToJson(
114113
ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(),
115114
google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE);
116115

117-
ValueReflection value_reflection;
118-
CEL_RETURN_IF_ERROR(value_reflection.Initialize(json->GetDescriptor()));
119-
google::protobuf::Message* json_object = value_reflection.MutableStructValue(json);
120-
121116
return internal::MessageToJson(*value_, descriptor_pool, message_factory,
122-
json_object);
117+
json);
123118
}
124119

125120
absl::Status ParsedMessageValue::ConvertToJsonObject(

common/values/parsed_message_value_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <utility>
1616

17+
#include "google/protobuf/field_mask.pb.h"
1718
#include "google/protobuf/struct.pb.h"
1819
#include "absl/status/status_matchers.h"
1920
#include "absl/strings/cord.h"
@@ -84,6 +85,19 @@ TEST_F(ParsedMessageValueTest, ConvertToJson) {
8485
R"pb(struct_value: {})pb"));
8586
}
8687

88+
TEST_F(ParsedMessageValueTest, ConvertToJsonFieldMask) {
89+
ParsedMessageValue value =
90+
MakeParsedMessage<google::protobuf::FieldMask>(R"pb(paths: "foo.bar"
91+
paths: "baz")pb");
92+
google::protobuf::Message* json =
93+
DynamicParseTextProto<google::protobuf::Value>(R"pb()pb");
94+
ASSERT_THAT(value.ConvertToJson(descriptor_pool(), message_factory(),
95+
cel::to_address(json)),
96+
IsOk());
97+
EXPECT_THAT(*json, EqualsTextProto<google::protobuf::Value>(
98+
R"pb(string_value: "foo.bar,baz")pb"));
99+
}
100+
87101
TEST_F(ParsedMessageValueTest, Equal) {
88102
MessageValue value = MakeParsedMessage<TestAllTypesProto3>();
89103
EXPECT_THAT(

conformance/BUILD

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,6 @@ _TESTS_TO_SKIP = [
175175
"namespace/namespace/self_eval_container_lookup,self_eval_container_lookup_unchecked",
176176

177177
# Skip until fixed.
178-
"wrappers/field_mask/to_json",
179-
"wrappers/empty/to_json",
180178
"fields/qualified_identifier_resolution/map_value_repeat_key_heterogeneous",
181179

182180
# Future features for CEL 1.0

eval/public/structs/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ cc_library(
7171
"@com_google_absl//absl/base:nullability",
7272
"@com_google_absl//absl/functional:overload",
7373
"@com_google_absl//absl/log:absl_check",
74+
"@com_google_absl//absl/log:absl_log",
7475
"@com_google_absl//absl/status",
7576
"@com_google_absl//absl/status:statusor",
7677
"@com_google_absl//absl/strings",
@@ -111,9 +112,11 @@ cc_test(
111112
"@com_google_absl//absl/status",
112113
"@com_google_absl//absl/strings",
113114
"@com_google_absl//absl/time",
115+
"@com_google_absl//absl/types:span",
114116
"@com_google_protobuf//:any_cc_proto",
115117
"@com_google_protobuf//:duration_cc_proto",
116118
"@com_google_protobuf//:empty_cc_proto",
119+
"@com_google_protobuf//:field_mask_cc_proto",
117120
"@com_google_protobuf//:protobuf",
118121
"@com_google_protobuf//:struct_cc_proto",
119122
"@com_google_protobuf//:wrappers_cc_proto",
@@ -218,6 +221,7 @@ cc_test(
218221
"@com_google_absl//absl/status:statusor",
219222
"@com_google_absl//absl/strings",
220223
"@com_google_absl//absl/time",
224+
"@com_google_absl//absl/types:span",
221225
"@com_google_protobuf//:any_cc_proto",
222226
"@com_google_protobuf//:duration_cc_proto",
223227
"@com_google_protobuf//:empty_cc_proto",

eval/public/structs/cel_proto_wrap_util.cc

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cstddef>
1818
#include <cstdint>
1919
#include <limits>
20+
#include <optional>
2021
#include <string>
2122
#include <type_traits>
2223
#include <utility>
@@ -31,6 +32,7 @@
3132
#include "absl/base/optimization.h"
3233
#include "absl/functional/overload.h"
3334
#include "absl/log/absl_check.h"
35+
#include "absl/log/absl_log.h"
3436
#include "absl/status/status.h"
3537
#include "absl/status/statusor.h"
3638
#include "absl/strings/cord.h"
@@ -50,6 +52,7 @@
5052
#include "internal/well_known_types.h"
5153
#include "google/protobuf/arena.h"
5254
#include "google/protobuf/descriptor.h"
55+
#include "google/protobuf/json/json.h"
5356
#include "google/protobuf/message.h"
5457
#include "google/protobuf/message_lite.h"
5558

@@ -79,6 +82,8 @@ using google::protobuf::Descriptor;
7982
using google::protobuf::DescriptorPool;
8083
using google::protobuf::Message;
8184
using google::protobuf::MessageFactory;
85+
using google::protobuf::json::MessageToJsonString;
86+
using google::protobuf::json::PrintOptions;
8287

8388
// kMaxIntJSON is defined as the Number.MAX_SAFE_INTEGER value per EcmaScript 6.
8489
constexpr int64_t kMaxIntJSON = (1ll << 53) - 1;
@@ -98,6 +103,27 @@ static bool IsJSONSafe(uint64_t i) {
98103
return i <= static_cast<uint64_t>(kMaxIntJSON);
99104
}
100105

106+
static std::optional<std::string> GetFieldMaskJsonString(
107+
const google::protobuf::Message& message) {
108+
// TODO(b/540507668): Refactor to pipe descriptor_pool through
109+
// ValueFromValue to use internal::MessageToJson.
110+
PrintOptions json_options;
111+
std::string json_str;
112+
auto status = MessageToJsonString(message, &json_str, json_options);
113+
if (!status.ok()) {
114+
ABSL_LOG(ERROR) << "Failed to convert FieldMask to JSON: " << status;
115+
return std::nullopt;
116+
}
117+
// If JSON marshalling is correct, we know we'll always get a plain
118+
// JSON string value and it shouldn't contain any escapes that we need
119+
// to interpret.
120+
if (json_str.size() >= 2 && json_str.front() == '"' &&
121+
json_str.back() == '"') {
122+
return json_str.substr(1, json_str.size() - 2);
123+
}
124+
return json_str;
125+
}
126+
101127
// Map implementation wrapping google.protobuf.ListValue
102128
class DynamicList : public CelList {
103129
public:
@@ -1079,6 +1105,25 @@ google::protobuf::Message* ValueFromValue(google::protobuf::Message* message, co
10791105
return message;
10801106
}
10811107
} break;
1108+
case CelValue::Type::kMessage: {
1109+
const google::protobuf::Message* message_ptr = value.MessageOrDie();
1110+
if (message_ptr->GetDescriptor()->full_name() ==
1111+
"google.protobuf.Empty") {
1112+
reflection.MutableStructValue(message);
1113+
return message;
1114+
}
1115+
if (message_ptr->GetDescriptor()->full_name() ==
1116+
"google.protobuf.FieldMask") {
1117+
std::optional<std::string> fm_str =
1118+
GetFieldMaskJsonString(*message_ptr);
1119+
if (fm_str.has_value()) {
1120+
reflection.SetStringValue(message, *fm_str);
1121+
return message;
1122+
}
1123+
return nullptr;
1124+
}
1125+
return nullptr;
1126+
} break;
10821127
case CelValue::Type::kNullType:
10831128
reflection.SetNullValue(message);
10841129
return message;
@@ -1229,6 +1274,25 @@ bool ValueFromValue(Value* json, const CelValue& value, google::protobuf::Arena*
12291274
return ListFromValue(json->mutable_list_value(), value, arena);
12301275
case CelValue::Type::kMap:
12311276
return StructFromValue(json->mutable_struct_value(), value, arena);
1277+
case CelValue::Type::kMessage: {
1278+
const google::protobuf::Message* message_ptr = value.MessageOrDie();
1279+
if (message_ptr->GetDescriptor()->full_name() ==
1280+
"google.protobuf.Empty") {
1281+
json->mutable_struct_value();
1282+
return true;
1283+
}
1284+
if (message_ptr->GetDescriptor()->full_name() ==
1285+
"google.protobuf.FieldMask") {
1286+
std::optional<std::string> fm_str =
1287+
GetFieldMaskJsonString(*message_ptr);
1288+
if (fm_str.has_value()) {
1289+
json->set_string_value(*fm_str);
1290+
return true;
1291+
}
1292+
return false;
1293+
}
1294+
return false;
1295+
}
12321296
case CelValue::Type::kNullType:
12331297
json->set_null_value(protobuf::NULL_VALUE);
12341298
return true;
@@ -1254,7 +1318,7 @@ google::protobuf::Message* AnyFromValue(const google::protobuf::Message* prototy
12541318
case CelValue::Type::kBytes: {
12551319
BytesValue v;
12561320
type_name = v.GetTypeName();
1257-
v.set_value(std::string(value.BytesOrDie().value()));
1321+
v.set_value(value.BytesOrDie().value());
12581322
payload = v.SerializeAsCord();
12591323
} break;
12601324
case CelValue::Type::kDouble: {

0 commit comments

Comments
 (0)