a813c00ca91e81c080095b6cc2469ebd17c957fc
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / resource-resolution / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / resource / resolution / utils / ResourceAssignmentUtils.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright (c) 2019 IBM, Bell Canada.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
19
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.ResourceAssignment
46 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
47 import org.slf4j.LoggerFactory
48 import java.util.Date
49
50 class ResourceAssignmentUtils {
51     companion object {
52
53         private val logger = LoggerFactory.getLogger(ResourceAssignmentUtils::class.toString())
54
55         suspend fun resourceDefinitions(blueprintBasePath: String): MutableMap<String, ResourceDefinition> {
56             val dictionaryFile = normalizedFile(
57                 blueprintBasePath, BluePrintConstants.TOSCA_DEFINITIONS_DIR,
58                 ResourceResolutionConstants.FILE_NAME_RESOURCE_DEFINITION_TYPES
59             )
60             checkFileExists(dictionaryFile) { "resource definition file(${dictionaryFile.absolutePath}) is missing" }
61             return JacksonReactorUtils.getMapFromFile(dictionaryFile, ResourceDefinition::class.java)
62         }
63
64         @Throws(BluePrintProcessorException::class)
65         fun setResourceDataValue(
66             resourceAssignment: ResourceAssignment,
67             raRuntimeService: ResourceAssignmentRuntimeService,
68             value: Any?
69         ) {
70             // TODO("See if Validation is needed in future with respect to conversion and Types")
71             return setResourceDataValue(resourceAssignment, raRuntimeService, value.asJsonType())
72         }
73
74         @Throws(BluePrintProcessorException::class)
75         fun setResourceDataValue(
76             resourceAssignment: ResourceAssignment,
77             raRuntimeService: ResourceAssignmentRuntimeService,
78             value: JsonNode
79         ) {
80             val resourceProp = checkNotNull(resourceAssignment.property) {
81                 "Failed in setting resource value for resource mapping $resourceAssignment"
82             }
83             checkNotEmpty(resourceAssignment.name) {
84                 "Failed in setting resource value for resource mapping $resourceAssignment"
85             }
86
87             if (resourceAssignment.dictionaryName.isNullOrEmpty()) {
88                 resourceAssignment.dictionaryName = resourceAssignment.name
89                 logger.warn(
90                     "Missing dictionary key, setting with template key (${resourceAssignment.name}) " +
91                             "as dictionary key (${resourceAssignment.dictionaryName})"
92                 )
93             }
94
95             try {
96                 if (resourceProp.type.isNotEmpty()) {
97                     val metadata = resourceAssignment.property!!.metadata
98                     val valueToPrint = getValueToLog(metadata, value)
99                     logger.info(
100                         "Setting Resource Value ($valueToPrint) for Resource Name " +
101                                 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
102                                 "of type (${resourceProp.type})"
103                     )
104                     setResourceValue(resourceAssignment, raRuntimeService, value)
105                     resourceAssignment.updatedDate = Date()
106                     resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
107                     resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
108                 }
109             } catch (e: Exception) {
110                 throw BluePrintProcessorException(
111                     "Failed in setting value for template key " +
112                             "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
113                             "type (${resourceProp.type}) with error message (${e.message})", e
114                 )
115             }
116         }
117
118         private fun setResourceValue(
119             resourceAssignment: ResourceAssignment,
120             raRuntimeService: ResourceAssignmentRuntimeService,
121             value: JsonNode
122         ) {
123             // TODO("See if Validation is needed wrt to type before storing")
124             raRuntimeService.putResolutionStore(resourceAssignment.name, value)
125             raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
126             resourceAssignment.property!!.value = value
127         }
128
129         fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
130             if (isNotEmpty(resourceAssignment.name)) {
131                 resourceAssignment.updatedDate = Date()
132                 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
133                 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
134                 resourceAssignment.message = message
135             }
136         }
137
138         @Throws(BluePrintProcessorException::class)
139         fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
140             val resourceProp = checkNotNull(resourceAssignment.property) {
141                 "Failed to populate mandatory resource resource mapping $resourceAssignment"
142             }
143             if (resourceProp.required != null && resourceProp.required!! && resourceProp.value.isNullOrMissing()) {
144                 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
145                 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
146             }
147         }
148
149         @Throws(BluePrintProcessorException::class)
150         fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
151             val result: String
152             try {
153                 val mapper = ObjectMapper()
154                 val root: ObjectNode = mapper.createObjectNode()
155
156                 var containsLogProtected = false
157                 assignments.forEach {
158                     if (isNotEmpty(it.name) && it.property != null) {
159                         val rName = it.name
160                         val metadata = it.property!!.metadata
161                         val type = nullToEmpty(it.property?.type).toLowerCase()
162                         val value = useDefaultValueIfNull(it, rName)
163                         val valueToPrint = getValueToLog(metadata, value)
164                         containsLogProtected = hasLogProtect(metadata)
165                         logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
166                         root.set(rName, value)
167                     }
168                 }
169                 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
170
171                 if (!containsLogProtected) {
172                     logger.info("Generated Resource Param Data ($result)")
173                 }
174             } catch (e: Exception) {
175                 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
176             }
177
178             return result
179         }
180
181         @Throws(BluePrintProcessorException::class)
182         fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
183             val data: MutableMap<String, JsonNode> = hashMapOf()
184             assignments.forEach {
185                 if (isNotEmpty(it.name) && it.property != null) {
186                     val rName = it.name
187                     val metadata = it.property!!.metadata
188                     val type = nullToEmpty(it.property?.type).toLowerCase()
189                     val value = useDefaultValueIfNull(it, rName)
190                     val valueToPrint = getValueToLog(metadata, value)
191
192                     logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
193                     data[rName] = value
194                 }
195             }
196             return data
197         }
198
199         private fun useDefaultValueIfNull(
200             resourceAssignment: ResourceAssignment,
201             resourceAssignmentName: String
202         ): JsonNode {
203             if (resourceAssignment.property?.value == null) {
204                 val defaultValue = "\${$resourceAssignmentName}"
205                 return TextNode(defaultValue)
206             } else {
207                 return resourceAssignment.property!!.value!!
208             }
209         }
210
211         fun transformToRARuntimeService(
212             blueprintRuntimeService: BluePrintRuntimeService<*>,
213             templateArtifactName: String
214         ): ResourceAssignmentRuntimeService {
215
216             val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(
217                 blueprintRuntimeService.id(),
218                 blueprintRuntimeService.bluePrintContext()
219             )
220             resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
221             resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
222
223             return resourceAssignmentRuntimeService
224         }
225
226         @Throws(BluePrintProcessorException::class)
227         fun getPropertyType(
228             raRuntimeService: ResourceAssignmentRuntimeService,
229             dataTypeName: String,
230             propertyName: String
231         ): String {
232             lateinit var type: String
233             try {
234                 val dataTypeProps =
235                     checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
236
237                 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
238                 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
239                 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
240             } catch (e: Exception) {
241                 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
242                 throw BluePrintProcessorException("${e.message}", e)
243             }
244             return type
245         }
246
247         @Throws(BluePrintProcessorException::class)
248         fun parseResponseNode(
249             responseNode: JsonNode,
250             resourceAssignment: ResourceAssignment,
251             raRuntimeService: ResourceAssignmentRuntimeService,
252             outputKeyMapping: MutableMap<String, String>
253         ): JsonNode {
254             val metadata = resourceAssignment.property!!.metadata
255             try {
256                 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
257                     throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
258                 }
259                 val type = resourceAssignment.property!!.type
260                 val valueToPrint = getValueToLog(metadata, responseNode)
261
262                 logger.info("For template key (${resourceAssignment.name}) trying to get value from responseNode ($valueToPrint)")
263                 return when (type) {
264                     in BluePrintTypes.validPrimitiveTypes() -> {
265                         // Primitive Types
266                         parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
267                     }
268                     in BluePrintTypes.validCollectionTypes() -> {
269                         // Array Types
270                         parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
271                     }
272                     else -> {
273                         // Complex Types
274                         parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
275                     }
276                 }
277             } catch (e: Exception) {
278                 logger.error("Fail to parse response data, error message $e")
279                 throw BluePrintProcessorException("${e.message}", e)
280             }
281         }
282
283         private fun parseResponseNodeForPrimitiveTypes(
284             responseNode: JsonNode,
285             outputKeyMapping: MutableMap<String, String>
286         ): JsonNode {
287             // Return responseNode if is not a Complex Type
288             if (!responseNode.isComplexType()) {
289                 return responseNode
290             }
291
292             val outputKey = outputKeyMapping.keys.firstOrNull()
293             var returnNode = if (responseNode is ArrayNode) {
294                 val arrayNode = responseNode.toList()
295                 if (outputKey.isNullOrEmpty()) {
296                     arrayNode.first()
297                 } else {
298                     arrayNode.firstOrNull { element ->
299                         element.isComplexType() && element.has(outputKeyMapping[outputKey])
300                     }
301                 }
302             } else {
303                 responseNode
304             }
305
306             if (returnNode.isNullOrMissing() || returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[outputKey])) {
307                 throw BluePrintProcessorException("Fail to find output key mapping ($outputKey) in the responseNode.")
308             }
309             return if (returnNode.isComplexType()) {
310                 returnNode[outputKeyMapping[outputKey]]
311             } else {
312                 returnNode
313             }
314         }
315
316         private fun parseResponseNodeForCollection(
317             responseNode: JsonNode,
318             resourceAssignment: ResourceAssignment,
319             raRuntimeService: ResourceAssignmentRuntimeService,
320             outputKeyMapping: MutableMap<String, String>
321         ): JsonNode {
322             val dName = resourceAssignment.dictionaryName
323             val metadata = resourceAssignment.property!!.metadata
324             var resultNode: JsonNode
325             if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
326                 throw BluePrintProcessorException(
327                     "Couldn't get data type for dictionary type " +
328                             "(${resourceAssignment.property!!.type}) and dictionary name ($dName)"
329                 )
330             }
331             val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
332
333             var arrayNode = JacksonUtils.objectMapper.createArrayNode()
334             if (outputKeyMapping.isNotEmpty()) {
335                 when (responseNode) {
336                     is ArrayNode -> {
337                         val responseArrayNode = responseNode.toList()
338                         for (responseSingleJsonNode in responseArrayNode) {
339                             val arrayChildNode = parseSingleElementOfArrayResponseNode(
340                                 entrySchemaType,
341                                 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata
342                             )
343                             arrayNode.add(arrayChildNode)
344                         }
345                         resultNode = arrayNode
346                     }
347                     is ObjectNode -> {
348                         val responseArrayNode = responseNode.rootFieldsToMap()
349                         resultNode =
350                             parseObjectResponseNode(entrySchemaType, outputKeyMapping, responseArrayNode, metadata)
351                     }
352                     else -> {
353                         throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
354                     }
355                 }
356             } else {
357                 when (responseNode) {
358                     is ArrayNode -> {
359                         responseNode.forEach { elementNode ->
360                             arrayNode.add(elementNode)
361                         }
362                         resultNode = arrayNode
363                     }
364                     is ObjectNode -> {
365                         val responseArrayNode = responseNode.rootFieldsToMap()
366                         for ((key, responseSingleJsonNode) in responseArrayNode) {
367                             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
368                             logKeyValueResolvedResource(metadata, key, responseSingleJsonNode, entrySchemaType)
369                             JacksonUtils.populateJsonNodeValues(
370                                 key,
371                                 responseSingleJsonNode,
372                                 entrySchemaType,
373                                 arrayChildNode
374                             )
375                             arrayNode.add(arrayChildNode)
376                         }
377                         resultNode = arrayNode
378                     }
379                     else -> {
380                         resultNode = responseNode
381                     }
382                 }
383             }
384
385             return resultNode
386         }
387
388         private fun parseSingleElementOfArrayResponseNode(
389             entrySchemaType: String,
390             outputKeyMapping: MutableMap<String, String>,
391             raRuntimeService: ResourceAssignmentRuntimeService,
392             responseNode: JsonNode,
393             metadata: MutableMap<String, String>?
394         ): ObjectNode {
395             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
396             when (entrySchemaType) {
397                 in BluePrintTypes.validPrimitiveTypes() -> {
398                     if (outputKeyMappingHasOnlyOneElement) {
399                         val outputKeyMap = outputKeyMapping.entries.first()
400                         return parseSingleElementNodeWithOneOutputKeyMapping(
401                             responseNode,
402                             outputKeyMap.key,
403                             outputKeyMap.value,
404                             entrySchemaType,
405                             metadata
406                         )
407                     } else {
408                         throw BluePrintProcessorException("Expect one entry in output-key-mapping")
409                     }
410                 }
411                 else -> {
412                     return when {
413                         checkOutputKeyMappingAllElementsInDataTypeProperties(
414                             entrySchemaType,
415                             outputKeyMapping,
416                             raRuntimeService
417                         ) -> {
418                             parseSingleElementNodeWithAllOutputKeyMapping(
419                                 responseNode,
420                                 outputKeyMapping,
421                                 entrySchemaType,
422                                 metadata
423                             )
424                         }
425                         outputKeyMappingHasOnlyOneElement -> {
426                             val outputKeyMap = outputKeyMapping.entries.first()
427                             parseSingleElementNodeWithOneOutputKeyMapping(
428                                 responseNode,
429                                 outputKeyMap.key,
430                                 outputKeyMap.value,
431                                 entrySchemaType,
432                                 metadata
433                             )
434                         }
435                         else -> {
436                             throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
437                         }
438                     }
439                 }
440             }
441         }
442
443         private fun parseObjectResponseNode(
444             entrySchemaType: String,
445             outputKeyMapping: MutableMap<String, String>,
446             responseArrayNode: MutableMap<String, JsonNode>,
447             metadata: MutableMap<String, String>?
448         ): ObjectNode {
449             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
450             if (outputKeyMappingHasOnlyOneElement) {
451                 val outputKeyMap = outputKeyMapping.entries.first()
452                 return parseObjectResponseNodeWithOneOutputKeyMapping(
453                     responseArrayNode, outputKeyMap.key, outputKeyMap.value,
454                     entrySchemaType, metadata
455                 )
456             } else {
457                 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
458             }
459         }
460
461         private fun parseSingleElementNodeWithOneOutputKeyMapping(
462             responseSingleJsonNode: JsonNode,
463             outputKeyMappingKey:
464             String,
465             outputKeyMappingValue: String,
466             type: String,
467             metadata: MutableMap<String, String>?
468         ): ObjectNode {
469             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
470
471             val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
472                 responseSingleJsonNode.get(outputKeyMappingValue)
473             } else {
474                 NullNode.getInstance()
475             }
476
477             logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
478             JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
479
480             return arrayChildNode
481         }
482
483         private fun parseSingleElementNodeWithAllOutputKeyMapping(
484             responseSingleJsonNode: JsonNode,
485             outputKeyMapping: MutableMap<String, String>,
486             type: String,
487             metadata: MutableMap<String, String>?
488         ): ObjectNode {
489             val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
490             outputKeyMapping.map {
491                 val responseKeyValue = if (responseSingleJsonNode.has(it.value)) {
492                     responseSingleJsonNode.get(it.value)
493                 } else {
494                     NullNode.getInstance()
495                 }
496
497                 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
498                 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
499             }
500             return arrayChildNode
501         }
502
503         private fun parseObjectResponseNodeWithOneOutputKeyMapping(
504             responseArrayNode: MutableMap<String, JsonNode>,
505             outputKeyMappingKey: String,
506             outputKeyMappingValue: String,
507             type: String,
508             metadata: MutableMap<String, String>?
509         ): ObjectNode {
510             val objectNode = JacksonUtils.objectMapper.createObjectNode()
511             val responseSingleJsonNode = responseArrayNode.filterKeys { key ->
512                 key == outputKeyMappingValue
513             }.entries.firstOrNull()
514
515             if (responseSingleJsonNode == null) {
516                 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
517                 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
518             } else {
519                 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
520                 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
521             }
522             return objectNode
523         }
524
525         private fun parseResponseNodeForComplexType(
526             responseNode: JsonNode,
527             resourceAssignment: ResourceAssignment,
528             raRuntimeService: ResourceAssignmentRuntimeService,
529             outputKeyMapping: MutableMap<String, String>
530         ): JsonNode {
531             val entrySchemaType = resourceAssignment.property!!.type
532             val dictionaryName = resourceAssignment.dictionaryName!!
533             val metadata = resourceAssignment.property!!.metadata
534             val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
535
536             if (outputKeyMapping.isNotEmpty()) {
537                 return when {
538                     checkOutputKeyMappingAllElementsInDataTypeProperties(
539                         entrySchemaType,
540                         outputKeyMapping,
541                         raRuntimeService
542                     ) -> {
543                         parseSingleElementNodeWithAllOutputKeyMapping(
544                             responseNode,
545                             outputKeyMapping,
546                             entrySchemaType,
547                             metadata
548                         )
549                     }
550                     outputKeyMappingHasOnlyOneElement -> {
551                         val outputKeyMap = outputKeyMapping.entries.first()
552                         parseSingleElementNodeWithOneOutputKeyMapping(
553                             responseNode, outputKeyMap.key, outputKeyMap.value,
554                             entrySchemaType, metadata
555                         )
556                     }
557                     else -> {
558                         throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
559                     }
560                 }
561             } else {
562                 val childNode = JacksonUtils.objectMapper.createObjectNode()
563                 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
564                 return childNode
565             }
566         }
567
568         private fun checkOutputKeyMappingAllElementsInDataTypeProperties(
569             dataTypeName: String,
570             outputKeyMapping: MutableMap<String, String>,
571             raRuntimeService: ResourceAssignmentRuntimeService
572         ): Boolean {
573             val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
574             val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
575             return result == null
576         }
577
578         private fun logKeyValueResolvedResource(
579             metadata: MutableMap<String, String>?,
580             key: String,
581             value: JsonNode,
582             type: String
583         ) {
584             val valueToPrint = getValueToLog(metadata, value)
585
586             logger.info(
587                 "For List Type Resource: key ($key), value ($valueToPrint), " +
588                         "type  ({$type})"
589             )
590         }
591
592         private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
593             return (outputKeyMapping.size == 1)
594         }
595
596         fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any =
597                 if (hasLogProtect(metadata)) LOG_REDACTED else value
598     }
599 }