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