diff options
Diffstat (limited to 'ms/blueprintsprocessor')
9 files changed, 535 insertions, 100 deletions
diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt index eafcaf44b..180ad7b48 100644 --- a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt +++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutor.kt @@ -15,9 +15,10 @@ * limitations under the License. */ -package org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor +package org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots import com.github.fge.jsonpatch.diff.JsonDiff +import org.apache.logging.log4j.util.Strings import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot.Status.RUNNING @@ -144,18 +145,20 @@ open class ComponentConfigSnapshotsExecutor(private val cfgSnapshotService: Reso */ private suspend fun compareConfigurationSnapshot(resourceId: String, resourceType: String, contentType : String) { + val cfgRunning = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, RUNNING) + val cfgCandidate = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, CANDIDATE) + + if (cfgRunning.isEmpty() || cfgCandidate.isEmpty()) { + setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, Strings.EMPTY) + return + } + when (contentType.toUpperCase()) { DIFF_JSON -> { - val cfgRunning = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, RUNNING) - val cfgCandidate = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, CANDIDATE) - val patchNode = JsonDiff.asJson(cfgRunning.jsonAsJsonType(), cfgCandidate.jsonAsJsonType()) setNodeOutputProperties(OUTPUT_STATUS_SUCCESS, patchNode.toString()) } DIFF_XML -> { - val cfgRunning = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, RUNNING) - val cfgCandidate = cfgSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, CANDIDATE) - val myDiff = DiffBuilder .compare(Input.fromString(cfgRunning)) .withTest(Input.fromString(cfgCandidate)) diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt index 50c90f332..5fcba5b0c 100644 --- a/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt +++ b/ms/blueprintsprocessor/functions/config-snapshots/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotService.kt @@ -17,6 +17,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.apache.logging.log4j.util.Strings import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot.Status.RUNNING import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException import org.slf4j.LoggerFactory @@ -40,7 +41,7 @@ class ResourceConfigSnapshotService(private val repository: ResourceConfigSnapsh status : ResourceConfigSnapshot.Status = RUNNING): String = withContext(Dispatchers.IO) { repository.findByResourceIdAndResourceTypeAndStatus(resourceId, resourceType, status) - ?.config_snapshot ?: throw NoSuchElementException() + ?.config_snapshot ?: Strings.EMPTY } suspend fun write(snapshot: String, resId: String, resType: String, diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt index 79dd93037..c212908b9 100644 --- a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt +++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/ComponentConfigSnapshotsExecutorTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor +package org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots import com.fasterxml.jackson.databind.JsonNode import kotlinx.coroutines.runBlocking @@ -27,11 +27,12 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertyConfiguration import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.db.BluePrintDBLibConfiguration -import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.DIFF_JSON -import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.DIFF_XML -import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_DIFF -import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_FETCH -import org.onap.ccsdk.cds.blueprintsprocessor.functions.ansible.executor.ComponentConfigSnapshotsExecutor.Companion.OPERATION_STORE +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor.Companion.DIFF_JSON +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor.Companion.DIFF_XML +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor.Companion.OPERATION_DIFF +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor.Companion.OPERATION_FETCH +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.ComponentConfigSnapshotsExecutor.Companion.OPERATION_STORE import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshotService import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException @@ -192,9 +193,7 @@ class ComponentConfigSnapshotsExecutorTest { return@runBlocking } - // then; we should get error and the PAYLOAD payload in our output properties - assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size > 0 ) - assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_ERROR.asJsonPrimitive(), + assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_SUCCESS.asJsonPrimitive(), bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName, ComponentConfigSnapshotsExecutor.OUTPUT_STATUS)) } @@ -234,7 +233,10 @@ class ComponentConfigSnapshotsExecutorTest { runBlocking { // when; asking for unknown content type diff operation; should get an error response try { - prepareRequestProperties(OPERATION_DIFF, "asdasd", "PNF", "YANG") + val resId = "121111" + val resType = "PNF" + cfgSnapshotService.write("snapshotConfig", resId, resType, ResourceConfigSnapshot.Status.CANDIDATE) + prepareRequestProperties(OPERATION_DIFF, resId, resType, "YANG") cfgSnapshotComponent.processNB(executionRequest) @@ -245,7 +247,6 @@ class ComponentConfigSnapshotsExecutorTest { } // then; we should get error in our output properties - assertTrue( bluePrintRuntimeService.getBluePrintError().errors.size == 1 ) assertEquals(ComponentConfigSnapshotsExecutor.OUTPUT_STATUS_ERROR.asJsonPrimitive(), bluePrintRuntimeService.getNodeTemplateAttributeValue(nodeTemplateName, ComponentConfigSnapshotsExecutor.OUTPUT_STATUS)) diff --git a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt index 2830cb547..3c989c154 100644 --- a/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt +++ b/ms/blueprintsprocessor/functions/config-snapshots/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/config/snapshots/db/ResourceConfigSnapshotServiceTest.kt @@ -48,18 +48,6 @@ class ResourceConfigSnapshotServiceTest { } } - @Test(expected = NoSuchElementException::class) - fun notFoundEntryReturnsExceptionTest() { - val tr = ResourceConfigSnapshot() - runBlocking { - every { - cfgRepository.findByResourceIdAndResourceTypeAndStatus(any(), any(), any()) - } returns tr - val snap = cfgService.findByResourceIdAndResourceTypeAndStatus("MISSING_ID", "UNKNOWN_TYPE") - assertTrue ( snap.isBlank(), "Not found but returned a non empty string" ) - } - } - @Test fun createNewResourceConfigSnapshotTest() { val tr = ResourceConfigSnapshot() diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt index 2a9218df3..769644288 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt @@ -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 diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt index 01cfd723b..117df1e5b 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt @@ -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 diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtilsTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtilsTest.kt index 9b87c12eb..9365c3e34 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtilsTest.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtilsTest.kt @@ -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. @@ -18,16 +19,86 @@ * ============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 diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt index c45ebc127..5a6ba0661 100644 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt @@ -18,6 +18,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.core.api.data import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.node.ObjectNode import io.swagger.annotations.ApiModelProperty @@ -40,6 +41,7 @@ open class ExecutionServiceInput { " and the input for resource resolution located within the xxx-request block, contained within xxx-properties") lateinit var payload: ObjectNode @get:ApiModelProperty(hidden = true) + @get:JsonIgnore var stepData: StepData? = null } @@ -56,6 +58,7 @@ open class ExecutionServiceOutput { " and the input for resource resolution located within the xxx-request block, contained within xxx-properties") lateinit var payload: ObjectNode @get:ApiModelProperty(hidden = true) + @get:JsonIgnore var stepData: StepData? = null } diff --git a/ms/blueprintsprocessor/modules/inbounds/configs-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotControllerTest.kt b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotControllerTest.kt index c3f18fcba..b4c1ad0e0 100644 --- a/ms/blueprintsprocessor/modules/inbounds/configs-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotControllerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotControllerTest.kt @@ -129,14 +129,15 @@ class ResourceConfigSnapshotControllerTest { } @Test - fun `get returns 404 if entry not found`() { + fun `get returns 200 if entry not found`() { runBlocking { webTestClient .get() .uri("/api/v1/configs?resourceId=MISSING&resourceType=PNF") .exchange() - .expectStatus().isNotFound + .expectStatus().is2xxSuccessful + .expectBody() } } |