Added support for https 27/3527/2
authorPatrikBuhr <patrik.buhr@est.tech>
Tue, 21 Apr 2020 06:37:07 +0000 (08:37 +0200)
committerPatrikBuhr <patrik.buhr@est.tech>
Thu, 30 Apr 2020 06:15:11 +0000 (08:15 +0200)
The backend listens to port 8434 for https.
Port 8080 is still open for http

In this commit, the backend uses http://policy-agent-container:8081 towards the
policy agent.
Using https://policy-agent-container:8433 is tested.

Change-Id: I2c09d888a55ca156c33b12a83f27b7131478caf8
Issue-ID: NONRTRIC-195
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
webapp-backend/pom.xml
webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/ControlPanelApplication.java
webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/config/TomcatWebServerConfiguration.java [new file with mode: 0644]
webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/policyagentapi/PolicyAgentApiImpl.java
webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClient.java [new file with mode: 0644]
webapp-backend/src/main/resources/application.properties
webapp-backend/src/main/resources/keystore.jks [new file with mode: 0644]
webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/controller/AbstractControllerTest.java
webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/controller/PortalRestCentralServiceTest.java
webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/policyagentapi/PolicyAgentApiImplTest.java
webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClientTest.java [new file with mode: 0644]

index be9a13e..8b6599d 100644 (file)
@@ -1,8 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-
-<![CDATA[
+<!--<![CDATA[
 ========================LICENSE_START=================================
 O-RAN-SC
 %%
@@ -108,6 +105,10 @@ limitations under the License.
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
@@ -178,6 +179,16 @@ limitations under the License.
             <artifactId>junit-platform-launcher</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>mockwebserver</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
index dee84ad..8f5befe 100644 (file)
@@ -46,8 +46,7 @@ public class ControlPanelApplication {
     /**
      * Gets version details for the specified class.
      *
-     * @param clazz
-     *        Class to get the version
+     * @param clazz Class to get the version
      *
      * @return the value of the MANIFEST.MF property Implementation-Version as
      *         written by maven when packaged in a jar; 'unknown' otherwise.
diff --git a/webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/config/TomcatWebServerConfiguration.java b/webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/config/TomcatWebServerConfiguration.java
new file mode 100644 (file)
index 0000000..7e9f8d1
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.portal.nonrtric.controlpanel.config;
+
+import org.apache.catalina.connector.Connector;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class TomcatWebServerConfiguration {
+
+    @Bean
+    public ServletWebServerFactory servletContainer() {
+        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
+        tomcat.addAdditionalTomcatConnectors(getHttpConnector());
+        return tomcat;
+    }
+
+    private static Connector getHttpConnector() {
+        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
+        connector.setScheme("http");
+        connector.setPort(8080);
+        connector.setSecure(false);
+        return connector;
+    }
+
+}
index 30dcdc5..20e72bb 100644 (file)
@@ -32,7 +32,8 @@ import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
+
+import javax.net.ssl.SSLException;
 
 import org.immutables.gson.Gson;
 import org.immutables.value.Value;
@@ -41,61 +42,42 @@ import org.oransc.portal.nonrtric.controlpanel.model.PolicyInfo;
 import org.oransc.portal.nonrtric.controlpanel.model.PolicyInstances;
 import org.oransc.portal.nonrtric.controlpanel.model.PolicyType;
 import org.oransc.portal.nonrtric.controlpanel.model.PolicyTypes;
+import org.oransc.portal.nonrtric.controlpanel.util.AsyncRestClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.HttpServerErrorException;
-import org.springframework.web.client.RestTemplate;
 
 @Component("PolicyAgentApi")
 public class PolicyAgentApiImpl implements PolicyAgentApi {
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-    RestTemplate restTemplate;
+    private final AsyncRestClient webClient;
 
     private static com.google.gson.Gson gson = new GsonBuilder() //
         .serializeNulls() //
         .create(); //
 
-    private final String urlPrefix;
-
     @Autowired
     public PolicyAgentApiImpl(
         @org.springframework.beans.factory.annotation.Value("${policycontroller.url.prefix}") final String urlPrefix) {
-        this(urlPrefix, new RestTemplate());
+        this(new AsyncRestClient(urlPrefix));
         logger.debug("ctor prefix '{}'", urlPrefix);
     }
 
-    public PolicyAgentApiImpl(String urlPrefix, RestTemplate restTemplate) {
-        this.urlPrefix = urlPrefix;
-        this.restTemplate = restTemplate;
-    }
-
-    private String baseUrl() {
-        return urlPrefix;
-    }
-
-    @Value.Immutable
-    @Gson.TypeAdapters
-    interface PolicyTypeInfo {
-
-        public String name();
-
-        public String schema();
+    public PolicyAgentApiImpl(AsyncRestClient webClient) {
+        this.webClient = webClient;
     }
 
     @Override
     public ResponseEntity<String> getAllPolicyTypes() {
         try {
-            String url = baseUrl() + "/policy_schemas";
-            ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
+            final String url = "/policy_schemas";
+            ResponseEntity<String> rsp = webClient.getForEntity(url).block();
             if (!rsp.getStatusCode().is2xxSuccessful()) {
                 return rsp;
             }
@@ -122,14 +104,13 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<String> getPolicyInstancesForType(String type) {
-        String url = baseUrl() + "/policies?type={type}";
-        Map<String, ?> uriVariables = Map.of("type", type);
-        ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class, uriVariables);
-        if (!rsp.getStatusCode().is2xxSuccessful()) {
-            return rsp;
-        }
-
         try {
+            String url = "/policies?type=" + type;
+            ResponseEntity<String> rsp = webClient.getForEntity(url).block();
+            if (!rsp.getStatusCode().is2xxSuccessful()) {
+                return rsp;
+            }
+
             Type listType = new TypeToken<List<ImmutablePolicyInfo>>() {}.getType();
             List<PolicyInfo> rspParsed = gson.fromJson(rsp.getBody(), listType);
             PolicyInstances result = new PolicyInstances();
@@ -144,24 +125,27 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<Object> getPolicyInstance(String id) {
-        String url = baseUrl() + "/policy?id={id}";
-        Map<String, ?> uriVariables = Map.of("id", id);
-
-        return this.restTemplate.getForEntity(url, Object.class, uriVariables);
+        try {
+            String url = "/policy?id=" + id;
+            ResponseEntity<String> rsp = webClient.getForEntity(url).block();
+            JsonObject obj = JsonParser.parseString(rsp.getBody()).getAsJsonObject();
+            String str = obj.toString();
+            return new ResponseEntity<>(str, rsp.getStatusCode());
+        } catch (Exception e) {
+            ResponseEntity<String> rsp = handleException(e);
+            return new ResponseEntity<>(rsp.getBody(), rsp.getStatusCode());
+        }
     }
 
     @Override
     public ResponseEntity<String> putPolicy(String policyTypeIdString, String policyInstanceId, Object json,
         String ric) {
-        String url = baseUrl() + "/policy?type={type}&id={id}&ric={ric}&service={service}";
-        Map<String, ?> uriVariables = Map.of( //
-            "type", policyTypeIdString, //
-            "id", policyInstanceId, //
-            "ric", ric, //
-            "service", "controlpanel");
+        String url =
+            "/policy?type=" + policyTypeIdString + "&id=" + policyInstanceId + "&ric=" + ric + "&service=controlpanel";
 
         try {
-            this.restTemplate.put(url, createJsonHttpEntity(json), uriVariables);
+            String jsonStr = json.toString();
+            webClient.putForEntity(url, jsonStr).block();
             return new ResponseEntity<>(HttpStatus.OK);
         } catch (Exception e) {
             return handleException(e);
@@ -170,15 +154,13 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<String> deletePolicy(String policyInstanceId) {
-        String url = baseUrl() + "/policy?id={id}";
-        Map<String, ?> uriVariables = Map.of("id", policyInstanceId);
+        String url = "/policy?id=" + policyInstanceId;
         try {
-            this.restTemplate.delete(url, uriVariables);
+            webClient.deleteForEntity(url).block();
             return new ResponseEntity<>(HttpStatus.OK);
         } catch (Exception e) {
             return handleException(e);
         }
-
     }
 
     @Value.Immutable
@@ -193,29 +175,23 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
 
     @Override
     public ResponseEntity<String> getRicsSupportingType(String typeName) {
-        String url = baseUrl() + "/rics?policyType={typeName}";
-        Map<String, ?> uriVariables = Map.of("typeName", typeName);
-        String rsp = this.restTemplate.getForObject(url, String.class, uriVariables);
-
         try {
+            String url = "/rics?policyType=" + typeName;
+            ResponseEntity<String> rsp = webClient.getForEntity(url).block();
+
             Type listType = new TypeToken<List<ImmutableRicInfo>>() {}.getType();
-            List<RicInfo> rspParsed = gson.fromJson(rsp, listType);
+            List<RicInfo> rspParsed = gson.fromJson(rsp.getBody(), listType);
             Collection<String> result = new ArrayList<>(rspParsed.size());
             for (RicInfo ric : rspParsed) {
                 result.add(ric.ricName());
             }
-            return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+            String json = gson.toJson(result);
+            return new ResponseEntity<>(json, HttpStatus.OK);
         } catch (Exception e) {
             return handleException(e);
         }
     }
 
-    private HttpEntity<Object> createJsonHttpEntity(Object content) {
-        HttpHeaders headers = new HttpHeaders();
-        headers.setContentType(MediaType.APPLICATION_JSON);
-        return new HttpEntity<>(content, headers);
-    }
-
     private ResponseEntity<String> handleException(Exception throwable) {
         if (throwable instanceof HttpClientErrorException) {
             HttpClientErrorException e = (HttpClientErrorException) throwable;
@@ -223,8 +199,11 @@ public class PolicyAgentApiImpl implements PolicyAgentApi {
         } else if (throwable instanceof HttpServerErrorException) {
             HttpServerErrorException e = (HttpServerErrorException) throwable;
             return new ResponseEntity<>(e.getResponseBodyAsString(), e.getStatusCode());
+        } else if (throwable instanceof SSLException) {
+            SSLException e = (SSLException) throwable;
+            return new ResponseEntity<>("Could not create WebClient " + e.getMessage(),
+                HttpStatus.INTERNAL_SERVER_ERROR);
         }
         return new ResponseEntity<>(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
     }
-
 }
diff --git a/webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClient.java b/webapp-backend/src/main/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClient.java
new file mode 100644 (file)
index 0000000..aa8d0bc
--- /dev/null
@@ -0,0 +1,174 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.portal.nonrtric.controlpanel.util;
+
+import io.netty.channel.ChannelOption;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.handler.timeout.WriteTimeoutHandler;
+
+import java.lang.invoke.MethodHandles;
+
+import javax.net.ssl.SSLException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.tcp.TcpClient;
+
+/**
+ * Generic reactive REST client.
+ */
+public class AsyncRestClient {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+    private WebClient webClient = null;
+    private final String baseUrl;
+
+    public AsyncRestClient(String baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
+        logger.debug("PUT uri = '{}{}''", baseUrl, uri);
+        return getWebClient() //
+            .flatMap(client -> {
+                RequestHeadersSpec<?> request = client.put() //
+                    .uri(uri) //
+                    .contentType(MediaType.APPLICATION_JSON) //
+                    .bodyValue(body);
+                return retrieve(request);
+            });
+    }
+
+    public Mono<ResponseEntity<String>> putForEntity(String uri) {
+        logger.debug("PUT uri = '{}{}''", baseUrl, uri);
+        return getWebClient() //
+            .flatMap(client -> {
+                RequestHeadersSpec<?> request = client.put() //
+                    .uri(uri);
+                return retrieve(request);
+            });
+    }
+
+    public Mono<String> put(String uri, String body) {
+        return putForEntity(uri, body) //
+            .flatMap(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> getForEntity(String uri) {
+        logger.debug("GET uri = '{}{}''", baseUrl, uri);
+        return getWebClient() //
+            .flatMap(client -> {
+                RequestHeadersSpec<?> request = client.get().uri(uri);
+                return retrieve(request);
+            });
+    }
+
+    public Mono<String> get(String uri) {
+        return getForEntity(uri) //
+            .flatMap(this::toBody);
+    }
+
+    public Mono<ResponseEntity<String>> deleteForEntity(String uri) {
+        logger.debug("DELETE uri = '{}{}''", baseUrl, uri);
+        return getWebClient() //
+            .flatMap(client -> {
+                RequestHeadersSpec<?> request = client.delete().uri(uri);
+                return retrieve(request);
+            });
+    }
+
+    public Mono<String> delete(String uri) {
+        return deleteForEntity(uri) //
+            .flatMap(this::toBody);
+    }
+
+    private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
+        return request.retrieve() //
+            .toEntity(String.class) //
+            .doOnError(this::onHttpError);
+    }
+
+    private void onHttpError(Throwable t) {
+        if (t instanceof WebClientResponseException) {
+            WebClientResponseException exception = (WebClientResponseException) t;
+            logger.debug("HTTP error status = '{}', body '{}'", exception.getStatusCode(),
+                exception.getResponseBodyAsString());
+        } else {
+            logger.debug("HTTP error: {}", t.getMessage());
+        }
+    }
+
+    private Mono<String> toBody(ResponseEntity<String> entity) {
+        if (entity.getBody() == null) {
+            return Mono.just("");
+        } else {
+            return Mono.just(entity.getBody());
+        }
+    }
+
+    private static SslContext createSslContext() throws SSLException {
+        return SslContextBuilder.forClient() //
+            .trustManager(InsecureTrustManagerFactory.INSTANCE) //
+            .build();
+    }
+
+    private static WebClient createWebClient(String baseUrl, SslContext sslContext) {
+        TcpClient tcpClient = TcpClient.create() //
+            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) //
+            .secure(c -> c.sslContext(sslContext)) //
+            .doOnConnected(connection -> {
+                connection.addHandler(new ReadTimeoutHandler(10));
+                connection.addHandler(new WriteTimeoutHandler(30));
+            });
+        HttpClient httpClient = HttpClient.from(tcpClient);
+        ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
+
+        return WebClient.builder() //
+            .clientConnector(connector) //
+            .baseUrl(baseUrl) //
+            .build();
+    }
+
+    private Mono<WebClient> getWebClient() {
+        if (this.webClient == null) {
+            try {
+                SslContext sslContext = createSslContext();
+                this.webClient = createWebClient(this.baseUrl, sslContext);
+            } catch (SSLException e) {
+                logger.error("Could not create WebClient {}", e.getMessage());
+                return Mono.error(e);
+            }
+        }
+        return Mono.just(this.webClient);
+    }
+
+}
index 84bf1a4..267fff3 100644 (file)
 
 # A spring property but without a "spring" prefix;
 # the port number is chosen RANDOMLY when running tests
-server.port = 8080
+server.port = 8434
+server.ssl.key-store-type = PKCS12
+server.ssl.key-store-password = ericsson_kwdjfhw
+server.ssl.key-store =  classpath:keystore.jks
+server.ssl.key-password = ericsson_kwdjfhw
+
+
 
 # path to file that stores user details;
 # use a persistent volume in a K8S deployment
@@ -44,6 +50,7 @@ portalapi.password =
 # endpoint URLs must be supplied at deployment time
 # NOTE: change policycontroller.url.prefix to http://localhost:8081 when running
 #       controlpanel locally (i.e., not inside the docker container)
+# policycontroller.url.prefix = https://policy-agent-container:8433
 policycontroller.url.prefix = http://policy-agent-container:8081
 
 # Mimic slow endpoints by defining sleep period, in milliseconds
diff --git a/webapp-backend/src/main/resources/keystore.jks b/webapp-backend/src/main/resources/keystore.jks
new file mode 100644 (file)
index 0000000..6de24a9
Binary files /dev/null and b/webapp-backend/src/main/resources/keystore.jks differ
index 08c1c74..f462096 100644 (file)
@@ -27,13 +27,11 @@ import java.util.Map;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.oransc.portal.nonrtric.controlpanel.config.WebSecurityMockConfiguration;
+import org.oransc.portal.nonrtric.controlpanel.util.AsyncRestClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
-import org.springframework.boot.test.web.client.TestRestTemplate;
 import org.springframework.boot.web.server.LocalServerPort;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -52,22 +50,19 @@ public class AbstractControllerTest {
     @LocalServerPort
     private int localServerPort;
 
-    @Autowired
-    protected TestRestTemplate restTemplate;
+    protected final AsyncRestClient webClient = new AsyncRestClient("");
 
     /**
      * Flexible URI builder.
      *
-     * @param queryParams
-     *        Map of string-string query parameters
-     * @param path
-     *        Array of path components. If a component has an
-     *        embedded slash, the string is split and each
-     *        subcomponent is added individually.
+     * @param queryParams Map of string-string query parameters
+     * @param path Array of path components. If a component has an embedded
+     *        slash, the string is split and each subcomponent is added
+     *        individually.
      * @return URI
      */
     protected URI buildUri(final Map<String, String> queryParams, final String... path) {
-        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:" + localServerPort + "/");
+        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://localhost:" + localServerPort + "/");
         for (int p = 0; p < path.length; ++p) {
             if (path[p] == null || path[p].isEmpty()) {
                 throw new IllegalArgumentException("Unexpected null or empty at path index " + Integer.toString(p));
@@ -100,14 +95,4 @@ public class AbstractControllerTest {
         logger.info("Context loads on mock profile");
     }
 
-    public TestRestTemplate testRestTemplateAdminRole() {
-        return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_ADMIN,
-            WebSecurityMockConfiguration.TEST_CRED_ADMIN);
-    }
-
-    public TestRestTemplate testRestTemplateStandardRole() {
-        return restTemplate.withBasicAuth(WebSecurityMockConfiguration.TEST_CRED_STANDARD,
-            WebSecurityMockConfiguration.TEST_CRED_STANDARD);
-    }
-
 }
index a09f693..94e7eeb 100644 (file)
@@ -20,6 +20,8 @@
  */
 package org.oransc.portal.nonrtric.controlpanel.controller;
 
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
 import java.lang.invoke.MethodHandles;
 import java.net.URI;
 
@@ -28,8 +30,7 @@ import org.junit.jupiter.api.Test;
 import org.onap.portalsdk.core.onboarding.util.PortalApiConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
 
 public class PortalRestCentralServiceTest extends AbstractControllerTest {
 
@@ -40,73 +41,23 @@ public class PortalRestCentralServiceTest extends AbstractControllerTest {
         // paths are hardcoded here exactly like the EPSDK-FW library :(
         URI uri = buildUri(null, PortalApiConstants.API_PREFIX, "/analytics");
         logger.info("Invoking {}", uri);
-        ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class);
+        WebClientResponseException e = assertThrows(WebClientResponseException.class, () -> {
+            webClient.getForEntity(uri.toString()).block();
+        });
         // No Portal is available so this always fails
-        Assertions.assertTrue(response.getStatusCode().is4xxClientError());
+        Assertions.assertTrue(e.getStatusCode().is4xxClientError());
     }
 
     @Test
     public void getErrorPageTest() {
         // Send unauthorized request
+
         URI uri = buildUri(null, "/favicon.ico");
         logger.info("Invoking {}", uri);
-        ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class);
-        Assertions.assertTrue(response.getStatusCode().is4xxClientError());
-        Assertions.assertTrue(response.getBody().contains("Static error page"));
+        WebClientResponseException e = assertThrows(WebClientResponseException.class, () -> {
+            webClient.getForEntity(uri.toString()).block();
+        });
+        Assertions.assertTrue(e.getStatusCode().is4xxClientError());
+        Assertions.assertTrue(e.getResponseBodyAsString().contains("Static error page"));
     }
-
-    /*
-     * private HttpEntity<Object> getEntityWithHeaders(Object body) {
-     * HttpHeaders headers = new HttpHeaders();
-     * headers.set(PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY,
-     * PortalApIMockConfiguration.PORTAL_USERNAME_HEADER_KEY);
-     * headers.set(PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY,
-     * PortalApIMockConfiguration.PORTAL_PASSWORD_HEADER_KEY);
-     * HttpEntity<Object> entity = new HttpEntity<>(body, headers);
-     * return entity;
-     * }
-     *
-     * private EcompUser createEcompUser(String loginId) {
-     * EcompUser user = new EcompUser();
-     * user.setLoginId(loginId);
-     * EcompRole role = new EcompRole();
-     * role.setRoleFunctions(Collections.EMPTY_SET);
-     * role.setId(1L);
-     * role.setName(ControlPanelConstants.ROLE_NAME_ADMIN);
-     * Set<EcompRole> roles = new HashSet<>();
-     * roles.add(role);
-     * user.setRoles(roles);
-     * return user;
-     * }
-     *
-     * @Test
-     *
-     * @Test
-     * public void createUserTest() {
-     * final String loginId = "login1";
-     * URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user");
-     * logger.info("Invoking {}", create);
-     * HttpEntity<Object> requestEntity = getEntityWithHeaders(createEcompUser(loginId));
-     * ResponseEntity<String> response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class);
-     * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful());
-     * }
-     *
-     * @Test
-     * public void updateUserTest() {
-     * final String loginId = "login2";
-     * URI create = buildUri(null, PortalApiConstants.API_PREFIX, "user");
-     * EcompUser user = createEcompUser(loginId);
-     * logger.info("Invoking {}", create);
-     * HttpEntity<Object> requestEntity = getEntityWithHeaders(user);
-     * // Create
-     * ResponseEntity<String> response = restTemplate.exchange(create, HttpMethod.POST, requestEntity, String.class);
-     * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful());
-     * URI update = buildUri(null, PortalApiConstants.API_PREFIX, "user", loginId);
-     * user.setEmail("user@company.org");
-     * requestEntity = getEntityWithHeaders(user);
-     * response = restTemplate.exchange(update, HttpMethod.POST, requestEntity, String.class);
-     * Assertions.assertTrue(response.getStatusCode().is2xxSuccessful());
-     * }
-     */
-
 }
index dcbce40..37fdd8d 100644 (file)
@@ -24,49 +24,30 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
 import com.google.gson.reflect.TypeToken;
 
 import java.lang.reflect.Type;
-import java.util.ArrayList;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.oransc.portal.nonrtric.controlpanel.model.ImmutablePolicyInfo;
 import org.oransc.portal.nonrtric.controlpanel.model.PolicyInfo;
 import org.oransc.portal.nonrtric.controlpanel.model.PolicyInstances;
-import org.oransc.portal.nonrtric.controlpanel.model.PolicyType;
-import org.oransc.portal.nonrtric.controlpanel.model.PolicyTypes;
-import org.oransc.portal.nonrtric.controlpanel.policyagentapi.PolicyAgentApiImpl.RicInfo;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
+import org.oransc.portal.nonrtric.controlpanel.util.AsyncRestClient;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.client.HttpClientErrorException;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.web.client.HttpServerErrorException;
+import reactor.core.publisher.Mono;
 
 public class PolicyAgentApiImplTest {
-    private static final String URL_PREFIX = "UrlPrefix";
     private static final String URL_POLICY_SCHEMAS = "/policy_schemas";
-    private static final String URL_POLICY_INSTANCES = "/policies?type={type}";
-    private static final String URL_POLICY_INSTANCE = "/policy?id={id}";
-    private static final String URL_PUT_POLICY = "/policy?type={type}&id={id}&ric={ric}&service={service}";
-    private static final String URL_DELETE_POLICY = "/policy?id={id}";
-    private static final String URL_RIC_INFO = "/rics?policyType={typeName}";
     private static final String POLICY_TYPE_1_ID = "type1";
     private static final String POLICY_TYPE_1_VALID = "{\"title\":\"type1\"}";
     private static final String POLICY_TYPE_1_INVALID = "\"title\":\"type1\"}";
@@ -75,8 +56,8 @@ public class PolicyAgentApiImplTest {
     private static final String POLICY_1_VALID = "{\"policyId\":\"policy1\"}";
     private static final String POLICY_1_INVALID = "\"policyId\":\"policy1\"}";
     private static final String RIC_1_ID = "ric1";
-    private static final String RIC_1_INFO_VALID = "{\"name\":\"ric1\",\"policyTypes\":[\"type1\"]}";
-    private static final String RIC_1_INFO_INVALID = "{\"name\":\"ric1\",\"policyTypes\":\"type1\"]}";
+    private static final String RIC_1_INFO_VALID = "{\"ricName\":\"ric1\",\"policyTypes\":[\"type1\"]}";
+    private static final String RIC_1_INFO_INVALID = "{\"ricName\":\"ric1\",\"policyTypes\":\"type1\"]}";
     private static final String CLIENT_ERROR_MESSAGE = "XXXXXXX";
 
     private static com.google.gson.Gson gson = new GsonBuilder() //
@@ -85,67 +66,63 @@ public class PolicyAgentApiImplTest {
 
     PolicyAgentApiImpl apiUnderTest;
 
-    RestTemplate restTemplateMock;
+    AsyncRestClient restClient;
 
     @BeforeEach
     public void init() {
-        restTemplateMock = mock(RestTemplate.class);
-        apiUnderTest = new PolicyAgentApiImpl(URL_PREFIX, restTemplateMock);
+        restClient = mock(AsyncRestClient.class);
+        apiUnderTest = new PolicyAgentApiImpl(restClient);
+    }
+
+    private void whenGetReturnOK(String url, HttpStatus status, String body) {
+        ResponseEntity<String> ret = new ResponseEntity<>(body, status);
+        when(restClient.getForEntity(eq(url))).thenReturn(Mono.just(ret));
+    }
+
+    private void whenGetReturnFailure(String url, HttpStatus status, String body) {
+        HttpServerErrorException e = new HttpServerErrorException(status, body);
+        when(restClient.getForEntity(eq(url))).thenReturn(Mono.error(e));
     }
 
     @Test
     public void testGetAllPolicyTypesFailure() {
-        ResponseEntity<String> getAllPolicyTypesResp = new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_SCHEMAS), eq(String.class)))
-            .thenReturn(getAllPolicyTypesResp);
-
+        whenGetReturnFailure(URL_POLICY_SCHEMAS, HttpStatus.NOT_FOUND, "");
         ResponseEntity<String> returnedResp = apiUnderTest.getAllPolicyTypes();
-
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_SCHEMAS, String.class);
-        assertNull(returnedResp.getBody());
         assertEquals(HttpStatus.NOT_FOUND, returnedResp.getStatusCode());
     }
 
     @Test
     public void testGetAllPolicyTypesSuccessValidJson() {
         String policyTypes = Arrays.asList(POLICY_TYPE_1_VALID, POLICY_TYPE_2_VALID).toString();
-        String policyTypesJson = parsePolicyTypesJson(policyTypes);
-        ResponseEntity<String> getAllPolicyTypesResp = new ResponseEntity<>(policyTypes, HttpStatus.OK);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_SCHEMAS), eq(String.class)))
-            .thenReturn(getAllPolicyTypesResp);
 
-        ResponseEntity<String> returnedResp = apiUnderTest.getAllPolicyTypes();
+        whenGetReturnOK(URL_POLICY_SCHEMAS, HttpStatus.OK, policyTypes);
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_SCHEMAS, String.class);
-        assertEquals(returnedResp.getBody(), policyTypesJson);
-        assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
+        ResponseEntity<String> resp = apiUnderTest.getAllPolicyTypes();
+        assertTrue(resp.getBody().contains("\"name\":\"type1\""));
+        assertEquals(HttpStatus.OK, resp.getStatusCode());
     }
 
     @Test
     public void testGetAllPolicyTypesSuccessInvalidJson() {
         String policyTypes = Arrays.asList(POLICY_TYPE_1_INVALID, POLICY_TYPE_2_VALID).toString();
-        ResponseEntity<String> getAllPolicyTypesResp = new ResponseEntity<>(policyTypes, HttpStatus.OK);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_SCHEMAS), eq(String.class)))
-            .thenReturn(getAllPolicyTypesResp);
+        whenGetReturnOK(URL_POLICY_SCHEMAS, HttpStatus.OK, policyTypes);
 
         ResponseEntity<String> returnedResp = apiUnderTest.getAllPolicyTypes();
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_SCHEMAS, String.class);
         assertTrue(returnedResp.getBody().contains("Exception"));
         assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, returnedResp.getStatusCode());
     }
 
