Merge "Add sdnc-a1-controller in build process"
authorHenrik Andersson <henrik.b.andersson@est.tech>
Thu, 12 Mar 2020 15:03:23 +0000 (15:03 +0000)
committerGerrit Code Review <gerrit@o-ran-sc.org>
Thu, 12 Mar 2020 15:03:23 +0000 (15:03 +0000)
55 files changed:
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardApplication.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/DashboardUserManager.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/config/SpringContextCache.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/CustomResponseEntityExceptionHandler.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/controller/SimpleErrorController.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/model/EcompUserDetails.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApi.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManager.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthenticationFilter.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalRestCentralServiceImpl.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/portalapi/PortalSdkDecryptorAes.java
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/util/HttpsURLConnectionUtils.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardTestServer.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java [new file with mode: 0644]
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java [new file with mode: 0644]
near-rt-ric-simulator/auto-test/Dockerize_test.sh
near-rt-ric-simulator/common/test_env.sh
near-rt-ric-simulator/common/testcase_common.sh
near-rt-ric-simulator/ric-plt/a1/main.py
near-rt-ric-simulator/simulator-group/consul_cbs/config.json
near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml [new file with mode: 0644]
near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml [new file with mode: 0644]
policy-agent/config/application.yaml
policy-agent/docs/api.yaml
policy-agent/src/main/java/org/oransc/policyagent/SwaggerConfig.java
policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientFactory.java
policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java
policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOnapA1Client.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicRepositoryController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceController.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceStatus.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/StatusController.java
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageConsumer.java
policy-agent/src/main/java/org/oransc/policyagent/dmaap/DmaapMessageHandler.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Service.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Services.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java [moved from policy-agent/src/main/java/org/oransc/policyagent/tasks/RepositorySupervision.java with 92% similarity]
policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSynchronizationTask.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java
policy-agent/src/test/java/org/oransc/policyagent/ApplicationTest.java
policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java
policy-agent/src/test/java/org/oransc/policyagent/clients/A1ClientFactoryTest.java
policy-agent/src/test/java/org/oransc/policyagent/clients/AsyncRestClientTest.java
policy-agent/src/test/java/org/oransc/policyagent/configuration/ApplicationConfigTest.java
policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageConsumerTest.java
policy-agent/src/test/java/org/oransc/policyagent/dmaap/DmaapMessageHandlerTest.java
policy-agent/src/test/java/org/oransc/policyagent/repository/LockTest.java
policy-agent/src/test/java/org/oransc/policyagent/tasks/RefreshConfigTaskTest.java
policy-agent/src/test/java/org/oransc/policyagent/tasks/RicSupervisionTest.java [moved from policy-agent/src/test/java/org/oransc/policyagent/tasks/RepositorySupervisionTest.java with 88% similarity]
policy-agent/src/test/java/org/oransc/policyagent/tasks/ServiceSupervisionTest.java

