Skip to content

Commit 81f67b5

Browse files
l46kokcopybara-github
authored andcommitted
Add code for Exercise 6 Codelabs
PiperOrigin-RevId: 630153668
1 parent 1ea0a28 commit 81f67b5

File tree

7 files changed

+392
-1
lines changed

7 files changed

+392
-1
lines changed

codelab/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Some key areas covered are:
1616
* [Creating variables](#creating-variables)
1717
* [Commutatibe logical AND/OR](#logical-andor)
1818
* [Adding custom functions](#custom-functions)
19+
* [Building Protos](#building-protos)
1920

2021
### Prerequisites
2122
This codelab builds upon a basic understanding of Protocol Buffers and Java.
@@ -299,7 +300,7 @@ CelAbstractSyntaxTree compile(String expression, String variableName, CelType va
299300
The compiler's `addVar` method allows us to declare variables. Note that you must supply the type of the variable being declared. Supported CEL types can be found [here](https://github.com/google/cel-java/tree/main/common/src/main/java/dev/cel/common/types).
300301

301302
> [!TIP]
302-
> Best practice: You may have noticed addVar has an overloaded method which accepts a proto based Type instead of the CEL-Java native CelType used in this example. While the two types are functionally equivalent, we recommend using the native types whenever possible.
303+
> Best practice: You may have noticed `addVar` has an overloaded method which accepts a proto based Type instead of the CEL-Java native CelType used in this example. While the two types are functionally equivalent, we recommend using the native types whenever possible.
303304
304305
Let's make the evaluation work now. Copy into the eval method:
305306

@@ -635,3 +636,6 @@ private static boolean mapContainsKeyValue(Object[] args) {
635636
> [!TIP]
636637
> Best practice: Declare overload ids according to their types and function names. e.g. targetType_func_argType_argType. In the case where argType is a type param, use a descriptive name instead of the simple type name.
637638

639+
## Building Protos
640+
641+
CEL can also build protobuf messages for any message type compiled into the application.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2024 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 codelab;
16+
17+
import com.google.rpc.context.AttributeContext.Request;
18+
import dev.cel.common.CelAbstractSyntaxTree;
19+
import dev.cel.common.CelValidationException;
20+
import dev.cel.common.types.StructTypeReference;
21+
import dev.cel.compiler.CelCompiler;
22+
import dev.cel.compiler.CelCompilerFactory;
23+
import dev.cel.runtime.CelEvaluationException;
24+
import dev.cel.runtime.CelRuntime;
25+
import dev.cel.runtime.CelRuntimeFactory;
26+
import java.util.Map;
27+
28+
/**
29+
* Exercise6 describes how to build proto message types within CEL.
30+
*
31+
* <p>Given an input `jwt` and time `now` construct a `google.rpc.context.AttributeContext.Request`
32+
* with the `time` and `auth` fields populated according to the
33+
* google.rpc.context.AttributeContext.Request specification.
34+
*/
35+
final class Exercise6 {
36+
37+
/**
38+
* Compiles the input expression.
39+
*
40+
* @throws IllegalArgumentException If the expression is malformed due to syntactic or semantic
41+
* errors.
42+
*/
43+
CelAbstractSyntaxTree compile(String expression) {
44+
CelCompiler celCompiler =
45+
CelCompilerFactory.standardCelCompilerBuilder()
46+
// Set the container here for "google.rpc.context.AttributeContext"
47+
// Declare variables for "jwt" and "now" here
48+
.addMessageTypes(Request.getDescriptor())
49+
.setResultType(StructTypeReference.create(Request.getDescriptor().getFullName()))
50+
.build();
51+
52+
try {
53+
return celCompiler.compile(expression).getAst();
54+
} catch (CelValidationException e) {
55+
throw new IllegalArgumentException("Failed to compile expression.", e);
56+
}
57+
}
58+
59+
/** Evaluates the compiled AST with the user provided parameter values. */
60+
Object eval(CelAbstractSyntaxTree ast, Map<String, ?> parameterValues) {
61+
CelRuntime celRuntime =
62+
CelRuntimeFactory.standardCelRuntimeBuilder()
63+
.addMessageTypes(Request.getDescriptor())
64+
.build();
65+
66+
try {
67+
CelRuntime.Program program = celRuntime.createProgram(ast);
68+
return program.eval(parameterValues);
69+
} catch (CelEvaluationException e) {
70+
throw new IllegalArgumentException("Evaluation error has occurred.", e);
71+
}
72+
}
73+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2024 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 codelab.solutions;
16+
17+
import com.google.rpc.context.AttributeContext.Request;
18+
import dev.cel.common.CelAbstractSyntaxTree;
19+
import dev.cel.common.CelValidationException;
20+
import dev.cel.common.types.SimpleType;
21+
import dev.cel.common.types.StructTypeReference;
22+
import dev.cel.compiler.CelCompiler;
23+
import dev.cel.compiler.CelCompilerFactory;
24+
import dev.cel.runtime.CelEvaluationException;
25+
import dev.cel.runtime.CelRuntime;
26+
import dev.cel.runtime.CelRuntimeFactory;
27+
import java.util.Map;
28+
29+
/**
30+
* Exercise6 describes how to build proto message types within CEL.
31+
*
32+
* <p>Given an input `jwt` and time `now` construct a `google.rpc.context.AttributeContext.Request`
33+
* with the `time` and `auth` fields populated according to the
34+
* google.rpc.context.AttributeContext.Request specification.
35+
*/
36+
final class Exercise6 {
37+
38+
/**
39+
* Compiles the input expression.
40+
*
41+
* @throws IllegalArgumentException If the expression is malformed due to syntactic or semantic
42+
* errors.
43+
*/
44+
CelAbstractSyntaxTree compile(String expression) {
45+
CelCompiler celCompiler =
46+
CelCompilerFactory.standardCelCompilerBuilder()
47+
.setContainer("google.rpc.context.AttributeContext")
48+
.addVar("jwt", SimpleType.DYN)
49+
.addVar("now", SimpleType.TIMESTAMP)
50+
.addMessageTypes(Request.getDescriptor())
51+
.setResultType(StructTypeReference.create(Request.getDescriptor().getFullName()))
52+
.build();
53+
54+
try {
55+
return celCompiler.compile(expression).getAst();
56+
} catch (CelValidationException e) {
57+
throw new IllegalArgumentException("Failed to compile expression.", e);
58+
}
59+
}
60+
61+
/** Evaluates the compiled AST with the user provided parameter values. */
62+
Object eval(CelAbstractSyntaxTree ast, Map<String, ?> parameterValues) {
63+
CelRuntime celRuntime =
64+
CelRuntimeFactory.standardCelRuntimeBuilder()
65+
.addMessageTypes(Request.getDescriptor())
66+
.build();
67+
68+
try {
69+
CelRuntime.Program program = celRuntime.createProgram(ast);
70+
return program.eval(parameterValues);
71+
} catch (CelEvaluationException e) {
72+
throw new IllegalArgumentException("Evaluation error has occurred.", e);
73+
}
74+
}
75+
}

codelab/src/test/codelab/BUILD.bazel

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ java_test(
8282
],
8383
)
8484

85+
java_test(
86+
name = "Exercise6Test",
87+
srcs = ["Exercise6Test.java"],
88+
tags = ["notap"],
89+
test_class = "codelab.Exercise6Test",
90+
deps = [
91+
"//:java_truth",
92+
"//codelab",
93+
"//common",
94+
"@maven//:com_google_api_grpc_proto_google_common_protos",
95+
"@maven//:com_google_guava_guava",
96+
"@maven//:com_google_protobuf_protobuf_java",
97+
"@maven//:com_google_protobuf_protobuf_java_util",
98+
"@maven//:com_google_testparameterinjector_test_parameter_injector",
99+
"@maven//:junit_junit",
100+
],
101+
)
102+
85103
test_suite(
86104
name = "exercise_test_suite",
87105
tags = ["notap"],
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2024 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 codelab;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
19+
import com.google.common.collect.ImmutableMap;
20+
import com.google.protobuf.Struct;
21+
import com.google.protobuf.Timestamp;
22+
import com.google.protobuf.Value;
23+
import com.google.protobuf.util.Timestamps;
24+
import com.google.rpc.context.AttributeContext;
25+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
26+
import dev.cel.common.CelAbstractSyntaxTree;
27+
import org.junit.Test;
28+
import org.junit.runner.RunWith;
29+
30+
@RunWith(TestParameterInjector.class)
31+
public final class Exercise6Test {
32+
private final Exercise6 exercise6 = new Exercise6();
33+
34+
@Test
35+
public void evaluate_constructAttributeContext() {
36+
// Given JSON web token and the current time as input variables,
37+
// Setup an expression to construct an AttributeContext protobuf object.
38+
//
39+
// Note: the field names within the proto message types are not quoted as they
40+
// are well-defined names composed of valid identifier characters. Also, note
41+
// that when building nested proto objects, the message name needs to prefix
42+
// the object construction.
43+
String expression =
44+
"Request{\n"
45+
+ "auth: Auth{"
46+
+ " principal: jwt.iss + '/' + jwt.sub,"
47+
+ " audiences: [jwt.aud],"
48+
+ " presenter: 'azp' in jwt ? jwt.azp : '',"
49+
+ " claims: jwt"
50+
+ "},"
51+
+ "time: now"
52+
+ "}";
53+
// Values for `now` and `jwt` variables to be passed into the runtime
54+
Timestamp now = Timestamps.now();
55+
ImmutableMap<String, Object> jwt =
56+
ImmutableMap.of(
57+
"sub", "serviceAccount:delegate@acme.co",
58+
"aud", "my-project",
59+
"iss", "auth.acme.com:12350",
60+
"extra_claims", ImmutableMap.of("group", "admin"));
61+
AttributeContext.Request expectedMessage =
62+
AttributeContext.Request.newBuilder()
63+
.setTime(now)
64+
.setAuth(
65+
AttributeContext.Auth.newBuilder()
66+
.setPrincipal("auth.acme.com:12350/serviceAccount:delegate@acme.co")
67+
.addAudiences("my-project")
68+
.setClaims(
69+
Struct.newBuilder()
70+
.putAllFields(
71+
ImmutableMap.of(
72+
"sub", newStringValue("serviceAccount:delegate@acme.co"),
73+
"aud", newStringValue("my-project"),
74+
"iss", newStringValue("auth.acme.com:12350")))
75+
.putFields(
76+
"extra_claims",
77+
Value.newBuilder()
78+
.setStructValue(
79+
Struct.newBuilder()
80+
.putFields("group", newStringValue("admin"))
81+
.build())
82+
.build())))
83+
.build();
84+
85+
// Compile the `Request` message construction expression and validate that
86+
// the resulting expression type matches the fully qualified message name.
87+
CelAbstractSyntaxTree ast = exercise6.compile(expression);
88+
AttributeContext.Request evaluatedResult =
89+
(AttributeContext.Request)
90+
exercise6.eval(
91+
ast,
92+
ImmutableMap.of(
93+
"now", now,
94+
"jwt", jwt));
95+
96+
assertThat(evaluatedResult).isEqualTo(expectedMessage);
97+
}
98+
99+
private static Value newStringValue(String value) {
100+
return Value.newBuilder().setStringValue(value).build();
101+
}
102+
}

codelab/src/test/codelab/solutions/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,20 @@ java_test(
7676
"@maven//:junit_junit",
7777
],
7878
)
79+
80+
java_test(
81+
name = "Exercise6Test",
82+
srcs = ["Exercise6Test.java"],
83+
test_class = "codelab.solutions.Exercise6Test",
84+
deps = [
85+
"//:java_truth",
86+
"//codelab:solutions",
87+
"//common",
88+
"@maven//:com_google_api_grpc_proto_google_common_protos",
89+
"@maven//:com_google_guava_guava",
90+
"@maven//:com_google_protobuf_protobuf_java",
91+
"@maven//:com_google_protobuf_protobuf_java_util",
92+
"@maven//:com_google_testparameterinjector_test_parameter_injector",
93+
"@maven//:junit_junit",
94+
],
95+
)

0 commit comments

Comments
 (0)