+    private String urlPolicyInstances(String type) {
+        return "/policies?type=" + type;
+    }
+
     @Test
     public void testGetPolicyInstancesForTypeFailure() {
-        ResponseEntity<String> getPolicyInstancesForTypeResp = new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
-        Map<String, ?> uriVariables = Map.of("type", POLICY_TYPE_1_ID);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_INSTANCES), eq(String.class), eq(uriVariables)))
-            .thenReturn(getPolicyInstancesForTypeResp);
+        whenGetReturnFailure(urlPolicyInstances(POLICY_TYPE_1_ID), HttpStatus.NOT_FOUND, "");
 
         ResponseEntity<String> returnedResp = apiUnderTest.getPolicyInstancesForType(POLICY_TYPE_1_ID);
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_INSTANCES, String.class, uriVariables);
-        assertNull(returnedResp.getBody());
         assertEquals(HttpStatus.NOT_FOUND, returnedResp.getStatusCode());
     }
 
@@ -153,14 +130,11 @@ public class PolicyAgentApiImplTest {
     public void testGetPolicyInstancesForTypeSuccessValidJson() {
         String policyInstances = Arrays.asList(POLICY_1_VALID).toString();
         String policyInstancesJson = parsePolicyInstancesJson(policyInstances);
-        ResponseEntity<String> getPolicyInstancesForTypeResp = new ResponseEntity<>(policyInstances, HttpStatus.OK);
-        Map<String, ?> uriVariables = Map.of("type", POLICY_TYPE_1_ID);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_INSTANCES), eq(String.class), eq(uriVariables)))
-            .thenReturn(getPolicyInstancesForTypeResp);
+
+        whenGetReturnOK(urlPolicyInstances(POLICY_TYPE_1_ID), HttpStatus.OK, policyInstances);
 
         ResponseEntity<String> returnedResp = apiUnderTest.getPolicyInstancesForType(POLICY_TYPE_1_ID);
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_INSTANCES, String.class, uriVariables);
         assertEquals(returnedResp.getBody(), policyInstancesJson);
         assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
     }
