2 * Copyright © 2017-2018 AT&T Intellectual Property.
3 * Modifications Copyright (c) 2019 IBM, Bell Canada.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
20 import com.fasterxml.jackson.databind.JsonNode
21 import com.fasterxml.jackson.databind.ObjectMapper
22 import com.fasterxml.jackson.databind.node.ArrayNode
23 import com.fasterxml.jackson.databind.node.NullNode
24 import com.fasterxml.jackson.databind.node.ObjectNode
25 import com.fasterxml.jackson.databind.node.TextNode
26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
27 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
29 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
30 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
31 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
32 import org.onap.ccsdk.cds.controllerblueprints.core.checkFileExists
33 import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
34 import org.onap.ccsdk.cds.controllerblueprints.core.isComplexType
35 import org.onap.ccsdk.cds.controllerblueprints.core.isNotEmpty
36 import org.onap.ccsdk.cds.controllerblueprints.core.isNullOrMissing
37 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
38 import org.onap.ccsdk.cds.controllerblueprints.core.nullToEmpty
39 import org.onap.ccsdk.cds.controllerblueprints.core.rootFieldsToMap
40 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
41 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonReactorUtils
42 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
43 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
44 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
45 import org.slf4j.LoggerFactory
48 class ResourceAssignmentUtils {
51 private val logger = LoggerFactory.getLogger(ResourceAssignmentUtils::class.toString())
53 suspend fun resourceDefinitions(blueprintBasePath: String): MutableMap<String, ResourceDefinition> {
54 val dictionaryFile = normalizedFile(
55 blueprintBasePath, BluePrintConstants.TOSCA_DEFINITIONS_DIR,
56 ResourceResolutionConstants.FILE_NAME_RESOURCE_DEFINITION_TYPES
58 checkFileExists(dictionaryFile) { "resource definition file(${dictionaryFile.absolutePath}) is missing" }
59 return JacksonReactorUtils.getMapFromFile(dictionaryFile, ResourceDefinition::class.java)
62 @Throws(BluePrintProcessorException::class)
63 fun setResourceDataValue(
64 resourceAssignment: ResourceAssignment,
65 raRuntimeService: ResourceAssignmentRuntimeService,
68 // TODO("See if Validation is needed in future with respect to conversion and Types")
69 return setResourceDataValue(resourceAssignment, raRuntimeService, value.asJsonType())
72 @Throws(BluePrintProcessorException::class)
73 fun setResourceDataValue(
74 resourceAssignment: ResourceAssignment,
75 raRuntimeService: ResourceAssignmentRuntimeService,
78 val resourceProp = checkNotNull(resourceAssignment.property) {
79 "Failed in setting resource value for resource mapping $resourceAssignment"
81 checkNotEmpty(resourceAssignment.name) {
82 "Failed in setting resource value for resource mapping $resourceAssignment"
85 if (resourceAssignment.dictionaryName.isNullOrEmpty()) {
86 resourceAssignment.dictionaryName = resourceAssignment.name
88 "Missing dictionary key, setting with template key (${resourceAssignment.name}) " +
89 "as dictionary key (${resourceAssignment.dictionaryName})"
94 if (resourceProp.type.isNotEmpty()) {
95 val metadata = resourceAssignment.property!!.metadata
96 val valueToPrint = getValueToLog(metadata, value)
98 "Setting Resource Value ($valueToPrint) for Resource Name " +
99 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
100 "of type (${resourceProp.type})"
102 setResourceValue(resourceAssignment, raRuntimeService, value)
103 resourceAssignment.updatedDate = Date()
104 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
105 resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
107 } catch (e: Exception) {
108 throw BluePrintProcessorException(
109 "Failed in setting value for template key " +
110 "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
111 "type (${resourceProp.type}) with error message (${e.message})", e
116 private fun setResourceValue(
117 resourceAssignment: ResourceAssignment,
118 raRuntimeService: ResourceAssignmentRuntimeService,
121 // TODO("See if Validation is needed wrt to type before storing")
122 raRuntimeService.putResolutionStore(resourceAssignment.name, value)
123 raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
124 resourceAssignment.property!!.value = value
127 fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
128 if (isNotEmpty(resourceAssignment.name)) {
129 resourceAssignment.updatedDate = Date()
130 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
131 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
132 resourceAssignment.message = message
136 @Throws(BluePrintProcessorException::class)
137 fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
138 val resourceProp = checkNotNull(resourceAssignment.property) {
139 "Failed to populate mandatory resource resource mapping $resourceAssignment"
141 if (resourceProp.required != null && resourceProp.required!! && resourceProp.value.isNullOrMissing()) {
142 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
143 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
147 @Throws(BluePrintProcessorException::class)
148 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
151 val mapper = ObjectMapper()
152 val root: ObjectNode = mapper.createObjectNode()
154 var containsLogProtected = false
155 assignments.forEach {
156 if (isNotEmpty(it.name) && it.property != null) {
158 val metadata = it.property!!.metadata
159 val type = nullToEmpty(it.property?.type).toLowerCase()
160 val value = useDefaultValueIfNull(it, rName)
161 val valueToPrint = getValueToLog(metadata, value)
162 if (checkIfLogIsProtected(metadata)) {
163 containsLogProtected = true
165 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
166 root.set(rName, value)
169 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
171 if (!containsLogProtected) {
172 logger.info("Generated Resource Param Data ($result)")
174 } catch (e: Exception) {
175 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
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) {
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)
192 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
199 private fun useDefaultValueIfNull(
200 resourceAssignment: ResourceAssignment,
201 resourceAssignmentName: String
203 if (resourceAssignment.property?.value == null) {
204 val defaultValue = "\${$resourceAssignmentName}"
205 return TextNode(defaultValue)
207 return resourceAssignment.property!!.value!!
211 fun transformToRARuntimeService(
212 blueprintRuntimeService: BluePrintRuntimeService<*>,
213 templateArtifactName: String
214 ): ResourceAssignmentRuntimeService {
216 val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(
217 blueprintRuntimeService.id(),
218 blueprintRuntimeService.bluePrintContext()
220 resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
221 resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
223 return resourceAssignmentRuntimeService
226 @Throws(BluePrintProcessorException::class)
228 raRuntimeService: ResourceAssignmentRuntimeService,
229 dataTypeName: String,
232 lateinit var type: String
235 checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
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)
247 @Throws(BluePrintProcessorException::class)
248 fun parseResponseNode(
249 responseNode: JsonNode,
250 resourceAssignment: ResourceAssignment,
251 raRuntimeService: ResourceAssignmentRuntimeService,
252 outputKeyMapping: MutableMap<String, String>
254 val metadata = resourceAssignment.property!!.metadata
256 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
257 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
259 val type = resourceAssignment.property!!.type
260 val valueToPrint = getValueToLog(metadata, responseNode)
262 logger.info("For template key (${resourceAssignment.name}) trying to get value from responseNode ($valueToPrint)")
264 in BluePrintTypes.validPrimitiveTypes() -> {
266 parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
268 in BluePrintTypes.validCollectionTypes() -> {
270 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
274 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
277 } catch (e: Exception) {
278 logger.error("Fail to parse response data, error message $e")
279 throw BluePrintProcessorException("${e.message}", e)
283 private fun parseResponseNodeForPrimitiveTypes(
284 responseNode: JsonNode,
285 outputKeyMapping: MutableMap<String, String>
287 // Return responseNode if is not a Complex Type
288 if (!responseNode.isComplexType()) {
292 val outputKey = outputKeyMapping.keys.firstOrNull()
293 var returnNode = if (responseNode is ArrayNode) {
294 val arrayNode = responseNode.toList()
295 if (outputKey.isNullOrEmpty()) {
298 arrayNode.firstOrNull { element ->
299 element.isComplexType() && element.has(outputKeyMapping[outputKey])
306 if (returnNode.isNullOrMissing() || returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[outputKey])) {
307 throw BluePrintProcessorException("Fail to find output key mapping ($outputKey) in the responseNode.")
309 return if (returnNode.isComplexType()) {
310 returnNode[outputKeyMapping[outputKey]]
316 private fun parseResponseNodeForCollection(
317 responseNode: JsonNode,
318 resourceAssignment: ResourceAssignment,
319 raRuntimeService: ResourceAssignmentRuntimeService,
320 outputKeyMapping: MutableMap<String, String>
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)"
331 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
333 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
334 if (outputKeyMapping.isNotEmpty()) {
335 when (responseNode) {
337 val responseArrayNode = responseNode.toList()
338 for (responseSingleJsonNode in responseArrayNode) {
339 val arrayChildNode = parseSingleElementOfArrayResponseNode(
341 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata
343 arrayNode.add(arrayChildNode)
345 resultNode = arrayNode
348 val responseArrayNode = responseNode.rootFieldsToMap()
350 parseObjectResponseNode(entrySchemaType, outputKeyMapping, responseArrayNode, metadata)
353 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
357 when (responseNode) {
359 responseNode.forEach { elementNode ->
360 arrayNode.add(elementNode)
362 resultNode = arrayNode
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(
371 responseSingleJsonNode,
375 arrayNode.add(arrayChildNode)
377 resultNode = arrayNode
380 resultNode = responseNode
388 private fun parseSingleElementOfArrayResponseNode(
389 entrySchemaType: String,
390 outputKeyMapping: MutableMap<String, String>,
391 raRuntimeService: ResourceAssignmentRuntimeService,
392 responseNode: JsonNode,
393 metadata: MutableMap<String, String>?
395 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
396 when (entrySchemaType) {
397 in BluePrintTypes.validPrimitiveTypes() -> {
398 if (outputKeyMappingHasOnlyOneElement) {
399 val outputKeyMap = outputKeyMapping.entries.first()
400 return parseSingleElementNodeWithOneOutputKeyMapping(
408 throw BluePrintProcessorException("Expect one entry in output-key-mapping")
413 checkOutputKeyMappingAllElementsInDataTypeProperties(
418 parseSingleElementNodeWithAllOutputKeyMapping(
425 outputKeyMappingHasOnlyOneElement -> {
426 val outputKeyMap = outputKeyMapping.entries.first()
427 parseSingleElementNodeWithOneOutputKeyMapping(
436 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
443 private fun parseObjectResponseNode(
444 entrySchemaType: String,
445 outputKeyMapping: MutableMap<String, String>,
446 responseArrayNode: MutableMap<String, JsonNode>,
447 metadata: MutableMap<String, String>?
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
457 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
461 private fun parseSingleElementNodeWithOneOutputKeyMapping(
462 responseSingleJsonNode: JsonNode,
465 outputKeyMappingValue: String,
467 metadata: MutableMap<String, String>?
469 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
471 val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
472 responseSingleJsonNode.get(outputKeyMappingValue)
474 NullNode.getInstance()
477 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
478 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
480 return arrayChildNode
483 private fun parseSingleElementNodeWithAllOutputKeyMapping(
484 responseSingleJsonNode: JsonNode,
485 outputKeyMapping: MutableMap<String, String>,
487 metadata: MutableMap<String, String>?
489 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
490 outputKeyMapping.map {
491 val responseKeyValue = if (responseSingleJsonNode.has(it.value)) {
492 responseSingleJsonNode.get(it.value)
494 NullNode.getInstance()
497 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
498 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
500 return arrayChildNode
503 private fun parseObjectResponseNodeWithOneOutputKeyMapping(
504 responseArrayNode: MutableMap<String, JsonNode>,
505 outputKeyMappingKey: String,
506 outputKeyMappingValue: String,
508 metadata: MutableMap<String, String>?
510 val objectNode = JacksonUtils.objectMapper.createObjectNode()
511 val responseSingleJsonNode = responseArrayNode.filterKeys { key ->
512 key == outputKeyMappingValue
513 }.entries.firstOrNull()
515 if (responseSingleJsonNode == null) {
516 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
517 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
519 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
520 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
525 private fun parseResponseNodeForComplexType(
526 responseNode: JsonNode,
527 resourceAssignment: ResourceAssignment,
528 raRuntimeService: ResourceAssignmentRuntimeService,
529 outputKeyMapping: MutableMap<String, String>
531 val entrySchemaType = resourceAssignment.property!!.type
532 val dictionaryName = resourceAssignment.dictionaryName!!
533 val metadata = resourceAssignment.property!!.metadata
534 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
536 if (outputKeyMapping.isNotEmpty()) {
538 checkOutputKeyMappingAllElementsInDataTypeProperties(
543 parseSingleElementNodeWithAllOutputKeyMapping(
550 outputKeyMappingHasOnlyOneElement -> {
551 val outputKeyMap = outputKeyMapping.entries.first()
552 parseSingleElementNodeWithOneOutputKeyMapping(
553 responseNode, outputKeyMap.key, outputKeyMap.value,
554 entrySchemaType, metadata
558 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
562 val childNode = JacksonUtils.objectMapper.createObjectNode()
563 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
568 private fun checkOutputKeyMappingAllElementsInDataTypeProperties(
569 dataTypeName: String,
570 outputKeyMapping: MutableMap<String, String>,
571 raRuntimeService: ResourceAssignmentRuntimeService
573 val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
574 val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
575 return result == null
578 private fun logKeyValueResolvedResource(
579 metadata: MutableMap<String, String>?,
584 val valueToPrint = getValueToLog(metadata, value)
587 "For List Type Resource: key ($key), value ($valueToPrint), " +
592 private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
593 return (outputKeyMapping.size == 1)
596 fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any {
597 return if (checkIfLogIsProtected(metadata)) {
598 "******REDACTED******"
604 private fun checkIfLogIsProtected(metadata: MutableMap<String, String>?): Boolean {
605 var checkProtected = false
606 if (metadata != null &&
607 metadata.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_LOG_PROTECTED_METADATA)
609 val protectedMetadata = metadata[ResourceResolutionConstants.RESOURCE_RESOLUTION_LOG_PROTECTED_METADATA]
610 if (protectedMetadata == "yes" || protectedMetadata == "y") {
611 checkProtected = true
614 return checkProtected