Skip to content

Commit ced1843

Browse files
authored
Add string helper functions to CEL executor (#2247)
* Add string helper functions to CEL executor * Add unit test
1 parent 84b1347 commit ced1843

File tree

7 files changed

+483
-3
lines changed

7 files changed

+483
-3
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Cel.Checker;
2+
using Google.Api.Expr.V1Alpha1;
3+
using Type = Google.Api.Expr.V1Alpha1.Type;
4+
5+
namespace Confluent.SchemaRegistry.Rules
6+
{
7+
public class BuiltinDeclarations
8+
{
9+
public static IList<Decl> Create()
10+
{
11+
IList<Decl> decls = new List<Decl>();
12+
13+
decls.Add(
14+
Decls.NewFunction(
15+
"isEmail",
16+
Decls.NewInstanceOverload(
17+
"is_email", new List<Type> { Decls.String }, Decls.Bool)));
18+
19+
decls.Add(
20+
Decls.NewFunction(
21+
"isHostname",
22+
Decls.NewInstanceOverload(
23+
"is_hostname", new List<Type> { Decls.String }, Decls.Bool)));
24+
25+
decls.Add(
26+
Decls.NewFunction(
27+
"isIpv4",
28+
Decls.NewInstanceOverload(
29+
"is_ipv4", new List<Type> { Decls.String }, Decls.Bool)));
30+
31+
decls.Add(
32+
Decls.NewFunction(
33+
"isIpv6",
34+
Decls.NewInstanceOverload(
35+
"is_ipv6", new List<Type> { Decls.String }, Decls.Bool)));
36+
37+
decls.Add(
38+
Decls.NewFunction(
39+
"isUriRef",
40+
Decls.NewInstanceOverload(
41+
"is_uri_ref", new List<Type> { Decls.String }, Decls.Bool)));
42+
43+
decls.Add(
44+
Decls.NewFunction(
45+
"isUri",
46+
Decls.NewInstanceOverload(
47+
"is_uri", new List<Type> { Decls.String }, Decls.Bool)));
48+
49+
decls.Add(
50+
Decls.NewFunction(
51+
"isUuid",
52+
Decls.NewInstanceOverload(
53+
"is_uuid", new List<Type> { Decls.String }, Decls.Bool)));
54+
55+
return decls;
56+
}
57+
}
58+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Cel;
2+
3+
namespace Confluent.SchemaRegistry.Rules
4+
{
5+
public class BuiltinLibrary : ILibrary
6+
{
7+
8+
public virtual IList<EnvOption> CompileOptions
9+
{
10+
get => new List<EnvOption> { IEnvOption.Declarations(BuiltinDeclarations.Create()) };
11+
}
12+
13+
public virtual IList<ProgramOption> ProgramOptions
14+
{
15+
get => new List<ProgramOption>
16+
{
17+
IProgramOption.EvalOptions(EvalOption.OptOptimize),
18+
IProgramOption.Functions(BuiltinOverload.Create())
19+
};
20+
}
21+
}
22+
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Net;
3+
using System.Net.Sockets;
4+
using Cel.Common.Types;
5+
using Cel.Common.Types.Ref;
6+
using Cel.Interpreter.Functions;
7+
8+
namespace Confluent.SchemaRegistry.Rules
9+
{
10+
public class BuiltinOverload
11+
{
12+
private const string OverloadIsEmail = "isEmail";
13+
private const string OverloadIsHostname = "isHostname";
14+
private const string OverloadIsIpv4 = "isIpv4";
15+
private const string OverloadIsIpv6 = "isIpv6";
16+
private const string OverloadIsUri = "isUri";
17+
private const string OverloadIsUriRef = "isUriRef";
18+
private const string OverloadIsUuid = "isUuid";
19+
20+
public static Overload[] Create()
21+
{
22+
return new Overload[]
23+
{
24+
IsEmail(),
25+
IsHostname(),
26+
IsIpv4(),
27+
IsIpv6(),
28+
IsUri(),
29+
IsUriRef(),
30+
IsUuid(),
31+
};
32+
}
33+
34+
private static Overload IsEmail()
35+
{
36+
return Overload.Unary(
37+
OverloadIsEmail,
38+
value =>
39+
{
40+
if (value.Type().TypeEnum() != TypeEnum.String)
41+
{
42+
return Err.NoSuchOverload(value, OverloadIsEmail, null);
43+
}
44+
45+
string input = (string)value.Value();
46+
return string.IsNullOrEmpty(input)
47+
? BoolT.False
48+
: Types.BoolOf(ValidateEmail(input));
49+
});
50+
}
51+
52+
private static Overload IsHostname()
53+
{
54+
return Overload.Unary(
55+
OverloadIsHostname,
56+
value =>
57+
{
58+
if (value.Type().TypeEnum() != TypeEnum.String)
59+
{
60+
return Err.NoSuchOverload(value, OverloadIsHostname, null);
61+
}
62+
63+
string input = (string)value.Value();
64+
return string.IsNullOrEmpty(input)
65+
? BoolT.False
66+
: Types.BoolOf(ValidateHostname(input));
67+
});
68+
}
69+
70+
private static Overload IsIpv4()
71+
{
72+
return Overload.Unary(
73+
OverloadIsIpv4,
74+
value =>
75+
{
76+
if (value.Type().TypeEnum() != TypeEnum.String)
77+
{
78+
return Err.NoSuchOverload(value, OverloadIsIpv4, null);
79+
}
80+
81+
string input = (string)value.Value();
82+
return string.IsNullOrEmpty(input)
83+
? BoolT.False
84+
: Types.BoolOf(ValidateIpv4(input));
85+
});
86+
}
87+
88+
private static Overload IsIpv6()
89+
{
90+
return Overload.Unary(
91+
OverloadIsIpv6,
92+
value =>
93+
{
94+
if (value.Type().TypeEnum() != TypeEnum.String)
95+
{
96+
return Err.NoSuchOverload(value, OverloadIsIpv6, null);
97+
}
98+
99+
string input = (string)value.Value();
100+
return string.IsNullOrEmpty(input)
101+
? BoolT.False
102+
: Types.BoolOf(ValidateIpv6(input));
103+
});
104+
}
105+
106+
private static Overload IsUri()
107+
{
108+
return Overload.Unary(
109+
OverloadIsUri,
110+
value =>
111+
{
112+
if (value.Type().TypeEnum() != TypeEnum.String)
113+
{
114+
return Err.NoSuchOverload(value, OverloadIsUri, null);
115+
}
116+
117+
string input = (string)value.Value();
118+
return string.IsNullOrEmpty(input)
119+
? BoolT.False
120+
: Types.BoolOf(ValidateUri(input));
121+
});
122+
}
123+
124+
private static Overload IsUriRef()
125+
{
126+
return Overload.Unary(
127+
OverloadIsUriRef,
128+
value =>
129+
{
130+
if (value.Type().TypeEnum() != TypeEnum.String)
131+
{
132+
return Err.NoSuchOverload(value, OverloadIsUriRef, null);
133+
}
134+
135+
string input = (string)value.Value();
136+
return string.IsNullOrEmpty(input)
137+
? BoolT.False
138+
: Types.BoolOf(ValidateUriRef(input));
139+
});
140+
}
141+
142+
private static Overload IsUuid()
143+
{
144+
return Overload.Unary(
145+
OverloadIsUuid,
146+
value =>
147+
{
148+
if (value.Type().TypeEnum() != TypeEnum.String)
149+
{
150+
return Err.NoSuchOverload(value, OverloadIsUuid, null);
151+
}
152+
153+
string input = (string)value.Value();
154+
return string.IsNullOrEmpty(input)
155+
? BoolT.False
156+
: Types.BoolOf(ValidateUuid(input));
157+
});
158+
}
159+
160+
public static bool ValidateEmail(string input)
161+
{
162+
return new EmailAddressAttribute().IsValid(input);
163+
}
164+
165+
public static bool ValidateHostname(string input)
166+
{
167+
return Uri.CheckHostName(input) != UriHostNameType.Unknown;
168+
}
169+
170+
public static bool ValidateIpv4(string input)
171+
{
172+
if (IPAddress.TryParse(input, out IPAddress address))
173+
{
174+
return address.AddressFamily == AddressFamily.InterNetwork;
175+
}
176+
177+
return false;
178+
}
179+
180+
public static bool ValidateIpv6(string input)
181+
{
182+
if (IPAddress.TryParse(input, out IPAddress address))
183+
{
184+
return address.AddressFamily == AddressFamily.InterNetworkV6;
185+
}
186+
187+
return false;
188+
}
189+
190+
public static bool ValidateUri(string input)
191+
{
192+
return Uri.TryCreate(input, UriKind.Absolute, out _);
193+
}
194+
195+
public static bool ValidateUriRef(string input)
196+
{
197+
return Uri.TryCreate(input, UriKind.RelativeOrAbsolute, out _);
198+
}
199+
200+
public static bool ValidateUuid(string input)
201+
{
202+
return Guid.TryParse(input, out _);
203+
}
204+
}
205+
}

src/Confluent.SchemaRegistry.Rules/CelExecutor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Cel.Common.Types.Avro;
77
using Cel.Common.Types.Json;
88
using Cel.Common.Types.Pb;
9+
using Cel.Extension;
910
using Cel.Tools;
1011
using Duration = Google.Protobuf.WellKnownTypes.Duration;
1112
using Google.Api.Expr.V1Alpha1;
@@ -166,8 +167,7 @@ private Script BuildScript(RuleWithArgs ruleWithArgs, object msg)
166167
.WithDeclarations(new List<Decl>(ruleWithArgs.Decls.Values))
167168
.WithTypes(type);
168169

169-
// TODO builtin library
170-
//scriptBuilder = scriptBuilder.WithLibraries(new StringsLib(), new BuiltinLibrary());
170+
scriptBuilder = scriptBuilder.WithLibraries(new StringsLib(), new BuiltinLibrary());
171171
return scriptBuilder.Build();
172172
}
173173

src/Confluent.SchemaRegistry.Rules/Confluent.SchemaRegistry.Rules.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</ItemGroup>
1616

1717
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
18-
<PackageReference Include="Cel.NET" Version="0.3.0" />
18+
<PackageReference Include="Cel.NET" Version="0.3.1" />
1919
</ItemGroup>
2020

2121
</Project>

0 commit comments

Comments
 (0)