@@ -168,132 +142,130 @@ public class PolicyAgentApiImplTest {
     @Test
     public void testGetPolicyInstancesForTypeSuccessInvalidJson() {
         String policyInstances = Arrays.asList(POLICY_1_INVALID).toString();
-        ResponseEntity<String> getPolicyInstancesForTypeResp = new ResponseEntity<>(policyInstances, HttpStatus.OK);
-        Map<String, ?> uriVariables = Map.of("type", POLICY_TYPE_1_ID);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_INSTANCES), eq(String.class), eq(uriVariables)))
-            .thenReturn(getPolicyInstancesForTypeResp);
+
+        whenGetReturnOK(urlPolicyInstances(POLICY_TYPE_1_ID), HttpStatus.OK, policyInstances);
 
         ResponseEntity<String> returnedResp = apiUnderTest.getPolicyInstancesForType(POLICY_TYPE_1_ID);
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_INSTANCES, String.class, uriVariables);
         assertTrue(returnedResp.getBody().contains("Exception"));
         assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, returnedResp.getStatusCode());
     }
 
+    private String urlPolicyInstance(String id) {
+        return "/policy?id=" + id;
+    }
+
     @Test
     public void testGetPolicyInstance() {
-        ResponseEntity<Object> getPolicyInstanceResp = new ResponseEntity<>(POLICY_1_VALID, HttpStatus.OK);
-        Map<String, ?> uriVariables = Map.of("id", POLICY_1_ID);
-        when(restTemplateMock.getForEntity(eq(URL_PREFIX + URL_POLICY_INSTANCE), eq(Object.class), eq(uriVariables)))
-            .thenReturn(getPolicyInstanceResp);
+        whenGetReturnOK(urlPolicyInstance(POLICY_1_ID), HttpStatus.OK, POLICY_1_VALID);
 
         ResponseEntity<Object> returnedResp = apiUnderTest.getPolicyInstance(POLICY_1_ID);
 
-        verify(restTemplateMock).getForEntity(URL_PREFIX + URL_POLICY_INSTANCE, Object.class, uriVariables);
-        assertEquals(POLICY_1_VALID, returnedResp.getBody());
         assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
+        assertEquals(POLICY_1_VALID, returnedResp.getBody());
+
+    }
+
+    private String urlPutPolicy(String type, String id, String ric) {
+        return "/policy?type=" + type + "&id=" + id + "&ric=" + ric + "&service=controlpanel";
+    }
+
+    private void whenPutReturnOK(String url, String putBody, HttpStatus status, String body) {
+        ResponseEntity<String> ret = new ResponseEntity<>(body, status);
+        when(restClient.putForEntity(eq(url), eq(putBody))).thenReturn(Mono.just(ret));
+    }
+
+    private void whenPutReturnFailure(String url, String putBody, HttpStatus status, String body) {
+        HttpServerErrorException e =
+            new HttpServerErrorException(status, body, body.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+        when(restClient.putForEntity(eq(url), eq(putBody))).thenReturn(Mono.error(e));
     }
 
     @Test
     public void testPutPolicyFailure() {
-        HttpEntity<Object> jsonHttpEntity = createJsonHttpEntity(POLICY_1_VALID);
-        Map<String, ?> uriVariables = Map.of( //
-            "type", POLICY_TYPE_1_ID, //
-            "id", POLICY_1_ID, //
-            "ric", RIC_1_ID, //
-            "service", "controlpanel");
-        HttpClientErrorException exception = new HttpClientErrorException(HttpStatus.NOT_FOUND, CLIENT_ERROR_MESSAGE);
-        doThrow(exception).when(restTemplateMock).put(eq(URL_PREFIX + URL_PUT_POLICY), eq(jsonHttpEntity),
-            eq(uriVariables));
+        String url = urlPutPolicy(POLICY_TYPE_1_ID, POLICY_1_ID, RIC_1_ID);
+        whenPutReturnFailure(url, POLICY_1_VALID, HttpStatus.NOT_FOUND, CLIENT_ERROR_MESSAGE);
 
         ResponseEntity<String> returnedResp =
             apiUnderTest.putPolicy(POLICY_TYPE_1_ID, POLICY_1_ID, POLICY_1_VALID, RIC_1_ID);
 
-        verify(restTemplateMock).put(URL_PREFIX + URL_PUT_POLICY, jsonHttpEntity, uriVariables);
         assertTrue(returnedResp.getBody().contains(CLIENT_ERROR_MESSAGE));
         assertEquals(HttpStatus.NOT_FOUND, returnedResp.getStatusCode());
     }
 
     @Test
     public void testPutPolicySuccess() {
-        HttpEntity<Object> jsonHttpEntity = createJsonHttpEntity(POLICY_1_VALID);
-        Map<String, ?> uriVariables = Map.of( //
-            "type", POLICY_TYPE_1_ID, //
-            "id", POLICY_1_ID, //
-            "ric", RIC_1_ID, //
-            "service", "controlpanel");
+        String url = urlPutPolicy(POLICY_TYPE_1_ID, POLICY_1_ID, RIC_1_ID);
+        whenPutReturnOK(url, POLICY_1_VALID, HttpStatus.OK, POLICY_1_VALID);
 
         ResponseEntity<String> returnedResp =
             apiUnderTest.putPolicy(POLICY_TYPE_1_ID, POLICY_1_ID, POLICY_1_VALID, RIC_1_ID);
 
-        verify(restTemplateMock).put(URL_PREFIX + URL_PUT_POLICY, jsonHttpEntity, uriVariables);
         assertNull(returnedResp.getBody());
         assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
     }
 
+    private void whenDeleteReturnOK(String url, HttpStatus status) {
+        ResponseEntity<String> ret = new ResponseEntity<>(status);
+        when(restClient.deleteForEntity(eq(url))).thenReturn(Mono.just(ret));
+    }
+
+    private void whenDeleteReturnFailure(String url, HttpStatus status, String body) {
+        HttpServerErrorException e =
+            new HttpServerErrorException(status, body, body.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+        when(restClient.deleteForEntity(eq(url))).thenReturn(Mono.error(e));
+    }
+
+    private String deletePolicyUrl(String id) {
+        return "/policy?id=" + id;
+    }
+
     @Test
     public void testDeletePolicyFailure() {
-        Map<String, ?> uriVariables = Map.of("id", POLICY_1_ID);
-        HttpClientErrorException exception = new HttpClientErrorException(HttpStatus.NOT_FOUND, CLIENT_ERROR_MESSAGE);
-        doThrow(exception).when(restTemplateMock).delete(eq(URL_PREFIX + URL_DELETE_POLICY), eq(uriVariables));
+        whenDeleteReturnFailure(deletePolicyUrl(POLICY_1_ID), HttpStatus.NOT_FOUND, CLIENT_ERROR_MESSAGE);
 
         ResponseEntity<String> returnedResp = apiUnderTest.deletePolicy(POLICY_1_ID);
 
-        verify(restTemplateMock).delete(URL_PREFIX + URL_DELETE_POLICY, uriVariables);
         assertTrue(returnedResp.getBody().contains(CLIENT_ERROR_MESSAGE));
         assertEquals(HttpStatus.NOT_FOUND, returnedResp.getStatusCode());
     }
 
     @Test
     public void testDeletePolicySuccess() {
-        Map<String, ?> uriVariables = Map.of("id", POLICY_1_ID);
-
+        whenDeleteReturnOK(deletePolicyUrl(POLICY_1_ID), HttpStatus.OK);
         ResponseEntity<String> returnedResp = apiUnderTest.deletePolicy(POLICY_1_ID);
 
-        verify(restTemplateMock).delete(URL_PREFIX + URL_DELETE_POLICY, uriVariables);
-        assertNull(returnedResp.getBody());
         assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
     }
 
+    private String urlRicInfo(String typeName) {
+        return "/rics?policyType=" + typeName;
+    }
+
     @Test
     public void testGetRicsSupportingTypeValidJson() {
         String rics = Arrays.asList(RIC_1_INFO_VALID).toString();
-        String ricsJson = parseRicsJson(rics);
-        Map<String, ?> uriVariables = Map.of("typeName", POLICY_TYPE_1_ID);
-        when(restTemplateMock.getForObject(eq(URL_PREFIX + URL_RIC_INFO), eq(String.class), eq(uriVariables)))
-            .thenReturn(rics);
 
-        ResponseEntity<String> returnedResp = apiUnderTest.getRicsSupportingType(POLICY_TYPE_1_ID);
+        this.whenGetReturnOK(urlRicInfo(POLICY_TYPE_1_ID), HttpStatus.OK, rics);
 
-        verify(restTemplateMock).getForObject(URL_PREFIX + URL_RIC_INFO, String.class, uriVariables);
-        assertEquals(returnedResp.getBody(), ricsJson);
-        assertEquals(HttpStatus.OK, returnedResp.getStatusCode());
+        ResponseEntity<String> resp = apiUnderTest.getRicsSupportingType(POLICY_TYPE_1_ID);
+
+        assertEquals(HttpStatus.OK, resp.getStatusCode());
+        assertEquals("[\"ric1\"]", resp.getBody());
     }
 
     @Test
     public void testGetRicsSupportingTypeInvalidJson() {
         String rics = Arrays.asList(RIC_1_INFO_INVALID).toString();
-        Map<String, ?> uriVariables = Map.of("typeName", POLICY_TYPE_1_ID);
-        when(restTemplateMock.getForObject(eq(URL_PREFIX + URL_RIC_INFO), eq(String.class), eq(uriVariables)))
-            .thenReturn(rics);
+
+        this.whenGetReturnOK(urlRicInfo(POLICY_TYPE_1_ID), HttpStatus.OK, rics);
 
         ResponseEntity<String> returnedResp = apiUnderTest.getRicsSupportingType(POLICY_TYPE_1_ID);
 
-        verify(restTemplateMock).getForObject(URL_PREFIX + URL_RIC_INFO, String.class, uriVariables);
         assertTrue(returnedResp.getBody().contains("Exception"));
         assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, returnedResp.getStatusCode());
     }
 
-    private String parsePolicyTypesJson(String inputString) {
-        PolicyTypes policyTypes = new PolicyTypes();
-        JsonArray schemas = JsonParser.parseString(inputString).getAsJsonArray();
-        for (JsonElement schema : schemas) {
-            JsonObject schemaObj = schema.getAsJsonObject();
-            policyTypes.add(new PolicyType(schemaObj.get("title").getAsString(), schemaObj.toString()));
-        }
-        return gson.toJson(policyTypes);
-    }
-
     private String parsePolicyInstancesJson(String inputString) {
         Type listType = new TypeToken<List<ImmutablePolicyInfo>>() {}.getType();
         List<PolicyInfo> rspParsed = gson.fromJson(inputString, listType);
@@ -303,20 +275,4 @@ public class PolicyAgentApiImplTest {
         }
         return gson.toJson(policyInstances);
     }
-
-    private String parseRicsJson(String inputString) {
-        Type listType = new TypeToken<List<ImmutableRicInfo>>() {}.getType();
-        List<RicInfo> rspParsed = gson.fromJson(inputString, listType);
-        Collection<String> rics = new ArrayList<>(rspParsed.size());
-        for (RicInfo ric : rspParsed) {
-            rics.add(ric.ricName());
-        }
-        return gson.toJson(rics);
-    }
-
-    private HttpEntity<Object> createJsonHttpEntity(Object content) {
-        HttpHeaders headers = new HttpHeaders();
-        headers.setContentType(MediaType.APPLICATION_JSON);
-        return new HttpEntity<>(content, headers);
-    }
 }
