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