Skip to content

Commit 3a35cc4

Browse files
Implement getField CEL function (#290)
I'm proposing this as an eventual replacement (before 1.0) of our hack around the fact that the `in` identifier is reserved in CEL. This is especially urgent for protovalidate-cc which is currently carrying patches to the CEL implementation in order to enable it, since cel-cpp doesn't allow this sort of functionality to be added in at runtime. Protovalidate PR: bufbuild/protovalidate#352
1 parent 44885c1 commit 3a35cc4

File tree

2 files changed

+19
-4
lines changed

2 files changed

+19
-4
lines changed

protovalidate/internal/constraints.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def make_timestamp(msg: message.Message) -> celtypes.TimestampType:
4040

4141

4242
def unwrap(msg: message.Message) -> celtypes.Value:
43-
return _field_to_cel(msg, msg.DESCRIPTOR.fields_by_name["value"])
43+
return field_to_cel(msg, msg.DESCRIPTOR.fields_by_name["value"])
4444

4545

4646
_MSG_TYPE_URL_TO_CTOR: dict[str, typing.Callable[..., celtypes.Value]] = {
@@ -70,7 +70,7 @@ def __init__(self, msg: message.Message):
7070
for field in self.desc.fields:
7171
if field.containing_oneof is not None and not self.msg.HasField(field.name):
7272
continue
73-
self[field.name] = _field_to_cel(self.msg, field)
73+
self[field.name] = field_to_cel(self.msg, field)
7474

7575
def __getitem__(self, name):
7676
field = self.desc.fields_by_name[name]
@@ -175,7 +175,7 @@ def _map_field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -
175175
return _map_field_value_to_cel(_proto_message_get_field(msg, field), field)
176176

177177

178-
def _field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -> celtypes.Value:
178+
def field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -> celtypes.Value:
179179
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
180180
return _repeated_field_to_cel(msg, field)
181181
elif field.message_type is not None and not _proto_message_has_field(msg, field):
@@ -374,7 +374,7 @@ def add_rule(
374374
rule_cel = None
375375
if rule_field is not None and self._rules is not None:
376376
rule_value = _proto_message_get_field(self._rules, rule_field)
377-
rule_cel = _field_to_cel(self._rules, rule_field)
377+
rule_cel = field_to_cel(self._rules, rule_field)
378378
self._cel.append(
379379
CelRunner(
380380
runner=prog,

protovalidate/internal/extra_func.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,27 @@
2121
from celpy import celtypes
2222

2323
from protovalidate.internal import string_format
24+
from protovalidate.internal.constraints import MessageType, field_to_cel
2425

2526
# See https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
2627
_email_regex = re.compile(
2728
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
2829
)
2930

3031

32+
def cel_get_field(message: celtypes.Value, field_name: celtypes.Value) -> celpy.Result:
33+
if not isinstance(message, MessageType):
34+
msg = "invalid argument, expected message"
35+
raise celpy.CELEvalError(msg)
36+
if not isinstance(field_name, celtypes.StringType):
37+
msg = "invalid argument, expected string"
38+
raise celpy.CELEvalError(msg)
39+
if field_name not in message.desc.fields_by_name:
40+
msg = f"no such field: {field_name}"
41+
raise celpy.CELEvalError(msg)
42+
return field_to_cel(message.msg, message.desc.fields_by_name[field_name])
43+
44+
3145
def cel_is_ip(val: celtypes.Value, ver: typing.Optional[celtypes.Value] = None) -> celpy.Result:
3246
"""Return True if the string is an IPv4 or IPv6 address, optionally limited to a specific version.
3347
@@ -1545,6 +1559,7 @@ def make_extra_funcs(locale: str) -> dict[str, celpy.CELFunction]:
15451559
# Missing standard functions
15461560
"format": string_fmt.format,
15471561
# protovalidate specific functions
1562+
"getField": cel_get_field,
15481563
"isNan": cel_is_nan,
15491564
"isInf": cel_is_inf,
15501565
"isIp": cel_is_ip,

0 commit comments

Comments
 (0)