Skip to content

Commit 384fd79

Browse files
authored
feat: add cel testing endpoint (#5701)
1 parent a5f2b7c commit 384fd79

18 files changed

Lines changed: 677 additions & 4 deletions

File tree

core/common/cel-core/src/main/java/org/eclipse/edc/policy/cel/service/CelPolicyExpressionServiceImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
import org.eclipse.edc.policy.cel.engine.CelExpressionEngine;
1818
import org.eclipse.edc.policy.cel.model.CelExpression;
19+
import org.eclipse.edc.policy.cel.model.CelExpressionTestRequest;
20+
import org.eclipse.edc.policy.cel.model.CelExpressionTestResponse;
1921
import org.eclipse.edc.policy.cel.store.CelExpressionStore;
22+
import org.eclipse.edc.policy.model.Operator;
2023
import org.eclipse.edc.spi.query.Criterion;
2124
import org.eclipse.edc.spi.query.QuerySpec;
2225
import org.eclipse.edc.spi.result.ServiceResult;
@@ -91,4 +94,16 @@ public ServiceResult<Void> delete(String id) {
9194
public ServiceResult<List<CelExpression>> query(QuerySpec querySpec) {
9295
return ServiceResult.success(store.query(querySpec));
9396
}
97+
98+
@Override
99+
public ServiceResult<CelExpressionTestResponse> test(CelExpressionTestRequest request) {
100+
var result = engine.test(request.getExpression(), request.getLeftOperand(), Operator.valueOf(request.getOperator()), request.getRightOperand(), request.getParams());
101+
var response = CelExpressionTestResponse.Builder.newInstance();
102+
if (result.succeeded()) {
103+
response.evaluationResult(result.getContent());
104+
} else {
105+
response.error(result.getFailureDetail());
106+
}
107+
return ServiceResult.success(response.build());
108+
}
94109
}

core/common/lib/json-ld-lib/src/main/resources/document/management-context-v2.jsonld

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,26 @@
509509
"description": "edc:description"
510510
}
511511
},
512+
"CelExpressionTestRequest": {
513+
"@id": "edc:CelExpressionTestRequest",
514+
"@context": {
515+
"expression": "edc:expression",
516+
"leftOperand": "edc:leftOperand",
517+
"operator": "edc:operator",
518+
"rightOperand": "edc:rightOperand",
519+
"params": {
520+
"@id": "edc:params",
521+
"@type": "@json"
522+
}
523+
}
524+
},
525+
"CelExpressionTestResponse": {
526+
"@id": "edc:CelExpressionTestResponse",
527+
"@context": {
528+
"evaluationResult": "edc:evaluationResult",
529+
"error": "edc:error"
530+
}
531+
},
512532
"inForceDate": "edc:inForceDate",
513533
"ruleFunctions": {
514534
"@id": "edc:ruleFunctions",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.transform.edc.cel.from;
16+
17+
import jakarta.json.JsonBuilderFactory;
18+
import jakarta.json.JsonObject;
19+
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
20+
import org.eclipse.edc.policy.cel.model.CelExpressionTestResponse;
21+
import org.eclipse.edc.transform.spi.TransformerContext;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
26+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestResponse.CEL_EXPRESSION_TEST_RESPONSE_ERROR_IRI;
27+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestResponse.CEL_EXPRESSION_TEST_RESPONSE_EVALUATION_RESULT_IRI;
28+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestResponse.CEL_EXPRESSION_TEST_RESPONSE_TYPE_IRI;
29+
30+
public class JsonObjectFromCelExpressionTestResponseTransformer extends AbstractJsonLdTransformer<CelExpressionTestResponse, JsonObject> {
31+
32+
private final JsonBuilderFactory factory;
33+
34+
public JsonObjectFromCelExpressionTestResponseTransformer(JsonBuilderFactory factory) {
35+
super(CelExpressionTestResponse.class, JsonObject.class);
36+
this.factory = factory;
37+
}
38+
39+
@Override
40+
public @Nullable JsonObject transform(@NotNull CelExpressionTestResponse response, @NotNull TransformerContext context) {
41+
var builder = factory.createObjectBuilder().add(TYPE, CEL_EXPRESSION_TEST_RESPONSE_TYPE_IRI);
42+
if (response.getEvaluationResult() != null) {
43+
builder.add(CEL_EXPRESSION_TEST_RESPONSE_EVALUATION_RESULT_IRI, response.getEvaluationResult());
44+
}
45+
if (response.getError() != null) {
46+
builder.add(CEL_EXPRESSION_TEST_RESPONSE_ERROR_IRI, response.getError());
47+
}
48+
return builder.build();
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2025 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.connector.controlplane.transform.edc.cel.to;
16+
17+
import jakarta.json.JsonObject;
18+
import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer;
19+
import org.eclipse.edc.policy.cel.model.CelExpressionTestRequest;
20+
import org.eclipse.edc.transform.spi.TransformerContext;
21+
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.annotations.Nullable;
23+
24+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestRequest.CEL_EXPRESSION_TEST_REQUEST_EXPRESSION_IRI;
25+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestRequest.CEL_EXPRESSION_TEST_REQUEST_LEFT_OPERAND_IRI;
26+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestRequest.CEL_EXPRESSION_TEST_REQUEST_OPERATOR_IRI;
27+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestRequest.CEL_EXPRESSION_TEST_REQUEST_PARAMS_IRI;
28+
import static org.eclipse.edc.policy.cel.model.CelExpressionTestRequest.CEL_EXPRESSION_TEST_REQUEST_RIGHT_OPERAND_IRI;
29+
30+
public class JsonObjectToCelExpressionTestRequestTransformer extends AbstractJsonLdTransformer<JsonObject, CelExpressionTestRequest> {
31+
32+
public JsonObjectToCelExpressionTestRequestTransformer() {
33+
super(JsonObject.class, CelExpressionTestRequest.class);
34+
}
35+
36+
@Override
37+
public @Nullable CelExpressionTestRequest transform(@NotNull JsonObject object, @NotNull TransformerContext context) {
38+
39+
40+
var expression = transformString(object.get(CEL_EXPRESSION_TEST_REQUEST_EXPRESSION_IRI), context);
41+
var operandLeft = transformString(object.get(CEL_EXPRESSION_TEST_REQUEST_LEFT_OPERAND_IRI), context);
42+
var operandRight = transformGenericProperty(object.get(CEL_EXPRESSION_TEST_REQUEST_RIGHT_OPERAND_IRI), context);
43+
var operator = transformString(object.get(CEL_EXPRESSION_TEST_REQUEST_OPERATOR_IRI), context);
44+
45+
var builder = CelExpressionTestRequest.Builder.newInstance()
46+
.leftOperand(operandLeft)
47+
.rightOperand(operandRight)
48+
.operator(operator)
49+
.expression(expression);
50+
51+
var params = object.get(CEL_EXPRESSION_TEST_REQUEST_PARAMS_IRI);
52+
if (params != null) {
53+
var jsonValue = nodeJsonValue(params);
54+
if (jsonValue instanceof JsonObject json) {
55+
visitProperties(json, (key, value) -> builder.param(key, transformGenericProperty(value, context)));
56+
} else {
57+
context.reportProblem("Expected properties to be a JsonObject");
58+
return null;
59+
}
60+
}
61+
return builder.build();
62+
}
63+
}

extensions/common/api/lib/management-api-lib/src/main/java/org/eclipse/edc/api/management/schema/ManagementApiJsonSchema.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ interface V4 {
4848
String PARTICIPANT_CONTEXT = EDC_MGMT_V4_SCHEMA_PREFIX + "/participant-context-schema.json";
4949
String PARTICIPANT_CONTEXT_CONFIG = EDC_MGMT_V4_SCHEMA_PREFIX + "/participant-context-config-schema.json";
5050
String CEL_EXPRESSION = EDC_MGMT_V4_SCHEMA_PREFIX + "/cel-expression-schema.json";
51+
String CEL_EXPRESSION_TEST_REQUEST = EDC_MGMT_V4_SCHEMA_PREFIX + "/cel-expression-test-request-schema.json";
52+
String CEL_EXPRESSION_TEST_RESPONSE = EDC_MGMT_V4_SCHEMA_PREFIX + "/cel-expression-test-response-schema.json";
5153

5254

5355
static String version() {

extensions/common/api/management-api-configuration/src/main/resources/management-api-version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{
1515
"version": "5.0.0-beta",
1616
"urlPath": "/v5beta",
17-
"lastUpdated": "2026-04-23T09:00:00Z",
17+
"lastUpdated": "2026-04-24T09:00:00Z",
1818
"maturity": "beta"
1919
}
2020
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2019-09/schema",
3+
"title": "CelExpressionTestRequestSchema",
4+
"type": "object",
5+
"allOf": [
6+
{
7+
"$ref": "#/definitions/CelExpressionTestRequest"
8+
}
9+
],
10+
"$id": "https://w3id.org/edc/connector/management/schema/v4/cel-expression-test-request-schema.json",
11+
"definitions": {
12+
"CelExpressionTestRequest": {
13+
"type": "object",
14+
"properties": {
15+
"@context": {
16+
"$ref": "https://w3id.org/edc/connector/management/schema/v4/context-schema.json"
17+
},
18+
"@type": {
19+
"type": "string",
20+
"const": "CelExpressionTestRequest"
21+
},
22+
"expression": {
23+
"type": "string"
24+
},
25+
"leftOperand": {
26+
"type": "string"
27+
},
28+
"operator": {
29+
"type": "string"
30+
},
31+
"rightOperand": {
32+
"type": "object"
33+
},
34+
"params": {
35+
"type": "object"
36+
}
37+
},
38+
"required": [
39+
"@context",
40+
"@type",
41+
"leftOperand",
42+
"expression",
43+
"rightOperand",
44+
"operator"
45+
]
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2019-09/schema",
3+
"title": "CelExpressionTestRequestSchema",
4+
"type": "object",
5+
"allOf": [
6+
{
7+
"$ref": "#/definitions/CelExpressionTestResponse"
8+
}
9+
],
10+
"$id": "https://w3id.org/edc/connector/management/schema/v4/cel-expression-test-response-schema.json",
11+
"definitions": {
12+
"CelExpressionTestResponse": {
13+
"type": "object",
14+
"properties": {
15+
"@context": {
16+
"$ref": "https://w3id.org/edc/connector/management/schema/v4/context-schema.json"
17+
},
18+
"@type": {
19+
"type": "string",
20+
"const": "CelExpressionTestResponse"
21+
},
22+
"evaluationResult": {
23+
"type": "boolean"
24+
},
25+
"error": {
26+
"type": "string"
27+
}
28+
},
29+
"required": [
30+
"@context",
31+
"@type"
32+
]
33+
}
34+
}
35+
}

extensions/control-plane/api/management-api-v5/cel-api-v5/src/main/java/org/eclipse/edc/connector/controlplane/api/management/cel/CelExpressionManagementApiExtension.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import jakarta.json.Json;
1818
import org.eclipse.edc.api.management.schema.ManagementApiJsonSchema;
1919
import org.eclipse.edc.connector.controlplane.api.management.cel.v5.CelExpressionApiV5Controller;
20+
import org.eclipse.edc.connector.controlplane.transform.edc.cel.from.JsonObjectFromCelExpressionTestResponseTransformer;
2021
import org.eclipse.edc.connector.controlplane.transform.edc.cel.from.JsonObjectFromCelExpressionTransformer;
22+
import org.eclipse.edc.connector.controlplane.transform.edc.cel.to.JsonObjectToCelExpressionTestRequestTransformer;
2123
import org.eclipse.edc.connector.controlplane.transform.edc.cel.to.JsonObjectToCelExpressionTransformer;
2224
import org.eclipse.edc.jsonld.spi.JsonLd;
2325
import org.eclipse.edc.policy.cel.service.CelPolicyExpressionService;
@@ -68,7 +70,9 @@ public void initialize(ServiceExtensionContext context) {
6870
var managementApiTransformerRegistry = transformerRegistry.forContext("management-api");
6971

7072
managementApiTransformerRegistry.register(new JsonObjectFromCelExpressionTransformer(factory));
73+
managementApiTransformerRegistry.register(new JsonObjectFromCelExpressionTestResponseTransformer(factory));
7174
managementApiTransformerRegistry.register(new JsonObjectToCelExpressionTransformer());
75+
managementApiTransformerRegistry.register(new JsonObjectToCelExpressionTestRequestTransformer());
7276

7377
webService.registerResource(ApiContext.MANAGEMENT, new CelExpressionApiV5Controller(service, managementApiTransformerRegistry));
7478
webService.registerDynamicResource(ApiContext.MANAGEMENT, CelExpressionApiV5Controller.class, new JerseyJsonLdInterceptor(jsonLd, typeManager, JSON_LD, MANAGEMENT_SCOPE_V4, validatorRegistry, ManagementApiJsonSchema.V4.version()));

extensions/control-plane/api/management-api-v5/cel-api-v5/src/main/java/org/eclipse/edc/connector/controlplane/api/management/cel/v5/CelExpressionApiV5.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
public interface CelExpressionApiV5 {
3333

3434
@Operation(description = "Create a Cel Expression.",
35-
requestBody = @RequestBody(content = @Content(schema = @Schema(ref = ManagementApiJsonSchema.V4.PARTICIPANT_CONTEXT_CONFIG), mediaType = "application/json")),
35+
requestBody = @RequestBody(content = @Content(schema = @Schema(ref = ManagementApiJsonSchema.V4.CEL_EXPRESSION), mediaType = "application/json")),
3636
responses = {
3737
@ApiResponse(responseCode = "200", description = "The Cel Expression was created successfully"),
3838
@ApiResponse(responseCode = "400", description = "Request body was malformed, or the request could not be processed",
@@ -43,7 +43,21 @@ public interface CelExpressionApiV5 {
4343
content = @Content(array = @ArraySchema(schema = @Schema(ref = ManagementApiJsonSchema.V4.API_ERROR)), mediaType = "application/json"))
4444
}
4545
)
46-
JsonObject createExpressionV5(JsonObject expression);
46+
JsonObject createExpressionV5(JsonObject request);
47+
48+
49+
@Operation(description = "Test a Cel Expression.",
50+
requestBody = @RequestBody(content = @Content(schema = @Schema(ref = ManagementApiJsonSchema.V4.CEL_EXPRESSION_TEST_REQUEST), mediaType = "application/json")),
51+
responses = {
52+
@ApiResponse(responseCode = "200", description = "The Cel Expression was tested successfully",
53+
content = @Content(schema = @Schema(ref = ManagementApiJsonSchema.V4.CEL_EXPRESSION_TEST_RESPONSE))),
54+
@ApiResponse(responseCode = "400", description = "Request body was malformed, or the request could not be processed",
55+
content = @Content(array = @ArraySchema(schema = @Schema(ref = ManagementApiJsonSchema.V4.API_ERROR)), mediaType = "application/json")),
56+
@ApiResponse(responseCode = "401", description = "The request could not be completed, because either the authentication was missing or was not valid.",
57+
content = @Content(array = @ArraySchema(schema = @Schema(ref = ManagementApiJsonSchema.V4.API_ERROR)), mediaType = "application/json"))
58+
}
59+
)
60+
JsonObject testExpressionV5(JsonObject expression);
4761

4862
@Operation(description = "Gets an Expression by ID.",
4963
responses = {

0 commit comments

Comments
 (0)