Use Spring WebClient for REST calls in Policy Participant 37/142637/3
authorFrancescoFioraEst <francesco.fiora@est.tech>
Tue, 2 Dec 2025 16:36:23 +0000 (16:36 +0000)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Thu, 4 Dec 2025 10:12:26 +0000 (10:12 +0000)
Issue-ID: POLICY-5507
Change-Id: I0e3ee296eed151030e242953b374444cdce4ba1a
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
13 files changed:
participant/participant-impl/participant-impl-a1pms/pom.xml
participant/participant-impl/participant-impl-http/pom.xml
participant/participant-impl/participant-impl-policy/pom.xml
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/AbstractHttpClient.java
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyApiHttpClient.java
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/client/PolicyPapHttpClient.java
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandler.java
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/ParticipantPolicyParameters.java
participant/participant-impl/participant-impl-policy/src/main/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/RestClientParameters.java [new file with mode: 0644]
participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/client/HttpClientTest.java
participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/handler/AutomationCompositionElementHandlerTest.java
participant/participant-impl/participant-impl-policy/src/test/java/org/onap/policy/clamp/acm/participant/policy/main/parameters/CommonTestData.java
participant/pom.xml

index 105245e..2a0a77c 100644 (file)
             <groupId>org.apache.httpcomponents.core5</groupId>
             <artifactId>httpcore5</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.inject</groupId>
-            <artifactId>jersey-hk2</artifactId>
-            <version>${version.jersey}</version>
-        </dependency>
     </dependencies>
 
     <build>
index cf99802..5a4050c 100644 (file)
     <name>${project.artifactId}</name>
     <description>HTTP participant, that performs CRUD operations on Microservices through REST</description>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.glassfish.jersey.inject</groupId>
-            <artifactId>jersey-hk2</artifactId>
-            <version>${version.jersey}</version>
-        </dependency>
-    </dependencies>
-
     <build>
         <plugins>
             <plugin>
index 64cb4d1..d147348 100644 (file)
             <groupId>org.apache.httpcomponents.core5</groupId>
             <artifactId>httpcore5</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.inject</groupId>
-            <artifactId>jersey-hk2</artifactId>
-            <version>${version.jersey}</version>
-        </dependency>
     </dependencies>
 
     <build>
index 5a98b2a..20dbb13 100644 (file)
@@ -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.
 
 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<String> 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<Void> 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);
+        }
     }
 }
index d48028f..689674a 100644 (file)
@@ -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);
     }
 }
index 3f6c4cb..cd8686e 100644 (file)
@@ -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);
     }
 }
index c410ddb..0daf6e4 100644 (file)
@@ -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<ToscaConceptIdentifier> getPolicyTypeList(ToscaServiceTemplate serviceTemplate) {
         List<ToscaConceptIdentifier> policyTypeList = new ArrayList<>();
         if (serviceTemplate.getPolicyTypes() != null) {
index 2deb99c..ae5804a 100644 (file)
@@ -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 (file)
index 0000000..611d8d8
--- /dev/null
@@ -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;
+}
index 467af60..349a644 100644 (file)
 
 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;
     }
-
-
-
 }
index cc8bb0a..5ced117 100644 (file)
@@ -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();
index c010187..143d7a1 100644 (file)
@@ -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<TopicParameters> 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;
+        }
+    }
 }
+
index 07c5eac..1d3b467 100644 (file)
@@ -98,6 +98,7 @@
             <groupId>org.onap.policy.clamp</groupId>
             <artifactId>policy-clamp-examples</artifactId>
             <version>${project.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.kafka</groupId>