2  * Copyright © 2017-2018 AT&T Intellectual Property.
 
   3  * Modifications Copyright (c) 2019 IBM, Bell Canada.
 
   5  * Licensed under the Apache License, Version 2.0 (the "License");
 
   6  * you may not use this file except in compliance with the License.
 
   7  * You may obtain a copy of the License at
 
   9  *     http://www.apache.org/licenses/LICENSE-2.0
 
  11  * Unless required by applicable law or agreed to in writing, software
 
  12  * distributed under the License is distributed on an "AS IS" BASIS,
 
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  14  * See the License for the specific language governing permissions and
 
  15  * limitations under the License.
 
  18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
 
  20 import com.fasterxml.jackson.databind.JsonNode
 
  21 import com.fasterxml.jackson.databind.ObjectMapper
 
  22 import com.fasterxml.jackson.databind.node.ArrayNode
 
  23 import com.fasterxml.jackson.databind.node.NullNode
 
  24 import com.fasterxml.jackson.databind.node.ObjectNode
 
  25 import com.fasterxml.jackson.databind.node.TextNode
 
  26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
 
  27 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
 
  28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 
  29 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
 
  30 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
 
  31 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
 
  32 import org.onap.ccsdk.cds.controllerblueprints.core.checkFileExists
 
  33 import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
 
  34 import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants.LOG_REDACTED
 
  35 import org.onap.ccsdk.cds.controllerblueprints.core.isComplexType
 
  36 import org.onap.ccsdk.cds.controllerblueprints.core.isNotEmpty
 
  37 import org.onap.ccsdk.cds.controllerblueprints.core.isNullOrMissing
 
  38 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
 
  39 import org.onap.ccsdk.cds.controllerblueprints.core.nullToEmpty
 
  40 import org.onap.ccsdk.cds.controllerblueprints.core.rootFieldsToMap
 
  41 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
 
  42 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonReactorUtils
 
  43 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 
  44 import org.onap.ccsdk.cds.controllerblueprints.core.utils.PropertyDefinitionUtils.Companion.hasLogProtect
 
  45 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.DictionaryMetadataEntry
 
  46 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.KeyIdentifier
 
  47 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResolutionSummary
 
  48 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
 
  49 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
 
  50 import org.slf4j.LoggerFactory
 
  53 class ResourceAssignmentUtils {
 
  56         private val logger = LoggerFactory.getLogger(ResourceAssignmentUtils::class.toString())
 
  58         suspend fun resourceDefinitions(blueprintBasePath: String): MutableMap<String, ResourceDefinition> {
 
  59             val dictionaryFile = normalizedFile(
 
  60                 blueprintBasePath, BluePrintConstants.TOSCA_DEFINITIONS_DIR,
 
  61                 ResourceResolutionConstants.FILE_NAME_RESOURCE_DEFINITION_TYPES
 
  63             checkFileExists(dictionaryFile) { "resource definition file(${dictionaryFile.absolutePath}) is missing" }
 
  64             return JacksonReactorUtils.getMapFromFile(dictionaryFile, ResourceDefinition::class.java)
 
  67         @Throws(BluePrintProcessorException::class)
 
  68         fun setResourceDataValue(
 
  69             resourceAssignment: ResourceAssignment,
 
  70             raRuntimeService: ResourceAssignmentRuntimeService,
 
  73             // TODO("See if Validation is needed in future with respect to conversion and Types")
 
  74             return setResourceDataValue(resourceAssignment, raRuntimeService, value.asJsonType())
 
  77         @Throws(BluePrintProcessorException::class)
 
  78         fun setResourceDataValue(
 
  79             resourceAssignment: ResourceAssignment,
 
  80             raRuntimeService: ResourceAssignmentRuntimeService,
 
  83             val resourceProp = checkNotNull(resourceAssignment.property) {
 
  84                 "Failed in setting resource value for resource mapping $resourceAssignment"
 
  86             checkNotEmpty(resourceAssignment.name) {
 
  87                 "Failed in setting resource value for resource mapping $resourceAssignment"
 
  90             if (resourceAssignment.dictionaryName.isNullOrEmpty()) {
 
  91                 resourceAssignment.dictionaryName = resourceAssignment.name
 
  93                     "Missing dictionary key, setting with template key (${resourceAssignment.name}) " +
 
  94                             "as dictionary key (${resourceAssignment.dictionaryName})"
 
  99                 if (resourceProp.type.isNotEmpty()) {
 
 100                     val metadata = resourceAssignment.property!!.metadata
 
 101                     val valueToPrint = getValueToLog(metadata, value)
 
 103                         "Setting Resource Value ($valueToPrint) for Resource Name " +
 
 104                                 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
 
 105                                 "of type (${resourceProp.type})"
 
 107                     setResourceValue(resourceAssignment, raRuntimeService, value)
 
 108                     resourceAssignment.updatedDate = Date()
 
 109                     resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
 
 110                     resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
 
 112             } catch (e: Exception) {
 
 113                 throw BluePrintProcessorException(
 
 114                     "Failed in setting value for template key " +
 
 115                             "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
 
 116                             "type (${resourceProp.type}) with error message (${e.message})", e
 
 121         private fun setResourceValue(
 
 122             resourceAssignment: ResourceAssignment,
 
 123             raRuntimeService: ResourceAssignmentRuntimeService,
 
 126             // TODO("See if Validation is needed wrt to type before storing")
 
 127             raRuntimeService.putResolutionStore(resourceAssignment.name, value)
 
 128             raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
 
 129             resourceAssignment.property!!.value = value
 
 132         fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
 
 133             if (isNotEmpty(resourceAssignment.name)) {
 
 134                 resourceAssignment.updatedDate = Date()
 
 135                 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
 
 136                 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
 
 137                 resourceAssignment.message = message
 
 141         @Throws(BluePrintProcessorException::class)
 
 142         fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
 
 143             val resourceProp = checkNotNull(resourceAssignment.property) {
 
 144                 "Failed to populate mandatory resource resource mapping $resourceAssignment"
 
 146             if (resourceProp.required != null && resourceProp.required!! && resourceProp.value.isNullOrMissing()) {
 
 147                 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
 
 148                 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
 
 152         @Throws(BluePrintProcessorException::class)
 
 153         fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
 
 156                 val mapper = ObjectMapper()
 
 157                 val root: ObjectNode = mapper.createObjectNode()
 
 159                 var containsLogProtected = false
 
 160                 assignments.forEach {
 
 161                     if (isNotEmpty(it.name) && it.property != null) {
 
 163                         val metadata = it.property!!.metadata
 
 164                         val type = nullToEmpty(it.property?.type).toLowerCase()
 
 165                         val value = useDefaultValueIfNull(it, rName)
 
 166                         val valueToPrint = getValueToLog(metadata, value)
 
 167                         containsLogProtected = hasLogProtect(metadata)
 
 168                         logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
 
 169                         root.set<JsonNode>(rName, value)
 
 172                 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
 
 174                 if (!containsLogProtected) {
 
 175                     logger.info("Generated Resource Param Data ($result)")
 
 177             } catch (e: Exception) {
 
 178                 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
 
 184         @Throws(BluePrintProcessorException::class)
 
 185         fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
 
 186             val data: MutableMap<String, JsonNode> = hashMapOf()
 
 187             assignments.forEach {
 
 188                 if (isNotEmpty(it.name) && it.property != null) {
 
 190                     val metadata = it.property!!.metadata
 
 191                     val type = nullToEmpty(it.property?.type).toLowerCase()
 
 192                     val value = useDefaultValueIfNull(it, rName)
 
 193                     val valueToPrint = getValueToLog(metadata, value)
 
 195                     logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
 
 202         fun generateResolutionSummaryData(
 
 203             resourceAssignments: List<ResourceAssignment>,
 
 204             resourceDefinitions: Map<String, ResourceDefinition>
 
 206             val emptyTextNode = TextNode.valueOf("")
 
 207             val resolutionSummaryList = resourceAssignments.map {
 
 208                 val definition = resourceDefinitions[it.name]
 
 209                 val description = definition?.property?.description ?: ""
 
 210                 val value = it.property?.value
 
 211                         ?.let { v -> if (v.isNullOrMissing()) emptyTextNode else v }
 
 214                 var payload: JsonNode = definition?.sources?.get(it.dictionarySource)
 
 215                         ?.properties?.get("resolved-payload")
 
 216                         ?.let { p -> if (p.isNullOrMissing()) emptyTextNode else p }
 
 219                 val metadata = definition?.property?.metadata
 
 220                         ?.map { e -> DictionaryMetadataEntry(e.key, e.value) }
 
 221                         ?.toMutableList() ?: mutableListOf()
 
 223                 val keyIdentifiers: MutableList<KeyIdentifier> = it.keyIdentifiers.map { k ->
 
 224                     if (k.value.isNullOrMissing()) KeyIdentifier(k.name, emptyTextNode) else k
 
 230                         it.property?.required ?: false,
 
 231                         it.property?.type ?: "",
 
 235                         it.dictionaryName ?: "",
 
 236                         it.dictionarySource ?: "",
 
 242             // Wrapper needed for integration with SDNC
 
 243             val data = mapOf("resolution-summary" to resolutionSummaryList)
 
 244             return JacksonUtils.getJson(data, includeNull = true)
 
 247         private fun useDefaultValueIfNull(
 
 248             resourceAssignment: ResourceAssignment,
 
 249             resourceAssignmentName: String
 
 251             if (resourceAssignment.property?.value == null) {
 
 252                 val defaultValue = "\${$resourceAssignmentName}"
 
 253                 return TextNode(defaultValue)
 
 255                 return resourceAssignment.property!!.value!!
 
 259         fun transformToRARuntimeService(
 
 260             blueprintRuntimeService: BluePrintRuntimeService<*>,
 
 261             templateArtifactName: String
 
 262         ): ResourceAssignmentRuntimeService {
 
 264             val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(
 
 265                 blueprintRuntimeService.id(),
 
 266                 blueprintRuntimeService.bluePrintContext()
 
 268             resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
 
 269             resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
 
 271             return resourceAssignmentRuntimeService
 
 274         @Throws(BluePrintProcessorException::class)
 
 276             raRuntimeService: ResourceAssignmentRuntimeService,
 
 277             dataTypeName: String,
 
 280             lateinit var type: String
 
 283                     checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
 
 285                 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
 
 286                 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
 
 287                 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
 
 288             } catch (e: Exception) {
 
 289                 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
 
 290                 throw BluePrintProcessorException("${e.message}", e)
 
 295         @Throws(BluePrintProcessorException::class)
 
 296         fun parseResponseNode(
 
 297             responseNode: JsonNode,
 
 298             resourceAssignment: ResourceAssignment,
 
 299             raRuntimeService: ResourceAssignmentRuntimeService,
 
 300             outputKeyMapping: MutableMap<String, String>
 
 302             val metadata = resourceAssignment.property!!.metadata
 
 304                 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
 
 305                     throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
 
 307                 val type = resourceAssignment.property!!.type
 
 308                 val valueToPrint = getValueToLog(metadata, responseNode)
 
 310                 logger.info("For template key (${resourceAssignment.name}) trying to get value from responseNode ($valueToPrint)")
 
 312                     in BluePrintTypes.validPrimitiveTypes() -> {
 
 314                         parseResponseNodeForPrimitiveTypes(responseNode, resourceAssignment, outputKeyMapping)
 
 316                     in BluePrintTypes.validCollectionTypes() -> {
 
 318                         parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
 
 322                         parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
 
 325             } catch (e: Exception) {
 
 326                 logger.error("Fail to parse response data, error message $e")
 
 327                 throw BluePrintProcessorException("${e.message}", e)
 
 331         private fun parseResponseNodeForPrimitiveTypes(
 
 332             responseNode: JsonNode,
 
 333             resourceAssignment: ResourceAssignment,
 
 334             outputKeyMapping: MutableMap<String, String>
 
 336             // Return responseNode if is not a Complex Type
 
 337             if (!responseNode.isComplexType()) {
 
 341             val outputKey = outputKeyMapping.keys.firstOrNull()
 
 342             var returnNode = if (responseNode is ArrayNode) {
 
 343                 val arrayNode = responseNode.toList()
 
 344                 if (outputKey.isNullOrEmpty()) {
 
 347                     arrayNode.firstOrNull { element ->
 
 348                         element.isComplexType() && element.has(outputKeyMapping[outputKey])
 
 355             if (returnNode.isNullOrMissing() || returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[outputKey])) {
 
 356                 throw BluePrintProcessorException("Fail to find output key mapping ($outputKey) in the responseNode.")
 
 359             val returnValue = if (returnNode.isComplexType()) {
 
 360                 returnNode[outputKeyMapping[outputKey]]
 
 365             outputKey?.let { KeyIdentifier(it, returnValue) }
 
 366                 ?.let { resourceAssignment.keyIdentifiers.add(it) }
 
 370         private fun parseResponseNodeForCollection(
 
 371             responseNode: JsonNode,
 
 372             resourceAssignment: ResourceAssignment,
 
 373             raRuntimeService: ResourceAssignmentRuntimeService,
 
 374             outputKeyMapping: MutableMap<String, String>
 
 376             val dName = resourceAssignment.dictionaryName
 
 377             val metadata = resourceAssignment.property!!.metadata
 
 378             var resultNode: JsonNode
 
 379             if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
 
 380                 throw BluePrintProcessorException(
 
 381                     "Couldn't get data type for dictionary type " +
 
 382                             "(${resourceAssignment.property!!.type}) and dictionary name ($dName)"
 
 385             val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
 
 387             var arrayNode = JacksonUtils.objectMapper.createArrayNode()
 
 388             if (outputKeyMapping.isNotEmpty()) {
 
 389                 when (responseNode) {
 
 391                         val responseArrayNode = responseNode.toList()
 
 392                         for (responseSingleJsonNode in responseArrayNode) {
 
 393                             val arrayChildNode = parseSingleElementOfArrayResponseNode(
 
 394                                 entrySchemaType, resourceAssignment,
 
 395                                 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata
 
 397                             arrayNode.add(arrayChildNode)
 
 399                         resultNode = arrayNode
 
 402                         val responseArrayNode = responseNode.rootFieldsToMap()
 
 404                             parseObjectResponseNode(
 
 405                                 resourceAssignment, entrySchemaType, outputKeyMapping,
 
 406                                 responseArrayNode, metadata
 
 410                         throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
 
 414                 when (responseNode) {
 
 416                         responseNode.forEach { elementNode ->
 
 417                             arrayNode.add(elementNode)
 
 419                         resultNode = arrayNode
 
 422                         val responseArrayNode = responseNode.rootFieldsToMap()
 
 423                         for ((key, responseSingleJsonNode) in responseArrayNode) {
 
 424                             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
 
 425                             logKeyValueResolvedResource(metadata, key, responseSingleJsonNode, entrySchemaType)
 
 426                             JacksonUtils.populateJsonNodeValues(
 
 428                                 responseSingleJsonNode,
 
 432                             arrayNode.add(arrayChildNode)
 
 434                         resultNode = arrayNode
 
 437                         resultNode = responseNode
 
 445         private fun parseSingleElementOfArrayResponseNode(
 
 446             entrySchemaType: String,
 
 447             resourceAssignment: ResourceAssignment,
 
 448             outputKeyMapping: MutableMap<String, String>,
 
 449             raRuntimeService: ResourceAssignmentRuntimeService,
 
 450             responseNode: JsonNode,
 
 451             metadata: MutableMap<String, String>?
 
 453             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
 
 454             when (entrySchemaType) {
 
 455                 in BluePrintTypes.validPrimitiveTypes() -> {
 
 456                     if (outputKeyMappingHasOnlyOneElement) {
 
 457                         val outputKeyMap = outputKeyMapping.entries.first()
 
 458                         if (resourceAssignment.keyIdentifiers.none { it.name == outputKeyMap.key }) {
 
 459                             resourceAssignment.keyIdentifiers.add(
 
 460                                     KeyIdentifier(outputKeyMap.key, JacksonUtils.objectMapper.createArrayNode())
 
 463                         return parseSingleElementNodeWithOneOutputKeyMapping(
 
 472                         throw BluePrintProcessorException("Expect one entry in output-key-mapping")
 
 477                         checkOutputKeyMappingAllElementsInDataTypeProperties(
 
 482                             parseSingleElementNodeWithAllOutputKeyMapping(
 
 490                         outputKeyMappingHasOnlyOneElement -> {
 
 491                             val outputKeyMap = outputKeyMapping.entries.first()
 
 492                             parseSingleElementNodeWithOneOutputKeyMapping(
 
 502                             throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
 
 509         private fun parseObjectResponseNode(
 
 510             resourceAssignment: ResourceAssignment,
 
 511             entrySchemaType: String,
 
 512             outputKeyMapping: MutableMap<String, String>,
 
 513             responseArrayNode: MutableMap<String, JsonNode>,
 
 514             metadata: MutableMap<String, String>?
 
 516             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
 
 517             if (outputKeyMappingHasOnlyOneElement) {
 
 518                 val outputKeyMap = outputKeyMapping.entries.first()
 
 519                 val returnValue = parseObjectResponseNodeWithOneOutputKeyMapping(
 
 520                     responseArrayNode, outputKeyMap.key, outputKeyMap.value,
 
 521                     entrySchemaType, metadata
 
 523                 resourceAssignment.keyIdentifiers.add(KeyIdentifier(outputKeyMap.key, returnValue))
 
 526                 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
 
 530         private fun parseSingleElementNodeWithOneOutputKeyMapping(
 
 531             resourceAssignment: ResourceAssignment,
 
 532             responseSingleJsonNode: JsonNode,
 
 533             outputKeyMappingKey: String,
 
 534             outputKeyMappingValue: String,
 
 536             metadata: MutableMap<String, String>?
 
 538             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
 
 540             val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
 
 541                 responseSingleJsonNode.get(outputKeyMappingValue)
 
 543                 NullNode.getInstance()
 
 546             logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
 
 547             JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
 
 548             resourceAssignment.keyIdentifiers.find { it.name == outputKeyMappingKey && it.value.isArray }
 
 551                             (it.value as ArrayNode).add(responseKeyValue)
 
 553                             resourceAssignment.keyIdentifiers.add(
 
 554                                     KeyIdentifier(outputKeyMappingKey, responseKeyValue))
 
 556             return arrayChildNode
 
 559         private fun parseSingleElementNodeWithAllOutputKeyMapping(
 
 560             resourceAssignment: ResourceAssignment,
 
 561             responseSingleJsonNode: JsonNode,
 
 562             outputKeyMapping: MutableMap<String, String>,
 
 564             metadata: MutableMap<String, String>?
 
 566             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
 
 567             outputKeyMapping.map {
 
 568                 val responseKeyValue = if (responseSingleJsonNode.has(it.value)) {
 
 569                     responseSingleJsonNode.get(it.value)
 
 571                     NullNode.getInstance()
 
 574                 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
 
 575                 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
 
 576                 resourceAssignment.keyIdentifiers.add(KeyIdentifier(it.key, responseKeyValue))
 
 578             return arrayChildNode
 
 581         private fun parseObjectResponseNodeWithOneOutputKeyMapping(
 
 582             responseArrayNode: MutableMap<String, JsonNode>,
 
 583             outputKeyMappingKey: String,
 
 584             outputKeyMappingValue: String,
 
 586             metadata: MutableMap<String, String>?
 
 588             val objectNode = JacksonUtils.objectMapper.createObjectNode()
 
 589             val responseSingleJsonNode = responseArrayNode.filterKeys { key ->
 
 590                 key == outputKeyMappingValue
 
 591             }.entries.firstOrNull()
 
 593             if (responseSingleJsonNode == null) {
 
 594                 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
 
 595                 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
 
 597                 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
 
 598                 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
 
 603         private fun parseResponseNodeForComplexType(
 
 604             responseNode: JsonNode,
 
 605             resourceAssignment: ResourceAssignment,
 
 606             raRuntimeService: ResourceAssignmentRuntimeService,
 
 607             outputKeyMapping: MutableMap<String, String>
 
 609             val entrySchemaType = resourceAssignment.property!!.type
 
 610             val dictionaryName = resourceAssignment.dictionaryName!!
 
 611             val metadata = resourceAssignment.property!!.metadata
 
 612             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
 
 614             if (outputKeyMapping.isNotEmpty()) {
 
 616                     checkOutputKeyMappingAllElementsInDataTypeProperties(
 
 621                         parseSingleElementNodeWithAllOutputKeyMapping(
 
 629                     outputKeyMappingHasOnlyOneElement -> {
 
 630                         val outputKeyMap = outputKeyMapping.entries.first()
 
 631                         parseSingleElementNodeWithOneOutputKeyMapping(
 
 632                             resourceAssignment, responseNode, outputKeyMap.key,
 
 633                             outputKeyMap.value, entrySchemaType, metadata
 
 637                         throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
 
 641                 val childNode = JacksonUtils.objectMapper.createObjectNode()
 
 642                 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
 
 647         private fun checkOutputKeyMappingAllElementsInDataTypeProperties(
 
 648             dataTypeName: String,
 
 649             outputKeyMapping: MutableMap<String, String>,
 
 650             raRuntimeService: ResourceAssignmentRuntimeService
 
 652             val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
 
 653             val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
 
 654             return result == null
 
 657         private fun logKeyValueResolvedResource(
 
 658             metadata: MutableMap<String, String>?,
 
 663             val valueToPrint = getValueToLog(metadata, value)
 
 666                 "For List Type Resource: key ($key), value ($valueToPrint), " +
 
 671         private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
 
 672             return (outputKeyMapping.size == 1)
 
 675         fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any =
 
 676                 if (hasLogProtect(metadata)) LOG_REDACTED else value