Merge "Bugfix"
diff --git a/policy-agent/config/application_configuration.json b/policy-agent/config/application_configuration.json_example
similarity index 100%
rename from policy-agent/config/application_configuration.json
rename to policy-agent/config/application_configuration.json_example
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java
index 5110718..f0b2ce3 100644
--- a/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java
+++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/AsyncRestClient.java
@@ -34,6 +34,7 @@
 import org.springframework.lang.Nullable;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
+import org.springframework.web.reactive.function.client.WebClientResponseException;
 
 import reactor.core.publisher.Mono;
 import reactor.netty.http.client.HttpClient;
@@ -137,10 +138,21 @@
 
     private Mono<ResponseEntity<String>> retrieve(RequestHeadersSpec<?> request) {
         return request.retrieve() //
-            .toEntity(String.class);
+            .toEntity(String.class) //
+            .doOnError(this::onHttpError);
     }
 
-    Mono<String> toBody(ResponseEntity<String> entity) {
+    private void onHttpError(Throwable t) {
+        if (t instanceof WebClientResponseException) {
+            WebClientResponseException exception = (WebClientResponseException) t;
+            logger.debug("HTTP error status = '{}', body '{}'", exception.getStatusCode(),
+                exception.getResponseBodyAsString());
+        } else {
+            logger.debug("HTTP error: {}", t.getMessage());
+        }
+    }
+
+    private Mono<String> toBody(ResponseEntity<String> entity) {
         if (entity.getBody() == null) {
             return Mono.just("");
         } else {
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncJsonHelper.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncJsonHelper.java
index 0245a15..4d3cbf8 100644
--- a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncJsonHelper.java
+++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncJsonHelper.java
@@ -26,12 +26,15 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
+import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -39,10 +42,13 @@
 /**
  * Common json functionality used by the SDNC clients
  */
+@SuppressWarnings("java:S1192") // Same text in several traces
 class SdncJsonHelper {
     private static Gson gson = new GsonBuilder() //
         .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) //
         .create();
+    private static final String OUTPUT = "output";
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
     private SdncJsonHelper() {
     }
@@ -58,6 +64,7 @@
             }
             return Flux.fromIterable(arrayList);
         } catch (JSONException ex) { // invalid json
+            logger.debug("Invalid json {}", ex.getMessage());
             return Flux.error(ex);
         }
     }
@@ -69,20 +76,35 @@
         return gson.toJson(jsonObj);
     }
 