index b492ebd..7602c06 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index ee5f3e1..0de3ae2 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index aaf4665..3887e53 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index 52a51fb..b113d64 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
@@ -69,7 +70,8 @@ public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptio
     @ExceptionHandler({RestClientResponseException.class})
     public final ResponseEntity<ErrorTransport> handleProxyMethodException(Exception ex, WebRequest request) {
         // Capture the full stack trace in the log.
-        log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false), ex);
+        log.error("handleProxyMethodException: request {}, exception {}", request.getDescription(false),
+            ex.getMessage());
         if (ex instanceof HttpStatusCodeException) {
             HttpStatusCodeException hsce = (HttpStatusCodeException) ex;
             return new ResponseEntity<>(new ErrorTransport(hsce.getRawStatusCode(), hsce.getResponseBodyAsString(),
index 78f5ca9..23af786 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index bfeb7d1..f58dae8 100644 (file)
@@ -3,13 +3,14 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
@@ -46,6 +47,7 @@ public class EcompUserDetails implements UserDetails {
      * Gets a list of authorities (roles) for this user. To keep Spring happy, every
      * item has prefix ROLE_.
      */
+    @Override
     public Collection<? extends GrantedAuthority> getAuthorities() {
         List<GrantedAuthority> roleList = new ArrayList<>();
         Iterator<EcompRole> roleIter = ecompUser.getRoles().iterator();
@@ -58,26 +60,32 @@ public class EcompUserDetails implements UserDetails {
         return roleList;
     }
 
+    @Override
     public String getPassword() {
         return null;
     }
 
+    @Override
     public String getUsername() {
         return ecompUser.getLoginId();
     }
 
+    @Override
     public boolean isAccountNonExpired() {
         return true;
     }
 
+    @Override
     public boolean isAccountNonLocked() {
         return true;
     }
 
+    @Override
     public boolean isCredentialsNonExpired() {
         return true;
     }
 
+    @Override
     public boolean isEnabled() {
         return ecompUser.isActive();
     }
index 7855464..25144cd 100644 (file)
@@ -2,7 +2,7 @@
  * ========================LICENSE_START=================================
  * O-RAN-SC
  * %%
- * Copyright (C) 2019 AT&T Intellectual Property
+ * 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.
index b41c30e..a2d8c3e 100644 (file)
@@ -2,7 +2,7 @@
  * ========================LICENSE_START=================================
  * O-RAN-SC
  * %%
- * Copyright (C) 2019 AT&T Intellectual Property
+ * 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.
index a94332b..d1e7adf 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index fee668f..5c1f75c 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index 02a27d7..b8f3e89 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index 4cbcf38..09b4dfe 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
index b86bbfe..a4fbcea 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
@@ -41,13 +42,13 @@ public final class HttpsURLConnectionUtils {
 
     private static final HostnameVerifier jvmHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
 
-    private static final HostnameVerifier trivialHostnameVerifier = (hostname, sslSession) -> true;
+    private static final HostnameVerifier trivialHostnameVerifier =
+        (hostname, sslSession) -> hostname.equalsIgnoreCase(sslSession.getPeerHost());
 
     private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[] {new X509TrustManager() {
-        @SuppressWarnings("squid:S1168") // Must return null to get wanted behaviour.
         @Override
         public java.security.cert.X509Certificate[] getAcceptedIssuers() {
-            return null;
+            return new java.security.cert.X509Certificate[0];
         }
 
         @Override
@@ -64,7 +65,7 @@ public final class HttpsURLConnectionUtils {
     public static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
         HttpsURLConnection.setDefaultHostnameVerifier(trivialHostnameVerifier);
         // Install the all-trusting trust manager
-        SSLContext sc = SSLContext.getInstance("SSL");
+        SSLContext sc = SSLContext.getInstance("TLS");
         sc.init(null, UNQUESTIONING_TRUST_MANAGER, null);
         HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
     }
@@ -72,7 +73,7 @@ public final class HttpsURLConnectionUtils {
     public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException {
         HttpsURLConnection.setDefaultHostnameVerifier(jvmHostnameVerifier);
         // Return it to the initial state (discovered by reflection, now hardcoded)
-        SSLContext sc = SSLContext.getInstance("SSL");
+        SSLContext sc = SSLContext.getInstance("TLS");
         sc.init(null, null, null);
         HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
     }
index 9ed3869..80a272f 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 AT&T Intellectual Property
+ * Modifications 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.
diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/DashboardUserManagerTest.java
new file mode 100644 (file)
index 0000000..466e579
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property
+ * %%
+ * 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.ric.portal.dashboard;
+
+import java.lang.invoke.MethodHandles;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.onap.portalsdk.core.onboarding.exception.PortalAPIException;
+import org.onap.portalsdk.core.restful.domain.EcompRole;
+import org.onap.portalsdk.core.restful.domain.EcompUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ActiveProfiles;
+
+@ActiveProfiles("test")
+public class DashboardUserManagerTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    public static EcompUser createEcompUser(String loginId) {
+        EcompUser user = new EcompUser();
+        user.setActive(true);
+        user.setLoginId(loginId);
+        user.setFirstName("First");
+        user.setLastName("Last");
+        EcompRole role = new EcompRole();
+        role.setId(1L);
+        role.setName(DashboardConstants.ROLE_NAME_ADMIN);
+        Set<EcompRole> roles = new HashSet<>();
+        roles.add(role);
+        user.setRoles(roles);
+        return user;
+    }
+
+    @Test
+    public void testUserMgr() throws Exception {
+        final String loginId = "demo";
+        DashboardUserManager dum = new DashboardUserManager(true);
+        EcompUser user = createEcompUser(loginId);
+        dum.createUser(user);
+        logger.info("Created user {}", user);
+        try {
+            dum.createUser(user);
+            throw new Exception("Unexpected success");
+        } catch (PortalAPIException ex) {
+            logger.info("caught expected exception: {}", ex.toString());
+        }
+        Assert.assertFalse(dum.getUsers().isEmpty());
+        EcompUser fetched = dum.getUser(loginId);
+        Assert.assertEquals(fetched, user);
+        fetched.setLastName("Lastier");
+        dum.updateUser(loginId, fetched);
+        EcompUser missing = dum.getUser("foo");
+        Assert.assertNull(missing);
+        EcompUser unk = createEcompUser("unknown");
+        try {
+            dum.updateUser("unk", unk);
+        } catch (PortalAPIException ex) {
+            logger.info("caught expected exception: {}", ex.toString());
+        }
+    }
+
+}
index 9c6d40c..836463d 100644 (file)
@@ -3,6 +3,7 @@
  * O-RAN-SC
  * %%
  * Copyright (C) 2019 Nordix Foundation
+ * Modifications 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.
diff --git a/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java b/dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/portalapi/PortalAuthManagerTest.java
new file mode 100644 (file)
index 0000000..050fbf0
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 AT&T Intellectual Property
+ * %%
+ * 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.ric.portal.dashboard.portalapi;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.onap.portalsdk.core.onboarding.util.PortalApiConstants;
+import org.oransc.ric.portal.dashboard.DashboardUserManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class PortalAuthManagerTest {
+
+    @Value("${portalapi.decryptor}")
+    private String decryptor;
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    @Test
+    public void testPortalStuff() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
+        InvocationTargetException, NoSuchMethodException, IOException, ServletException {
+
+        PortalAuthManager m = new PortalAuthManager("app", "user", "secret", decryptor, "cookie");
+        Assert.assertNotNull(m.getAppCredentials());
+        String s = null;
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        s = m.validateEcompSso(request);
+        logger.debug("validateEcompSso answers {}", s);
+        Assert.assertNull(s);
+
+        Cookie cookie = new Cookie(PortalApiConstants.EP_SERVICE, "bogus");
+        request.setCookies(cookie);
+        s = m.validateEcompSso(request);
+        logger.debug("validateEcompSso answers {}", s);
+        Assert.assertNull(s);
+
+        DashboardUserManager dum = new DashboardUserManager(true);
+        PortalAuthenticationFilter filter = new PortalAuthenticationFilter(false, m, dum);
+        filter.init(null);
+        filter.destroy();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        try {
+            filter.doFilter(request, response, null);
+        } catch (NullPointerException ex) {
+            logger.debug("chain is null");
+        }
+
+        filter = new PortalAuthenticationFilter(true, m, dum);
+        try {
+            filter.doFilter(request, response, null);
+        } catch (NullPointerException ex) {
+            logger.debug("chain is null");
+        }
+    }
+
+}
index 93cc310..f959ef8 100755 (executable)
@@ -2,7 +2,7 @@
 
 TC_ONELINE_DESCR="dockerirze the test, setup docker container for policy agent, cbs, consul, near realtime ric simulator"
 
-. ../common/testcase_common.sh $1 $2
+. ../common/testcase_common.sh $1 $2 $3
 
 #### TEST BEGIN ####
 
@@ -16,6 +16,10 @@ start_simulators
 
 consul_config_app         "../simulator-group/consul_cbs/config.json"
 
+start_dashboard
+
+start_sdnc
+
 start_policy_agent
 
 check_policy_agent_logs
index b307a69..b50a02d 100755 (executable)
@@ -2,10 +2,14 @@
 
 # Set the images for the Policy agent app to use for the auto tests. Do not add the image tag.
 #
-# Local image and tag, shall point to locally built image (non-nexus path)
+# Local Policy Agent image and tag, shall point to locally built image (non-nexus path)
 export POLICY_AGENT_LOCAL_IMAGE=o-ran-sc/nonrtric-policy-agent
 # Remote image
 export POLICY_AGENT_REMOTE_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-policy-agent
+# SDNC A1 Adapter remote image
+export SDNC_A1_ADAPTER_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-a1-controller:1.7.4
+# Dashboard remote image
+export DASHBOARD_IMAGE=nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1
 
 # Common env var for auto-test.
 
index d9a012e..7d55b15 100755 (executable)
@@ -2,7 +2,7 @@
 
 . ../common/test_env.sh
 
-echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2
+echo "Test case started as: ${BASH_SOURCE[$i+1]} "$1 $2 $3
 echo "Numbers of ric simulator started" $2
 
 # This is a script that contains all the functions needed for auto test
@@ -13,9 +13,10 @@ START_ARG=$1
 IMAGE_TAG="1.0.0-SNAPSHOT"
 IMAGE_TAG_REMOTE="latest"
 RIC_NUMBER=$2
+SDNC=$3
 
-if [ $# -lt 1 ] || [ $# -gt 2 ]; then
-       echo "Expected arg: local  | remote  and numbers of the rics "
+if [ $# -lt 1 ] || [ $# -gt 4 ]; then
+       echo "Expected arg: local  | remote  and numbers of the rics and SDNC "
        exit 1
 elif [ $1 == "local" ]; then
        if [ -z $POLICY_AGENT_LOCAL_IMAGE ]; then
@@ -165,6 +166,42 @@ start_ric_simulator() {
        echo ""
 }
 
+start_dashboard() {
+
+  DOCKER_SIM_NWNAME="nonrtric-docker-net"
+  echo "Creating docker network $DOCKER_SIM_NWNAME, if needed"
+  docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME
+
+  echo "start dashboard"
+  curdir=$PWD
+  cd $SIM_GROUP
+  cd dashboard/
+
+  docker-compose up -d
+
+  cd $curdir
+       echo ""
+}
+
+start_sdnc() {
+
+  if [ $SDNC == "sdnc" ]; then
+    DOCKER_SIM_NWNAME="nonrtric-docker-net"
+    echo "Creating docker network $DOCKER_SIM_NWNAME, if needed"
+    docker network ls| grep $DOCKER_SIM_NWNAME > /dev/null || docker network create $DOCKER_SIM_NWNAME
+
+    echo "start sdnc"
+    curdir=$PWD
+    cd $SIM_GROUP
+    cd sdnc/
+
+    docker-compose up -d a1-controller
+
+    cd $curdir
+         echo ""
+       fi
+}
+
 prepare_consul_config() {
   echo "prepare consul config"
   curdir=$PWD
@@ -193,6 +230,8 @@ start_simulators() {
        echo ""
 }
 
+
+
 clean_containers() {
        echo "Stopping all containers, policy agent app(s) and simulators with name prefix 'policy_agent'"
        docker stop $(docker ps -q --filter name=/policy-agent) &> /dev/null
@@ -202,6 +241,16 @@ clean_containers() {
        docker stop $(docker ps -q --filter name=ric-simulator) &> /dev/null
        echo "Removing all containers, policy agent app and simulators with name prefix 'ric-simulator'"
        docker rm $(docker ps -a -q --filter name=ric-simulator) &> /dev/null
+       echo "Removing all containers, policy agent app and simulators with name prefix 'dashboard'"
+       docker rm $(docker ps -a -q --filter name=dashboard) &> /dev/null
+       echo "Removing all containers, policy agent app and simulators with name prefix 'a1-controller'"
+       docker rm $(docker ps -a -q --filter name=a1-controller) &> /dev/null
+       echo "Removing all containers, policy agent app and simulators with name prefix 'sdnc_db_container'"
+       docker rm $(docker ps -a -q --filter name=sdnc_db_container) &> /dev/null
+       echo "Removing all containers, policy agent app and simulators with name prefix 'cbs'"
+       docker rm $(docker ps -a -q --filter name=polman_cbs) &> /dev/null
+       echo "Removing all containers, policy agent app and simulators with name prefix 'consul'"
+       docker rm $(docker ps -a -q --filter name=polman_consul) &> /dev/null
        echo "Removing unused docker networks with substring 'policy agent' in network name"
        docker network rm $(docker network ls -q --filter name=nonrtric)
        echo ""
index 48b7f2e..a715f59 100644 (file)
@@ -88,6 +88,18 @@ def set_status_with_reason(policyId, enforceStatus, enforceReason):
   policy_status[policyId] = ps
   return("Status updated for policy: " + policyId, 200)
 
+#Metrics function
+
+@app.route('/counter/<string:countername>', methods=['GET'])
+def getCounter(countername):
+    if (countername == "num_instances"):
+        return str(len(policy_instances)),200
+    elif (countername == "num_types"):
+        return str(len(policy_types)),200
+    else:
+        return "Counter name: "+countername+" not found.",404
+
+
 port_number = 8085
 if len(sys.argv) >= 2:
   if isinstance(sys.argv[1], int):
index 432eddf..52acc38 100644 (file)
@@ -1,21 +1,5 @@
 {
     "ric": [
-        {
-            "name": "ric_ric-simulator_6",
-            "baseUrl": "http://ric_ric-simulator_6:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_6",
-                "stockholm_ric_ric-simulator_6"
-            ]
-        },
-        {
-            "name": "ric_ric-simulator_2",
-            "baseUrl": "http://ric_ric-simulator_2:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_2",
-                "stockholm_ric_ric-simulator_2"
-            ]
-        },
         {
             "name": "ric_ric-simulator_3",
             "baseUrl": "http://ric_ric-simulator_3:8085/",
             ]
         },
         {
-            "name": "ric_ric-simulator_7",
-            "baseUrl": "http://ric_ric-simulator_7:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_7",
-                "stockholm_ric_ric-simulator_7"
-            ]
-        },
-        {
-            "name": "ric_ric-simulator_8",
-            "baseUrl": "http://ric_ric-simulator_8:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_8",
-                "stockholm_ric_ric-simulator_8"
-            ]
-        },
-        {
-            "name": "ric_ric-simulator_5",
-            "baseUrl": "http://ric_ric-simulator_5:8085/",
+            "name": "ric_ric-simulator_2",
+            "baseUrl": "http://ric_ric-simulator_2:8085/",
             "managedElementIds": [
-                "kista_ric_ric-simulator_5",
-                "stockholm_ric_ric-simulator_5"
+                "kista_ric_ric-simulator_2",
+                "stockholm_ric_ric-simulator_2"
             ]
         },
         {
                 "kista_ric_ric-simulator_1",
                 "stockholm_ric_ric-simulator_1"
             ]
-        },
-        {
-            "name": "ric_ric-simulator_9",
-            "baseUrl": "http://ric_ric-simulator_9:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_9",
-                "stockholm_ric_ric-simulator_9"
-            ]
-        },
-        {
-            "name": "ric_ric-simulator_4",
-            "baseUrl": "http://ric_ric-simulator_4:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_4",
-                "stockholm_ric_ric-simulator_4"
-            ]
-        },
-        {
-            "name": "ric_ric-simulator_10",
-            "baseUrl": "http://ric_ric-simulator_10:8085/",
-            "managedElementIds": [
-                "kista_ric_ric-simulator_10",
-                "stockholm_ric_ric-simulator_10"
-            ]
         }
     ],
     "streams_publishes": {
         "dmaap_publisher": {
             "type": "message_router",
             "dmaap_info": {
-                "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-WRITE"
+                "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-WRITE"
             }
         }
     },
@@ -93,7 +37,7 @@
         "dmaap_subscriber": {
             "type": "message_router",
             "dmaap_info": {
-                "topic_url": "http://admin:admin@localhost:6845/events/A1-POLICY-AGENT-READ/users/policy-agent"
+                "topic_url": "http://message-router:3905/events/A1-POLICY-AGENT-READ/users/policy-agent"
             }
         }
     }
diff --git a/near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml b/near-rt-ric-simulator/simulator-group/dashboard/docker-compose.yml
new file mode 100644 (file)
index 0000000..ef4ecd3
--- /dev/null
@@ -0,0 +1,15 @@
+networks:
+  nonrtric-docker-net:
+    external:
+      name: nonrtric-docker-net
+services:
+  dashboard:
+    image: nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dashboard:1.0.1
+
+    networks:
+      nonrtric-docker-net: null
+    ports:
+    - 8080:8080
+version: '3.0'
+
+
diff --git a/near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml b/near-rt-ric-simulator/simulator-group/sdnc/docker-compose.yml
new file mode 100644 (file)
index 0000000..c5007c8
--- /dev/null
@@ -0,0 +1,65 @@
+# ==================================================================================
+#   Modifications 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.
+# ==================================================================================
+version: '2.1'
+
+networks:
+  default:
+    driver: bridge
+    name: nonrtric-docker-net
+
+services:
+  db:
+    image: mysql/mysql-server:5.6
+    container_name: sdnc_db_container
+    networks:
+      - default
+    ports:
+      - "3306"
+    environment:
+      - MYSQL_ROOT_PASSWORD=openECOMP1.0
+      - MYSQL_ROOT_HOST=%
+    logging:
+      driver:   "json-file"
+      options:
+        max-size: "30m"
+        max-file: "5"
+
+  a1-controller:
+    image: nexus3.o-ran-sc.org:10002/o-ran-sc/nonrtric-a1-controller:1.7.4
+    depends_on :
+      - db
+    container_name: a1-controller-container
+    networks:
+      - default
+    entrypoint: ["/opt/onap/sdnc/bin/startODL.sh"]
+    ports:
+      - "8282:8181"
+    links:
+      - db:dbhost
+      - db:sdnctldb01
+      - db:sdnctldb02
+    environment:
+      - MYSQL_ROOT_PASSWORD=openECOMP1.0
+      - SDNC_CONFIG_DIR=/opt/onap/sdnc/data/properties
+    dns:
+      - ${DNS_IP_ADDR-10.0.100.1}
+    logging:
+      driver:   "json-file"
+      options:
+        max-size: "30m"
+        max-file: "5"
+    extra_hosts:
+        aaf.osaaf.org: 10.12.6.214
index 90b73a4..9fb3fba 100644 (file)
@@ -15,7 +15,7 @@ logging:
     org.springframework: ERROR
     org.springframework.data: ERROR
     org.springframework.web.reactive.function.client.ExchangeFunctions: ERROR
-    org.oransc.policyagent: WARN
+    org.oransc.policyagent: INFO
   file: /var/log/policy-agent/application.log
 app:
   filepath: /opt/app/policy-agent/config/application_configuration.json
index 1c41d42..a2f1b57 100644 (file)
@@ -1,21 +1,21 @@
 swagger: '2.0'
 info:
-  description: This page lists all the rest apis for Policy server.
+  description: This page lists all the rest apis for the service.
   version: '1.0'
-  title: Policy server
+  title: A1 Policy management service
 host: 'localhost:8081'
 basePath: /
 tags:
-  - name: operation-handler
-    description: Operation Handler
-  - name: policy-controller
+  - name: A1 Policy Management
     description: Policy Controller
-  - name: ric-repository-controller
+  - name: Health check
+    description: Status Controller
+  - name: RIC Repository
     description: Ric Repository Controller
-  - name: service-controller
+  - name: Service registry and supervision
     description: Service Controller
-  - name: status-controller
-    description: Status Controller
+  - name: operation-handler
+    description: Operation Handler
   - name: web-mvc-links-handler
     description: Web Mvc Links Handler
 paths:
@@ -328,7 +328,7 @@ paths:
   /policies:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Query policies
       operationId: getPoliciesUsingGET
       produces:
@@ -361,12 +361,14 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: RIC or type not found
+          schema:
+            type: string
       deprecated: false
   /policy:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Returns a policy configuration
       operationId: getPolicyUsingGET
       produces:
@@ -382,20 +384,16 @@ paths:
           description: Policy found
           schema:
             type: object
-        '204':
-          description: Policy is not found
-          schema:
-            type: string
         '401':
           description: Unauthorized
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: Policy is not found
       deprecated: false
     put:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Put a policy
       operationId: putPolicyUsingPUT
       consumes:
@@ -431,21 +429,33 @@ paths:
           type: string
       responses:
         '200':
-          description: Policy created or updated
+          description: Policy updated
           schema:
-            type: string
+            type: object
         '201':
-          description: Created
+          description: Policy created
+          schema:
+            type: object
         '401':
           description: Unauthorized
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: RIC or policy type is not found
+          schema:
+            type: string
+        '405':
+          description: Change is not allowed
+          schema:
+            type: string
+        '423':
+          description: RIC is locked
+          schema:
+            type: string
       deprecated: false
     delete:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Delete a policy
       operationId: deletePolicyUsingDELETE
       produces:
@@ -469,11 +479,19 @@ paths:
           description: Unauthorized
         '403':
           description: Forbidden
+        '404':
+          description: Policy is not found
+          schema:
+            type: string
+        '423':
+          description: RIC is locked
+          schema:
+            type: string
       deprecated: false
   /policy_schema:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Returns one policy type schema definition
       operationId: getPolicySchemaUsingGET
       produces:
@@ -494,12 +512,14 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: RIC is not found
+          schema:
+            type: string
       deprecated: false
   /policy_schemas:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Returns policy type schema definitions
       operationId: getPolicySchemasUsingGET
       produces:
@@ -522,12 +542,14 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: RIC is not found
+          schema:
+            type: string
       deprecated: false
   /policy_status:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Returns a policy status
       operationId: getPolicyStatusUsingGET
       produces:
@@ -543,21 +565,19 @@ paths:
           description: Policy status
           schema:
             type: object
-        '204':
-          description: Policy is not found
-          schema:
-            type: string
         '401':
           description: Unauthorized
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: Policy is not found
+          schema:
+            type: string
       deprecated: false
   /policy_types:
     get:
       tags:
-        - policy-controller
+        - A1 Policy Management
       summary: Query policy type names
       operationId: getPolicyTypesUsingGET
       produces:
@@ -580,12 +600,14 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: RIC is not found
+          schema:
+            type: string
       deprecated: false
   /ric:
     get:
       tags:
-        - ric-repository-controller
+        - RIC Repository
       summary: Returns the name of a RIC managing one Mananged Element
       operationId: getRicUsingGET
       produces:
@@ -613,7 +635,7 @@ paths:
   /rics:
     get:
       tags:
-        - ric-repository-controller
+        - RIC Repository
       summary: Query NearRT RIC information
       operationId: getRicsUsingGET
       produces:
@@ -636,12 +658,14 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: Policy type is not found
+          schema:
+            type: string
       deprecated: false
   /service:
     put:
       tags:
-        - service-controller
+        - Service registry and supervision
       summary: Register a service
       operationId: putServiceUsingPUT
       consumes:
@@ -662,6 +686,10 @@ paths:
             type: string
         '201':
           description: Created
+        '400':
+          description: Cannot parse the ServiceRegistrationInfo
+          schema:
+            type: string
         '401':
           description: Unauthorized
         '403':
@@ -672,7 +700,7 @@ paths:
   /services:
     get:
       tags:
-        - service-controller
+        - Service registry and supervision
       summary: Returns service information
       operationId: getServicesUsingGET
       produces:
@@ -695,19 +723,21 @@ paths:
         '403':
           description: Forbidden
         '404':
-          description: Not Found
+          description: Service is not found
+          schema:
+            type: string
       deprecated: false
     delete:
       tags:
-        - service-controller
+        - Service registry and supervision
       summary: Delete a service
       operationId: deleteServiceUsingDELETE
       produces:
         - '*/*'
       parameters:
-        - name: serviceName
+        - name: name
           in: query
-          description: serviceName
+          description: name
           required: true
           type: string
       responses:
@@ -716,26 +746,32 @@ paths:
           schema:
             type: string
         '204':
-          description: No Content
+          description: OK
+          schema:
+            type: string
         '401':
           description: Unauthorized
         '403':
           description: Forbidden
+        '404':
+          description: Service not found
+          schema:
+            type: string
       deprecated: false
   /services/keepalive:
     post:
       tags:
-        - service-controller
-      summary: Keep the poilicies alive for a service
+        - Service registry and supervision
+      summary: Keep the policies alive for a service
       operationId: keepAliveServiceUsingPOST
       consumes:
         - application/json
       produces:
         - '*/*'
       parameters:
-        - name: serviceName
+        - name: name
           in: query
-          description: serviceName
+          description: name
           required: true
           type: string
       responses:
@@ -755,7 +791,7 @@ paths:
   /status:
     get:
       tags:
-        - status-controller
+        - Health check
       summary: Returns status and statistics of this service
       operationId: getStatusUsingGET
       produces:
@@ -786,9 +822,9 @@ definitions:
     title: 'Map«string,Link»'
     additionalProperties:
       $ref: '#/definitions/Link'
-  Mono«ResponseEntity«Void»»:
+  Mono«ResponseEntity«object»»:
     type: object
-    title: Mono«ResponseEntity«Void»»
+    title: Mono«ResponseEntity«object»»
   Mono«ResponseEntity«string»»:
     type: object
     title: Mono«ResponseEntity«string»»
@@ -822,17 +858,19 @@ definitions:
         description: O1 identities for managed entities
         items:
           type: string
-      name:
-        type: string
-        description: identity of the ric
       policyTypes:
         type: array
         description: supported policy types
         items:
           type: string
+      ricName:
+        type: string
+        description: identity of the ric
     title: RicInfo
   ServiceRegistrationInfo:
     type: object
+    required:
+      - serviceName
     properties:
       callbackUrl:
         type: string
@@ -848,6 +886,9 @@ definitions:
   ServiceStatus:
     type: object
     properties:
+      callbackUrl:
+        type: string
+        description: callback for notifying of RIC recovery
       keepAliveIntervalSeconds:
         type: integer
         format: int64
index df5e7b4..01065f9 100644 (file)
@@ -36,17 +36,17 @@ import springfox.documentation.spring.web.plugins.Docket;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
 /**
- * Swagger configuration class that uses swagger2 documentation type and scans all the controllers
- * under org.oransc.policyagent.controllers package. To access the swagger gui go to
- * http://ip:port/swagger-ui.html
+ * Swagger configuration class that uses swagger2 documentation type and scans
+ * all the controllers under org.oransc.policyagent.controllers package. To
+ * access the swagger gui go to http://ip:port/swagger-ui.html
  *
  */
 @Configuration
 @EnableSwagger2
 public class SwaggerConfig extends WebMvcConfigurationSupport {
 
-    static final String API_TITLE = "Policy server";
-    static final String DESCRIPTION = "This page lists all the rest apis for Policy server.";
+    static final String API_TITLE = "A1 Policy management service";
+    static final String DESCRIPTION = "This page lists all the rest apis for the service.";
     static final String VERSION = "1.0";
     static final String RESOURCES_PATH = "classpath:/META-INF/resources/";
     static final String WEBJARS_PATH = RESOURCES_PATH + "webjars/";
index d906930..636a69f 100644 (file)
@@ -29,7 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import reactor.core.publisher.Mono;
 
 /**
- * Factory for A1 clients that supports four different protocol versions of the A1 api.
+ * Factory for A1 clients that supports four different protocol versions of the
+ * A1 api.
  */
 public class A1ClientFactory {
 
@@ -46,16 +47,17 @@ public class A1ClientFactory {
      * Creates an A1 client with the correct A1 protocol for the provided Ric.
      *
      * <p>
-     * It detects the protocol version by trial and error, since there is no getVersion method specified in the A1
-     * api yet.
+     * It detects the protocol version by trial and error, since there is no
+     * getVersion method specified in the A1 api yet.
      *
      * <p>
-     * As a side effect it also sets the protocol version in the provided Ric. This means that after the first
-     * successful creation it won't have to try which protocol to use, but can create the client directly.
+     * As a side effect it also sets the protocol version in the provided Ric. This
+     * means that after the first successful creation it won't have to try which
+     * protocol to use, but can create the client directly.
      *
-     * @param ric The Ric to get a client for.
-     * @return a client with the correct protocol, or a ServiceException if none of the protocols are supported by the
-     *         Ric.
+     * @param ric The RIC to get a client for.
+     * @return a client with the correct protocol, or a ServiceException if none of
+     *         the protocols are supported by the Ric.
      */
     public Mono<A1Client> createA1Client(Ric ric) {
         return getProtocolVersion(ric) //
@@ -69,14 +71,14 @@ public class A1ClientFactory {
             return Mono.just(createOscA1Client(ric));
         } else if (version == A1ProtocolType.SDNC_OSC) {
             return Mono.just(createSdncOscA1Client(ric));
-        } else { // A1ProtocolType.SDNR_ONAP
-            return Mono.just(createSdnrOnapA1Client(ric));
+        } else { // A1ProtocolType.SDNC_ONAP
+            return Mono.just(createSdncOnapA1Client(ric));
         }
     }
 
     private Mono<A1Client.A1ProtocolType> getProtocolVersion(Ric ric) {
         if (ric.getProtocolVersion() == A1ProtocolType.UNKNOWN) {
-            return fetchVersion(createSdnrOnapA1Client(ric)) //
+            return fetchVersion(createSdncOnapA1Client(ric)) //
                 .onErrorResume(notUsed -> fetchVersion(createSdncOscA1Client(ric))) //
                 .onErrorResume(notUsed -> fetchVersion(createOscA1Client(ric))) //
                 .onErrorResume(notUsed -> fetchVersion(createStdA1ClientImpl(ric))) //
@@ -101,7 +103,7 @@ public class A1ClientFactory {
             appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword());
     }
 
-    protected A1Client createSdnrOnapA1Client(Ric ric) {
+    protected A1Client createSdncOnapA1Client(Ric ric) {
         return new SdncOnapA1Client(ric.getConfig(), appConfig.getA1ControllerBaseUrl(),
             appConfig.getA1ControllerUsername(), appConfig.getA1ControllerPassword());
     }
index 12c0745..2435ef0 100644 (file)
@@ -24,9 +24,11 @@ import java.lang.invoke.MethodHandles;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.Nullable;
 import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
 import reactor.core.publisher.Mono;
 
 public class AsyncRestClient {
@@ -34,79 +36,84 @@ public class AsyncRestClient {
     private final WebClient client;
     private final String baseUrl;
 
-    public class AsyncRestClientException extends Exception {
-
-        private static final long serialVersionUID = 1L;
-
-        public AsyncRestClientException(String message) {
-            super(message);
-        }
-    }
-
     public AsyncRestClient(String baseUrl) {
         this.client = WebClient.create(baseUrl);
         this.baseUrl = baseUrl;
     }
 
-    public Mono<String> post(String uri, String body) {
+    public Mono<ResponseEntity<String>> postForEntity(String uri, @Nullable String body) {
         logger.debug("POST uri = '{}{}''", baseUrl, uri);
-        return client.post() //
+        Mono<String> bodyProducer = body != null ? Mono.just(body) : Mono.empty();
+        RequestHeadersSpec<?> request = client.post() //
             .uri(uri) //
             .contentType(MediaType.APPLICATION_JSON) //
-            .bodyValue(body) //
-            .retrieve() //
-            .onStatus(HttpStatus::isError,
-                response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
-            .bodyToMono(String.class) //
-            .defaultIfEmpty("");
+            .body(bodyProducer, String.class);
+        return retrieve(request);
+    }
+
+    public Mono<String> post(String uri, @Nullable String body) {
+        return postForEntity(uri, body) //
+            .flatMap(this::toBody);
     }
 
     public Mono<String> postWithAuthHeader(String uri, String body, String username, String password) {
         logger.debug("POST (auth) uri = '{}{}''", baseUrl, uri);
-        return client.post() //
+        RequestHeadersSpec<?> request = client.post() //
             .uri(uri) //
             .headers(headers -> headers.setBasicAuth(username, password)) //
             .contentType(MediaType.APPLICATION_JSON) //
-            .bodyValue(body) //
-            .retrieve() //
-            .onStatus(HttpStatus::isError,
-                response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
-            .bodyToMono(String.class) //
-            .defaultIfEmpty("");
+            .bodyValue(body);
+        return retrieve(request) //
+            .flatMap(this::toBody);
     }
 
-    public Mono<String> put(String uri, String body) {
+    public Mono<ResponseEntity<String>> putForEntity(String uri, String body) {
         logger.debug("PUT uri = '{}{}''", baseUrl, uri);
-        return client.put() //
+        RequestHeadersSpec<?> request = client.put() //
             .uri(uri) //
             .contentType(MediaType.APPLICATION_JSON) //
-            .bodyValue(body) //
-            .retrieve() //
-            .onStatus(HttpStatus::isError,
-                response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
-            .bodyToMono(String.class) //
-            .defaultIfEmpty("");
+            .bodyValue(body);
+        return retrieve(request);
     }
 
-    public Mono<String> get(String uri) {
+    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 client.get() //
-            .uri(uri) //
-            .retrieve() //
-            .onStatus(HttpStatus::isError,
-                response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
-            .bodyToMono(String.class) //
-            .defaultIfEmpty("");
+        RequestHeadersSpec<?> request = client.get().uri(uri);
+        return retrieve(request);
     }
 
-    public Mono<String> delete(String uri) {
+    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 client.delete() //
-            .uri(uri) //
-            .retrieve() //
-            .onStatus(HttpStatus::isError,
-                response -> Mono.error(new AsyncRestClientException(response.statusCode().toString()))) //
-            .bodyToMono(String.class) //
-            .defaultIfEmpty("");
+        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);
+    }
+
+    Mono<String> toBody(ResponseEntity<String> entity) {
+        if (entity.getBody() == null) {
+            return Mono.just("");
+        } else {
+            return Mono.just(entity.getBody());
+        }
+    }
+
 }
index b9d7f35..0568c36 100644 (file)
@@ -45,7 +45,7 @@ public class SdncOnapA1Client implements A1Client {
     public SdncOnapA1Client(RicConfig ricConfig, String baseUrl, String username, String password) {
         this(ricConfig, username, password, new AsyncRestClient(baseUrl + "/restconf/operations"));
         if (logger.isDebugEnabled()) {
-            logger.debug("SdnrOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl);
+            logger.debug("SdncOnapA1Client for ric: {}, a1ControllerBaseUrl: {}", ricConfig.name(), baseUrl);
         }
     }
 
index 3a49ff1..987cd97 100644 (file)
@@ -33,7 +33,6 @@ import java.util.Collection;
 import java.util.List;
 
 import org.oransc.policyagent.clients.A1ClientFactory;
-import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.oransc.policyagent.repository.ImmutablePolicy;
 import org.oransc.policyagent.repository.Lock.LockType;
@@ -43,6 +42,8 @@ import org.oransc.policyagent.repository.PolicyType;
 import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Rics;
+import org.oransc.policyagent.repository.Service;
+import org.oransc.policyagent.repository.Services;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -55,32 +56,30 @@ import org.springframework.web.bind.annotation.RestController;
 import reactor.core.publisher.Mono;
 
 @RestController
-@Api(value = "Policy Management API")
+@Api(tags = "A1 Policy Management")
 public class PolicyController {
 
-    private final Rics rics;
-    private final PolicyTypes policyTypes;
-    private final Policies policies;
-    private final A1ClientFactory a1ClientFactory;
+    @Autowired
+    private Rics rics;
+    @Autowired
+    private PolicyTypes policyTypes;
+    @Autowired
+    private Policies policies;
+    @Autowired
+    private A1ClientFactory a1ClientFactory;
+    @Autowired
+    private Services services;
 
     private static Gson gson = new GsonBuilder() //
         .serializeNulls() //
         .create(); //
 
-    @Autowired
-    PolicyController(ApplicationConfig config, PolicyTypes types, Policies policies, Rics rics,
-        A1ClientFactory a1ClientFactory) {
-        this.policyTypes = types;
-        this.policies = policies;
-        this.rics = rics;
-        this.a1ClientFactory = a1ClientFactory;
-    }
-
     @GetMapping("/policy_schemas")
     @ApiOperation(value = "Returns policy type schema definitions")
     @ApiResponses(
         value = {
-            @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List")})
+            @ApiResponse(code = 200, message = "Policy schemas", response = Object.class, responseContainer = "List"), //
+            @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
     public ResponseEntity<String> getPolicySchemas(@RequestParam(name = "ric", required = false) String ricName) {
         synchronized (this.policyTypes) {
             if (ricName == null) {
@@ -99,7 +98,10 @@ public class PolicyController {
 
     @GetMapping("/policy_schema")
     @ApiOperation(value = "Returns one policy type schema definition")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy schema", response = Object.class)})
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "Policy schema", response = Object.class),
+            @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
     public ResponseEntity<String> getPolicySchema(@RequestParam(name = "id", required = true) String id) {
         try {
             PolicyType type = policyTypes.getType(id);
@@ -112,11 +114,13 @@ public class PolicyController {
     @GetMapping("/policy_types")
     @ApiOperation(value = "Query policy type names")
     @ApiResponses(
-        value = {@ApiResponse(
-            code = 200,
-            message = "Policy type names",
-            response = String.class,
-            responseContainer = "List")})
+        value = {
+            @ApiResponse(
+                code = 200,
+                message = "Policy type names",
+                response = String.class,
+                responseContainer = "List"),
+            @ApiResponse(code = 404, message = "RIC is not found", response = String.class)})
     public ResponseEntity<String> getPolicyTypes(@RequestParam(name = "ric", required = false) String ricName) {
         synchronized (this.policyTypes) {
             if (ricName == null) {
@@ -138,7 +142,7 @@ public class PolicyController {
     @ApiResponses(
         value = { //
             @ApiResponse(code = 200, message = "Policy found", response = Object.class), //
-            @ApiResponse(code = 204, message = "Policy is not found")} //
+            @ApiResponse(code = 404, message = "Policy is not found")} //
     )
     public ResponseEntity<String> getPolicy( //
         @RequestParam(name = "instance", required = true) String instance) {
@@ -146,17 +150,26 @@ public class PolicyController {
             Policy p = policies.getPolicy(instance);
             return new ResponseEntity<>(p.json(), HttpStatus.OK);
         } catch (ServiceException e) {
-            return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
         }
     }
 
     @DeleteMapping("/policy")
     @ApiOperation(value = "Delete a policy", response = Object.class)
-    @ApiResponses(value = {@ApiResponse(code = 204, message = "Policy deleted", response = Object.class)})
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 204, message = "Policy deleted", response = Object.class),
+            @ApiResponse(code = 404, message = "Policy is not found", response = String.class),
+            @ApiResponse(code = 423, message = "RIC is locked", response = String.class)})
     public Mono<ResponseEntity<Object>> deletePolicy( //
         @RequestParam(name = "instance", required = true) String id) {
-        Policy policy = policies.get(id);
-        if (policy != null && policy.ric().getState() == Ric.RicState.IDLE) {
+        Policy policy;
+        try {
+            policy = policies.getPolicy(id);
+            keepServiceAlive(policy.ownerServiceName());
+            if (policy.ric().getState() != Ric.RicState.IDLE) {
+                return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED));
+            }
             Ric ric = policy.ric();
             return ric.getLock().lock(LockType.SHARED) // //
                 .flatMap(lock -> a1ClientFactory.createA1Client(policy.ric())) //
@@ -165,16 +178,20 @@ public class PolicyController {
                 .doOnNext(notUsed -> ric.getLock().unlockBlocking()) //
                 .doOnError(notUsed -> ric.getLock().unlockBlocking()) //
                 .flatMap(notUsed -> Mono.just(new ResponseEntity<>(HttpStatus.NO_CONTENT)));
-        } else if (policy != null) {
-            return Mono.just(new ResponseEntity<>("Busy, recovering", HttpStatus.LOCKED));
-        } else {
+        } catch (ServiceException e) {
             return Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND));
         }
     }
 
     @PutMapping(path = "/policy")
     @ApiOperation(value = "Put a policy", response = String.class)
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "Policy created or updated")})
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 201, message = "Policy created", response = Object.class), //
+            @ApiResponse(code = 200, message = "Policy updated", response = Object.class), //
+            @ApiResponse(code = 423, message = "RIC is locked", response = String.class), //
+            @ApiResponse(code = 404, message = "RIC or policy type is not found", response = String.class), //
+            @ApiResponse(code = 405, message = "Change is not allowed", response = String.class)})
     public Mono<ResponseEntity<Object>> putPolicy( //
         @RequestParam(name = "type", required = true) String typeName, //
         @RequestParam(name = "instance", required = true) String instanceId, //
@@ -183,9 +200,9 @@ public class PolicyController {
         @RequestBody Object jsonBody) {
 
         String jsonString = gson.toJson(jsonBody);
-
         Ric ric = rics.get(ricName);
         PolicyType type = policyTypes.get(typeName);
+        keepServiceAlive(service);
         if (ric != null && type != null && ric.getState() == Ric.RicState.IDLE) {
             Policy policy = ImmutablePolicy.builder() //
                 .id(instanceId) //
@@ -209,8 +226,8 @@ public class PolicyController {
                 .onErrorResume(t -> Mono.just(new ResponseEntity<>(t.getMessage(), HttpStatus.METHOD_NOT_ALLOWED)));
         }
 
-        return ric == null && type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND))
-            : Mono.just(new ResponseEntity<>(HttpStatus.CONFLICT)); // Recovering
+        return ric == null || type == null ? Mono.just(new ResponseEntity<>(HttpStatus.NOT_FOUND))
+            : Mono.just(new ResponseEntity<>(HttpStatus.LOCKED)); // Recovering
     }
 
     private Mono<Object> validateModifiedPolicy(Policy policy) {
@@ -228,12 +245,19 @@ public class PolicyController {
     @ApiOperation(value = "Query policies")
     @ApiResponses(
         value = {
-            @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List")})
+            @ApiResponse(code = 200, message = "Policies", response = PolicyInfo.class, responseContainer = "List"),
+            @ApiResponse(code = 404, message = "RIC or type not found", response = String.class)})
     public ResponseEntity<String> getPolicies( //
         @RequestParam(name = "type", required = false) String type, //
         @RequestParam(name = "ric", required = false) String ric, //
         @RequestParam(name = "service", required = false) String service) //
     {
+        if ((type != null && this.policyTypes.get(type) == null)) {
+            return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
+        }
+        if ((ric != null && this.rics.get(ric) == null)) {
+            return new ResponseEntity<>("RIC not found", HttpStatus.NOT_FOUND);
+        }
         synchronized (policies) {
             Collection<Policy> result = null;
 
@@ -253,7 +277,7 @@ public class PolicyController {
             try {
                 policiesJson = policiesToJson(result);
             } catch (ServiceException e) {
-                return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+                return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
             }
             return new ResponseEntity<>(policiesJson, HttpStatus.OK);
         }
@@ -264,7 +288,7 @@ public class PolicyController {
     @ApiResponses(
         value = { //
             @ApiResponse(code = 200, message = "Policy status", response = Object.class), //
-            @ApiResponse(code = 204, message = "Policy is not found", response = String.class)} //
+            @ApiResponse(code = 404, message = "Policy is not found", response = String.class)} //
     )
     public Mono<ResponseEntity<String>> getPolicyStatus( //
         @RequestParam(name = "instance", required = true) String instance) {
@@ -275,7 +299,14 @@ public class PolicyController {
                 .flatMap(client -> client.getPolicyStatus(policy)) //
                 .flatMap(status -> Mono.just(new ResponseEntity<>(status, HttpStatus.OK)));
         } catch (ServiceException e) {
-            return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT));
+            return Mono.just(new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND));
+        }
+    }
+
+    private void keepServiceAlive(String name) {
+        Service s = this.services.get(name);
+        if (s != null) {
+            s.keepAlive();
         }
     }
 
index f6fc850..a96766d 100644 (file)
@@ -32,7 +32,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
-import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Rics;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -43,20 +43,19 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
-@Api(value = "RIC Management API")
+@Api(tags = "RIC Repository")
 public class RicRepositoryController {
 
     @Autowired
     private Rics rics;
 
+    @Autowired
+    PolicyTypes types;
+
     private static Gson gson = new GsonBuilder() //
         .serializeNulls() //
         .create(); //
 
-    @Autowired
-    RicRepositoryController(ApplicationConfig appConfig) {
-    }
-
     /**
      * Example: http://localhost:8081/rics?managedElementId=kista_1
      */
@@ -80,18 +79,21 @@ public class RicRepositoryController {
     }
 
     /**
-     * @return a Json array of all RIC data
-     *         Example: http://localhost:8081/ric
+     * @return a Json array of all RIC data Example: http://localhost:8081/ric
      */
     @GetMapping("/rics")
     @ApiOperation(value = "Query NearRT RIC information")
     @ApiResponses(
         value = { //
-            @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List") //
-        })
+            @ApiResponse(code = 200, message = "OK", response = RicInfo.class, responseContainer = "List"), //
+            @ApiResponse(code = 404, message = "Policy type is not found", response = String.class)})
     public ResponseEntity<String> getRics(
         @RequestParam(name = "policyType", required = false) String supportingPolicyType) {
 
+        if ((supportingPolicyType != null) && (this.types.get(supportingPolicyType) == null)) {
+            return new ResponseEntity<>("Policy type not found", HttpStatus.NOT_FOUND);
+        }
+
         List<RicInfo> result = new ArrayList<>();
         synchronized (rics) {
             for (Ric ric : rics.getRics()) {
index 464511f..3d36228 100644 (file)
@@ -23,6 +23,7 @@ package org.oransc.policyagent.controllers;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
@@ -49,13 +50,13 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
+@Api(tags = "Service registry and supervision")
 public class ServiceController {
 
     private final Services services;
     private final Policies policies;
 
     private static Gson gson = new GsonBuilder() //
-        .serializeNulls() //
         .create(); //
 
     @Autowired
@@ -67,10 +68,16 @@ public class ServiceController {
     @GetMapping("/services")
     @ApiOperation(value = "Returns service information")
     @ApiResponses(
-        value = {@ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List")})
+        value = { //
+            @ApiResponse(code = 200, message = "OK", response = ServiceStatus.class, responseContainer = "List"), //
+            @ApiResponse(code = 404, message = "Service is not found", response = String.class)})
     public ResponseEntity<String> getServices(//
         @RequestParam(name = "name", required = false) String name) {
 
+        if (name != null && this.services.get(name) == null) {
+            return new ResponseEntity<>("Service not found", HttpStatus.NOT_FOUND);
+        }
+
         Collection<ServiceStatus> servicesStatus = new ArrayList<>();
         synchronized (this.services) {
             for (Service s : this.services.getAll()) {
@@ -85,24 +92,38 @@ public class ServiceController {
     }
 
     private ServiceStatus toServiceStatus(Service s) {
-        return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds());
+        return new ServiceStatus(s.getName(), s.getKeepAliveInterval().toSeconds(), s.timeSinceLastPing().toSeconds(),
+            s.getCallbackUrl());
+    }
+
+    private void validateRegistrationInfo(ServiceRegistrationInfo registrationInfo) throws ServiceException {
+        if (registrationInfo.serviceName.isEmpty()) {
+            throw new ServiceException("Missing mandatory parameter 'serviceName'");
+        }
     }
 
     @ApiOperation(value = "Register a service")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)})
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 200, message = "OK", response = String.class),
+            @ApiResponse(code = 400, message = "Cannot parse the ServiceRegistrationInfo", response = String.class)})
     @PutMapping("/service")
     public ResponseEntity<String> putService(//
         @RequestBody ServiceRegistrationInfo registrationInfo) {
         try {
+            validateRegistrationInfo(registrationInfo);
             this.services.put(toService(registrationInfo));
             return new ResponseEntity<>("OK", HttpStatus.OK);
         } catch (Exception e) {
-            return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
         }
     }
 
     @ApiOperation(value = "Delete a service")
-    @ApiResponses(value = {@ApiResponse(code = 200, message = "OK")})
+    @ApiResponses(
+        value = { //
+            @ApiResponse(code = 204, message = "OK"),
+            @ApiResponse(code = 404, message = "Service not found", response = String.class)})
     @DeleteMapping("/services")
     public ResponseEntity<String> deleteService(//
         @RequestParam(name = "name", required = true) String serviceName) {
@@ -113,21 +134,22 @@ public class ServiceController {
             removePolicies(service);
             return new ResponseEntity<>("OK", HttpStatus.NO_CONTENT);
         } catch (Exception e) {
-            return new ResponseEntity<>(e.getMessage(), HttpStatus.NO_CONTENT);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
         }
     }
 
-    @ApiOperation(value = "Keep the poilicies alive for a service")
+    @ApiOperation(value = "Heartbeat from a serice")
     @ApiResponses(
-        value = {@ApiResponse(code = 200, message = "Policies timeout supervision refreshed"),
+        value = { //
+            @ApiResponse(code = 200, message = "Service supervision timer refreshed, OK"),
             @ApiResponse(code = 404, message = "The service is not found, needs re-registration")})
     @PostMapping("/services/keepalive")
     public ResponseEntity<String> keepAliveService(//
         @RequestParam(name = "name", required = true) String serviceName) {
         try {
-            services.getService(serviceName).ping();
+            services.getService(serviceName).keepAlive();
             return new ResponseEntity<>("OK", HttpStatus.OK);
-        } catch (Exception e) {
+        } catch (ServiceException e) {
             return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
         }
     }
index 907fa1c..e532a36 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.oransc.policyagent.controllers;
 
+import com.google.gson.annotations.SerializedName;
+
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -29,16 +31,23 @@ import org.immutables.gson.Gson;
 @ApiModel(value = "ServiceRegistrationInfo")
 public class ServiceRegistrationInfo {
 
-    @ApiModelProperty(value = "identity of the service")
-    public String serviceName;
+    @ApiModelProperty(value = "identity of the service", required = true, allowEmptyValue = false)
+    @SerializedName(value = "serviceName", alternate = {"name"})
 
-    @ApiModelProperty(
-        value = "keep alive interval for policies owned by the service. 0 means no timeout supervision."
-            + " Polcies that are not refreshed within this time are removed")
-    public long keepAliveIntervalSeconds;
+    public String serviceName = "";
 
-    @ApiModelProperty(value = "callback for notifying of RIC recovery")
-    public String callbackUrl;
+    @ApiModelProperty(
+        value = "keep alive interval for the service. This is a heartbeat supervision of the service, "
+            + "which in regular intevals must invoke a 'keepAlive' REST call. "
+            + "When a service does not invoke this call within the given time, it is considered unavailble. "
+            + "An unavailable service will be automatically deregistered and its policies will be deleted. "
+            + "Value 0 means no timeout supervision.")
+    @SerializedName("keepAliveIntervalSeconds")
+    public long keepAliveIntervalSeconds = 0;
+
+    @ApiModelProperty(value = "callback for notifying of RIC recovery", required = false, allowEmptyValue = true)
+    @SerializedName("callbackUrl")
+    public String callbackUrl = "";
 
     public ServiceRegistrationInfo() {
     }
index fc80834..8f4daac 100644 (file)
@@ -36,12 +36,16 @@ public class ServiceStatus {
     public final long keepAliveIntervalSeconds;
 
     @ApiModelProperty(value = "time since last invocation by the service")
-    public final long timeSincePingSeconds;
+    public final long timeSinceLastActivitySeconds;
 
-    ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds) {
+    @ApiModelProperty(value = "callback for notifying of RIC recovery")
+    public String callbackUrl;
+
+    ServiceStatus(String name, long keepAliveIntervalSeconds, long timeSincePingSeconds, String callbackUrl) {
         this.serviceName = name;
         this.keepAliveIntervalSeconds = keepAliveIntervalSeconds;
-        this.timeSincePingSeconds = timeSincePingSeconds;
+        this.timeSinceLastActivitySeconds = timeSincePingSeconds;
+        this.callbackUrl = callbackUrl;
     }
 
 }
index cacf64d..c55d5be 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.oransc.policyagent.controllers;
 
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
@@ -31,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController;
 import reactor.core.publisher.Mono;
 
 @RestController
+@Api(tags = "Health check")
 public class StatusController {
 
     @GetMapping("/status")
index e72fcf0..cee9327 100644 (file)
@@ -22,7 +22,6 @@ package org.oransc.policyagent.dmaap;
 
 import com.google.common.collect.Iterables;
 
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.time.Duration;
 import java.util.Properties;
@@ -40,12 +39,16 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
+/**
+ * The class fetched incoming requests from DMAAP on regular intervals. Each
+ * received request is proceesed by DmaapMessageHandler.
+ */
 @Component
 public class DmaapMessageConsumer implements Runnable {
 
     private static final Logger logger = LoggerFactory.getLogger(DmaapMessageConsumer.class);
 
-    private final static Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10);
+    private static final Duration TIME_BETWEEN_DMAAP_POLLS = Duration.ofSeconds(10);
 
     private final ApplicationConfig applicationConfig;
 
@@ -60,10 +63,6 @@ public class DmaapMessageConsumer implements Runnable {
         thread.start();
     }
 
-    DmaapMessageConsumer(ApplicationConfig applicationConfig, boolean start) {
-        this.applicationConfig = applicationConfig;
-    }
-
     private boolean isDmaapConfigured() {
         Properties consumerCfg = applicationConfig.getDmaapConsumerConfig();
         Properties producerCfg = applicationConfig.getDmaapPublisherConfig();
@@ -129,7 +128,7 @@ public class DmaapMessageConsumer implements Runnable {
         }
     }
 
-    MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws FileNotFoundException, IOException {
+    MRConsumer getMessageRouterConsumer(Properties dmaapConsumerProperties) throws IOException {
         return MRClientFactory.createConsumer(dmaapConsumerProperties);
     }
 
@@ -141,8 +140,7 @@ public class DmaapMessageConsumer implements Runnable {
         return new AsyncRestClient(agentBaseUrl);
     }
 
-    MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties)
-        throws FileNotFoundException, IOException {
+    MRBatchingPublisher getMessageRouterPublisher(Properties dmaapPublisherProperties) throws IOException {
         return MRClientFactory.createBatchingPublisher(dmaapPublisherProperties);
     }
 }
index dc979de..6d1603c 100644 (file)
@@ -17,7 +17,6 @@
  * limitations under the License.
  * ========================LICENSE_END===================================
  */
-
 package org.oransc.policyagent.dmaap;
 
 import com.google.gson.Gson;
@@ -29,19 +28,25 @@ import java.util.Optional;
 
 import org.onap.dmaap.mr.client.MRBatchingPublisher;
 import org.oransc.policyagent.clients.AsyncRestClient;
+import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation;
 import org.oransc.policyagent.exceptions.ServiceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
 import reactor.core.publisher.Mono;
 
+/**
+ * The class handles incoming requests from DMAAP.
+ * <p>
+ * That means: invoke a REST call towards this services and to send back a
+ * response though DMAAP
+ */
 public class DmaapMessageHandler {
-
     private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandler.class);
-
     private static Gson gson = new GsonBuilder() //
         .create(); //
-
     private final MRBatchingPublisher dmaapClient;
     private final AsyncRestClient agentClient;
 
@@ -60,61 +65,58 @@ public class DmaapMessageHandler {
     Mono<String> createTask(String msg) {
         try {
             DmaapRequestMessage dmaapRequestMessage = gson.fromJson(msg, ImmutableDmaapRequestMessage.class);
-
             return this.invokePolicyAgent(dmaapRequestMessage) //
                 .onErrorResume(t -> handleAgentCallError(t, msg, dmaapRequestMessage)) //
-                .flatMap(response -> sendDmaapResponse(response, dmaapRequestMessage, HttpStatus.OK));
-
+                .flatMap(
+                    response -> sendDmaapResponse(response.getBody(), dmaapRequestMessage, response.getStatusCode()));
         } catch (Exception e) {
             logger.warn("Received unparsable message from DMAAP: {}", msg);
-            return Mono.error(e);
+            return Mono.error(e); // Cannot make any response
         }
     }
 
-    private Mono<String> handleAgentCallError(Throwable t, String origianalMessage,
+    private Mono<ResponseEntity<String>> handleAgentCallError(Throwable t, String originalMessage,
         DmaapRequestMessage dmaapRequestMessage) {
         logger.debug("Agent call failed: {}", t.getMessage());
-        if (t instanceof ServiceException) {
-            String errorMessage = prepareBadOperationErrorMessage(t, origianalMessage);
-            return sendDmaapResponse(errorMessage, dmaapRequestMessage, HttpStatus.NOT_FOUND) //
-                .flatMap(notUsed -> Mono.empty());
-        } else {
-            return sendDmaapResponse(t.toString(), dmaapRequestMessage, HttpStatus.NOT_FOUND) //
-                .flatMap(notUsed -> Mono.empty());
+        HttpStatus status = HttpStatus.NOT_FOUND;
+        String errorMessage = t.getMessage();
+        if (t instanceof WebClientResponseException) {
+            WebClientResponseException exception = (WebClientResponseException) t;
+            status = exception.getStatusCode();
+            errorMessage = exception.getResponseBodyAsString();
+        } else if (t instanceof ServiceException) {
+            status = HttpStatus.BAD_REQUEST;
+            errorMessage = prepareBadOperationErrorMessage(t, originalMessage);
+
         }
+        return sendDmaapResponse(errorMessage, dmaapRequestMessage, status) //
+            .flatMap(notUsed -> Mono.empty());
     }
 
-    private String prepareBadOperationErrorMessage(Throwable t, String origianalMessage) {
-        String badOperation = origianalMessage.substring(origianalMessage.indexOf("operation\":\"") + 12,
-            origianalMessage.indexOf(",\"url\":"));
-        String errorMessage = t.getMessage().replace("null", badOperation);
-        return errorMessage;
+    private String prepareBadOperationErrorMessage(Throwable t, String originalMessage) {
+        String operationParameterStart = "operation\":\"";
+        int indexOfOperationStart = originalMessage.indexOf(operationParameterStart) + operationParameterStart.length();
+        int indexOfOperationEnd = originalMessage.indexOf("\",\"", indexOfOperationStart);
+        String badOperation = originalMessage.substring(indexOfOperationStart, indexOfOperationEnd);
+        return t.getMessage().replace("null", badOperation);
     }
 
-    private Mono<String> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
+    private Mono<ResponseEntity<String>> invokePolicyAgent(DmaapRequestMessage dmaapRequestMessage) {
         DmaapRequestMessage.Operation operation = dmaapRequestMessage.operation();
-        if (operation == null) {
-            return Mono.error(new ServiceException("Not implemented operation: " + operation));
-        }
-        Mono<String> result = null;
         String uri = dmaapRequestMessage.url();
-        switch (operation) {
-            case DELETE:
-                result = agentClient.delete(uri);
-                break;
-            case GET:
-                result = agentClient.get(uri);
-                break;
-            case PUT:
-                result = agentClient.put(uri, payload(dmaapRequestMessage));
-                break;
-            case POST:
-                result = agentClient.post(uri, payload(dmaapRequestMessage));
-                break;
-            default:
-                // Nothing, can never get here.
+
+        if (operation == Operation.DELETE) {
+            return agentClient.deleteForEntity(uri);
+        } else if (operation == Operation.GET) {
+            return agentClient.getForEntity(uri);
+        } else if (operation == Operation.PUT) {
+            return agentClient.putForEntity(uri, payload(dmaapRequestMessage));
+        } else if (operation == Operation.POST) {
+            return agentClient.postForEntity(uri, payload(dmaapRequestMessage));
+        } else {
+            return Mono.error(new ServiceException("Not implemented operation: " + operation));
         }
-        return result;
+
     }
 
     private String payload(DmaapRequestMessage message) {
@@ -129,7 +131,7 @@ public class DmaapMessageHandler {
 
     private Mono<String> sendDmaapResponse(String response, DmaapRequestMessage dmaapRequestMessage,
         HttpStatus status) {
-        return getDmaapResponseMessage(dmaapRequestMessage, response, status) //
+        return createDmaapResponseMessage(dmaapRequestMessage, response, status) //
             .flatMap(this::sendToDmaap) //
             .onErrorResume(this::handleResponseCallError);
     }
@@ -146,11 +148,11 @@ public class DmaapMessageHandler {
     }
 
     private Mono<String> handleResponseCallError(Throwable t) {
-        logger.debug("Failed to send respons to DMaaP: {}", t.getMessage());
+        logger.debug("Failed to send response to DMaaP: {}", t.getMessage());
         return Mono.empty();
     }
 
-    private Mono<String> getDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response,
+    private Mono<String> createDmaapResponseMessage(DmaapRequestMessage dmaapRequestMessage, String response,
         HttpStatus status) {
         DmaapResponseMessage dmaapResponseMessage = ImmutableDmaapResponseMessage.builder() //
             .status(status.toString()) //
@@ -162,7 +164,6 @@ public class DmaapMessageHandler {
             .timestamp(dmaapRequestMessage.timestamp()) //
             .build();
         String str = gson.toJson(dmaapResponseMessage);
-
         return Mono.just(str);
 
     }
index f0863a5..7b2c9bd 100644 (file)
@@ -36,14 +36,14 @@ public class Service {
         this.name = name;
         this.keepAliveInterval = keepAliveInterval;
         this.callbackUrl = callbackUrl;
-        ping();
+        keepAlive();
     }
 
     public synchronized Duration getKeepAliveInterval() {
         return this.keepAliveInterval;
     }
 
-    public synchronized void ping() {
+    public synchronized void keepAlive() {
         this.lastPing = Instant.now();
     }
 
index 568f002..f6c55dc 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.oransc.policyagent.repository;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -50,7 +51,7 @@ public class Services {
     }
 
     public synchronized Iterable<Service> getAll() {
-        return registeredServices.values();
+        return Collections.unmodifiableCollection(registeredServices.values());
     }
 
     public synchronized void remove(String name) {
index 81735d7..de4a771 100644 (file)
@@ -39,6 +39,9 @@ import java.util.ServiceLoader;
 
 import javax.validation.constraints.NotNull;
 
+import lombok.AccessLevel;
+import lombok.Getter;
+
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClient;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsClientFactory;
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.api.CbsRequests;
@@ -78,6 +81,7 @@ public class RefreshConfigTask {
     public Properties systemEnvironment;
 
     final ApplicationConfig appConfig;
+    @Getter(AccessLevel.PROTECTED)
     private Disposable refreshTask = null;
     private boolean isConsulUsed = false;
 
@@ -112,11 +116,10 @@ public class RefreshConfigTask {
     public void stop() {
         if (refreshTask != null) {
             refreshTask.dispose();
-            refreshTask = null;
         }
     }
 
-    Flux<ApplicationConfig> createRefreshTask() {
+    Flux<RicConfigUpdate.Type> createRefreshTask() {
         Flux<JsonObject> loadFromFile = Flux.interval(Duration.ZERO, FILE_CONFIG_REFRESH_INTERVAL) //
             .filter(notUsed -> configFileExists()) //
             .filter(notUsed -> !this.isConsulUsed) //
@@ -137,7 +140,7 @@ public class RefreshConfigTask {
             .flatMap(this::parseConfiguration) //
             .flatMap(this::updateConfig) //
             .doOnNext(this::handleUpdatedRicConfig) //
-            .flatMap(configUpdate -> Flux.just(this.appConfig)) //
+            .flatMap(configUpdate -> Flux.just(configUpdate.getType())) //
             .doOnTerminate(() -> logger.error("Configuration refresh task is terminated"));
     }
 
@@ -194,7 +197,7 @@ public class RefreshConfigTask {
             } else if (event == RicConfigUpdate.Type.CHANGED) {
                 Ric ric = this.rics.get(ricName);
                 if (ric == null) {
-                    // Should not happend,just for robustness
+                    // Should not happen,just for robustness
                     addRic(updatedInfo.getRicConfig());
                 } else {
                     ric.setRicConfig(updatedInfo.getRicConfig());
@@ -47,8 +47,8 @@ import reactor.core.publisher.Mono;
  */
 @Component
 @EnableScheduling
-public class RepositorySupervision {
-    private static final Logger logger = LoggerFactory.getLogger(RepositorySupervision.class);
+public class RicSupervision {
+    private static final Logger logger = LoggerFactory.getLogger(RicSupervision.class);
 
     private final Rics rics;
     private final Policies policies;
@@ -57,7 +57,7 @@ public class RepositorySupervision {
     private final Services services;
 
     @Autowired
-    public RepositorySupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes,
+    public RicSupervision(Rics rics, Policies policies, A1ClientFactory a1ClientFactory, PolicyTypes policyTypes,
         Services services) {
         this.rics = rics;
         this.policies = policies;
@@ -72,7 +72,9 @@ public class RepositorySupervision {
     @Scheduled(fixedRate = 1000 * 60)
     public void checkAllRics() {
         logger.debug("Checking Rics starting");
-        createTask().subscribe(this::onRicChecked, null, this::onComplete);
+        createTask().subscribe(ric -> logger.debug("Ric: {} checked", ric.ric.name()), //
+            null, //
+            () -> logger.debug("Checking Rics completed"));
     }
 
     private Flux<RicData> createTask() {
@@ -163,15 +165,6 @@ public class RepositorySupervision {
         return Mono.error(new Exception("Syncronization started"));
     }
 
-    @SuppressWarnings("squid:S2629")
-    private void onRicChecked(RicData ric) {
-        logger.debug("Ric: {} checked", ric.ric.name());
-    }
-
-    private void onComplete() {
-        logger.debug("Checking Rics completed");
-    }
-
     RicSynchronizationTask createSynchronizationTask() {
         return new RicSynchronizationTask(a1ClientFactory, policyTypes, policies, services);
     }
index 19036ef..0a0ab82 100644 (file)
@@ -72,7 +72,7 @@ public class RicSynchronizationTask {
         this.services = services;
     }
 
-    @SuppressWarnings("squid:S2629")
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
     public void run(Ric ric) {
         logger.debug("Handling ric: {}", ric.getConfig().name());
 
@@ -101,9 +101,9 @@ public class RicSynchronizationTask {
         return Flux.concat(recoverTypes, policiesDeletedInRic, policiesRecreatedInRic);
     }
 
-    @SuppressWarnings("squid:S2629")
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
     private void onSynchronizationComplete(Ric ric) {
-        logger.debug("Synchronization completed for: {}", ric.name());
+        logger.info("Synchronization completed for: {}", ric.name());
         ric.setState(RicState.IDLE);
         notifyAllServices("Synchronization completed for:" + ric.name());
     }
@@ -124,7 +124,7 @@ public class RicSynchronizationTask {
         }
     }
 
-    @SuppressWarnings("squid:S2629")
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
     private void onSynchronizationError(Ric ric, Throwable t) {
         logger.warn("Synchronization failed for ric: {}, reason: {}", ric.name(), t.getMessage());
         // If synchronization fails, try to remove all instances
@@ -142,7 +142,7 @@ public class RicSynchronizationTask {
                 () -> onSynchronizationComplete(ric));
     }
 
-    @SuppressWarnings("squid:S2629")
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
     private void onRecoveryError(Ric ric, Throwable t) {
         logger.warn("Synchronization failure recovery failed for ric: {}, reason: {}", ric.name(), t.getMessage());
         ric.setState(RicState.UNDEFINED);
index 626a9b6..4be26eb 100644 (file)
 
 package org.oransc.policyagent.tasks;
 
+import java.time.Duration;
+
 import org.oransc.policyagent.clients.A1ClientFactory;
+import org.oransc.policyagent.repository.Lock;
+import org.oransc.policyagent.repository.Lock.LockType;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.Service;
@@ -29,16 +33,17 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 /**
- * Periodically checks that services with a keepAliveInterval set are alive. If a service is deemed not alive,
- * all the service's policies are deleted, both in the repository and in the affected Rics, and the service is
- * removed from the repository. This means that the service needs to register again after this.
+ * Periodically checks that services with a keepAliveInterval set are alive. If
+ * a service is deemed not alive, all the service's policies are deleted, both
+ * in the repository and in the affected Rics, and the service is removed from
+ * the repository. This means that the service needs to register again after
+ * this.
  */
 @Component
 @EnableScheduling
@@ -47,39 +52,55 @@ public class ServiceSupervision {
     private final Services services;
     private final Policies policies;
     private A1ClientFactory a1ClientFactory;
+    private final Duration checkInterval;
 
     @Autowired
     public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory) {
+        this(services, policies, a1ClientFactory, Duration.ofMinutes(1));
+    }
+
+    public ServiceSupervision(Services services, Policies policies, A1ClientFactory a1ClientFactory,
+        Duration checkInterval) {
         this.services = services;
         this.policies = policies;
         this.a1ClientFactory = a1ClientFactory;
+        this.checkInterval = checkInterval;
+        start();
     }
 
-    @Scheduled(fixedRate = 1000 * 60)
-    public void checkAllServices() {
+    private void start() {
         logger.debug("Checking services starting");
-        createTask().subscribe(this::onPolicyDeleted, null, this::onComplete);
+        createTask().subscribe(null, null, () -> logger.error("Checking services unexpectedly terminated"));
     }
 
-    @SuppressWarnings("squid:S2629")
-    private void onPolicyDeleted(Policy policy) {
-        logger.debug("Policy deleted due to inactivity: {}, service: {}", policy.id(), policy.ownerServiceName());
+    private Flux<?> createTask() {
+        return Flux.interval(this.checkInterval) //
+            .flatMap(notUsed -> checkAllServices());
     }
 
-    private void onComplete() {
-        logger.debug("Checking services completed");
+    Flux<Policy> checkAllServices() {
+        return Flux.fromIterable(services.getAll()) //
+            .filter(Service::isExpired) //
+            .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) //
+            .doOnNext(service -> services.remove(service.getName())) //
+            .flatMap(this::getAllPoliciesForService) //
+            .flatMap(this::deletePolicy);
     }
 
-    private Flux<Policy> createTask() {
-        synchronized (services) {
-            return Flux.fromIterable(services.getAll()) //
-                .filter(Service::isExpired) //
-                .doOnNext(service -> logger.info("Service is expired: {}", service.getName())) //
-                .doOnNext(service -> services.remove(service.getName())) //
-                .flatMap(this::getAllPoliciesForService) //
-                .doOnNext(policies::remove) //
-                .flatMap(this::deletePolicyInRic);
-        }
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
+    private Flux<Policy> deletePolicy(Policy policy) {
+        Lock lock = policy.ric().getLock();
+        return lock.lock(LockType.SHARED) //
+            .doOnNext(notUsed -> policies.remove(policy)) //
+            .flatMap(notUsed -> deletePolicyInRic(policy))
+            .doOnNext(notUsed -> logger.debug("Policy deleted due to service inactivity: {}, service: {}", policy.id(),
+                policy.ownerServiceName())) //
+            .doOnNext(notUsed -> lock.unlockBlocking()) //
+            .doOnError(throwable -> lock.unlockBlocking()) //
+            .doOnError(throwable -> logger.debug("Failed to delete inactive policy: {}, reason: {}", policy.id(),
+                throwable.getMessage())) //
+            .flatMapMany(notUsed -> Flux.just(policy)) //
+            .onErrorResume(throwable -> Flux.empty());
     }
 
     private Flux<Policy> getAllPoliciesForService(Service service) {
@@ -95,7 +116,7 @@ public class ServiceSupervision {
                 .map(nothing -> policy));
     }
 
-    @SuppressWarnings("squid:S2629")
+    @SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
     private Mono<String> handleDeleteFromRicFailure(Policy policy, Throwable e) {
         logger.warn("Could not delete policy: {} from ric: {}", policy.id(), policy.ric().name(), e);
         return Mono.empty();
index 05485d7..a5bf3cb 100644 (file)
@@ -22,6 +22,7 @@ package org.oransc.policyagent;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
@@ -29,7 +30,6 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParser;
 
-import java.io.IOException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
@@ -40,6 +40,7 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.oransc.policyagent.clients.AsyncRestClient;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.configuration.ImmutableRicConfig;
 import org.oransc.policyagent.configuration.RicConfig;
@@ -58,9 +59,12 @@ import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Ric.RicState;
 import org.oransc.policyagent.repository.Rics;
 import org.oransc.policyagent.repository.Services;
-import org.oransc.policyagent.tasks.RepositorySupervision;
+import org.oransc.policyagent.tasks.RicSupervision;
+import org.oransc.policyagent.tasks.ServiceSupervision;
 import org.oransc.policyagent.utils.MockA1Client;
 import org.oransc.policyagent.utils.MockA1ClientFactory;
+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;
@@ -70,19 +74,21 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.HttpStatus.Series;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpResponse;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
-import org.springframework.web.client.ResponseErrorHandler;
 import org.springframework.web.client.RestTemplate;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
+
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
 
 @ExtendWith(SpringExtension.class)
 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
 public class ApplicationTest {
+    private static final Logger logger = LoggerFactory.getLogger(ApplicationTest.class);
+
     @Autowired
     ApplicationContext context;
 
@@ -99,7 +105,7 @@ public class ApplicationTest {
     MockA1ClientFactory a1ClientFactory;
 
     @Autowired
-    RepositorySupervision supervision;
+    RicSupervision supervision;
 
     @Autowired
     Services services;
@@ -121,6 +127,9 @@ public class ApplicationTest {
     @TestConfiguration
     static class TestBeanFactory {
         private final PolicyTypes policyTypes = new PolicyTypes();
+        private final Services services = new Services();
+        private final Policies policies = new Policies();
+        MockA1ClientFactory a1ClientFactory = null;
 
         @Bean
         public ApplicationConfig getApplicationConfig() {
@@ -129,37 +138,36 @@ public class ApplicationTest {
 
         @Bean
         MockA1ClientFactory getA1ClientFactory() {
-            return new MockA1ClientFactory(this.policyTypes);
+            if (a1ClientFactory == null) {
+                this.a1ClientFactory = new MockA1ClientFactory(this.policyTypes);
+            }
+            return this.a1ClientFactory;
         }
 
         @Bean
         public PolicyTypes getPolicyTypes() {
             return this.policyTypes;
         }
-    }
-
-    @LocalServerPort
-    private int port;
 
-    private final RestTemplate restTemplate = new RestTemplate();
-
-    public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
+        @Bean
+        Policies getPolicies() {
+            return this.policies;
+        }
 
-        @Override
-        public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
-            return (httpResponse.getStatusCode().series() == Series.CLIENT_ERROR
-                || httpResponse.getStatusCode().series() == Series.SERVER_ERROR);
+        @Bean
+        Services getServices() {
+            return this.services;
         }
 
-        @Override
-        public void handleError(ClientHttpResponse httpResponse) throws IOException {
-            System.out.println("Error " + httpResponse.toString());
+        @Bean
+        public ServiceSupervision getServiceSupervision() {
+            Duration checkInterval = Duration.ofMillis(1);
+            return new ServiceSupervision(this.services, this.policies, this.getA1ClientFactory(), checkInterval);
         }
     }
 
-    private void setRestErrorhandler() {
-        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
-    }
+    @LocalServerPort
+    private int port;
 
     @BeforeEach
     public void reset() {
@@ -182,14 +190,14 @@ public class ApplicationTest {
     @Test
     public void testGetRics() throws Exception {
         addRic("kista_1");
-        String url = baseUrl() + "/rics";
-        String rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println(rsp);
+        this.addPolicyType("type1", "kista_1");
+        String url = "/rics?policyType=type1";
+        String rsp = restClient().get(url).block();
         assertThat(rsp).contains("kista_1");
 
-        url = baseUrl() + "/rics?policyType=STD_PolicyModelUnconstrained_0.2.0";
-        rsp = this.restTemplate.getForObject(url, String.class);
-        assertThat(rsp).isEqualTo("[]");
+        // Non existing policy type
+        url = "/rics?policyType=XXXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
@@ -219,18 +227,19 @@ public class ApplicationTest {
         String managedElementId = "kista_1";
         addRic(ricName, managedElementId);
 
-        String url = baseUrl() + "/ric?managedElementId=" + managedElementId;
-        String rsp = this.restTemplate.getForObject(url, String.class);
-
+        String url = "/ric?managedElementId=" + managedElementId;
+        String rsp = restClient().get(url).block();
         assertThat(rsp).isEqualTo(ricName);
+
+        // test GET RIC for ManagedElement that does not exist
+        url = "/ric?managedElementId=" + "junk";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
-    @Test
-    public void testGetRicForManagedElementThatDoesNotExist() throws Exception {
-        this.setRestErrorhandler();
-        String url = baseUrl() + "/ric?managedElementId=kista_1";
-        ResponseEntity<String> entity = this.restTemplate.getForEntity(url, String.class);
-        assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+    private String putPolicyUrl(String serviceName, String ricName, String policyTypeName, String policyInstanceId) {
+        String url = "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
+            + "&service=" + serviceName;
+        return url;
     }
 
     @Test
@@ -243,12 +252,11 @@ public class ApplicationTest {
         putService(serviceName);
         addPolicyType(policyTypeName, ricName);
 
-        String url = baseUrl() + "/policy?type=" + policyTypeName + "&instance=" + policyInstanceId + "&ric=" + ricName
-            + "&service=" + serviceName;
-        final String json = jsonString();
+        String url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
+        final String policyBody = jsonString();
         this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
 
-        this.restTemplate.put(url, createJsonHttpEntity(json));
+        restClient().put(url, policyBody).block();
 
         Policy policy = policies.getPolicy(policyInstanceId);
         assertThat(policy).isNotNull();
@@ -256,10 +264,21 @@ public class ApplicationTest {
         assertThat(policy.ownerServiceName()).isEqualTo(serviceName);
         assertThat(policy.ric().name()).isEqualTo("ric1");
 
-        url = baseUrl() + "/policies";
-        String rsp = this.restTemplate.getForObject(url, String.class);
+        url = "/policies";
+        String rsp = restClient().get(url).block();
         assertThat(rsp.contains(policyInstanceId)).isTrue();
 
+        // Test of error codes
+        url = putPolicyUrl(serviceName, ricName + "XX", policyTypeName, policyInstanceId);
+        testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
+
+        url = putPolicyUrl(serviceName, ricName, policyTypeName + "XX", policyInstanceId);
+        testErrorCode(restClient().put(url, policyBody), HttpStatus.NOT_FOUND);
+
+        url = putPolicyUrl(serviceName, ricName, policyTypeName, policyInstanceId);
+        this.rics.getRic(ricName).setState(Ric.RicState.SYNCHRONIZING);
+        testErrorCode(restClient().put(url, policyBody), HttpStatus.LOCKED);
+        this.rics.getRic(ricName).setState(Ric.RicState.IDLE);
     }
 
     @Test
@@ -268,41 +287,40 @@ public class ApplicationTest {
         // In this case service is attempted to be changed
         this.addRic("ric1");
         this.addRic("ricXXX");
-
         this.addPolicy("instance1", "type1", "service1", "ric1");
-        this.setRestErrorhandler();
-        String urlWrongRic = baseUrl() + "/policy?type=type1&instance=instance1&ric=ricXXX&service=service1";
-        ResponseEntity<String> entity = this.putForEntity(urlWrongRic, jsonString());
-        assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.METHOD_NOT_ALLOWED);
 
-        Policy policy = policies.getPolicy("instance1");
-        assertThat(policy.ric().name()).isEqualTo("ric1"); // Not changed
+        // Try change ric1 -> ricXXX
+        String urlWrongRic = putPolicyUrl("service1", "ricXXX", "type1", "instance1");
+        testErrorCode(restClient().put(urlWrongRic, jsonString()), HttpStatus.METHOD_NOT_ALLOWED);
     }
 
     @Test
     public void testGetPolicy() throws Exception {
-        String url = baseUrl() + "/policy?instance=id";
+        String url = "/policy?instance=id";
         Policy policy = addPolicy("id", "typeName", "service1", "ric1");
         {
-            String rsp = this.restTemplate.getForObject(url, String.class);
+            String rsp = restClient().get(url).block();
             assertThat(rsp).isEqualTo(policy.json());
         }
         {
             policies.remove(policy);
-            ResponseEntity<String> rsp = this.restTemplate.getForEntity(url, String.class);
-            assertThat(rsp.getStatusCodeValue()).isEqualTo(HttpStatus.NO_CONTENT.value());
+            testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
         }
     }
 
     @Test
     public void testDeletePolicy() throws Exception {
-        String url = baseUrl() + "/policy?instance=id";
         addPolicy("id", "typeName", "service1", "ric1");
         assertThat(policies.size()).isEqualTo(1);
 
-        this.restTemplate.delete(url);
+        String url = "/policy?instance=id";
+        ResponseEntity<String> entity = restClient().deleteForEntity(url).block();
 
+        assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
         assertThat(policies.size()).isEqualTo(0);
+
+        // Delete a non existing policy
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
@@ -310,20 +328,23 @@ public class ApplicationTest {
         addPolicyType("type1", "ric1");
         addPolicyType("type2", "ric2");
 
-        String url = baseUrl() + "/policy_schemas";
-        String rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println("*** " + rsp);
+        String url = "/policy_schemas";
+        String rsp = this.restClient().get(url).block();
         assertThat(rsp).contains("type1");
         assertThat(rsp).contains("[{\"title\":\"type2\"}");
 
         List<String> info = parseSchemas(rsp);
         assertThat(info.size()).isEqualTo(2);
 
-        url = baseUrl() + "/policy_schemas?ric=ric1";
-        rsp = this.restTemplate.getForObject(url, String.class);
+        url = "/policy_schemas?ric=ric1";
+        rsp = restClient().get(url).block();
         assertThat(rsp).contains("type1");
         info = parseSchemas(rsp);
         assertThat(info.size()).isEqualTo(1);
+
+        // Get schema for non existing RIC
+        url = "/policy_schemas?ric=ric1XXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
@@ -331,11 +352,15 @@ public class ApplicationTest {
         addPolicyType("type1", "ric1");
         addPolicyType("type2", "ric2");
 
-        String url = baseUrl() + "/policy_schema?id=type1";
-        String rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println(rsp);
+        String url = "/policy_schema?id=type1";
+        String rsp = restClient().get(url).block();
+        logger.info(rsp);
         assertThat(rsp).contains("type1");
         assertThat(rsp).contains("title");
+
+        // Get non existing schema
+        url = "/policy_schema?id=type1XX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
@@ -343,23 +368,27 @@ public class ApplicationTest {
         addPolicyType("type1", "ric1");
         addPolicyType("type2", "ric2");
 
-        String url = baseUrl() + "/policy_types";
-        String rsp = this.restTemplate.getForObject(url, String.class);
+        String url = "/policy_types";
+        String rsp = restClient().get(url).block();
         assertThat(rsp).isEqualTo("[\"type2\",\"type1\"]");
 
-        url = baseUrl() + "/policy_types?ric=ric1";
-        rsp = this.restTemplate.getForObject(url, String.class);
+        url = "/policy_types?ric=ric1";
+        rsp = restClient().get(url).block();
         assertThat(rsp).isEqualTo("[\"type1\"]");
+
+        // Get policy types for non existing RIC
+        url = "/policy_types?ric=ric1XXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
     public void testGetPolicies() throws Exception {
         reset();
-        String url = baseUrl() + "/policies";
         addPolicy("id1", "type1", "service1");
 
-        String rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println(rsp);
+        String url = "/policies";
+        String rsp = restClient().get(url).block();
+        logger.info(rsp);
         List<PolicyInfo> info = parseList(rsp, PolicyInfo.class);
         assertThat(info).size().isEqualTo(1);
         PolicyInfo policyInfo = info.get(0);
@@ -375,57 +404,86 @@ public class ApplicationTest {
         addPolicy("id2", "type1", "service2");
         addPolicy("id3", "type2", "service1");
 
-        String url = baseUrl() + "/policies?type=type1";
-        String rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println(rsp);
+        String url = "/policies?type=type1";
+        String rsp = restClient().get(url).block();
+        logger.info(rsp);
         assertThat(rsp).contains("id1");
         assertThat(rsp).contains("id2");
         assertThat(rsp.contains("id3")).isFalse();
 
-        url = baseUrl() + "/policies?type=type1&service=service2";
-        rsp = this.restTemplate.getForObject(url, String.class);
-        System.out.println(rsp);
+        url = "/policies?type=type1&service=service2";
+        rsp = restClient().get(url).block();
+        logger.info(rsp);
         assertThat(rsp.contains("id1")).isFalse();
         assertThat(rsp).contains("id2");
         assertThat(rsp.contains("id3")).isFalse();
+
+        // Test get policies for non existing type
+        url = "/policies?type=type1XXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
+
+        // Test get policies for non existing RIC
+        url = "/policies?ric=XXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     @Test
     public void testPutAndGetService() throws Exception {
         // PUT
-        putService("name");
+        putService("name", 0);
 
-        // GET
-        String url = baseUrl() + "/services?serviceName=name";
-        String rsp = this.restTemplate.getForObject(url, String.class);
+        // GET one service
+        String url = "/services?name=name";
+        String rsp = restClient().get(url).block();
         List<ServiceStatus> info = parseList(rsp, ServiceStatus.class);
         assertThat(info.size()).isEqualTo(1);
         ServiceStatus status = info.iterator().next();
-        assertThat(status.keepAliveIntervalSeconds).isEqualTo(1);
+        assertThat(status.keepAliveIntervalSeconds).isEqualTo(0);
         assertThat(status.serviceName).isEqualTo("name");
 
         // GET (all)
-        url = baseUrl() + "/services";
-        rsp = this.restTemplate.getForObject(url, String.class);
+        url = "/services";
+        rsp = restClient().get(url).block();
         assertThat(rsp.contains("name")).isTrue();
-        System.out.println(rsp);
+        logger.info(rsp);
 
         // Keep alive
-        url = baseUrl() + "/services/keepalive?name=name";
-        ResponseEntity<String> entity = this.restTemplate.postForEntity(url, null, String.class);
+        url = "/services/keepalive?name=name";
+        ResponseEntity<String> entity = restClient().postForEntity(url, null).block();
         assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
 
-        // DELETE
+        // DELETE service
         assertThat(services.size()).isEqualTo(1);
-        url = baseUrl() + "/services?name=name";
-        this.restTemplate.delete(url);
+        url = "/services?name=name";
+        restClient().delete(url).block();
         assertThat(services.size()).isEqualTo(0);
 
         // Keep alive, no registerred service
-        url = baseUrl() + "/services/keepalive?name=name";
-        setRestErrorhandler();
-        entity = this.restTemplate.postForEntity(url, null, String.class);
-        assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+        testErrorCode(restClient().post("/services/keepalive?name=name", ""), HttpStatus.NOT_FOUND);
+
+        // PUT servive with crap payload
+        testErrorCode(restClient().put("/service", "crap"), HttpStatus.BAD_REQUEST);
+        testErrorCode(restClient().put("/service", "{}"), HttpStatus.BAD_REQUEST);
+
+        // GET non existing servive
+        testErrorCode(restClient().get("/services?name=XXX"), HttpStatus.NOT_FOUND);
+    }
+
+    @Test
+    public void testServiceSupervision() throws Exception {
+        putService("service1", 1);
+        addPolicyType("type1", "ric1");
+
+        String url = putPolicyUrl("service1", "ric1", "type1", "instance1");
+        final String policyBody = jsonString();
+        restClient().put(url, policyBody).block();
+
+        assertThat(policies.size()).isEqualTo(1);
+        assertThat(services.size()).isEqualTo(1);
+
+        // Timeout after ~1 second
+        await().untilAsserted(() -> assertThat(policies.size()).isEqualTo(0));
+        assertThat(services.size()).isEqualTo(0);
     }
 
     @Test
@@ -433,9 +491,13 @@ public class ApplicationTest {
         addPolicy("id", "typeName", "service1", "ric1");
         assertThat(policies.size()).isEqualTo(1);
 
-        String url = baseUrl() + "/policy_status?instance=id";
-        String rsp = this.restTemplate.getForObject(url, String.class);
+        String url = "/policy_status?instance=id";
+        String rsp = restClient().get(url).block();
         assertThat(rsp.equals("OK")).isTrue();
+
+        // GET non existing policy status
+        url = "/policy_status?instance=XXX";
+        testErrorCode(restClient().get(url), HttpStatus.NOT_FOUND);
     }
 
     private Policy addPolicy(String id, String typeName, String service, String ric) throws ServiceException {
@@ -454,17 +516,21 @@ public class ApplicationTest {
         return addPolicy(id, typeName, service, "ric");
     }
 
-    private String createServiceJson(String name) {
-        ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, 1, "callbackUrl");
+    private String createServiceJson(String name, long keepAliveIntervalSeconds) {
+        ServiceRegistrationInfo service = new ServiceRegistrationInfo(name, keepAliveIntervalSeconds, "callbackUrl");
 
         String json = gson.toJson(service);
         return json;
     }
 
     private void putService(String name) {
-        String url = baseUrl() + "/service";
-        HttpEntity<String> entity = createJsonHttpEntity(createServiceJson(name));
-        this.restTemplate.put(url, entity);
+        putService(name, 0);
+    }
+
+    private void putService(String name, long keepAliveIntervalSeconds) {
+        String url = "/service";
+        String body = createServiceJson(name, keepAliveIntervalSeconds);
+        restClient().put(url, body).block();
     }
 
     private String baseUrl() {
@@ -480,9 +546,9 @@ public class ApplicationTest {
         private final String baseUrl;
         static AtomicInteger nextCount = new AtomicInteger(0);
         private final int count;
-        private final RepositorySupervision supervision;
+        private final RicSupervision supervision;
 
-        ConcurrencyTestRunnable(String baseUrl, RepositorySupervision supervision) {
+        ConcurrencyTestRunnable(String baseUrl, RicSupervision supervision) {
             this.baseUrl = baseUrl;
             this.count = nextCount.incrementAndGet();
             this.supervision = supervision;
@@ -502,12 +568,12 @@ public class ApplicationTest {
 
         private void putPolicy(String name) {
             String putUrl = baseUrl + "/policy?type=type1&instance=" + name + "&ric=ric1&service=service1";
-            this.restTemplate.put(putUrl, createJsonHttpEntity("{}"));
+            restTemplate.put(putUrl, createJsonHttpEntity("{}"));
         }
 
         private void deletePolicy(String name) {
             String deleteUrl = baseUrl + "/policy?instance=" + name;
-            this.restTemplate.delete(deleteUrl);
+            restTemplate.delete(deleteUrl);
         }
     }
 
@@ -527,7 +593,25 @@ public class ApplicationTest {
             t.join();
         }
         assertThat(policies.size()).isEqualTo(0);
-        System.out.println("Concurrency test took " + Duration.between(startTime, Instant.now()));
+        logger.info("Concurrency test took " + Duration.between(startTime, Instant.now()));
+    }
+
+    private AsyncRestClient restClient() {
+        return new AsyncRestClient(baseUrl());
+    }
+
+    private void testErrorCode(Mono<?> request, HttpStatus expStatus) {
+        StepVerifier.create(request) //
+            .expectSubscription() //
+            .expectErrorMatches(t -> checkWebClientError(t, expStatus)) //
+            .verify();
+    }
+
+    private boolean checkWebClientError(Throwable t, HttpStatus expStatus) {
+        assertTrue(t instanceof WebClientResponseException);
+        WebClientResponseException e = (WebClientResponseException) t;
+        assertThat(e.getStatusCode()).isEqualTo(expStatus);
+        return true;
     }
 
     private MockA1Client getA1Client(String ricName) throws ServiceException {
@@ -574,10 +658,6 @@ public class ApplicationTest {
         return new HttpEntity<String>(content, headers);
     }
 
-    private ResponseEntity<String> putForEntity(String url, String jsonBody) {
-        return restTemplate.exchange(url, HttpMethod.PUT, createJsonHttpEntity(jsonBody), String.class);
-    }
-
     private static <T> List<T> parseList(String jsonString, Class<T> clazz) {
         List<T> result = new ArrayList<>();
         JsonArray jsonArr = JsonParser.parseString(jsonString).getAsJsonArray();
index 1ea677c..efbd576 100644 (file)
@@ -37,6 +37,8 @@ import org.oransc.policyagent.repository.PolicyType;
 import org.oransc.policyagent.repository.PolicyTypes;
 import org.oransc.policyagent.repository.Rics;
 import org.oransc.policyagent.utils.MockA1ClientFactory;
+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;
@@ -48,6 +50,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 @ExtendWith(SpringExtension.class)
 @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
 public class MockPolicyAgent {
+    private static final Logger logger = LoggerFactory.getLogger(MockPolicyAgent.class);
 
     @Autowired
     Rics rics;
@@ -117,7 +120,7 @@ public class MockPolicyAgent {
                     PolicyType type = ImmutablePolicyType.builder().name(typeName).schema(schema).build();
                     policyTypes.put(type);
                 } catch (Exception e) {
-                    System.out.println("Could not load json schema " + e);
+                    logger.error("Could not load json schema ", e);
                 }
             }
         }
@@ -127,13 +130,13 @@ public class MockPolicyAgent {
     private int port;
 
     private void keepServerAlive() {
-        System.out.println("Keeping server alive!");
+        logger.info("Keeping server alive!");
         try {
             synchronized (this) {
                 this.wait();
             }
         } catch (Exception ex) {
-            System.out.println("Unexpected: " + ex.toString());
+            logger.error("Unexpected: " + ex);
         }
     }
 
@@ -144,6 +147,8 @@ public class MockPolicyAgent {
     }
 
     @Test
+    @SuppressWarnings("squid:S2699") // Tests should include assertions. This test is only for keeping the server alive,
+                                     // so it will only be confusing to add an assertion.
     public void runMock() throws Exception {
         keepServerAlive();
     }
index 23cb671..6122c19 100644 (file)
@@ -66,7 +66,7 @@ public class A1ClientFactoryTest {
     A1Client sdncOscA1ClientMock;
 
     @Mock
-    A1Client sdnrOnapA1ClientMock;
+    A1Client sdncOnapA1ClientMock;
 
     private ImmutableRicConfig ricConfig =
         ImmutableRicConfig.builder().name(RIC_NAME).baseUrl("baseUrl").managedElementIds(new Vector<>()).build();
@@ -81,7 +81,7 @@ public class A1ClientFactoryTest {
 
     @Test
     public void createStd_ok() {
-        whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+        whenGetProtocolVersionSdncOnapA1ClientThrowException();
         whenGetProtocolVersionSdncOscA1ClientThrowException();
         whenGetProtocolVersionOscA1ClientThrowException();
         whenGetProtocolVersionStdA1ClientReturnCorrectProtocol();
@@ -96,7 +96,7 @@ public class A1ClientFactoryTest {
 
     @Test
     public void createOsc_ok() {
-        whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+        whenGetProtocolVersionSdncOnapA1ClientThrowException();
         whenGetProtocolVersionSdncOscA1ClientThrowException();
         whenGetProtocolVersionOscA1ClientReturnCorrectProtocol();
 
@@ -110,7 +110,7 @@ public class A1ClientFactoryTest {
 
     @Test
     public void createSdncOsc_ok() {
-        whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+        whenGetProtocolVersionSdncOnapA1ClientThrowException();
         whenGetProtocolVersionSdncOscA1ClientReturnCorrectProtocol();
 
         StepVerifier.create(factoryUnderTest.createA1Client(ric)) //
@@ -122,12 +122,12 @@ public class A1ClientFactoryTest {
     }
 
     @Test
-    public void createSdnrOnap_ok() {
-        whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol();
+    public void createSdncOnap_ok() {
+        whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol();
 
         StepVerifier.create(factoryUnderTest.createA1Client(ric)) //
             .expectSubscription() //
-            .expectNext(sdnrOnapA1ClientMock) //
+            .expectNext(sdncOnapA1ClientMock) //
             .verifyComplete();
 
         assertEquals(A1ProtocolType.SDNC_ONAP, ric.getProtocolVersion(), "Not correct protocol");
@@ -135,7 +135,7 @@ public class A1ClientFactoryTest {
 
     @Test
     public void createWithNoProtocol_error() {
-        whenGetProtocolVersionSdnrOnapA1ClientThrowException();
+        whenGetProtocolVersionSdncOnapA1ClientThrowException();
         whenGetProtocolVersionSdncOscA1ClientThrowException();
         whenGetProtocolVersionOscA1ClientThrowException();
         whenGetProtocolVersionStdA1ClientThrowException();
@@ -167,20 +167,20 @@ public class A1ClientFactoryTest {
 
         assertEquals(A1ProtocolType.STD_V1, ric.getProtocolVersion(), "Not correct protocol");
 
-        verifyNoMoreInteractions(sdnrOnapA1ClientMock);
+        verifyNoMoreInteractions(sdncOnapA1ClientMock);
         verifyNoMoreInteractions(sdncOscA1ClientMock);
         verifyNoMoreInteractions(oscA1ClientMock);
         verifyNoMoreInteractions(stdA1ClientMock);
     }
 
-    private void whenGetProtocolVersionSdnrOnapA1ClientThrowException() {
-        doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(ric);
-        when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE)));
+    private void whenGetProtocolVersionSdncOnapA1ClientThrowException() {
+        doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(ric);
+        when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.error(new Exception(EXCEPTION_MESSAGE)));
     }
 
-    private void whenGetProtocolVersionSdnrOnapA1ClientReturnCorrectProtocol() {
-        doReturn(sdnrOnapA1ClientMock).when(factoryUnderTest).createSdnrOnapA1Client(any(Ric.class));
-        when(sdnrOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP));
+    private void whenGetProtocolVersionSdncOnapA1ClientReturnCorrectProtocol() {
+        doReturn(sdncOnapA1ClientMock).when(factoryUnderTest).createSdncOnapA1Client(any(Ric.class));
+        when(sdncOnapA1ClientMock.getProtocolVersion()).thenReturn(Mono.just(A1ProtocolType.SDNC_ONAP));
     }
 
     private void whenGetProtocolVersionSdncOscA1ClientThrowException() {
index 11f2409..884b36f 100644 (file)
@@ -31,9 +31,9 @@ import okhttp3.mockwebserver.MockWebServer;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.oransc.policyagent.clients.AsyncRestClient.AsyncRestClientException;
 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;
@@ -81,8 +81,8 @@ public class AsyncRestClientTest {
         mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
 
         Mono<String> returnedMono = clientUnderTest.get(REQUEST_URL);
-        StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
-            .verify();
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
     }
 
     @Test
@@ -100,8 +100,8 @@ public class AsyncRestClientTest {
         mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
 
         Mono<String> returnedMono = clientUnderTest.put(REQUEST_URL, TEST_JSON);
-        StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
-            .verify();
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
     }
 
     @Test
@@ -117,8 +117,8 @@ public class AsyncRestClientTest {
         mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
 
         Mono<String> returnedMono = clientUnderTest.delete(REQUEST_URL);
-        StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
-            .verify();
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
     }
 
     @Test
@@ -136,8 +136,8 @@ public class AsyncRestClientTest {
         mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
 
         Mono<String> returnedMono = clientUnderTest.post(REQUEST_URL, TEST_JSON);
-        StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
-            .verify();
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
     }
 
     @Test
@@ -155,7 +155,7 @@ public class AsyncRestClientTest {
         mockWebServer.enqueue(new MockResponse().setResponseCode(ERROR_CODE));
 
         Mono<String> returnedMono = clientUnderTest.postWithAuthHeader(REQUEST_URL, TEST_JSON, USERNAME, PASSWORD);
-        StepVerifier.create(returnedMono).expectErrorMatches(throwable -> throwable instanceof AsyncRestClientException)
-            .verify();
+        StepVerifier.create(returnedMono)
+            .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException).verify();
     }
 }
index 17f0997..d30e95a 100644 (file)
@@ -62,7 +62,7 @@ public class ApplicationConfigTest {
         RicConfigUpdate update =
             appConfigUnderTest.setConfiguration(Arrays.asList(RIC_CONFIG_1), null, null).blockFirst();
         assertEquals(RicConfigUpdate.Type.ADDED, update.getType());
-        assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configuraions.");
+        assertTrue(appConfigUnderTest.getRicConfigs().contains(RIC_CONFIG_1), "Ric not added to configurations.");
 
         assertEquals(RIC_CONFIG_1, appConfigUnderTest.getRic(RIC_CONFIG_1.name()),
             "Not correct Ric retrieved from configurations.");
index cd4bcb6..153c4ec 100644 (file)
@@ -68,7 +68,7 @@ public class DmaapMessageConsumerTest {
 
     @Test
     public void dmaapNotConfigured_thenDoNothing() {
-        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
 
         doReturn(true).when(messageConsumerUnderTest).sleep(any(Duration.class));
 
@@ -82,7 +82,7 @@ public class DmaapMessageConsumerTest {
 
     @Test
     public void dmaapConfiguredAndNoMessages_thenPollOnce() throws Exception {
-        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
 
         doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
 
@@ -113,7 +113,7 @@ public class DmaapMessageConsumerTest {
 
     @Test
     public void dmaapConfiguredAndErrorGettingMessages_thenLogWarning() throws Exception {
-        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
 
         doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
 
@@ -141,7 +141,7 @@ public class DmaapMessageConsumerTest {
 
     @Test
     public void dmaapConfiguredAndOneMessage_thenPollOnceAndProcessMessage() throws Exception {
-        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock, false));
+        messageConsumerUnderTest = spy(new DmaapMessageConsumer(applicationConfigMock));
 
         doReturn(true, false).when(messageConsumerUnderTest).sleep(any(Duration.class));
 
index 67d00a2..52147a8 100644 (file)
@@ -52,13 +52,17 @@ import org.oransc.policyagent.dmaap.DmaapRequestMessage.Operation;
 import org.oransc.policyagent.repository.ImmutablePolicyType;
 import org.oransc.policyagent.repository.PolicyType;
 import org.oransc.policyagent.utils.LoggingUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
 
 import reactor.core.publisher.Mono;
 import reactor.test.StepVerifier;
 
 public class DmaapMessageHandlerTest {
-
+    private static final Logger logger = LoggerFactory.getLogger(DmaapMessageHandlerTest.class);
     private static final String URL = "url";
 
     private final MRBatchingPublisher dmaapClient = mock(MRBatchingPublisher.class);
@@ -102,16 +106,21 @@ public class DmaapMessageHandlerTest {
         return gson.toJson(dmaapRequestMessage(operation));
     }
 
+    private Mono<ResponseEntity<String>> okResponse() {
+        ResponseEntity<String> entity = new ResponseEntity<>("OK", HttpStatus.OK);
+        return Mono.just(entity);
+    }
+
     @Test
     public void testMessageParsing() {
         String message = dmaapInputMessage(Operation.DELETE);
-        System.out.println(message);
+        logger.info(message);
         DmaapRequestMessage parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class);
         assertTrue(parsedMessage != null);
         assertFalse(parsedMessage.payload().isPresent());
 
         message = dmaapInputMessage(Operation.PUT);
-        System.out.println(message);
+        logger.info(message);
         parsedMessage = gson.fromJson(message, ImmutableDmaapRequestMessage.class);
         assertTrue(parsedMessage != null);
         assertTrue(parsedMessage.payload().isPresent());
@@ -129,7 +138,7 @@ public class DmaapMessageHandlerTest {
 
     @Test
     public void successfulDelete() throws IOException {
-        doReturn(Mono.just("OK")).when(agentClient).delete(anyString());
+        doReturn(okResponse()).when(agentClient).deleteForEntity(anyString());
         doReturn(1).when(dmaapClient).send(anyString());
         doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
 
@@ -141,7 +150,7 @@ public class DmaapMessageHandlerTest {
             .expectNext("OK") //
             .verifyComplete(); //
 
-        verify(agentClient).delete(URL);
+        verify(agentClient).deleteForEntity(URL);
         verifyNoMoreInteractions(agentClient);
 
         verify(dmaapClient).send(anyString());
@@ -151,7 +160,7 @@ public class DmaapMessageHandlerTest {
 
     @Test
     public void successfulGet() throws IOException {
-        doReturn(Mono.just("OK")).when(agentClient).get(anyString());
+        doReturn(okResponse()).when(agentClient).getForEntity(anyString());
         doReturn(1).when(dmaapClient).send(anyString());
         doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
 
@@ -161,7 +170,7 @@ public class DmaapMessageHandlerTest {
             .expectNext("OK") //
             .verifyComplete(); //
 
-        verify(agentClient).get(URL);
+        verify(agentClient).getForEntity(URL);
         verifyNoMoreInteractions(agentClient);
 
         verify(dmaapClient).send(anyString());
@@ -171,7 +180,7 @@ public class DmaapMessageHandlerTest {
 
     @Test
     public void successfulPut() throws IOException {
-        doReturn(Mono.just("OK")).when(agentClient).put(anyString(), anyString());
+        doReturn(okResponse()).when(agentClient).putForEntity(anyString(), anyString());
         doReturn(1).when(dmaapClient).send(anyString());
         doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
 
@@ -181,7 +190,7 @@ public class DmaapMessageHandlerTest {
             .expectNext("OK") //
             .verifyComplete(); //
 
-        verify(agentClient).put(URL, payloadAsString());
+        verify(agentClient).putForEntity(URL, payloadAsString());
         verifyNoMoreInteractions(agentClient);
 
         verify(dmaapClient).send(anyString());
@@ -191,7 +200,7 @@ public class DmaapMessageHandlerTest {
 
     @Test
     public void successfulPost() throws IOException {
-        doReturn(Mono.just("OK")).when(agentClient).post(anyString(), anyString());
+        doReturn(okResponse()).when(agentClient).postForEntity(anyString(), anyString());
         doReturn(1).when(dmaapClient).send(anyString());
         doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
 
@@ -201,7 +210,7 @@ public class DmaapMessageHandlerTest {
             .expectNext("OK") //
             .verifyComplete(); //
 
-        verify(agentClient).post(URL, payloadAsString());
+        verify(agentClient).postForEntity(URL, payloadAsString());
         verifyNoMoreInteractions(agentClient);
 
         verify(dmaapClient).send(anyString());
@@ -211,8 +220,8 @@ public class DmaapMessageHandlerTest {
 
     @Test
     public void exceptionWhenCallingPolicyAgent_thenNotFoundResponse() throws IOException {
-        String errorCause = "Refused";
-        doReturn(Mono.error(new Exception(errorCause))).when(agentClient).put(anyString(), any());
+        WebClientResponseException except = new WebClientResponseException(400, "Refused", null, null, null, null);
+        doReturn(Mono.error(except)).when(agentClient).putForEntity(anyString(), any());
         doReturn(1).when(dmaapClient).send(anyString());
         doReturn(new MRPublisherResponse()).when(dmaapClient).sendBatchWithResponse();
 
@@ -221,14 +230,13 @@ public class DmaapMessageHandlerTest {
             .expectSubscription() //
             .verifyComplete(); //
 
-        verify(agentClient).put(anyString(), anyString());
+        verify(agentClient).putForEntity(anyString(), anyString());
         verifyNoMoreInteractions(agentClient);
 
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
         verify(dmaapClient).send(captor.capture());
         String actualMessage = captor.getValue();
-        assertThat(actualMessage.contains(HttpStatus.NOT_FOUND + "\",\"message\":\"java.lang.Exception: " + errorCause))
-            .isTrue();
+        assertThat(actualMessage.contains(HttpStatus.BAD_REQUEST.toString())).isTrue();
 
         verify(dmaapClient).sendBatchWithResponse();
         verifyNoMoreInteractions(dmaapClient);
@@ -246,7 +254,7 @@ public class DmaapMessageHandlerTest {
         verify(dmaapClient).send(captor.capture());
         String actualMessage = captor.getValue();
         assertThat(actualMessage
-            .contains(HttpStatus.NOT_FOUND + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue();
+            .contains(HttpStatus.BAD_REQUEST + "\",\"message\":\"Not implemented operation: " + badOperation)).isTrue();
 
         verify(dmaapClient).sendBatchWithResponse();
         verifyNoMoreInteractions(dmaapClient);
index 825010a..6fd6c8b 100644 (file)
@@ -71,10 +71,8 @@ public class LockTest {
         Lock lock = new Lock();
 
         Mono<Lock> seq = lock.lock(LockType.EXCLUSIVE) //
-            .doOnNext(l -> System.out.println("1 " + l)) //
             .flatMap(l -> lock.lock(LockType.EXCLUSIVE)) //
-            .flatMap(l -> lock.unlock()) //
-            .doOnNext(l -> System.out.println("2 " + l)); //
+            .flatMap(l -> lock.unlock());
 
         asynchUnlock(lock);
         StepVerifier.create(seq) //
index 6073b26..f81962d 100644 (file)
 
 package org.oransc.policyagent.tasks;
 
+import static ch.qos.logback.classic.Level.ERROR;
+import static ch.qos.logback.classic.Level.WARN;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import ch.qos.logback.classic.spi.ILoggingEvent;
 import ch.qos.logback.core.read.ListAppender;
@@ -45,6 +49,7 @@ import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Properties;
 import java.util.Vector;
 
@@ -58,11 +63,16 @@ import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.EnvProperti
 import org.onap.dcaegen2.services.sdk.rest.services.cbs.client.model.ImmutableEnvProperties;
 import org.oransc.policyagent.clients.A1ClientFactory;
 import org.oransc.policyagent.configuration.ApplicationConfig;
+import org.oransc.policyagent.configuration.ApplicationConfig.RicConfigUpdate.Type;
 import org.oransc.policyagent.configuration.ApplicationConfigParser;
 import org.oransc.policyagent.configuration.ImmutableRicConfig;
 import org.oransc.policyagent.configuration.RicConfig;
+import org.oransc.policyagent.repository.ImmutablePolicy;
+import org.oransc.policyagent.repository.ImmutablePolicyType;
 import org.oransc.policyagent.repository.Policies;
+import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.PolicyTypes;
+import org.oransc.policyagent.repository.Ric;
 import org.oransc.policyagent.repository.Rics;
 import org.oransc.policyagent.repository.Services;
 import org.oransc.policyagent.utils.LoggingUtils;
@@ -74,6 +84,9 @@ import reactor.test.StepVerifier;
 @ExtendWith(MockitoExtension.class)
 public class RefreshConfigTaskTest {
 
+    private static final boolean CONFIG_FILE_EXISTS = true;
+    private static final boolean CONFIG_FILE_DOES_NOT_EXIST = false;
+
     private RefreshConfigTask refreshTaskUnderTest;
 
     @Spy
@@ -99,15 +112,59 @@ public class RefreshConfigTaskTest {
     }
 
     private RefreshConfigTask createTestObject(boolean configFileExists) {
-        RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, new Rics(), new Policies(), new Services(),
-            new PolicyTypes(), new A1ClientFactory(appConfig)));
-        doReturn(configFileExists).when(obj).configFileExists();
+        return createTestObject(configFileExists, new Rics(), new Policies(), true);
+    }
+
+    private RefreshConfigTask createTestObject(boolean configFileExists, Rics rics, Policies policies,
+        boolean stubConfigFileExists) {
+        RefreshConfigTask obj = spy(new RefreshConfigTask(appConfig, rics, policies, new Services(), new PolicyTypes(),
+            new A1ClientFactory(appConfig)));
+        if (stubConfigFileExists) {
+            doReturn(configFileExists).when(obj).configFileExists();
+        }
         return obj;
     }
 
+    @Test
+    public void startWithStubbedRefresh_thenTerminationLogged() {
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+        doReturn(Flux.empty()).when(refreshTaskUnderTest).createRefreshTask();
+
+        final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR);
+
+        refreshTaskUnderTest.start();
+
+        assertThat(logAppender.list.toString().contains("Configuration refresh terminated")).isTrue();
+    }
+
+    @Test
+    public void startWithStubbedRefreshReturnError_thenErrorAndTerminationLogged() {
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+        doReturn(Flux.error(new Exception("Error"))).when(refreshTaskUnderTest).createRefreshTask();
+
+        final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, ERROR);
+
+        refreshTaskUnderTest.start();
+
+        ILoggingEvent event = logAppender.list.get(0);
+        assertThat(event.getThrowableProxy().getMessage()).isEqualTo("Error");
+        assertThat(event.toString().contains("Configuration refresh terminated due to exception")).isTrue();
+    }
+
+    @Test
+    public void stop_thenTaskIsDisposed() throws Exception {
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, null, null, false);
+        refreshTaskUnderTest.systemEnvironment = new Properties();
+
+        refreshTaskUnderTest.start();
+        refreshTaskUnderTest.stop();
+
+        assertThat(refreshTaskUnderTest.getRefreshTask().isDisposed()).isTrue();
+    }
+
     @Test
     public void whenTheConfigurationFits_thenConfiguredRicsArePutInRepository() throws Exception {
-        refreshTaskUnderTest = this.createTestObject(true);
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
         refreshTaskUnderTest.systemEnvironment = new Properties();
         // When
         doReturn(getCorrectJson()).when(refreshTaskUnderTest).createInputStream(any());
@@ -115,13 +172,15 @@ public class RefreshConfigTaskTest {
 
         StepVerifier.create(refreshTaskUnderTest.createRefreshTask()) //
             .expectSubscription() //
-            .expectNext(this.appConfig) //
-            .expectNext(this.appConfig) //
+            .expectNext(Type.ADDED) //
+            .expectNext(Type.ADDED) //
             .thenCancel() //
             .verify();
 
         // Then
-        verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile();
+        verify(refreshTaskUnderTest).loadConfigurationFromFile();
+
+        verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any(Ric.class));
 
         Iterable<RicConfig> ricConfigs = appConfig.getRicConfigs();
         RicConfig ricConfig = ricConfigs.iterator().next();
@@ -131,7 +190,7 @@ public class RefreshConfigTaskTest {
 
     @Test
     public void whenFileExistsButJsonIsIncorrect_thenNoRicsArePutInRepository() throws Exception {
-        refreshTaskUnderTest = this.createTestObject(true);
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_EXISTS);
         refreshTaskUnderTest.systemEnvironment = new Properties();
 
         // When
@@ -145,24 +204,23 @@ public class RefreshConfigTaskTest {
             .verify();
 
         // Then
-        verify(refreshTaskUnderTest, times(1)).loadConfigurationFromFile();
+        verify(refreshTaskUnderTest).loadConfigurationFromFile();
         assertThat(appConfig.getRicConfigs().size()).isEqualTo(0);
     }
 
     @Test
     public void whenPeriodicConfigRefreshNoConsul_thenErrorIsLogged() {
-        refreshTaskUnderTest = this.createTestObject(false);
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST);
         refreshTaskUnderTest.systemEnvironment = new Properties();
 
         EnvProperties props = properties();
         doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any());
 
         doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props);
-        Flux<?> err = Flux.error(new IOException());
-        doReturn(err).when(cbsClient).updates(any(), any(), any());
+        when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.error(new IOException()));
 
-        final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class);
-        Flux<?> task = refreshTaskUnderTest.createRefreshTask();
+        final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(RefreshConfigTask.class, WARN);
+        Flux<Type> task = refreshTaskUnderTest.createRefreshTask();
 
         StepVerifier //
             .create(task) //
@@ -177,10 +235,22 @@ public class RefreshConfigTaskTest {
     }
 
     @Test
-    public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreated() throws Exception {
-        refreshTaskUnderTest = this.createTestObject(false);
+    public void whenPeriodicConfigRefreshSuccess_thenNewConfigIsCreatedAndRepositoryUpdated() throws Exception {
+        Rics rics = new Rics();
+        Policies policies = new Policies();
+        refreshTaskUnderTest = this.createTestObject(CONFIG_FILE_DOES_NOT_EXIST, rics, policies, false);
         refreshTaskUnderTest.systemEnvironment = new Properties();
 
+        RicConfig changedRicConfig = getRicConfig(RIC_1_NAME);
+        rics.put(new Ric(changedRicConfig));
+        RicConfig removedRicConfig = getRicConfig("removed");
+        Ric removedRic = new Ric(removedRicConfig);
+        rics.put(removedRic);
+        appConfig.setConfiguration(Arrays.asList(changedRicConfig, removedRicConfig), null, null);
+
+        Policy policy = getPolicy(removedRic);
+        policies.put(policy);
+
         EnvProperties props = properties();
         doReturn(Mono.just(props)).when(refreshTaskUnderTest).getEnvironment(any());
         doReturn(Mono.just(cbsClient)).when(refreshTaskUnderTest).createCbsClient(props);
@@ -188,22 +258,55 @@ public class RefreshConfigTaskTest {
         JsonObject configAsJson = getJsonRootObject();
         String newBaseUrl = "newBaseUrl";
         modifyTheRicConfiguration(configAsJson, newBaseUrl);
-        Flux<JsonObject> json = Flux.just(configAsJson);
-        doReturn(json).when(cbsClient).updates(any(), any(), any());
+        when(cbsClient.updates(any(), any(), any())).thenReturn(Flux.just(configAsJson));
+        doNothing().when(refreshTaskUnderTest).runRicSynchronization(any(Ric.class));
 
-        Flux<ApplicationConfig> task = refreshTaskUnderTest.createRefreshTask();
+        Flux<Type> task = refreshTaskUnderTest.createRefreshTask();
 
         StepVerifier //
             .create(task) //
             .expectSubscription() //
-            .expectNext(appConfig) //
-            .expectNext(appConfig) //
+            .expectNext(Type.CHANGED) //
+            .expectNext(Type.ADDED) //
+            .expectNext(Type.REMOVED) //
             .thenCancel() //
             .verify();
 
-        verify(refreshTaskUnderTest, times(2)).runRicSynchronization(any());
-        assertThat(appConfig.getRicConfigs()).isNotNull();
+        assertThat(appConfig.getRicConfigs().size()).isEqualTo(2);
         assertThat(appConfig.getRic(RIC_1_NAME).baseUrl()).isEqualTo(newBaseUrl);
+        String ric2Name = "ric2";
+        assertThat(appConfig.getRic(ric2Name)).isNotNull();
+
+        assertThat(rics.size()).isEqualTo(2);
+        assertThat(rics.get(RIC_1_NAME).getConfig().baseUrl()).isEqualTo(newBaseUrl);
+        assertThat(rics.get(ric2Name)).isNotNull();
+
+        assertThat(policies.size()).isEqualTo(0);
+    }
+
+    private RicConfig getRicConfig(String name) {
+        RicConfig ricConfig = ImmutableRicConfig.builder() //
+            .name(name) //
+            .baseUrl("url") //
+            .managedElementIds(Collections.emptyList()) //
+            .build();
+        return ricConfig;
+    }
+
+    private Policy getPolicy(Ric ric) {
+        ImmutablePolicyType type = ImmutablePolicyType.builder() //
+            .name("type") //
+            .schema("{}") //
+            .build();
+        Policy policy = ImmutablePolicy.builder() //
+            .id("id") //
+            .type(type) //
+            .lastModified("lastModified") //
+            .ric(ric) //
+            .json("{}") //
+            .ownerServiceName("ownerServiceName") //
+            .build();
+        return policy;
     }
 
     private void modifyTheRicConfiguration(JsonObject configAsJson, String newBaseUrl) {
@@ -56,7 +56,7 @@ import org.oransc.policyagent.repository.Rics;
 import reactor.core.publisher.Mono;
 
 @ExtendWith(MockitoExtension.class)
-public class RepositorySupervisionTest {
+public class RicSupervisionTest {
     private static final String POLICY_TYPE_1_NAME = "type1";
     private static final PolicyType POLICY_TYPE_1 = ImmutablePolicyType.builder() //
         .name(POLICY_TYPE_1_NAME) //
@@ -133,8 +133,7 @@ public class RepositorySupervisionTest {
         setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID)));
         setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME)));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         supervisorUnderTest.checkAllRics();
 
@@ -147,8 +146,7 @@ public class RepositorySupervisionTest {
         RIC_1.setState(RicState.UNDEFINED);
         rics.put(RIC_1);
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
 
@@ -165,8 +163,7 @@ public class RepositorySupervisionTest {
         RIC_1.setState(RicState.SYNCHRONIZING);
         rics.put(RIC_1);
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         supervisorUnderTest.checkAllRics();
 
@@ -182,8 +179,7 @@ public class RepositorySupervisionTest {
 
         setUpGetPolicyIdentitiesToReturn(new Exception("Failed"));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
         supervisorUnderTest.checkAllRics();
 
         verify(supervisorUnderTest).checkAllRics();
@@ -200,8 +196,7 @@ public class RepositorySupervisionTest {
 
         setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID)));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
 
@@ -223,8 +218,7 @@ public class RepositorySupervisionTest {
 
         setUpGetPolicyIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_1_ID, "Another_policy")));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
 
@@ -245,8 +239,7 @@ public class RepositorySupervisionTest {
         setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
         setUpGetPolicyTypeIdentitiesToReturn(new Exception("Failed"));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
         supervisorUnderTest.checkAllRics();
 
         verify(supervisorUnderTest).checkAllRics();
@@ -264,8 +257,7 @@ public class RepositorySupervisionTest {
         setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
         setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type")));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
 
@@ -292,8 +284,7 @@ public class RepositorySupervisionTest {
         setUpGetPolicyIdentitiesToReturn(Collections.emptyList());
         setUpGetPolicyTypeIdentitiesToReturn(new ArrayList<>(Arrays.asList(POLICY_TYPE_1_NAME, "another_policy_type")));
 
-        RepositorySupervision supervisorUnderTest =
-            spy(new RepositorySupervision(rics, policies, a1ClientFactory, types, null));
+        RicSupervision supervisorUnderTest = spy(new RicSupervision(rics, policies, a1ClientFactory, types, null));
 
         doReturn(recoveryTaskMock).when(supervisorUnderTest).createSynchronizationTask();
 
index a984528..070e8da 100644 (file)
@@ -101,7 +101,7 @@ public class ServiceSupervisionTest {
 
         await().atMost(Durations.FIVE_SECONDS).with().pollInterval(Durations.ONE_SECOND).until(service::isExpired);
 
-        serviceSupervisionUnderTest.checkAllServices();
+        serviceSupervisionUnderTest.checkAllServices().blockLast();
 
         assertThat(policies.size()).isEqualTo(0);
         assertThat(services.size()).isEqualTo(0);
@@ -125,7 +125,7 @@ public class ServiceSupervisionTest {
 
         final ListAppender<ILoggingEvent> logAppender = LoggingUtils.getLogListAppender(ServiceSupervision.class, WARN);
 
-        serviceSupervisionUnderTest.checkAllServices();
+        serviceSupervisionUnderTest.checkAllServices().blockLast();
 
         assertThat(policies.size()).isEqualTo(0);
         assertThat(services.size()).isEqualTo(0);
@@ -143,7 +143,7 @@ public class ServiceSupervisionTest {
         ServiceSupervision serviceSupervisionUnderTest =
             new ServiceSupervision(services, policies, a1ClientFactoryMock);
 
-        serviceSupervisionUnderTest.checkAllServices();
+        serviceSupervisionUnderTest.checkAllServices().blockLast();
 
         assertThat(policies.size()).isEqualTo(1);
         assertThat(services.size()).isEqualTo(1);
@@ -159,7 +159,7 @@ public class ServiceSupervisionTest {
         ServiceSupervision serviceSupervisionUnderTest =
             new ServiceSupervision(services, policies, a1ClientFactoryMock);
 
-        serviceSupervisionUnderTest.checkAllServices();
+        serviceSupervisionUnderTest.checkAllServices().blockLast();
 
         assertThat(policies.size()).isEqualTo(1);
         assertThat(services.size()).isEqualTo(1);