From bc1c35be07789aa89aa9b2d78e01aa8ef1651e3b Mon Sep 17 00:00:00 2001 From: FrancescoFioraEst Date: Tue, 2 Dec 2025 16:36:23 +0000 Subject: [PATCH] Use Spring WebClient for REST calls in Policy Participant Issue-ID: POLICY-5507 Change-Id: I0e3ee296eed151030e242953b374444cdce4ba1a Signed-off-by: FrancescoFioraEst --- .../participant-impl-a1pms/pom.xml | 5 - .../participant-impl/participant-impl-http/pom.xml | 8 -- .../participant-impl-policy/pom.xml | 5 - .../policy/client/AbstractHttpClient.java | 102 +++++++++++++-------- .../policy/client/PolicyApiHttpClient.java | 23 ++--- .../policy/client/PolicyPapHttpClient.java | 7 +- .../AutomationCompositionElementHandler.java | 34 ++++--- .../parameters/ParticipantPolicyParameters.java | 8 +- .../main/parameters/RestClientParameters.java | 46 ++++++++++ .../participant/policy/client/HttpClientTest.java | 69 +++++++------- .../AutomationCompositionElementHandlerTest.java | 60 ++++++++---- .../policy/main/parameters/CommonTestData.java | 23 ++++- participant/pom.xml | 1 + 13 files changed, 246 insertions(+), 145 deletions(-) create mode 100644 participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/RestClientParameters.java diff --git a/participant/participant-impl/participant-impl-a1pms/pom.xml b/participant/participant-impl/participant-impl-a1pms/pom.xml index 105245e88..2a0a77ccb 100644 --- a/participant/participant-impl/participant-impl-a1pms/pom.xml +++ b/participant/participant-impl/participant-impl-a1pms/pom.xml @@ -37,11 +37,6 @@ org.apache.httpcomponents.core5 httpcore5 - - org.glassfish.jersey.inject - jersey-hk2 - ${version.jersey} - diff --git a/participant/participant-impl/participant-impl-http/pom.xml b/participant/participant-impl/participant-impl-http/pom.xml index cf9980290..5a4050c44 100644 --- a/participant/participant-impl/participant-impl-http/pom.xml +++ b/participant/participant-impl/participant-impl-http/pom.xml @@ -32,14 +32,6 @@ ${project.artifactId} HTTP participant, that performs CRUD operations on Microservices through REST - - - org.glassfish.jersey.inject - jersey-hk2 - ${version.jersey} - - - diff --git a/participant/participant-impl/participant-impl-policy/pom.xml b/participant/participant-impl/participant-impl-policy/pom.xml index 64cb4d12f..d14734838 100644 --- a/participant/participant-impl/participant-impl-policy/pom.xml +++ b/participant/participant-impl/participant-impl-policy/pom.xml @@ -36,11 +36,6 @@ org.apache.httpcomponents.core5 httpcore5 - - org.glassfish.jersey.inject - jersey-hk2 - ${version.jersey} - diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/AbstractHttpClient.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/AbstractHttpClient.java index 5a98b2a0e..20dbb13ef 100644 --- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/AbstractHttpClient.java +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/AbstractHttpClient.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021, 2023-2024 Nordix Foundation. + * Copyright (C) 2021, 2023-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,56 +20,84 @@ package org.onap.policy.clamp.acm.participant.policy.client; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status; -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import org.onap.policy.clamp.common.acm.exception.AutomationCompositionRuntimeException; -import org.onap.policy.common.endpoints.http.client.HttpClient; -import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance; -import org.onap.policy.common.parameters.topic.BusTopicParams; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.onap.policy.clamp.acm.participant.policy.main.parameters.RestClientParameters; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; -public abstract class AbstractHttpClient implements AutoCloseable { +@RequiredArgsConstructor +public abstract class AbstractHttpClient { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractHttpClient.class); - private final HttpClient httpclient; + private static final Coder CODER = new StandardCoder(); + private final RestClientParameters parameters; - /** - * Constructor. - * - * @param policyParticipantParameters the parameters for the policy participant - * @throws AutomationCompositionRuntimeException on client start errors - */ - protected AbstractHttpClient(BusTopicParams policyParticipantParameters) { + protected String executePost(String path, final Object entity) { + var webClient = WebClient.builder().baseUrl(this.getBaseUrl()) + .defaultHeaders(this::headersConsumer).build(); + return webClient.post() + .uri(path) + .body(BodyInserters.fromValue(encode(entity))) + .exchangeToMono(this::responseHandler).block(); + } + + private String encode(Object entity) { try { - httpclient = HttpClientFactoryInstance.getClientFactory().build(policyParticipantParameters); - } catch (final Exception e) { - throw new AutomationCompositionRuntimeException(Status.INTERNAL_SERVER_ERROR, " Client failed to start", e); + return CODER.encode(entity); + } catch (CoderException e) { + throw new WebClientResponseException(HttpStatus.BAD_REQUEST.value(), e.getMessage(), null, null, null); + } + } + + private String getBaseUrl() { + return "http://" + parameters.getHostname() + ":" + parameters.getPort(); + } + + private Mono responseHandler(ClientResponse clientResponse) { + if (clientResponse.statusCode().is2xxSuccessful()) { + return clientResponse.bodyToMono(String.class); + } else { + LOGGER.error("Invocation Post failed Response status: {}", clientResponse.statusCode()); + return clientResponse.createException().flatMap(Mono::error); } } - protected Response executePost(String path, final Entity entity) { - var response = httpclient.post(path, entity, Map.of(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON, - HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)); - if (response.getStatus() / 100 != 2) { - LOGGER.error("Invocation of path {} failed for entity {}. Response status: {}, Response status info: {}", - path, entity, response.getStatus(), response.getStatusInfo()); + private void headersConsumer(HttpHeaders headers) { + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_JSON); + if (StringUtils.isNotBlank(parameters.getUserName()) + && StringUtils.isNotBlank(parameters.getPassword())) { + headers.setBasicAuth(parameters.getUserName(), parameters.getPassword()); } - return response; } - protected Response executeDelete(String path) { - return httpclient.delete(path, Collections.emptyMap()); + protected void executeDelete(String path) { + var webClient = WebClient.builder().baseUrl(this.getBaseUrl()) + .defaultHeaders(this::headersConsumer).build(); + webClient.delete() + .uri(path) + .exchangeToMono(this::responseDeleteHandler).block(); } - @Override - public void close() throws IOException { - httpclient.shutdown(); + private Mono responseDeleteHandler(ClientResponse clientResponse) { + if (clientResponse.statusCode().is2xxSuccessful()) { + return clientResponse.releaseBody(); + } else { + LOGGER.error("Invocation Delete failed Response status: {}", clientResponse.statusCode()); + return clientResponse.createException().flatMap(Mono::error); + } } } diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java index d48028f74..689674a12 100644 --- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021,2023 Nordix Foundation. + * Copyright (C) 2021,2023,2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,6 @@ package org.onap.policy.clamp.acm.participant.policy.client; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import org.onap.policy.clamp.acm.participant.policy.main.parameters.ParticipantPolicyParameters; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.springframework.stereotype.Component; @@ -47,8 +44,8 @@ public class PolicyApiHttpClient extends AbstractHttpClient { * @param toscaServiceTemplate the whole ToscaServiceTemplate * @return Response */ - public Response createPolicyType(ToscaServiceTemplate toscaServiceTemplate) { - return executePost(POLICY_URI + "policytypes", Entity.entity(toscaServiceTemplate, MediaType.APPLICATION_JSON)); + public String createPolicyType(ToscaServiceTemplate toscaServiceTemplate) { + return executePost(POLICY_URI + "policytypes", toscaServiceTemplate); } /** @@ -57,8 +54,8 @@ public class PolicyApiHttpClient extends AbstractHttpClient { * @param toscaServiceTemplate the whole ToscaServiceTemplate * @return Response */ - public Response createPolicy(final ToscaServiceTemplate toscaServiceTemplate) { - return executePost(POLICY_URI + "policies", Entity.entity(toscaServiceTemplate, MediaType.APPLICATION_JSON)); + public String createPolicy(final ToscaServiceTemplate toscaServiceTemplate) { + return executePost(POLICY_URI + "policies", toscaServiceTemplate); } /** @@ -66,10 +63,9 @@ public class PolicyApiHttpClient extends AbstractHttpClient { * * @param policyName the name of the policy to be deleted * @param policyVersion the version of the policy to be deleted - * @return Response */ - public Response deletePolicy(final String policyName, final String policyVersion) { - return executeDelete(POLICY_URI + "policies/" + policyName + "/versions/" + policyVersion); + public void deletePolicy(final String policyName, final String policyVersion) { + executeDelete(POLICY_URI + "policies/" + policyName + "/versions/" + policyVersion); } /** @@ -77,9 +73,8 @@ public class PolicyApiHttpClient extends AbstractHttpClient { * * @param policyTypeName the name of the policy to be deleted * @param policyTypeVersion the version of the policy to be deleted - * @return Response */ - public Response deletePolicyType(final String policyTypeName, final String policyTypeVersion) { - return executeDelete(POLICY_URI + "policytypes/" + policyTypeName + "/versions/" + policyTypeVersion); + public void deletePolicyType(final String policyTypeName, final String policyTypeVersion) { + executeDelete(POLICY_URI + "policytypes/" + policyTypeName + "/versions/" + policyTypeVersion); } } diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyPapHttpClient.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyPapHttpClient.java index 3f6c4cbbb..cd8686eab 100644 --- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyPapHttpClient.java +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyPapHttpClient.java @@ -20,9 +20,6 @@ package org.onap.policy.clamp.acm.participant.policy.client; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import java.util.List; import org.onap.policy.clamp.acm.participant.policy.concepts.DeploymentGroup; import org.onap.policy.clamp.acm.participant.policy.concepts.DeploymentGroups; @@ -57,7 +54,7 @@ public class PolicyPapHttpClient extends AbstractHttpClient { * @param action the action to deploy/undeploy policy * @return Response */ - public Response handlePolicyDeployOrUndeploy(final String policyName, final String policyVersion, + public String handlePolicyDeployOrUndeploy(final String policyName, final String policyVersion, final DeploymentSubGroup.Action action) { var policies = List.of(new ToscaConceptIdentifier(policyName, policyVersion)); @@ -74,6 +71,6 @@ public class PolicyPapHttpClient extends AbstractHttpClient { var groups = new DeploymentGroups(); groups.setGroups(List.of(group)); - return executePost(PAP_URI + "pdps/deployments/batch", Entity.entity(groups, MediaType.APPLICATION_JSON)); + return executePost(PAP_URI + "pdps/deployments/batch", groups); } } diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java index c410ddbbd..0daf6e45b 100644 --- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; -import org.apache.hc.core5.http.HttpStatus; import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto; import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto; import org.onap.policy.clamp.acm.participant.intermediary.api.ParticipantIntermediaryApi; @@ -46,6 +45,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClientResponseException; /** * This class handles implementation of automationCompositionElement updates. @@ -118,9 +118,11 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 { // Deploy all policies of this automationComposition from Policy Framework if (!policyList.isEmpty()) { for (var policy : policyList) { - var deployPolicyResp = papHttpClient.handlePolicyDeployOrUndeploy(policy.getName(), policy.getVersion(), - DeploymentSubGroup.Action.POST).getStatus(); - if (deployPolicyResp != HttpStatus.SC_ACCEPTED) { + try { + papHttpClient.handlePolicyDeployOrUndeploy(policy.getName(), policy.getVersion(), + DeploymentSubGroup.Action.POST); + } catch (WebClientResponseException e) { + LOGGER.error(e.getMessage(), e); deployFailure = true; } } @@ -162,8 +164,8 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 { @Override public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { - var createPolicyTypeResp = HttpStatus.SC_OK; - var createPolicyResp = HttpStatus.SC_OK; + var createPolicyTypeResp = true; + var createPolicyResp = true; var automationCompositionDefinition = getToscaServiceTemplate(instanceElement.inProperties()); if (automationCompositionDefinition.getToscaTopologyTemplate() == null) { @@ -175,18 +177,24 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 { if (automationCompositionDefinition.getPolicyTypes() != null) { LOGGER.info("Found Policy Types in automation composition definition: {} , Creating Policy Types", automationCompositionDefinition.getName()); - try (var response = apiHttpClient.createPolicyType(automationCompositionDefinition)) { - createPolicyTypeResp = response.getStatus(); + try { + apiHttpClient.createPolicyType(automationCompositionDefinition); + } catch (WebClientResponseException e) { + LOGGER.error(e.getMessage(), e); + createPolicyTypeResp = false; } } if (automationCompositionDefinition.getToscaTopologyTemplate().getPolicies() != null) { LOGGER.info("Found Policies in automation composition definition: {} , Creating Policies", automationCompositionDefinition.getName()); - try (var response = apiHttpClient.createPolicy(automationCompositionDefinition)) { - createPolicyResp = response.getStatus(); + try { + apiHttpClient.createPolicy(automationCompositionDefinition); + } catch (WebClientResponseException e) { + LOGGER.error(e.getMessage(), e); + createPolicyResp = false; } } - if (isSuccess(createPolicyTypeResp) && isSuccess(createPolicyResp)) { + if (createPolicyTypeResp && createPolicyResp) { LOGGER.info( "PolicyTypes/Policies for the automation composition element : {} are created " + "successfully", instanceElement.elementId()); @@ -199,10 +207,6 @@ public class AutomationCompositionElementHandler extends AcElementListenerV3 { } } - private boolean isSuccess(int status) { - return status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED; - } - private List getPolicyTypeList(ToscaServiceTemplate serviceTemplate) { List policyTypeList = new ArrayList<>(); if (serviceTemplate.getPolicyTypes() != null) { diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/ParticipantPolicyParameters.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/ParticipantPolicyParameters.java index 2deb99cc2..ae5804a3d 100644 --- a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/ParticipantPolicyParameters.java +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/ParticipantPolicyParameters.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021, 2024 Nordix Foundation. + * Copyright (C) 2021,2024-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ import lombok.Getter; import lombok.Setter; import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantIntermediaryParameters; import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters; -import org.onap.policy.common.parameters.rest.RestClientParameters; -import org.onap.policy.common.parameters.validation.ParameterGroupConstraint; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; @@ -47,11 +45,11 @@ public class ParticipantPolicyParameters implements ParticipantParameters { private ParticipantIntermediaryParameters intermediaryParameters; @NotNull - @ParameterGroupConstraint + @Valid private RestClientParameters policyApiParameters; @NotNull - @ParameterGroupConstraint + @Valid private RestClientParameters policyPapParameters; @NotBlank diff --git a/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/RestClientParameters.java b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/RestClientParameters.java new file mode 100644 index 000000000..611d8d89d --- /dev/null +++ b/participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/RestClientParameters.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.participant.policy.main.parameters; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; +import org.springframework.validation.annotation.Validated; + +@Validated +@Getter +@Setter +public class RestClientParameters { + @NotBlank + private String clientName; + + @Min(0) + @Max(65535) + private int port; + + private String userName; + private String password; + + @NotBlank + private String hostname; +} diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/client/HttpClientTest.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/client/HttpClientTest.java index 467af60ac..349a6446d 100644 --- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/client/HttpClientTest.java +++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/client/HttpClientTest.java @@ -20,13 +20,9 @@ package org.onap.policy.clamp.acm.participant.policy.client; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import java.io.IOException; import okhttp3.mockwebserver.Dispatcher; import okhttp3.mockwebserver.MockResponse; @@ -36,10 +32,11 @@ import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.onap.policy.clamp.acm.participant.policy.concepts.DeploymentSubGroup; +import org.onap.policy.clamp.acm.participant.policy.main.parameters.CommonTestData; import org.onap.policy.clamp.acm.participant.policy.main.parameters.ParticipantPolicyParameters; -import org.onap.policy.clamp.common.acm.exception.AutomationCompositionRuntimeException; -import org.onap.policy.common.parameters.rest.RestClientParameters; +import org.onap.policy.clamp.acm.participant.policy.main.parameters.RestClientParameters; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; +import org.springframework.web.reactive.function.client.WebClientException; /** * Tests for api and pap http clients. @@ -50,14 +47,18 @@ class HttpClientTest { private static PolicyPapHttpClient papHttpClient; + private static ToscaServiceTemplate serviceTemplate; + /** * Set up Mock server. */ @BeforeAll static void setUpMockServer() throws IOException { + serviceTemplate = CommonTestData + .getToscaServiceTemplate("clamp/acm/pmsh/funtional-pmsh-usecase-migration.yaml"); // Setup mock web server int mockServerPort = 42545; - MockWebServer mockServer = new MockWebServer(); + var mockServer = new MockWebServer(); mockServer.start(mockServerPort); mockServer.setDispatcher(new Dispatcher() { @NotNull @@ -70,6 +71,10 @@ class HttpClientTest { .setResponseCode(200) .addHeader("Content-Type", "application/json"); } + if (path.equals("/policy/api/v1/policies") && "POST".equals(request.getMethod()) + && request.getBody().size() < 40) { + return new MockResponse().setResponseCode(404); + } if (path.equals("/policy/api/v1/policies") && "POST".equals(request.getMethod())) { return new MockResponse() .setResponseCode(200) @@ -79,6 +84,10 @@ class HttpClientTest { && "DELETE".equals(request.getMethod())) { return new MockResponse().setResponseCode(200); } + if (path.matches("^/policy/api/v1/policies/wrongPolicy/versions/[^/]+$") + && "DELETE".equals(request.getMethod())) { + return new MockResponse().setResponseCode(404); + } if (path.matches("^/policy/api/v1/policies/[^/]+/versions/[^/]+$") && "DELETE".equals(request.getMethod())) { return new MockResponse().setResponseCode(200); @@ -94,8 +103,8 @@ class HttpClientTest { }); // Setup mock api and pap client - ParticipantPolicyParameters params = new ParticipantPolicyParameters(); - RestClientParameters restClientParameters = getMockClientParameters(mockServerPort); + var params = new ParticipantPolicyParameters(); + var restClientParameters = getMockClientParameters(mockServerPort); params.setPolicyApiParameters(restClientParameters); params.setPolicyPapParameters(restClientParameters); @@ -103,15 +112,20 @@ class HttpClientTest { papHttpClient = new PolicyPapHttpClient(params); } - @Test void testCreatePolicy() { - assertDoesNotThrow(() -> apiHttpClient.createPolicy(getTestToscaServiceTemplate())); + assertDoesNotThrow(() -> apiHttpClient.createPolicy(serviceTemplate)); + } + + @Test + void testCreateBabPolicy() { + var badServiceTemplate = new ToscaServiceTemplate(); + assertThrows(WebClientException.class, () -> apiHttpClient.createPolicy(badServiceTemplate)); } @Test void testCreatePolicyTypes() { - assertDoesNotThrow(() -> apiHttpClient.createPolicyType(getTestToscaServiceTemplate())); + assertDoesNotThrow(() -> apiHttpClient.createPolicyType(serviceTemplate)); } @Test @@ -119,6 +133,11 @@ class HttpClientTest { assertDoesNotThrow(() -> apiHttpClient.deletePolicy("dummyPolicy", "1.0.0")); } + @Test + void testBadDeletePolicy() { + assertThrows(WebClientException.class, () -> apiHttpClient.deletePolicy("wrongPolicy", "1.0.0")); + } + @Test void testDeletePolicyType() { assertDoesNotThrow(() -> apiHttpClient.deletePolicyType("dummyPolicy", "1.0.0")); @@ -138,32 +157,16 @@ class HttpClientTest { @Test void testInvalidEndpoint() { - Response response = apiHttpClient.executePost("/invalid", Entity.entity(getTestToscaServiceTemplate(), - MediaType.APPLICATION_JSON)); - assertThat(response.getStatus()).isEqualTo(404); - } - - @Test - void testInvalidClientParameter() { - var parameters = new ParticipantPolicyParameters(); - assertThrows(AutomationCompositionRuntimeException.class, - () -> new PolicyApiHttpClient(parameters)); - } - - - private ToscaServiceTemplate getTestToscaServiceTemplate() { - return new ToscaServiceTemplate(); + assertThrows(WebClientException.class, () -> apiHttpClient.executePost("/invalid", serviceTemplate)); } private static RestClientParameters getMockClientParameters(int port) { - RestClientParameters params = new RestClientParameters(); - params.setName("policyClient"); + var params = new RestClientParameters(); + params.setClientName("policyClient"); params.setHostname("localhost"); + params.setUserName("user"); + params.setPassword("pwd"); params.setPort(port); - params.setUseHttps(false); return params; } - - - } diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java index cc8bb0ac1..5ced1173b 100644 --- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java +++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,10 @@ package org.onap.policy.clamp.acm.participant.policy.main.handler; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import jakarta.ws.rs.core.Response; import java.util.List; import java.util.Map; import java.util.UUID; @@ -48,6 +47,8 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.onap.policy.models.tosca.authorative.concepts.ToscaTopologyTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.WebClientResponseException; class AutomationCompositionElementHandlerTest { @@ -97,12 +98,7 @@ class AutomationCompositionElementHandlerTest { void testDeploy() throws PfModelException { // Mock success scenario for policy creation and deployment var api = mock(PolicyApiHttpClient.class); - doReturn(Response.ok().build()).when(api).createPolicyType(any()); - doReturn(Response.ok().build()).when(api).createPolicy(any()); - var pap = mock(PolicyPapHttpClient.class); - doReturn(Response.accepted().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any()); - var intermediaryApi = mock(ParticipantIntermediaryApi.class); var handler = new AutomationCompositionElementHandler(api, pap, intermediaryApi); @@ -121,6 +117,20 @@ class AutomationCompositionElementHandlerTest { "Undeployed"); } + @Test + void testDeployError() { + var api = mock(PolicyApiHttpClient.class); + var pap = mock(PolicyPapHttpClient.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + var handler = new AutomationCompositionElementHandler(api, pap, intermediaryApi); + + var compositionElement = getCompositionElement(); + var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(), + Map.of("data_types", 100), Map.of()); + assertThatThrownBy(() -> handler.deploy(compositionElement, instanceElement)) + .isInstanceOf(PfModelException.class); + } + @Test void testDeployNoPolicy() throws PfModelException { var intermediaryApi = mock(ParticipantIntermediaryApi.class); @@ -163,14 +173,12 @@ class AutomationCompositionElementHandlerTest { } @Test - void testApiException() throws PfModelException { + void testApiPolicyTypeException() throws PfModelException { var api = mock(PolicyApiHttpClient.class); - doReturn(Response.serverError().build()).when(api).createPolicyType(any()); - doReturn(Response.ok().build()).when(api).createPolicy(any()); + when(api.createPolicyType(any())) + .thenThrow(new WebClientResponseException(HttpStatus.BAD_REQUEST.value(), "", null, null, null)); var pap = mock(PolicyPapHttpClient.class); - doReturn(Response.accepted().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any()); - var intermediaryApi = mock(ParticipantIntermediaryApi.class); var handler = new AutomationCompositionElementHandler(api, pap, intermediaryApi); @@ -185,15 +193,33 @@ class AutomationCompositionElementHandlerTest { } @Test - void testDeployPapException() { + void testApiPolicyException() throws PfModelException { var api = mock(PolicyApiHttpClient.class); - doReturn(Response.ok().build()).when(api).createPolicyType(any()); - doReturn(Response.ok().build()).when(api).createPolicy(any()); + when(api.createPolicy(any())) + .thenThrow(new WebClientResponseException(HttpStatus.BAD_REQUEST.value(), "", null, null, null)); + + var pap = mock(PolicyPapHttpClient.class); + var intermediaryApi = mock(ParticipantIntermediaryApi.class); + var handler = new AutomationCompositionElementHandler(api, pap, intermediaryApi); + + var compositionElement = getCompositionElement(); + var instanceElement = getInstanceElement(); + // Mock failure in policy creation + handler.deploy(compositionElement, instanceElement); + verify(intermediaryApi).updateAutomationCompositionElementState(instanceElement.instanceId(), + instanceElement.elementId(), DeployState.UNDEPLOYED, null, StateChangeResult.FAILED, + "Creation of PolicyTypes/Policies failed. Policies will not be deployed."); + } + + @Test + void testDeployPapException() { var pap = mock(PolicyPapHttpClient.class); - doReturn(Response.serverError().build()).when(pap).handlePolicyDeployOrUndeploy(any(), any(), any()); + when(pap.handlePolicyDeployOrUndeploy(any(), any(), any())) + .thenThrow(new WebClientResponseException(HttpStatus.BAD_REQUEST.value(), "", null, null, null)); var intermediaryApi = mock(ParticipantIntermediaryApi.class); + var api = mock(PolicyApiHttpClient.class); var handler = new AutomationCompositionElementHandler(api, pap, intermediaryApi); var compositionElement = getCompositionElement(); diff --git a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/CommonTestData.java b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/CommonTestData.java index c0101871a..143d7a1e0 100644 --- a/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/CommonTestData.java +++ b/participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/CommonTestData.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation. + * Copyright (C) 2021--2025 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ package org.onap.policy.clamp.acm.participant.policy.main.parameters; +import static org.junit.jupiter.api.Assertions.fail; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -30,6 +32,9 @@ import org.onap.policy.common.parameters.topic.TopicParameters; import org.onap.policy.common.utils.coder.Coder; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; +import org.onap.policy.common.utils.coder.StandardYamlCoder; +import org.onap.policy.common.utils.resources.ResourceUtils; +import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; /** * Class to hold/create all parameters for test cases. @@ -42,6 +47,7 @@ public class CommonTestData { public static final List SOURCE_TOPIC_PARAMS = List.of(getSinkTopicParams(), getSyncTopicParams()); public static final Coder CODER = new StandardCoder(); + private static final StandardYamlCoder YAML_TRANSLATOR = new StandardYamlCoder(); /** * Get ParticipantPolicyParameters. @@ -181,4 +187,19 @@ public class CommonTestData { public static UUID getParticipantId() { return UUID.fromString("101c62b3-8918-41b9-a747-d21eb79c6c03"); } + + /** + * Get ToscaServiceTemplate from resource. + * + * @param path path of the resource + */ + public static ToscaServiceTemplate getToscaServiceTemplate(String path) { + try { + return YAML_TRANSLATOR.decode(ResourceUtils.getResourceAsStream(path), ToscaServiceTemplate.class); + } catch (CoderException e) { + fail("Cannot read or decode " + path); + return null; + } + } } + diff --git a/participant/pom.xml b/participant/pom.xml index 07c5eac30..1d3b4671c 100644 --- a/participant/pom.xml +++ b/participant/pom.xml @@ -98,6 +98,7 @@ org.onap.policy.clamp policy-clamp-examples ${project.version} + test org.apache.kafka -- 2.16.6