-    public static Mono<String> getValueFromResponse(String response, String key) {
+    public static <T> String createOutputJsonString(T params) {
+        JsonElement paramsJson = gson.toJsonTree(params);
+        JsonObject jsonObj = new JsonObject();
+        jsonObj.add(OUTPUT, paramsJson);
+        return gson.toJson(jsonObj);
+    }
+
+    public static Mono<JSONObject> getOutput(String response) {
         try {
             JSONObject outputJson = new JSONObject(response);
-            JSONObject responseParams = outputJson.getJSONObject("output");
-            if (!responseParams.has(key)) {
-                return Mono.just("");
-            }
-            String value = responseParams.get(key).toString();
-            return Mono.just(value);
+            JSONObject responseParams = outputJson.getJSONObject(OUTPUT);
+            return Mono.just(responseParams);
         } catch (JSONException ex) { // invalid json
+            logger.debug("Invalid json {}", ex.getMessage());
             return Mono.error(ex);
         }
     }
 
+    public static Mono<String> getValueFromResponse(String response, String key) {
+        return getOutput(response) //
+            .flatMap(responseParams -> {
+                if (!responseParams.has(key)) {
+                    return Mono.just("");
+                }
+                String value = responseParams.get(key).toString();
+                return Mono.just(value);
+            });
+    }
+
     public static Mono<String> extractPolicySchema(String inputString) {
         try {
             JSONObject jsonObject = new JSONObject(inputString);
@@ -90,6 +112,7 @@
             String schemaString = schemaObject.toString();
             return Mono.just(schemaString);
         } catch (JSONException ex) { // invalid json
+            logger.debug("Invalid json {}", ex.getMessage());
             return Mono.error(ex);
         }
     }
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java
index ece40e1..6860173 100644
--- a/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java
+++ b/policy-agent/src/main/java/org/oransc/policyagent/clients/SdncOscA1Client.java
@@ -30,6 +30,7 @@
 import java.util.Optional;
 
 import org.immutables.value.Value;
+import org.json.JSONObject;
 import org.oransc.policyagent.configuration.ControllerConfig;
 import org.oransc.policyagent.configuration.RicConfig;
 import org.oransc.policyagent.repository.Policy;
@@ -57,8 +58,8 @@
 
     @Value.Immutable
     @org.immutables.gson.Gson.TypeAdapters
-    public interface AdapterResponse {
-        public String body();
+    public interface AdapterOutput {
+        public Optional<String> body();
 
         public int httpStatus();
     }
@@ -217,6 +218,7 @@
             .body(body) //
             .build();
         final String inputJsonString = SdncJsonHelper.createInputJsonString(inputParams);
+        logger.debug("POST inputJsonString = {}", inputJsonString);
 
         return restClient
             .postWithAuthHeader(controllerUrl(rpcName), inputJsonString, this.controllerConfig.userName(),
@@ -224,17 +226,25 @@
             .flatMap(this::extractResponseBody);
     }
 
-    private Mono<String> extractResponseBody(String response) {
-        AdapterResponse output = gson.fromJson(response, ImmutableAdapterResponse.class);
-        String body = output.body();
+    private Mono<String> extractResponse(JSONObject responseOutput) {
+        AdapterOutput output = gson.fromJson(responseOutput.toString(), ImmutableAdapterOutput.class);
+        Optional<String> optionalBody = output.body();
+        String body = optionalBody.isPresent() ? optionalBody.get() : "";
         if (HttpStatus.valueOf(output.httpStatus()).is2xxSuccessful()) {
             return Mono.just(body);
-        }
-        byte[] responseBodyBytes = body.getBytes(StandardCharsets.UTF_8);
-        WebClientResponseException e = new WebClientResponseException(output.httpStatus(), "statusText", null,
-            responseBodyBytes, StandardCharsets.UTF_8, null);
+        } else {
+            logger.debug("Error response: {} {}", output.httpStatus(), body);
+            byte[] responseBodyBytes = body.getBytes(StandardCharsets.UTF_8);
+            WebClientResponseException e = new WebClientResponseException(output.httpStatus(), "statusText", null,
+                responseBodyBytes, StandardCharsets.UTF_8, null);
 
-        return Mono.error(e);
+            return Mono.error(e);
+        }
+    }
+
+    private Mono<String> extractResponseBody(String responseStr) {
+        return SdncJsonHelper.getOutput(responseStr) //
+            .flatMap(this::extractResponse);
     }
 
     private String controllerUrl(String rpcName) {
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java
index 35da96d..7cfe486 100644
--- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java
+++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RefreshConfigTask.java
@@ -127,16 +127,16 @@
             .filter(notUsed -> !this.isConsulUsed) //
             .flatMap(notUsed -> loadConfigurationFromFile()) //
             .onErrorResume(this::ignoreError) //
-            .doOnNext(json -> logger.debug("loadFromFile")) //
+            .doOnNext(json -> logger.debug("loadFromFile succeeded")) //
             .doOnTerminate(() -> logger.error("loadFromFile Terminate"));
 
         Flux<JsonObject> loadFromConsul = getEnvironment(systemEnvironment) //
             .flatMap(this::createCbsClient) //
             .flatMapMany(this::periodicConfigurationUpdates) //
             .onErrorResume(this::ignoreError) //
-            .doOnNext(json -> logger.debug("loadFromConsul")) //
+            .doOnNext(json -> logger.debug("loadFromConsul succeeded")) //
             .doOnNext(json -> this.isConsulUsed = true) //
-            .doOnTerminate(() -> logger.error("loadFromConsul Terminate"));
+            .doOnTerminate(() -> logger.error("loadFromConsul Terminated"));
 
         return Flux.merge(loadFromFile, loadFromConsul) //
             .flatMap(this::parseConfiguration) //
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java
index c2dd18e..2666d60 100644
--- a/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java
+++ b/policy-agent/src/main/java/org/oransc/policyagent/tasks/RicSupervision.java
@@ -74,11 +74,7 @@
     @Scheduled(fixedRate = 1000 * 60)
     public void checkAllRics() {
         logger.debug("Checking Rics starting");
-        createTask().subscribe( //
-            ric -> logger.debug("Ric: {} checked", ric.ric.name()), //
-            null, //
-            () -> logger.debug("Checking Rics completed") //
-        );
+        createTask().subscribe(null, null, () -> logger.debug("Checking all RICs completed"));
     }
 
     private Flux<RicData> createTask() {
@@ -95,7 +91,9 @@
             .flatMap(x -> checkRicPolicies(ricData)) //
             .flatMap(x -> ricData.ric.getLock().unlock()) //
             .doOnError(throwable -> ricData.ric.getLock().unlockBlocking()) //
-            .flatMap(x -> checkRicPolicyTypes(ricData)); //
+            .flatMap(x -> checkRicPolicyTypes(ricData)) //
+            .doOnNext(x -> logger.debug("Ric: {} checked OK", ricData.ric.name())) //
+            .doOnError(t -> logger.debug("Ric: {} check Failed, exception: {}", ricData.ric.name(), t.getMessage()));
     }
 
     private static class RicData {
diff --git a/policy-agent/src/test/java/org/oransc/policyagent/clients/SdncOscA1ClientTest.java b/policy-agent/src/test/java/org/oransc/policyagent/clients/SdncOscA1ClientTest.java
index 18ef097..2b60fe0 100644
--- a/policy-agent/src/test/java/org/oransc/policyagent/clients/SdncOscA1ClientTest.java
+++ b/policy-agent/src/test/java/org/oransc/policyagent/clients/SdncOscA1ClientTest.java
@@ -30,6 +30,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -37,8 +38,7 @@
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.stubbing.OngoingStubbing;
 import org.oransc.policyagent.clients.A1Client.A1ProtocolType;
-import org.oransc.policyagent.clients.SdncOscA1Client.AdapterRequest;
-import org.oransc.policyagent.clients.SdncOscA1Client.AdapterResponse;
+import org.oransc.policyagent.clients.SdncOscA1Client.AdapterOutput;
 import org.oransc.policyagent.configuration.ControllerConfig;
 import org.oransc.policyagent.configuration.ImmutableControllerConfig;
 import org.oransc.policyagent.repository.Policy;
@@ -105,7 +105,7 @@
         assertEquals(POLICY_TYPE_1_ID, policyTypeIds.get(0), "");
 
         String expUrl = RIC_1_URL + "/a1-p/policytypes";
-        AdapterRequest expectedParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expectedParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(expUrl) //
             .build();
         String expInput = SdncJsonHelper.createInputJsonString(expectedParams);
@@ -122,13 +122,12 @@
         return SdncOscA1Client.gson;
     }
 
-    private String createResponse(Object obj) {
-        AdapterResponse output = ImmutableAdapterResponse.builder() //
-            .body(gson().toJson(obj)) //
+    private String createResponse(Object body) {
+        AdapterOutput output = ImmutableAdapterOutput.builder() //
+            .body(gson().toJson(body)) //
             .httpStatus(200) //
             .build();
-
-        return gson().toJson(output);
+        return SdncJsonHelper.createOutputJsonString(output);
     }
 
     @Test
@@ -140,7 +139,7 @@
         List<String> returned = clientUnderTest.getPolicyIdentities().block();
         assertEquals(2, returned.size(), "");
 
-        AdapterRequest expectedParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expectedParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(policiesUrl()) //
             .build();
         String expInput = SdncJsonHelper.createInputJsonString(expectedParams);
@@ -164,7 +163,7 @@
             .block();
         assertEquals("OK", returned, "");
         final String expUrl = policiesUrl() + "/" + POLICY_1_ID;
-        AdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(expUrl) //
             .body(POLICY_JSON_VALID) //
             .build();
@@ -176,19 +175,19 @@
     @Test
     public void testPutPolicyRejected() {
         final String policyJson = "{}";
-        AdapterResponse adapterResponse = ImmutableAdapterResponse.builder() //
+        AdapterOutput adapterOutput = ImmutableAdapterOutput.builder() //
             .body("NOK") //
             .httpStatus(400) // ERROR
             .build();
 
-        String resp = gson().toJson(adapterResponse);
+        String resp = SdncJsonHelper.createOutputJsonString(adapterOutput);
         whenAsyncPostThenReturn(Mono.just(resp));
 
         Mono<String> returnedMono = clientUnderTest
             .putPolicy(A1ClientHelper.createPolicy(RIC_1_URL, POLICY_1_ID, policyJson, POLICY_TYPE_1_ID));
 
         final String expUrl = policiesUrl() + "/" + POLICY_1_ID;
-        AdapterRequest expRequestParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expRequestParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(expUrl) //
             .body(policyJson) //
             .build();
@@ -208,7 +207,7 @@
             .block();
         assertEquals("OK", returned, "");
         final String expUrl = policiesUrl() + "/" + POLICY_1_ID;
-        AdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(expUrl) //
             .build();
         String expInput = SdncJsonHelper.createInputJsonString(expectedInputParams);
@@ -228,7 +227,7 @@
         assertEquals("OK", returnedStatus, "unexpected status");
 
         final String expUrl = policiesUrl() + "/" + POLICY_1_ID + "/status";
-        AdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
+        ImmutableAdapterRequest expectedInputParams = ImmutableAdapterRequest.builder() //
             .nearRtRicUrl(expUrl) //
             .build();
         String expInput = SdncJsonHelper.createInputJsonString(expectedInputParams);
@@ -242,15 +241,29 @@
         whenPostReturnOkResponse();
         A1ProtocolType returnedVersion = clientUnderTest.getProtocolVersion().block();
         assertEquals(A1ProtocolType.SDNC_OSC_STD_V1_1, returnedVersion, "");
+
+        whenPostReturnOkResponseNoBody();
+        returnedVersion = clientUnderTest.getProtocolVersion().block();
+        assertEquals(A1ProtocolType.SDNC_OSC_STD_V1_1, returnedVersion, "");
     }
 
     private void whenPostReturnOkResponse() {
-        AdapterResponse adapterResponse = ImmutableAdapterResponse.builder() //
+        AdapterOutput adapterOutput = ImmutableAdapterOutput.builder() //
             .body("OK") //
             .httpStatus(200) //
             .build();
 
-        String resp = gson().toJson(adapterResponse);
+        String resp = SdncJsonHelper.createOutputJsonString(adapterOutput);
+        whenAsyncPostThenReturn(Mono.just(resp));
+    }
+
+    private void whenPostReturnOkResponseNoBody() {
+        AdapterOutput adapterOutput = ImmutableAdapterOutput.builder() //
+            .httpStatus(200) //
+            .body(Optional.empty()) //
+            .build();
+
+        String resp = SdncJsonHelper.createOutputJsonString(adapterOutput);
         whenAsyncPostThenReturn(Mono.just(resp));
     }