Skip to content

Commit 149f12d

Browse files
l46kokcopybara-github
authored andcommitted
Add duration and regex literal validators
PiperOrigin-RevId: 561101227
1 parent 5593808 commit 149f12d

10 files changed

Lines changed: 686 additions & 53 deletions

File tree

validator/src/main/java/dev/cel/validator/validators/BUILD.bazel

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,55 @@ java_library(
1212
],
1313
tags = [
1414
],
15+
deps = [
16+
":literal_validator",
17+
"@maven//:com_google_protobuf_protobuf_java",
18+
],
19+
)
20+
21+
java_library(
22+
name = "duration",
23+
srcs = [
24+
"DurationLiteralValidator.java",
25+
],
26+
tags = [
27+
],
28+
deps = [
29+
":literal_validator",
30+
"@maven//:com_google_protobuf_protobuf_java",
31+
],
32+
)
33+
34+
java_library(
35+
name = "regex",
36+
srcs = [
37+
"RegexLiteralValidator.java",
38+
],
39+
tags = [
40+
],
41+
deps = [
42+
"//bundle:cel",
43+
"//common/ast",
44+
"//common/navigation",
45+
"//validator:ast_validator",
46+
"@maven//:com_google_guava_guava",
47+
],
48+
)
49+
50+
java_library(
51+
name = "literal_validator",
52+
srcs = [
53+
"LiteralValidator.java",
54+
],
55+
tags = [
56+
],
57+
visibility = ["//visibility:private"],
1558
deps = [
1659
"//bundle:cel",
1760
"//common",
1861
"//common/ast",
1962
"//common/navigation",
2063
"//validator:ast_validator",
2164
"@maven//:com_google_guava_guava",
22-
"@maven//:com_google_protobuf_protobuf_java",
2365
],
2466
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.validator.validators;
16+
17+
import com.google.protobuf.Duration;
18+
19+
/** DurationLiteralValidator ensures that duration literal arguments are valid. */
20+
public class DurationLiteralValidator extends LiteralValidator {
21+
public static final DurationLiteralValidator INSTANCE =
22+
new DurationLiteralValidator("duration", Duration.class);
23+
24+
private DurationLiteralValidator(String functionName, Class<?> expectedResultType) {
25+
super(functionName, expectedResultType);
26+
}
27+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.validator.validators;
16+
17+
import com.google.common.collect.ImmutableList;
18+
import dev.cel.bundle.Cel;
19+
import dev.cel.common.CelAbstractSyntaxTree;
20+
import dev.cel.common.CelSource;
21+
import dev.cel.common.ast.CelExpr;
22+
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
23+
import dev.cel.common.navigation.CelNavigableAst;
24+
import dev.cel.common.navigation.CelNavigableExpr;
25+
import dev.cel.validator.CelAstValidator;
26+
import java.util.Optional;
27+
28+
/**
29+
* LiteralValidator defines the common logic to handle simple validation of a literal in a function
30+
* call by evaluating it and ensuring that no errors are thrown (example: duration / timestamp
31+
* literals).
32+
*/
33+
abstract class LiteralValidator implements CelAstValidator {
34+
private final String functionName;
35+
private final Class<?> expectedResultType;
36+
37+
protected LiteralValidator(String functionName, Class<?> expectedResultType) {
38+
this.functionName = functionName;
39+
this.expectedResultType = expectedResultType;
40+
}
41+
42+
@Override
43+
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
44+
navigableAst
45+
.getRoot()
46+
.descendants()
47+
.filter(
48+
node ->
49+
node.getKind().equals(Kind.CONSTANT)
50+
&& node.parent()
51+
.map(
52+
parent -> parent.expr().callOrDefault().function().equals(functionName))
53+
.orElse(false))
54+
.map(CelNavigableExpr::expr)
55+
.forEach(
56+
expr -> {
57+
CelExpr callExpr =
58+
CelExpr.ofCallExpr(
59+
1,
60+
Optional.empty(),
61+
functionName,
62+
ImmutableList.of(CelExpr.ofConstantExpr(2, expr.constant())));
63+
64+
CelAbstractSyntaxTree ast =
65+
CelAbstractSyntaxTree.newParsedAst(callExpr, CelSource.newBuilder().build());
66+
try {
67+
ast = cel.check(ast).getAst();
68+
Object result = cel.createProgram(ast).eval();
69+
70+
if (!expectedResultType.isInstance(result)) {
71+
throw new IllegalStateException(
72+
String.format(
73+
"Expected %s type but got %s instead",
74+
expectedResultType.getName(), result.getClass().getName()));
75+
}
76+
77+
} catch (Exception e) {
78+
issuesFactory.addError(
79+
expr.id(),
80+
String.format(
81+
"%s validation failed. Reason: %s", functionName, e.getMessage()));
82+
}
83+
});
84+
}
85+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.validator.validators;
16+
17+
import com.google.common.collect.ImmutableList;
18+
import dev.cel.bundle.Cel;
19+
import dev.cel.common.ast.CelExpr;
20+
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
21+
import dev.cel.common.navigation.CelNavigableAst;
22+
import dev.cel.validator.CelAstValidator;
23+
import java.util.regex.Pattern;
24+
import java.util.regex.PatternSyntaxException;
25+
26+
/** RegexLiteralValidator ensures that regex patterns are valid. */
27+
public class RegexLiteralValidator implements CelAstValidator {
28+
public static final RegexLiteralValidator INSTANCE = new RegexLiteralValidator();
29+
30+
@Override
31+
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
32+
navigableAst
33+
.getRoot()
34+
.descendants()
35+
.filter(node -> node.expr().callOrDefault().function().equals("matches"))
36+
.filter(node -> ImmutableList.of(1, 2).contains(node.expr().call().args().size()))
37+
.map(node -> node.expr().call())
38+
.forEach(
39+
matchesCallExpr -> {
40+
CelExpr regexArg =
41+
matchesCallExpr.target().isPresent()
42+
? matchesCallExpr.args().get(0)
43+
: matchesCallExpr.args().get(1);
44+
if (!regexArg.exprKind().getKind().equals(Kind.CONSTANT)) {
45+
return;
46+
}
47+
48+
String regexPattern = regexArg.constant().stringValue();
49+
try {
50+
Pattern.compile(regexPattern);
51+
} catch (PatternSyntaxException e) {
52+
issuesFactory.addError(
53+
regexArg.id(), "Regex validation failed. Reason: " + e.getMessage());
54+
}
55+
});
56+
}
57+
58+
private RegexLiteralValidator() {}
59+
}

validator/src/main/java/dev/cel/validator/validators/TimestampLiteralValidator.java

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

1515
package dev.cel.validator.validators;
1616

17-
import com.google.common.collect.ImmutableList;
1817
import com.google.protobuf.Timestamp;
19-
import dev.cel.bundle.Cel;
20-
import dev.cel.common.CelAbstractSyntaxTree;
21-
import dev.cel.common.CelSource;
22-
import dev.cel.common.ast.CelExpr;
23-
import dev.cel.common.ast.CelExpr.ExprKind.Kind;
24-
import dev.cel.common.navigation.CelNavigableAst;
25-
import dev.cel.common.navigation.CelNavigableExpr;
26-
import dev.cel.validator.CelAstValidator;
27-
import java.util.Optional;
2818

29-
/** TimestampValidator ensures that timestamp literal arguments are valid. */
30-
public class TimestampLiteralValidator implements CelAstValidator {
31-
public static final TimestampLiteralValidator INSTANCE = new TimestampLiteralValidator();
19+
/** TimestampLiteralValidator ensures that timestamp literal arguments are valid. */
20+
public class TimestampLiteralValidator extends LiteralValidator {
21+
public static final TimestampLiteralValidator INSTANCE =
22+
new TimestampLiteralValidator("timestamp", Timestamp.class);
3223

33-
@Override
34-
public void validate(CelNavigableAst navigableAst, Cel cel, IssuesFactory issuesFactory) {
35-
navigableAst
36-
.getRoot()
37-
.descendants()
38-
.filter(
39-
node ->
40-
node.getKind().equals(Kind.CONSTANT)
41-
&& node.parent().isPresent()
42-
&& node.parent().get().expr().call().function().equals("timestamp"))
43-
.map(CelNavigableExpr::expr)
44-
.forEach(
45-
timestampExpr -> {
46-
try {
47-
CelExpr timestampCall =
48-
CelExpr.ofCallExpr(
49-
1,
50-
Optional.empty(),
51-
"timestamp",
52-
ImmutableList.of(CelExpr.ofConstantExpr(2, timestampExpr.constant())));
53-
54-
CelAbstractSyntaxTree ast =
55-
CelAbstractSyntaxTree.newParsedAst(
56-
timestampCall, CelSource.newBuilder().build());
57-
ast = cel.check(ast).getAst();
58-
Object result = cel.createProgram(ast).eval();
59-
60-
if (!(result instanceof Timestamp)) {
61-
throw new IllegalStateException(
62-
"Expected timestamp type but got " + result.getClass());
63-
}
64-
} catch (Exception e) {
65-
issuesFactory.addError(
66-
timestampExpr.id(), "Timestamp validation failed. Reason: " + e.getMessage());
67-
}
68-
});
24+
private TimestampLiteralValidator(String functionName, Class<?> expectedResultType) {
25+
super(functionName, expectedResultType);
6926
}
70-
71-
private TimestampLiteralValidator() {}
7227
}

validator/src/test/java/dev/cel/validator/validators/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ java_library(
1616
"//runtime",
1717
"//validator",
1818
"//validator:validator_builder",
19+
"//validator/validators:duration",
20+
"//validator/validators:regex",
1921
"//validator/validators:timestamp",
2022
"@maven//:com_google_guava_guava",
2123
"@maven//:com_google_protobuf_protobuf_java",

0 commit comments

Comments
 (0)