Fix required config validation

*Nested objects and list validation

Change-Id: Icf615a610dd6e669d66ae07adc1316ca167a671b
Issue-ID: DCAEGEN2-1327
Signed-off-by: emartin <ephraim.martin@est.tech>
diff --git a/src/main/java/org/onap/dcaegen2/services/pmmapper/model/MeasFilterConfig.java b/src/main/java/org/onap/dcaegen2/services/pmmapper/model/MeasFilterConfig.java
index 0f1aaa9..a7d211f 100644
--- a/src/main/java/org/onap/dcaegen2/services/pmmapper/model/MeasFilterConfig.java
+++ b/src/main/java/org/onap/dcaegen2/services/pmmapper/model/MeasFilterConfig.java
@@ -33,6 +33,7 @@
 @NoArgsConstructor

 public class MeasFilterConfig {

 

+  @GSONRequired

   @SerializedName("filters")

   public List<Filter> filters;

 

diff --git a/src/main/java/org/onap/dcaegen2/services/pmmapper/utils/RequiredFieldDeserializer.java b/src/main/java/org/onap/dcaegen2/services/pmmapper/utils/RequiredFieldDeserializer.java
index e956398..258b831 100644
--- a/src/main/java/org/onap/dcaegen2/services/pmmapper/utils/RequiredFieldDeserializer.java
+++ b/src/main/java/org/onap/dcaegen2/services/pmmapper/utils/RequiredFieldDeserializer.java
@@ -25,8 +25,13 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParseException;
 
+import lombok.NonNull;
+
 import java.lang.reflect.Field;
 import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
 
 
 /**
@@ -38,20 +43,35 @@
     @Override
     public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
         T obj = new Gson().fromJson(jsonElement, type);
-        for (Field field : obj.getClass().getDeclaredFields()) {
-            if (field.getAnnotation(GSONRequired.class) != null) {
-                field.setAccessible(true);
-                try {
-                    if (field.get(obj) == null) {
-                        throw new JsonParseException(String.format("Field: '%s', is required but not found.", field.getName()));
-                    }
-                } catch (Exception exception) {
-                    throw new JsonParseException("Failed to check fields.", exception);
-                }
+        validateRequiredFields(obj.getClass().getDeclaredFields(), obj);
+        return obj;
+    }
+
+    private void validateRequiredFields(@NonNull Field[] fields, @NonNull Object pojo) {
+        if (pojo instanceof List) {
+            final List<?> pojoList = (List<?>) pojo;
+            for (final Object pojoListPojo : pojoList) {
+                validateRequiredFields(pojoListPojo.getClass().getDeclaredFields(), pojoListPojo);
             }
         }
 
-        return obj;
+        Stream.of(fields)
+            .filter(field -> field.getAnnotation(GSONRequired.class) != null)
+            .forEach(field -> {
+                try {
+                    field.setAccessible(true);
+                    Object fieldObj = Optional.ofNullable(field.get(pojo))
+                        .orElseThrow(()-> new JsonParseException(
+                            String.format("Field '%s' in class '%s' is required but not found.",
+                            field.getName(), pojo.getClass().getSimpleName())));
+
+                    Field[] declaredFields = fieldObj.getClass().getDeclaredFields();
+                    validateRequiredFields(declaredFields, fieldObj);
+                }
+                catch (Exception exception) {
+                    throw new JsonParseException("Failed to check fields.", exception);
+                }
+            });
     }
 
 }
diff --git a/src/test/java/org/onap/dcaegen2/pmmapper/config/ConfigHandlerTests.java b/src/test/java/org/onap/dcaegen2/pmmapper/config/ConfigHandlerTests.java
index f6aa2a8..e2bb4f5 100644
--- a/src/test/java/org/onap/dcaegen2/pmmapper/config/ConfigHandlerTests.java
+++ b/src/test/java/org/onap/dcaegen2/pmmapper/config/ConfigHandlerTests.java
@@ -25,6 +25,8 @@
 import java.io.IOException;

 import java.io.InputStreamReader;

 import java.net.UnknownHostException;

+import java.util.HashMap;

+import java.util.Map;

 

 import static org.junit.Assert.assertEquals;

 import static org.junit.Assert.assertTrue;

@@ -48,6 +50,8 @@
 import org.powermock.modules.junit4.PowerMockRunner;

 

 import com.google.gson.Gson;

+import com.google.gson.JsonObject;

+import com.google.gson.JsonParser;

 

 import ch.qos.logback.classic.spi.ILoggingEvent;

 import ch.qos.logback.core.read.ListAppender;

@@ -114,8 +118,24 @@
 

     @Test

     public void mapper_parse_valid_json_missing_attributes() throws Exception {

-        when(sender.send(anyString())).thenReturn(getFileContents("incomplete_mapper_config.json"));

-        assertThrows(MapperConfigException.class, this::getMapperConfig);

+        Map<String,String> invalidConfigs = new HashMap<>();

+        invalidConfigs.put("streams_subscribes", "{}");

+        invalidConfigs.put("streams_publishes", "{}");

+        invalidConfigs.put("streams_publishes", null);

+        invalidConfigs.remove("streams_publishes");

+        invalidConfigs.put("pm-mapper-filter", null);

+        invalidConfigs.put("pm-mapper-filter", "{}");

+        invalidConfigs.put("pm-mapper-filter", "{ \"filters\": null},");

+        invalidConfigs.put("pm-mapper-filter", "{ \"filters\": [{\"pmDefVsn\": \"V9\"}] },");

+

+        invalidConfigs.forEach( (k,v) -> {

+            try {

+                when(sender.send(anyString())).thenReturn( getInvalidConfig(k,v));

+                assertThrows(MapperConfigException.class, this::getMapperConfig);

+            } catch (Exception e) {

+                e.printStackTrace();

+            }

+        });

     }

 

     private MapperConfig getMapperConfig()

@@ -136,4 +156,10 @@
         return fileAsString;

     }

 

+    private String getInvalidConfig(String validKey, String invalidValue) {

+        JsonObject invalidConfigJson = new JsonParser().parse(validMapperConfig).getAsJsonObject();

+        invalidConfigJson.addProperty(validKey, invalidValue);

+        return invalidConfigJson.toString();

+    }

+

 }

diff --git a/src/test/resources/incomplete_mapper_config.json b/src/test/resources/incomplete_mapper_config.json
deleted file mode 100644
index ed4ebd7..0000000
--- a/src/test/resources/incomplete_mapper_config.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{

-    "_comment": "This mapper config is missing streams_subscribes",

-    "pm-mapper-filter": {

-        "filters": "{[]}"

-    },

-    "3GPP.schema.file": "{\"3GPP_Schema\":\"./etc/3GPP_relaxed_schema.xsd\"}",

-    "streams_subscribes": null,

-    "streams_publishes": {

-        "pm_mapper_handle_out": {

-            "type": "message_router",

-            "aaf_password": null,

-            "dmaap_info": {

-                "topic_url": "https://we-are-message-router.us:3905/events/some-topic",

-                "client_role": null,

-                "location": null,

-                "client_id": null

-            },

-            "aaf_username": null

-        }

-    },

-    "some parameter": "unauthenticated.PM_VES_OUTPUT",

-    "some field": "1",

-    "services_calls": {}

-}
\ No newline at end of file