Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion conformance/policy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ cc_library(
"//env:env_std_extensions",
"//env:env_yaml",
"//env:runtime_std_extensions",
"//extensions/protobuf:bind_proto_to_activation",
"//extensions/protobuf:enum_adapter",
"//internal:runfiles",
"//internal:status_macros",
Expand All @@ -50,6 +49,7 @@ cc_library(
"//policy:yaml_policy_parser",
"//runtime",
"//runtime:activation",
"//runtime:bind_proto_to_activation",
"//runtime:function_adapter",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/log:absl_check",
Expand Down
7 changes: 3 additions & 4 deletions conformance/policy/policy_conformance_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
#include "env/env_std_extensions.h"
#include "env/env_yaml.h"
#include "env/runtime_std_extensions.h"
#include "extensions/protobuf/bind_proto_to_activation.h"
#include "extensions/protobuf/enum_adapter.h"
#include "internal/runfiles.h"
#include "internal/status_macros.h"
Expand All @@ -53,6 +52,7 @@
#include "policy/test_util.h"
#include "policy/yaml_policy_parser.h"
#include "runtime/activation.h"
#include "runtime/bind_proto_to_activation.h"
#include "runtime/function_adapter.h"
#include "runtime/runtime.h"
#include "cel/expr/conformance/test/suite.pb.h"
Expand Down Expand Up @@ -384,9 +384,8 @@ absl::Status PopulateActivation(
"Failed to resolve context message for test case");
}

return cel::extensions::BindProtoToActivation(
*context_message,
cel::extensions::BindProtoUnsetFieldBehavior::kBindDefaultValue,
return cel::BindProtoToActivation(
*context_message, cel::BindProtoUnsetFieldBehavior::kBindDefaultValue,
descriptor_pool, message_factory, arena, &activation);
}

Expand Down
3 changes: 2 additions & 1 deletion extensions/protobuf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,12 @@ cc_library(
srcs = ["bind_proto_to_activation.cc"],
hdrs = ["bind_proto_to_activation.h"],
deps = [
":value",
"//common:casting",
"//common:value",
"//internal:status_macros",
"//runtime:activation",
"//runtime:bind_proto_to_activation",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
Expand Down
77 changes: 0 additions & 77 deletions extensions/protobuf/bind_proto_to_activation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,80 +13,3 @@
// limitations under the License.

#include "extensions/protobuf/bind_proto_to_activation.h"

#include <utility>

#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "common/value.h"
#include "internal/status_macros.h"
#include "runtime/activation.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

namespace cel::extensions::protobuf_internal {

namespace {

using ::google::protobuf::Descriptor;

absl::StatusOr<bool> ShouldBindField(
const google::protobuf::FieldDescriptor* field_desc, const StructValue& struct_value,
BindProtoUnsetFieldBehavior unset_field_behavior) {
if (unset_field_behavior == BindProtoUnsetFieldBehavior::kBindDefaultValue ||
field_desc->is_repeated()) {
return true;
}
return struct_value.HasFieldByNumber(field_desc->number());
}

absl::StatusOr<Value> GetFieldValue(
const google::protobuf::FieldDescriptor* field_desc, const StructValue& struct_value,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
// Special case unset any.
if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
field_desc->message_type()->well_known_type() ==
Descriptor::WELLKNOWNTYPE_ANY) {
CEL_ASSIGN_OR_RETURN(bool present,
struct_value.HasFieldByNumber(field_desc->number()));
if (!present) {
return NullValue();
}
}

return struct_value.GetFieldByNumber(field_desc->number(), descriptor_pool,
message_factory, arena);
}

} // namespace

absl::Status BindProtoToActivation(
const Descriptor& descriptor, const StructValue& struct_value,
BindProtoUnsetFieldBehavior unset_field_behavior,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) {
for (int i = 0; i < descriptor.field_count(); i++) {
const google::protobuf::FieldDescriptor* field_desc = descriptor.field(i);
CEL_ASSIGN_OR_RETURN(
bool should_bind,
ShouldBindField(field_desc, struct_value, unset_field_behavior));
if (!should_bind) {
continue;
}

CEL_ASSIGN_OR_RETURN(
Value field, GetFieldValue(field_desc, struct_value, descriptor_pool,
message_factory, arena));

activation->InsertOrAssignValue(field_desc->name(), std::move(field));
}

return absl::OkStatus();
}

} // namespace cel::extensions::protobuf_internal
109 changes: 8 additions & 101 deletions extensions/protobuf/bind_proto_to_activation.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,116 +15,23 @@
#ifndef THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_BIND_PROTO_TO_ACTIVATION_H_
#define THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_BIND_PROTO_TO_ACTIVATION_H_

#include <type_traits>

#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "common/casting.h"
#include "common/value.h"
#include "extensions/protobuf/value.h"
#include "internal/status_macros.h"
#include "runtime/activation.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"
#include "absl/base/attributes.h"
#include "runtime/bind_proto_to_activation.h"

namespace cel::extensions {

// Option for handling unset fields on the context proto.
enum class BindProtoUnsetFieldBehavior {
// Bind the message defined default or zero value.
kBindDefaultValue,
// Skip binding unset fields, no value is bound for the corresponding
// variable.
kSkip
};
using BindProtoUnsetFieldBehavior ABSL_DEPRECATED(
"Use cel::BindProtoUnsetFieldBehavior instead") =
::cel::BindProtoUnsetFieldBehavior;

using ::cel::BindProtoToActivation;

namespace protobuf_internal {

// Implements binding provided the context message has already
// been adapted to a suitable struct value.
absl::Status BindProtoToActivation(
const google::protobuf::Descriptor& descriptor, const StructValue& struct_value,
BindProtoUnsetFieldBehavior unset_field_behavior,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation);
using ::cel::runtime_internal::BindProtoToActivation;

} // namespace protobuf_internal

// Utility method, that takes a protobuf Message and interprets it as a
// namespace, binding its fields to Activation. This is often referred to as a
// context message.
//
// Field names and values become respective names and values of parameters
// bound to the Activation object.
// Example:
// Assume we have a protobuf message of type:
// message Person {
// int age = 1;
// string name = 2;
// }
//
// The sample code snippet will look as follows:
//
// Person person;
// person.set_name("John Doe");
// person.age(42);
//
// CEL_RETURN_IF_ERROR(BindProtoToActivation(person, value_factory,
// activation));
//
// After this snippet, activation will have two parameters bound:
// "name", with string value of "John Doe"
// "age", with int value of 42.
//
// The default behavior for unset fields is to skip them. E.g. if the name field
// is not set on the Person message, it will not be bound in to the activation.
// BindProtoUnsetFieldBehavior::kBindDefault, will bind the cc proto api default
// for the field (either an explicit default value or a type specific default).
//
// For repeated fields, an unset field is bound as an empty list.
template <typename T>
absl::Status BindProtoToActivation(
const T& context, BindProtoUnsetFieldBehavior unset_field_behavior,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) {
static_assert(std::is_base_of_v<google::protobuf::Message, T>);
// TODO(uncreated-issue/68): for simplicity, just convert the whole message to a
// struct value. For performance, may be better to convert members as needed.
CEL_ASSIGN_OR_RETURN(
Value parent,
ProtoMessageToValue(context, descriptor_pool, message_factory, arena));

if (!InstanceOf<StructValue>(parent)) {
return absl::InvalidArgumentError(
absl::StrCat("context is a well-known type: ", context.GetTypeName()));
}
const StructValue& struct_value = Cast<StructValue>(parent);

const google::protobuf::Descriptor* descriptor = context.GetDescriptor();

if (descriptor == nullptr) {
return absl::InvalidArgumentError(
absl::StrCat("context missing descriptor: ", context.GetTypeName()));
}

return protobuf_internal::BindProtoToActivation(
*descriptor, struct_value, unset_field_behavior, descriptor_pool,
message_factory, arena, activation);
}
template <typename T>
absl::Status BindProtoToActivation(
const T& context,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) {
return BindProtoToActivation(context, BindProtoUnsetFieldBehavior::kSkip,
descriptor_pool, message_factory, arena,
activation);
}

} // namespace cel::extensions

#endif // THIRD_PARTY_CEL_CPP_EXTENSIONS_PROTOBUF_BIND_PROTO_TO_ACTIVATION_H_
36 changes: 36 additions & 0 deletions runtime/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,42 @@ cc_test(
],
)

cc_library(
name = "bind_proto_to_activation",
srcs = ["bind_proto_to_activation.cc"],
hdrs = ["bind_proto_to_activation.h"],
deps = [
":activation",
"//common:casting",
"//common:value",
"//internal:status_macros",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
],
)

cc_test(
name = "bind_proto_to_activation_test",
srcs = ["bind_proto_to_activation_test.cc"],
deps = [
":activation",
":bind_proto_to_activation",
"//common:casting",
"//common:value",
"//common:value_testing",
"//internal:testing",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:status_matchers",
"@com_google_absl//absl/types:optional",
"@com_google_cel_spec//proto/cel/expr/conformance/proto2:test_all_types_cc_proto",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//:wrappers_cc_proto",
],
)

cc_library(
name = "register_function_helper",
hdrs = ["register_function_helper.h"],
Expand Down
92 changes: 92 additions & 0 deletions runtime/bind_proto_to_activation.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2023 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 "runtime/bind_proto_to_activation.h"

#include <utility>

#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "common/value.h"
#include "internal/status_macros.h"
#include "runtime/activation.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"

namespace cel::runtime_internal {

namespace {

using ::google::protobuf::Descriptor;

absl::StatusOr<bool> ShouldBindField(
const google::protobuf::FieldDescriptor* field_desc, const StructValue& struct_value,
BindProtoUnsetFieldBehavior unset_field_behavior) {
if (unset_field_behavior == BindProtoUnsetFieldBehavior::kBindDefaultValue ||
field_desc->is_repeated()) {
return true;
}
return struct_value.HasFieldByNumber(field_desc->number());
}

absl::StatusOr<Value> GetFieldValue(
const google::protobuf::FieldDescriptor* field_desc, const StructValue& struct_value,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena) {
// Special case unset any.
if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
field_desc->message_type()->well_known_type() ==
Descriptor::WELLKNOWNTYPE_ANY) {
CEL_ASSIGN_OR_RETURN(bool present,
struct_value.HasFieldByNumber(field_desc->number()));
if (!present) {
return NullValue();
}
}

return struct_value.GetFieldByNumber(field_desc->number(), descriptor_pool,
message_factory, arena);
}

} // namespace

absl::Status BindProtoToActivation(
const Descriptor& descriptor, const StructValue& struct_value,
BindProtoUnsetFieldBehavior unset_field_behavior,
const google::protobuf::DescriptorPool* absl_nonnull descriptor_pool,
google::protobuf::MessageFactory* absl_nonnull message_factory,
google::protobuf::Arena* absl_nonnull arena, Activation* absl_nonnull activation) {
for (int i = 0; i < descriptor.field_count(); i++) {
const google::protobuf::FieldDescriptor* field_desc = descriptor.field(i);
CEL_ASSIGN_OR_RETURN(
bool should_bind,
ShouldBindField(field_desc, struct_value, unset_field_behavior));
if (!should_bind) {
continue;
}

CEL_ASSIGN_OR_RETURN(
Value field, GetFieldValue(field_desc, struct_value, descriptor_pool,
message_factory, arena));

activation->InsertOrAssignValue(field_desc->name(), std::move(field));
}

return absl::OkStatus();
}

} // namespace cel::runtime_internal
Loading
Loading