RR Complex types and Unit tests coverage 84/93484/9
authorSteve Siani <alphonse.steve.siani.djissitchi@ibm.com>
Tue, 13 Aug 2019 20:46:21 +0000 (16:46 -0400)
committerSteve Siani <alphonse.steve.siani.djissitchi@ibm.com>
Wed, 4 Sep 2019 14:12:24 +0000 (10:12 -0400)
Issue-ID: CCSDK-1615
Signed-off-by: Steve Siani <alphonse.steve.siani.djissitchi@ibm.com>
Change-Id: I1035aa05b4b038095c9d17fdcd00c462bed5085a

ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt
ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtilsTest.kt

index 2a9218d..7696442 100644 (file)
@@ -29,5 +29,4 @@ object ResourceResolutionConstants {
         const val RESOURCE_RESOLUTION_INPUT_OCCURRENCE = "occurrence"
         const val RESOURCE_RESOLUTION_INPUT_RESOURCE_ID = "resource-id"
         const val RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE = "resource-type"
-        val DATA_DICTIONARY_SECRET_SOURCE_TYPES = arrayOf("vault-data") //Add more secret data dictionary source type here
 }
\ No newline at end of file
index 01cfd72..117df1e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2017-2018 AT&T Intellectual Property.
- * Modifications Copyright © 2019 IBM.
+ * Modifications Copyright (c) 2019 IBM, Bell Canada.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.uti
 import com.fasterxml.jackson.databind.JsonNode
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.databind.node.ArrayNode
+import com.fasterxml.jackson.databind.node.NullNode
 import com.fasterxml.jackson.databind.node.ObjectNode
 import com.fasterxml.jackson.databind.node.TextNode
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
@@ -194,85 +195,214 @@ class ResourceAssignmentUtils {
         @Throws(BluePrintProcessorException::class)
         fun parseResponseNode(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
                               raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>): JsonNode {
+            try {
+                if ((resourceAssignment.property?.type).isNullOrEmpty()) {
+                    throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
+                }
+                val type = resourceAssignment.property!!.type
+                return when (type) {
+                    in BluePrintTypes.validPrimitiveTypes() -> {
+                        parseResponseNodeForPrimitiveTypes(responseNode, resourceAssignment, outputKeyMapping)
+                    }
+                    in BluePrintTypes.validCollectionTypes() -> {
+                        // Array Types
+                        parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
+                    }
+                    else -> {
+                        // Complex Types
+                        parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
+                    }
+                }
+            } catch (e: Exception) {
+                logger.error("Fail to parse response data, error message $e")
+                throw BluePrintProcessorException("${e.message}", e)
+            }
+        }
+
+        private fun parseResponseNodeForPrimitiveTypes(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
+                                                       outputKeyMapping: MutableMap<String, String>): JsonNode {
             val dName = resourceAssignment.dictionaryName
-            val dSource = resourceAssignment.dictionarySource
-            val type = nullToEmpty(resourceAssignment.property?.type)
-            lateinit var entrySchemaType: String
-            when (type) {
-                in BluePrintTypes.validPrimitiveTypes() -> {
-                    if (dSource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
-                        logger.info("For template key (${resourceAssignment.name}) setting value as ($responseNode)")
-                    val result = if (responseNode is ArrayNode)
-                        responseNode.get(0)
-                    else
-                        responseNode
-                    return if (result.isComplexType()) {
-                        check(result.has(outputKeyMapping[dName])) {
-                            "Fail to find output key mapping ($dName) in result."
+            logger.info("For template key (${resourceAssignment.name}) setting value as ($responseNode)")
+
+            var result: JsonNode? = responseNode
+            if (responseNode.isComplexType()) {
+                val key = outputKeyMapping.keys.firstOrNull()
+                var returnNode: JsonNode? = responseNode
+                if (responseNode is ArrayNode) {
+                    val arrayNode = responseNode.toList()
+                    val firstElement = if (key.isNullOrEmpty()) {
+                        arrayNode.first()
+                    }
+                    else{
+                        arrayNode.firstOrNull { element ->
+                            element.isComplexType() && element.has(outputKeyMapping[key])
+                        }
+                    }
+
+                    if (firstElement.isNull() || (firstElement!!.isComplexType() && !firstElement!!.has(outputKeyMapping[key]))
+                            || (!result!!.isComplexType() && result is NullNode)) {
+                        if (key.isNullOrEmpty()) {
+                            throw BluePrintProcessorException("Fail to find mapping in the responseNode.")
+                        }
+                        else {
+                            throw BluePrintProcessorException("Fail to find response with output key mapping ($key) in result.")
                         }
-                        result[outputKeyMapping[dName]]
-                    } else {
-                        result
                     }
+                    returnNode = firstElement
                 }
-                in BluePrintTypes.validCollectionTypes() -> {
-                    // Array Types
-                    entrySchemaType = checkNotEmpty(resourceAssignment.property?.entrySchema?.type) {
-                        "Entry schema is not defined for dictionary ($dName) info"
+                result = if (returnNode!!.isComplexType()) {
+                    returnNode[outputKeyMapping[key]]
+                }
+                else {
+                    returnNode
+                }
+            }
+            return result!!
+        }
+
+        private fun parseResponseNodeForCollection(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
+                                                   raRuntimeService: ResourceAssignmentRuntimeService,
+                                                   outputKeyMapping: MutableMap<String, String>): JsonNode {
+            val dName = resourceAssignment.dictionaryName
+            if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
+                throw BluePrintProcessorException("Couldn't get data type for dictionary type " +
+                        "(${resourceAssignment.property!!.type}) and dictionary name ($dName)")
+            }
+            val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
+
+            var arrayNode = JacksonUtils.objectMapper.createArrayNode()
+
+            if (outputKeyMapping.isNotEmpty()) {
+                when (responseNode) {
+                    is ArrayNode -> {
+                        val responseArrayNode = responseNode.toList()
+                        for (responseSingleJsonNode in responseArrayNode) {
+                            val arrayChildNode = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseSingleJsonNode,
+                                    outputKeyMapping, entrySchemaType)
+                            arrayNode.add(arrayChildNode)
+                        }
                     }
-                    val arrayNode = JacksonUtils.objectMapper.createArrayNode()
-                    lateinit var responseValueNode: JsonNode
-                    lateinit var propertyType: String
-                    outputKeyMapping.map {
-                        val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+                    is ObjectNode -> {
                         val responseArrayNode = responseNode.rootFieldsToMap()
-                        outer@ for ((key, responseSingleJsonNode) in responseArrayNode) {
-                            if (key == it.key) {
-                                if (entrySchemaType in BluePrintTypes.validPrimitiveTypes()) {
-                                    responseValueNode = responseSingleJsonNode
-                                    propertyType = entrySchemaType
-
-                                } else {
-                                    responseValueNode = responseSingleJsonNode.get(it.key)
-                                    propertyType = getPropertyType(raRuntimeService, entrySchemaType, it.key)
-                                }
-                                if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
-                                    logger.info("For List Type Resource: key (${it.key}), value ($responseValueNode), " +
-                                            "type  ({$propertyType})")
-                                JacksonUtils.populateJsonNodeValues(it.value,
-                                        responseValueNode, propertyType, arrayChildNode)
-                                arrayNode.add(arrayChildNode)
-                                break@outer
-                            }
-                        }
+                        val arrayNodeResult = parseObjectNodeWithOutputKeyMapping(responseArrayNode, outputKeyMapping, entrySchemaType)
+                        arrayNode.addAll(arrayNodeResult)
+                    }
+                    else -> {
+                        throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
                     }
-                    if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
-                        logger.info("For template key (${resourceAssignment.name}) setting value as ($arrayNode)")
-
-                    return arrayNode
                 }
-                else -> {
-                    // Complex Types
-                    entrySchemaType = checkNotEmpty(resourceAssignment.property?.type) {
-                        "Entry schema is not defined for dictionary ($dName) info"
+            }
+            else {
+                when (responseNode) {
+                    is ArrayNode -> {
+                        responseNode.forEach { elementNode ->
+                            arrayNode.add(elementNode)
+                        }
                     }
-                    val objectNode = JacksonUtils.objectMapper.createObjectNode()
+                    is ObjectNode -> {
+                        val responseArrayNode = responseNode.rootFieldsToMap()
+                        for ((key, responseSingleJsonNode) in responseArrayNode) {
+                            val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+                            JacksonUtils.populateJsonNodeValues(key, responseSingleJsonNode, entrySchemaType, arrayChildNode)
+                            arrayNode.add(arrayChildNode)
+                        }
+                    }
+                    else -> {
+                        arrayNode.add(responseNode)
+                    }
+                }
+            }
+
+            logger.info("For template key (${resourceAssignment.name}) setting value as ($arrayNode)")
+
+            return arrayNode
+        }
+
+        private fun parseResponseNodeForComplexType(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
+                                                    raRuntimeService: ResourceAssignmentRuntimeService,
+                                                    outputKeyMapping: MutableMap<String, String>): JsonNode {
+            val entrySchemaType = resourceAssignment.property!!.type
+            val dictionaryName = resourceAssignment.dictionaryName!!
+
+            var result: ObjectNode
+            if (checkOutputKeyMappingInDataTypeProperties(entrySchemaType, outputKeyMapping, raRuntimeService))
+            {
+                result = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseNode, outputKeyMapping, entrySchemaType)
+            }
+            else {
+                val childNode = JacksonUtils.objectMapper.createObjectNode()
+                if (outputKeyMapping.isNotEmpty()) {
                     outputKeyMapping.map {
-                        val responseKeyValue = responseNode.get(it.key)
-                        val propertyTypeForDataType = ResourceAssignmentUtils
-                                .getPropertyType(raRuntimeService, entrySchemaType, it.key)
+                        val responseKeyValue = if (responseNode.has(it.key)) {
+                            responseNode.get(it.key)
+                        }
+                        else {
+                            NullNode.getInstance()
+                        }
 
-                        if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
-                            logger.info("For List Type Resource: key (${it.key}), value ($responseKeyValue), type  ({$propertyTypeForDataType})")
-                        JacksonUtils.populateJsonNodeValues(it.value, responseKeyValue, propertyTypeForDataType, objectNode)
+                        JacksonUtils.populateJsonNodeValues(it.value,
+                                responseKeyValue, entrySchemaType, childNode)
                     }
+                }
+                else {
+                    JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
+                }
+                result = childNode
+            }
+            return result
+        }
+
+        private fun parseArrayNodeElementWithOutputKeyMapping(raRuntimeService: ResourceAssignmentRuntimeService,
+                                                              responseSingleJsonNode: JsonNode, outputKeyMapping:
+                                                              MutableMap<String, String>, entrySchemaType: String): ObjectNode {
+            val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+
+            outputKeyMapping.map {
+                val responseKeyValue = if (responseSingleJsonNode.has(it.key)) {
+                    responseSingleJsonNode.get(it.key)
+                }
+                else {
+                    NullNode.getInstance()
+                }
+                val propertyTypeForDataType = ResourceAssignmentUtils
+                        .getPropertyType(raRuntimeService, entrySchemaType, it.key)
+
+                logger.info("For List Type Resource: key (${it.key}), value ($responseKeyValue), " +
+                        "type  ({$propertyTypeForDataType})")
 
-                    if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
-                        logger.info("For template key (${resourceAssignment.name}) setting value as ($objectNode)")
+                JacksonUtils.populateJsonNodeValues(it.value,
+                        responseKeyValue, propertyTypeForDataType, arrayChildNode)
+            }
+
+            return arrayChildNode
+        }
+
+        private fun parseObjectNodeWithOutputKeyMapping(responseArrayNode: MutableMap<String, JsonNode>,
+                                                        outputKeyMapping: MutableMap<String, String>,
+                                                        entrySchemaType: String): ArrayNode {
+            val arrayNode = JacksonUtils.objectMapper.createArrayNode()
+            outputKeyMapping.map {
+                val objectNode = JacksonUtils.objectMapper.createObjectNode()
+                val responseSingleJsonNode = responseArrayNode.filterKeys { key -> key == it.key }.entries.firstOrNull()
 
-                    return objectNode
+                if (responseSingleJsonNode == null) {
+                    JacksonUtils.populateJsonNodeValues(it.value, NullNode.getInstance(), entrySchemaType, objectNode)
                 }
+                else
+                {
+                    JacksonUtils.populateJsonNodeValues(it.value, responseSingleJsonNode.value, entrySchemaType, objectNode)
+                }
+                arrayNode.add(objectNode)
             }
+
+            return arrayNode
+        }
+
+        private fun checkOutputKeyMappingInDataTypeProperties(dataTypeName: String, outputKeyMapping: MutableMap<String, String>,
+                                                              raRuntimeService: ResourceAssignmentRuntimeService): Boolean {
+            val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
+            val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
+            return result == null
         }
     }
 }
\ No newline at end of file
index 9b87c12..9365c3e 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (c) 2019 IBM, Bell Canada.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * ============LICENSE_END=========================================================
  */
 
-
 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
 
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.node.NullNode
 import com.fasterxml.jackson.databind.node.TextNode
+import io.mockk.every
+import io.mockk.spyk
+import org.junit.Before
 import org.junit.Test
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
+import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EntrySchema
 import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
 import kotlin.test.assertEquals
 
+data class IpAddress(val port: String, val ip: String)
+data class Host(val name: String, val ipAddress: IpAddress)
+data class ExpectedResponseIp(val ip: String)
+data class ExpectedResponsePort(val port: String)
+
 class ResourceAssignmentUtilsTest {
+    private lateinit var resourceAssignmentRuntimeService: ResourceAssignmentRuntimeService
+
+    @Before
+    fun setup() {
+
+        val bluePrintContext = BluePrintMetadataUtils.getBluePrintContext(
+                "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+
+        resourceAssignmentRuntimeService = spyk(ResourceAssignmentRuntimeService("1234", bluePrintContext))
+
+        val propertiesDefinition1 = PropertyDefinition().apply {
+            type = "string"
+            id = "port"
+        }
+
+        val propertiesDefinition2 = PropertyDefinition().apply {
+            type = "string"
+            id = "ip"
+        }
+
+        val propertiesDefinition3 = PropertyDefinition().apply {
+            type = "string"
+            id = "name"
+        }
+
+        val propertiesDefinition4 = PropertyDefinition().apply {
+            type = "ip-address"
+            id = "ipAddress"
+        }
+
+        var mapOfPropertiesIpAddress = mutableMapOf<String, PropertyDefinition>()
+        mapOfPropertiesIpAddress["port"] = propertiesDefinition1
+        mapOfPropertiesIpAddress["ip"] = propertiesDefinition2
+
+        var mapOfPropertiesHost = mutableMapOf<String, PropertyDefinition>()
+        mapOfPropertiesHost["name"] = propertiesDefinition3
+        mapOfPropertiesHost["ipAddress"] = propertiesDefinition4
+
+        val myDataTypeIpaddress = DataType().apply {
+            id = "ip-address"
+            properties = mapOfPropertiesIpAddress
+        }
+
+        val myDataTypeHost = DataType().apply {
+            id = "host"
+            properties = mapOfPropertiesHost
+        }
+
+        every { resourceAssignmentRuntimeService.bluePrintContext().dataTypeByName("ip-address") } returns myDataTypeIpaddress
+
+        every { resourceAssignmentRuntimeService.bluePrintContext().dataTypeByName("host") } returns myDataTypeHost
+
+        every { resourceAssignmentRuntimeService.setNodeTemplateAttributeValue(any(), any(), any()) } returns Unit
+    }
 
     @Test
     fun `generateResourceDataForAssignments - positive test`() {
@@ -43,7 +114,6 @@ class ResourceAssignmentUtilsTest {
         //then the assignment should produce a valid result
         val expected = "{\n" + "  \"pnf-id\" : \"valid_value\"\n" + "}"
         assertEquals(expected, outcome, "unexpected outcome generated")
-
     }
 
     @Test
@@ -76,4 +146,243 @@ class ResourceAssignmentUtilsTest {
         }
         return resourceAssignmentForTest
     }
+
+    @Test
+    fun parseResponseNodeTestForPrimitivesTypes(){
+        // Input values for primitive type
+        val keyValue = mutableMapOf<String, String>()
+        keyValue["value"]= "1.2.3.1"
+        val expectedPrimitiveType = TextNode("1.2.3.1")
+
+        var outcome = prepareResponseNodeForTest("sample-value", "string",
+                "", "1.2.3.1".asJsonPrimitive())
+        assertEquals(expectedPrimitiveType, outcome, "Unexpected outcome returned for primitive type of simple String")
+        outcome = prepareResponseNodeForTest("sample-key-value", "string", "", keyValue)
+        assertEquals(expectedPrimitiveType, outcome, "Unexpected outcome returned for primitive type of key-value String")
+    }
+
+    @Test
+    fun parseResponseNodeTestForCollectionsOfString(){
+        // Input values for collection type
+        val mapOfString = mutableMapOf<String, String>()
+        mapOfString["value1"] = "1.2.3.1"
+        mapOfString["port"] = "8888"
+        mapOfString["value2"] = "1.2.3.2"
+        val arrayOfKeyValue = arrayListOf(ExpectedResponseIp("1.2.3.1"),
+                ExpectedResponsePort( "8888"), ExpectedResponseIp("1.2.3.2"))
+
+        val mutableMapKeyValue = mutableMapOf<String, String>()
+        mutableMapKeyValue["value1"] = "1.2.3.1"
+        mutableMapKeyValue["port"] = "8888"
+
+        //List
+        val expectedListOfString = arrayOfKeyValue.asJsonType()
+        var outcome = prepareResponseNodeForTest("listOfString", "list",
+                "string", mapOfString.asJsonType())
+        assertEquals(expectedListOfString, outcome, "unexpected outcome returned for list of String")
+
+        //Map
+        val expectedMapOfString = mutableMapOf<String, JsonNode>()
+        expectedMapOfString["ip"] = "1.2.3.1".asJsonPrimitive()
+        expectedMapOfString["port"] = "8888".asJsonPrimitive()
+
+        val arrayNode = JacksonUtils.objectMapper.createArrayNode()
+        expectedMapOfString.map {
+            val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+            arrayChildNode.set(it.key, it.value)
+            arrayNode.add(arrayChildNode)
+        }
+        val arrayChildNode1 = JacksonUtils.objectMapper.createObjectNode()
+        arrayChildNode1.set("ip", NullNode.getInstance())
+        arrayNode.add(arrayChildNode1)
+        outcome = prepareResponseNodeForTest("mapOfString", "map", "string",
+                mutableMapKeyValue.asJsonType())
+        assertEquals(arrayNode, outcome, "unexpected outcome returned for map of String")
+    }
+
+    @Test
+    fun parseResponseNodeTestForCollectionsOfJsonNode(){
+        // Input values for collection type
+        val mapOfString = mutableMapOf<String, JsonNode>()
+        mapOfString["value1"] = "1.2.3.1".asJsonPrimitive()
+        mapOfString["port"] = "8888".asJsonPrimitive()
+        mapOfString["value2"] = "1.2.3.2".asJsonPrimitive()
+        val arrayOfKeyValue = arrayListOf(ExpectedResponseIp("1.2.3.1"),
+                ExpectedResponsePort( "8888"), ExpectedResponseIp("1.2.3.2"))
+
+        val mutableMapKeyValue = mutableMapOf<String, JsonNode>()
+        mutableMapKeyValue["value1"] = "1.2.3.1".asJsonPrimitive()
+        mutableMapKeyValue["port"] = "8888".asJsonPrimitive()
+
+        //List
+        val expectedListOfString = arrayOfKeyValue.asJsonType()
+        var outcome = prepareResponseNodeForTest("listOfString", "list",
+                "string", mapOfString.asJsonType())
+        assertEquals(expectedListOfString, outcome, "unexpected outcome returned for list of String")
+
+        //Map
+        val expectedMapOfString = mutableMapOf<String, JsonNode>()
+        expectedMapOfString["ip"] = "1.2.3.1".asJsonPrimitive()
+        expectedMapOfString["port"] = "8888".asJsonPrimitive()
+        val arrayNode = JacksonUtils.objectMapper.createArrayNode()
+        expectedMapOfString.map {
+            val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+            arrayChildNode.set(it.key, it.value)
+            arrayNode.add(arrayChildNode)
+        }
+        val arrayChildNode1 = JacksonUtils.objectMapper.createObjectNode()
+        arrayChildNode1.set("ip", NullNode.getInstance())
+        arrayNode.add(arrayChildNode1)
+        outcome = prepareResponseNodeForTest("mapOfString", "map",
+                "string", mutableMapKeyValue.asJsonType())
+        assertEquals(arrayNode, outcome, "unexpected outcome returned for map of String")
+    }
+
+    @Test
+    fun parseResponseNodeTestForCollectionsOfComplexType(){
+        // Input values for collection type
+        val mapOfComplexType = mutableMapOf<String, JsonNode>()
+        mapOfComplexType["value1"] = IpAddress("1111", "1.2.3.1").asJsonType()
+        mapOfComplexType["value2"] = IpAddress("2222", "1.2.3.2").asJsonType()
+        mapOfComplexType["value3"] = IpAddress("3333", "1.2.3.3").asJsonType()
+
+        //List
+        val arrayNode = JacksonUtils.objectMapper.createArrayNode()
+        mapOfComplexType.map {
+            val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
+            arrayChildNode.set("ipAddress", it.value)
+            arrayNode.add(arrayChildNode)
+        }
+        var outcome = prepareResponseNodeForTest("listOfMyDataType", "list",
+                "ip-address", mapOfComplexType.asJsonType())
+        assertEquals(arrayNode, outcome, "unexpected outcome returned for list of String")
+    }
+
+    @Test
+    fun `parseResponseNodeTestForComplexType find one output key mapping`(){
+        // Input values for complex type
+        val objectNode = JacksonUtils.objectMapper.createObjectNode()
+
+        // Input values for collection type
+        val mapOfComplexType = mutableMapOf<String, JsonNode>()
+        mapOfComplexType["value"] = Host("my-ipAddress", IpAddress("1111", "1.2.3.1")).asJsonType()
+        mapOfComplexType["port"] = "8888".asJsonType()
+        mapOfComplexType["something"] = "1.2.3.2".asJsonType()
+
+        val expectedComplexType = objectNode.set("ipAddress", Host("my-ipAddress", IpAddress("1111", "1.2.3.1")).asJsonType())
+        val outcome = prepareResponseNodeForTest("complexTypeOneKeys", "host",
+                "", mapOfComplexType.asJsonType())
+        assertEquals(expectedComplexType, outcome, "Unexpected outcome returned for complex type")
+    }
+
+    @Test
+    fun `parseResponseNodeTestForComplexType find all output key mapping`(){
+        // Input values for complex type
+        val objectNode = JacksonUtils.objectMapper.createObjectNode()
+
+        // Input values for collection type
+        val mapOfComplexType = mutableMapOf<String, JsonNode>()
+        mapOfComplexType["name"] = "my-ipAddress".asJsonType()
+        mapOfComplexType["ipAddress"] = IpAddress("1111", "1.2.3.1").asJsonType()
+
+        val expectedComplexType = Host("my-ipAddress", IpAddress("1111", "1.2.3.1")).asJsonType()
+        val outcome = prepareResponseNodeForTest("complexTypeAllKeys", "host",
+                "", mapOfComplexType.asJsonType())
+        assertEquals(expectedComplexType, outcome, "Unexpected outcome returned for complex type")
+    }
+
+    private fun prepareResponseNodeForTest(dictionary_source: String, sourceType: String, entrySchema: String,
+                                           response: Any): JsonNode {
+
+        val resourceAssignment = when (sourceType) {
+            "list", "map" -> {
+                prepareRADataDictionaryCollection(dictionary_source, sourceType, entrySchema)
+            }
+            "string" -> {
+                prepareRADataDictionaryOfPrimaryType(dictionary_source)
+            }
+            else -> {
+                prepareRADataDictionaryComplexType(dictionary_source, sourceType, entrySchema)
+            }
+        }
+
+        val responseNode = checkNotNull(JacksonUtils.getJsonNode(response)) {
+            "Failed to get database query result into Json node."
+        }
+
+        val outputKeyMapping = prepareOutputKeyMapping(dictionary_source)
+
+        return ResourceAssignmentUtils.parseResponseNode(responseNode, resourceAssignment, resourceAssignmentRuntimeService, outputKeyMapping)
+    }
+
+    private fun prepareRADataDictionaryOfPrimaryType(dictionary_source: String): ResourceAssignment {
+        return ResourceAssignment().apply {
+            name = "ipAddress"
+            dictionaryName = "sample-ip"
+            dictionarySource = "$dictionary_source"
+            property = PropertyDefinition().apply {
+                type = "string"
+            }
+        }
+    }
+
+    private fun prepareRADataDictionaryCollection(dictionary_source: String, sourceType: String, schema: String): ResourceAssignment {
+        return ResourceAssignment().apply {
+            name = "ipAddress-list"
+            dictionaryName = "sample-licenses"
+            dictionarySource = "$dictionary_source"
+            property = PropertyDefinition().apply {
+                type = "$sourceType"
+                entrySchema = EntrySchema().apply {
+                    type = "$schema"
+                }
+            }
+        }
+    }
+
+    private fun prepareRADataDictionaryComplexType(dictionary_source: String, sourceType: String, schema: String): ResourceAssignment {
+        return ResourceAssignment().apply {
+            name = "ipAddress-complexType"
+            dictionaryName = "sample-licenses"
+            dictionarySource = "$dictionary_source"
+            property = PropertyDefinition().apply {
+                type = "$sourceType"
+            }
+        }
+    }
+
+    private fun prepareOutputKeyMapping(dictionary_source: String): MutableMap<String, String> {
+        val outputMapping = mutableMapOf<String, String>()
+
+        when (dictionary_source) {
+            "listOfString", "mapOfString" -> {
+                //List of string
+                outputMapping["value1"] = "ip"
+                outputMapping["port"] = "port"
+                outputMapping["value2"] = "ip"
+            }
+            "listOfMyDataType", "mapOfMyDataType" -> {
+                //List or map of complex Type
+                outputMapping["value1"] = "ipAddress"
+                outputMapping["value2"] = "ipAddress"
+                outputMapping["value3"] = "ipAddress"
+            }
+            "sample-key-value", "sample-value" -> {
+                //Primary Type
+                if (dictionary_source=="sample-key-value")
+                    outputMapping["sample-ip"] = "value"
+            }
+            else -> {
+                //Complex Type
+                if (dictionary_source == "complexTypeOneKeys")
+                    outputMapping["value"] = "ipAddress"
+                else {
+                    outputMapping["name"] = "name"
+                    outputMapping["ipAddress"] = "ipAddress"
+                }
+
+            }
+        }
+        return outputMapping
+    }
 }
\ No newline at end of file