diff --git a/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClientTest.java b/webapp-backend/src/test/java/org/oransc/portal/nonrtric/controlpanel/util/AsyncRestClientTest.java
new file mode 100644 (file)
index 0000000..8135ad3
--- /dev/null
@@ -0,0 +1,122 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+
+package org.oransc.portal.nonrtric.controlpanel.util;
+
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.JdkLoggerFactory;
+
+import java.io.IOException;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+import reactor.util.Loggers;
+
+public class AsyncRestClientTest {
+    private static final String BASE_URL = "BaseUrl";
+    private static final String REQUEST_URL = "/test";
+    private static final String TEST_JSON = "{\"type\":\"type1\"}";
+    private static final int SUCCESS_CODE = 200;
+    private static final int ERROR_CODE = 500;
+
+    private static MockWebServer mockWebServer;
+
+    private static AsyncRestClient clientUnderTest;
+
+    @BeforeAll
+    public static void init() {
+        // skip a lot of unnecessary logs from MockWebServer
+        InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE);
+        Loggers.useJdkLoggers();
+        mockWebServer = new MockWebServer();
+        clientUnderTest = new AsyncRestClient(mockWebServer.url(BASE_URL).toString());
+    }
+
+    @AfterAll
+    public static void tearDown() throws IOException {
+        mockWebServer.shutdown();
+    }
+
+    @Test
+    public void testGetNoError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(SUCCESS_CODE) //
+            .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) //
+            .setBody(TEST_JSON));
+
+        Mono<String> returnedMono = clientUnderTest.get(REQUEST_URL);
+        StepVerifier.create(returnedMono).expectNext(TEST_JSON).expectComplete().verify();
+    }
+
+    @Test
+    public void testGetError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
+
+        Mono<String> returnedMono = clientUnderTest.get(REQUEST_URL);
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
+    }
+
+    @Test
+    public void testPutNoError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(SUCCESS_CODE) //
+            .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) //
+            .setBody(TEST_JSON));
+
+        Mono<String> returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON);
+        StepVerifier.create(returnedMono).expectNext(TEST_JSON).expectComplete().verify();
+    }
+
+    @Test
+    public void testPutError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
+
+        Mono<String> returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON);
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
+    }
+
+    @Test
+    public void testDeleteNoError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(SUCCESS_CODE));
+
+        Mono<String> returnedMono = clientUnderTest.delete(REQUEST_URL);
+        StepVerifier.create(returnedMono).expectNext("").expectComplete().verify();
+    }
+
+    @Test
+    public void testDeleteError() {
+        mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
+
+        Mono<String> returnedMono = clientUnderTest.delete(REQUEST_URL);
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
+    }
+
+}