From 361cee61471446cc0bccb8e2a178bfa7d3c3f3a1 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Fri, 30 Aug 2024 10:58:23 +0200 Subject: [PATCH] feat: adds REST API for policy validation --- .../ControlPlaneServicesExtension.java | 7 ++ .../PolicyValidationServiceImpl.java | 34 ++++++++ .../BasePolicyDefinitionApiController.java | 4 +- .../policy/PolicyDefinitionApiExtension.java | 8 ++ .../policy/model/PolicyValidationResult.java | 25 ++++++ ...FromPolicyValidationResultTransformer.java | 45 ++++++++++ .../v31alpha/PolicyDefinitionApiV31Alpha.java | 2 + ...PolicyDefinitionApiV31AlphaController.java | 34 +++++++- .../PolicyDefinitionApiExtensionTest.java | 2 + ...cyDefinitionApiV31AlphaControllerTest.java | 85 ++++++++++++++++++- .../PolicyValidationService.java | 25 ++++++ .../PolicyDefinitionApiEndToEndTest.java | 34 +++++++- 12 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/policydefinition/PolicyValidationServiceImpl.java create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/model/PolicyValidationResult.java create mode 100644 extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyValidationResultTransformer.java create mode 100644 spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/controlplane/services/spi/policydefinition/PolicyValidationService.java diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/ControlPlaneServicesExtension.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/ControlPlaneServicesExtension.java index c08459dc31e..5f3744160c8 100644 --- a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/ControlPlaneServicesExtension.java +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/ControlPlaneServicesExtension.java @@ -38,6 +38,7 @@ import org.eclipse.edc.connector.controlplane.services.contractnegotiation.ContractNegotiationServiceImpl; import org.eclipse.edc.connector.controlplane.services.policydefinition.PolicyDefinitionEventListener; import org.eclipse.edc.connector.controlplane.services.policydefinition.PolicyDefinitionServiceImpl; +import org.eclipse.edc.connector.controlplane.services.policydefinition.PolicyValidationServiceImpl; import org.eclipse.edc.connector.controlplane.services.protocol.ProtocolTokenValidatorImpl; import org.eclipse.edc.connector.controlplane.services.protocol.VersionProtocolServiceImpl; import org.eclipse.edc.connector.controlplane.services.secret.SecretEventListener; @@ -50,6 +51,7 @@ import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationProtocolService; import org.eclipse.edc.connector.controlplane.services.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyValidationService; import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolTokenValidator; import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersionRegistry; import org.eclipse.edc.connector.controlplane.services.spi.protocol.VersionProtocolService; @@ -259,5 +261,10 @@ public ProtocolTokenValidator protocolTokenValidator() { public VersionProtocolService versionProtocolService() { return new VersionProtocolServiceImpl(protocolVersionRegistry, protocolTokenValidator()); } + + @Provider + public PolicyValidationService policyValidationService() { + return new PolicyValidationServiceImpl(policyEngine); + } } diff --git a/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/policydefinition/PolicyValidationServiceImpl.java b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/policydefinition/PolicyValidationServiceImpl.java new file mode 100644 index 00000000000..aaa3e346c35 --- /dev/null +++ b/core/control-plane/control-plane-aggregate-services/src/main/java/org/eclipse/edc/connector/controlplane/services/policydefinition/PolicyValidationServiceImpl.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.controlplane.services.policydefinition; + +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyValidationService; +import org.eclipse.edc.policy.engine.spi.PolicyEngine; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.result.Result; + +public class PolicyValidationServiceImpl implements PolicyValidationService { + + private final PolicyEngine policyEngine; + + public PolicyValidationServiceImpl(PolicyEngine policyEngine) { + this.policyEngine = policyEngine; + } + + @Override + public Result validate(Policy policy) { + return policyEngine.validate(policy); + } +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java index a07b602a9ed..7d7da29e1a0 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/BasePolicyDefinitionApiController.java @@ -38,8 +38,8 @@ public abstract class BasePolicyDefinitionApiController { protected final Monitor monitor; - private final TypeTransformerRegistry transformerRegistry; - private final PolicyDefinitionService service; + protected final PolicyDefinitionService service; + protected final TypeTransformerRegistry transformerRegistry; private final JsonObjectValidatorRegistry validatorRegistry; public BasePolicyDefinitionApiController(Monitor monitor, TypeTransformerRegistry transformerRegistry, diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java index a64287859f3..b29c0d86a79 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtension.java @@ -16,11 +16,14 @@ import jakarta.json.Json; import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyDefinitionTransformer; +import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyValidationResultTransformer; import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectToPolicyDefinitionTransformer; import org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2Controller; import org.eclipse.edc.connector.controlplane.api.management.policy.v3.PolicyDefinitionApiV3Controller; +import org.eclipse.edc.connector.controlplane.api.management.policy.v31alpha.PolicyDefinitionApiV31AlphaController; import org.eclipse.edc.connector.controlplane.api.management.policy.validation.PolicyDefinitionValidator; import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyValidationService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; @@ -57,6 +60,9 @@ public class PolicyDefinitionApiExtension implements ServiceExtension { @Inject private TypeManager typeManager; + @Inject + private PolicyValidationService policyValidationService; + @Override public String name() { return NAME; @@ -69,11 +75,13 @@ public void initialize(ServiceExtensionContext context) { var mapper = typeManager.getMapper(JSON_LD); managementApiTransformerRegistry.register(new JsonObjectToPolicyDefinitionTransformer()); managementApiTransformerRegistry.register(new JsonObjectFromPolicyDefinitionTransformer(jsonBuilderFactory, mapper)); + managementApiTransformerRegistry.register(new JsonObjectFromPolicyValidationResultTransformer(jsonBuilderFactory)); validatorRegistry.register(EDC_POLICY_DEFINITION_TYPE, PolicyDefinitionValidator.instance()); var monitor = context.getMonitor(); webService.registerResource(ApiContext.MANAGEMENT, new PolicyDefinitionApiV2Controller(monitor, managementApiTransformerRegistry, service, validatorRegistry)); webService.registerResource(ApiContext.MANAGEMENT, new PolicyDefinitionApiV3Controller(monitor, managementApiTransformerRegistry, service, validatorRegistry)); + webService.registerResource(ApiContext.MANAGEMENT, new PolicyDefinitionApiV31AlphaController(monitor, managementApiTransformerRegistry, service, validatorRegistry, policyValidationService)); } } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/model/PolicyValidationResult.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/model/PolicyValidationResult.java new file mode 100644 index 00000000000..5634da2e4f4 --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/model/PolicyValidationResult.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.controlplane.api.management.policy.model; + +import java.util.List; + +import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE; + +public record PolicyValidationResult(boolean isValid, List errors) { + public static final String EDC_POLICY_VALIDATION_RESULT_TYPE = EDC_NAMESPACE + "PolicyValidationResult"; + public static final String EDC_POLICY_VALIDATION_RESULT_IS_VALID = EDC_NAMESPACE + "isValid"; + public static final String EDC_POLICY_VALIDATION_RESULT_ERRORS = EDC_NAMESPACE + "errors"; +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyValidationResultTransformer.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyValidationResultTransformer.java new file mode 100644 index 00000000000..e9752d0db81 --- /dev/null +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyValidationResultTransformer.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.controlplane.api.management.policy.transform; + +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonObject; +import org.eclipse.edc.connector.controlplane.api.management.policy.model.PolicyValidationResult; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.eclipse.edc.connector.controlplane.api.management.policy.model.PolicyValidationResult.EDC_POLICY_VALIDATION_RESULT_TYPE; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; + +public class JsonObjectFromPolicyValidationResultTransformer extends AbstractJsonLdTransformer { + + private final JsonBuilderFactory jsonFactory; + + public JsonObjectFromPolicyValidationResultTransformer(JsonBuilderFactory jsonFactory) { + super(PolicyValidationResult.class, JsonObject.class); + this.jsonFactory = jsonFactory; + } + + @Override + public @Nullable JsonObject transform(@NotNull PolicyValidationResult input, @NotNull TransformerContext context) { + var objectBuilder = jsonFactory.createObjectBuilder(); + objectBuilder.add(TYPE, EDC_POLICY_VALIDATION_RESULT_TYPE); + objectBuilder.add(PolicyValidationResult.EDC_POLICY_VALIDATION_RESULT_IS_VALID, input.isValid()); + objectBuilder.add(PolicyValidationResult.EDC_POLICY_VALIDATION_RESULT_ERRORS, jsonFactory.createArrayBuilder(input.errors())); + return objectBuilder.build(); + } +} diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31Alpha.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31Alpha.java index 74ded0b91cf..f5a6e7c6650 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31Alpha.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31Alpha.java @@ -99,6 +99,8 @@ public interface PolicyDefinitionApiV31Alpha { ) void updatePolicyDefinitionV3(String id, JsonObject policyDefinition); + JsonObject validatePolicyDefinitionV3(String id); + @Schema(name = "PolicyDefinitionInput", example = PolicyDefinitionInputSchema.POLICY_DEFINITION_INPUT_EXAMPLE) record PolicyDefinitionInputSchema( @Schema(name = CONTEXT, requiredMode = REQUIRED) diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaController.java b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaController.java index e20b9a1185e..05d891f4b24 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaController.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/main/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaController.java @@ -25,10 +25,17 @@ import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiController; +import org.eclipse.edc.connector.controlplane.api.management.policy.model.PolicyValidationResult; +import org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition; import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyValidationService; +import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.web.spi.exception.ObjectNotFoundException; + +import java.util.ArrayList; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; @@ -36,8 +43,13 @@ @Produces(APPLICATION_JSON) @Path("/v3.1alpha/policydefinitions") public class PolicyDefinitionApiV31AlphaController extends BasePolicyDefinitionApiController implements PolicyDefinitionApiV31Alpha { - public PolicyDefinitionApiV31AlphaController(Monitor monitor, TypeTransformerRegistry transformerRegistry, PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) { + private final PolicyValidationService policyValidationService; + + public PolicyDefinitionApiV31AlphaController(Monitor monitor, TypeTransformerRegistry transformerRegistry, PolicyDefinitionService service, + JsonObjectValidatorRegistry validatorRegistry, + PolicyValidationService policyValidationService) { super(monitor, transformerRegistry, service, validatorRegistry); + this.policyValidationService = policyValidationService; } @POST @@ -73,4 +85,24 @@ public void deletePolicyDefinitionV3(@PathParam("id") String id) { public void updatePolicyDefinitionV3(@PathParam("id") String id, JsonObject input) { updatePolicyDefinition(id, input); } + + @POST + @Path("{id}/validate") + @Override + public JsonObject validatePolicyDefinitionV3(@PathParam("id") String id) { + var definition = service.findById(id); + if (definition == null) { + throw new ObjectNotFoundException(PolicyDefinition.class, id); + } + + var messages = new ArrayList(); + + var result = policyValidationService.validate(definition.getPolicy()) + .onFailure(failure -> messages.addAll(failure.getMessages())); + + var validationResult = new PolicyValidationResult(result.succeeded(), messages); + + return transformerRegistry.transform(validationResult, JsonObject.class) + .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); + } } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java index 94a393201de..26eb3c0ae91 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/PolicyDefinitionApiExtensionTest.java @@ -17,6 +17,7 @@ import org.eclipse.edc.boot.system.injection.ObjectFactory; import org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2Controller; import org.eclipse.edc.connector.controlplane.api.management.policy.v3.PolicyDefinitionApiV3Controller; +import org.eclipse.edc.connector.controlplane.api.management.policy.v31alpha.PolicyDefinitionApiV31AlphaController; import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -64,5 +65,6 @@ void initialize_shouldRegisterControllers(ServiceExtensionContext context) { verify(webService).registerResource(any(), isA(PolicyDefinitionApiV2Controller.class)); verify(webService).registerResource(any(), isA(PolicyDefinitionApiV3Controller.class)); + verify(webService).registerResource(any(), isA(PolicyDefinitionApiV31AlphaController.class)); } } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaControllerTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaControllerTest.java index a17d92a25ea..fe0e95e8f8e 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaControllerTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/v31alpha/PolicyDefinitionApiV31AlphaControllerTest.java @@ -15,15 +15,98 @@ package org.eclipse.edc.connector.controlplane.api.management.policy.v31alpha; import io.restassured.specification.RequestSpecification; +import jakarta.json.Json; +import jakarta.json.JsonObject; import org.eclipse.edc.connector.controlplane.api.management.policy.BasePolicyDefinitionApiControllerTest; +import org.eclipse.edc.connector.controlplane.api.management.policy.model.PolicyValidationResult; +import org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition; +import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyValidationService; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.result.Result; +import org.junit.jupiter.api.Test; + +import java.util.List; import static io.restassured.RestAssured.given; +import static io.restassured.http.ContentType.JSON; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class PolicyDefinitionApiV31AlphaControllerTest extends BasePolicyDefinitionApiControllerTest { + protected final PolicyValidationService policyValidationService = mock(); + + @Test + void validate_shouldReturnNotFound_whenNotFound() { + when(service.findById(any())).thenReturn(null); + + baseRequest() + .contentType(JSON) + .post("/id/validate") + .then() + .statusCode(404); + } + + + @Test + void validate_shouldReturnValid_whenValidationSucceed() { + + var policyDefinition = PolicyDefinition.Builder.newInstance().policy(Policy.Builder.newInstance().build()).build(); + + when(service.findById(any())).thenReturn(policyDefinition); + when(policyValidationService.validate(policyDefinition.getPolicy())).thenReturn(Result.success()); + when(transformerRegistry.transform(any(PolicyValidationResult.class), eq(JsonObject.class))).then(answer -> { + PolicyValidationResult result = answer.getArgument(0); + var response = Json.createObjectBuilder() + .add("isValid", result.isValid()) + .add("errors", Json.createArrayBuilder(result.errors())) + .build(); + return Result.success(response); + }); + + baseRequest() + .contentType(JSON) + .post("/id/validate") + .then() + .statusCode(200) + .contentType(JSON) + .body("isValid", is(true)) + .body("errors.size()", is(0)); + } + + @Test + void validate_shouldReturnInvalidValid_whenValidationFails() { + + var policyDefinition = PolicyDefinition.Builder.newInstance().policy(Policy.Builder.newInstance().build()).build(); + + + when(service.findById(any())).thenReturn(policyDefinition); + when(policyValidationService.validate(policyDefinition.getPolicy())).thenReturn(Result.failure(List.of("error1", "error2"))); + when(transformerRegistry.transform(any(PolicyValidationResult.class), eq(JsonObject.class))).then(answer -> { + PolicyValidationResult result = answer.getArgument(0); + var response = Json.createObjectBuilder() + .add("isValid", result.isValid()) + .add("errors", Json.createArrayBuilder(result.errors())) + .build(); + return Result.success(response); + }); + + baseRequest() + .contentType(JSON) + .post("/id/validate") + .then() + .statusCode(200) + .contentType(JSON) + .body("isValid", is(false)) + .body("errors.size()", is(2)); + } + @Override protected Object controller() { - return new PolicyDefinitionApiV31AlphaController(monitor, transformerRegistry, service, validatorRegistry); + return new PolicyDefinitionApiV31AlphaController(monitor, transformerRegistry, service, validatorRegistry, policyValidationService); } @Override diff --git a/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/controlplane/services/spi/policydefinition/PolicyValidationService.java b/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/controlplane/services/spi/policydefinition/PolicyValidationService.java new file mode 100644 index 00000000000..2186437932d --- /dev/null +++ b/spi/control-plane/control-plane-spi/src/main/java/org/eclipse/edc/connector/controlplane/services/spi/policydefinition/PolicyValidationService.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.controlplane.services.spi.policydefinition; + +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.result.Result; + +@ExtensionPoint +public interface PolicyValidationService { + + Result validate(Policy policy); +} diff --git a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java index 9d125335d54..7a7861656b0 100644 --- a/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java +++ b/system-tests/management-api/management-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/managementapi/PolicyDefinitionApiEndToEndTest.java @@ -275,6 +275,34 @@ void shouldDeleteWithProperties(ManagementEndToEndTestContext context) { .statusCode(404); } + @Test + void shouldValidatePolicy(ManagementEndToEndTestContext context) { + var requestBody = createObjectBuilder() + .add(CONTEXT, createObjectBuilder() + .add(VOCAB, EDC_NAMESPACE) + .build()) + .add(TYPE, "PolicyDefinition") + .add("policy", sampleOdrlPolicy()) + .build(); + + var id = context.baseRequest() + .body(requestBody) + .contentType(JSON) + .post("/v3/policydefinitions") + .then() + .statusCode(200) + .extract().jsonPath().getString(ID); + + context.baseRequest() + .contentType(JSON) + .post("/v3.1alpha/policydefinitions/" + id + "/validate") + .then() + .statusCode(200) + .body("isValid", is(false)) + .body("errors.size()", is(3)); + + } + private JsonObject sampleOdrlPolicy() { return createObjectBuilder() .add(CONTEXT, "http://www.w3.org/ns/odrl.jsonld") @@ -314,11 +342,13 @@ private JsonObject sampleOdrlPolicy() { @Nested @EndToEndTest @ExtendWith(ManagementEndToEndExtension.InMemory.class) - class InMemory extends Tests { } + class InMemory extends Tests { + } @Nested @PostgresqlIntegrationTest @ExtendWith(ManagementEndToEndExtension.Postgres.class) - class Postgres extends Tests { } + class Postgres extends Tests { + } }