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.*
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
37 class ResourceAssignmentUtils {
40 private val logger = LoggerFactory.getLogger(ResourceAssignmentUtils::class.toString())
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
47 checkFileExists(dictionaryFile) { "resource definition file(${dictionaryFile.absolutePath}) is missing" }
48 return JacksonReactorUtils.getMapFromFile(dictionaryFile, ResourceDefinition::class.java)
51 @Throws(BluePrintProcessorException::class)
52 fun setResourceDataValue(
53 resourceAssignment: ResourceAssignment,
54 raRuntimeService: ResourceAssignmentRuntimeService, value: Any?
56 // TODO("See if Validation is needed in future with respect to conversion and Types")
57 return setResourceDataValue(resourceAssignment, raRuntimeService, value.asJsonType())
60 @Throws(BluePrintProcessorException::class)
61 fun setResourceDataValue(
62 resourceAssignment: ResourceAssignment,
63 raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode
65 val resourceProp = checkNotNull(resourceAssignment.property) {
66 "Failed in setting resource value for resource mapping $resourceAssignment"
68 checkNotEmpty(resourceAssignment.name) {
69 "Failed in setting resource value for resource mapping $resourceAssignment"
72 if (resourceAssignment.dictionaryName.isNullOrEmpty()) {
73 resourceAssignment.dictionaryName = resourceAssignment.name
75 "Missing dictionary key, setting with template key (${resourceAssignment.name}) " +
76 "as dictionary key (${resourceAssignment.dictionaryName})"
81 if (resourceProp.type.isNotEmpty()) {
82 val metadata = resourceAssignment.property!!.metadata
83 val valueToPrint = getValueToLog(metadata, value)
85 "Setting Resource Value ($valueToPrint) for Resource Name " +
86 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
87 "of type (${resourceProp.type})"
89 setResourceValue(resourceAssignment, raRuntimeService, value)
90 resourceAssignment.updatedDate = Date()
91 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
92 resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
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
103 private fun setResourceValue(
104 resourceAssignment: ResourceAssignment,
105 raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode
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
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
122 @Throws(BluePrintProcessorException::class)
123 fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
124 val resourceProp = checkNotNull(resourceAssignment.property) {
125 "Failed to populate mandatory resource resource mapping $resourceAssignment"
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)")
133 @Throws(BluePrintProcessorException::class)
134 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
137 val mapper = ObjectMapper()
138 val root: ObjectNode = mapper.createObjectNode()
140 var containsLogProtected = false
141 assignments.forEach {
142 if (isNotEmpty(it.name) && it.property != null) {
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
151 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
152 root.set(rName, value)
155 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
157 if (!containsLogProtected) {
158 logger.info("Generated Resource Param Data ($result)")
160 } catch (e: Exception) {
161 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
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) {
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)
178 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
185 private fun useDefaultValueIfNull(
186 resourceAssignment: ResourceAssignment,
187 resourceAssignmentName: String
189 if (resourceAssignment.property?.value == null) {
190 val defaultValue = "\${$resourceAssignmentName}"
191 return TextNode(defaultValue)
193 return resourceAssignment.property!!.value!!
197 fun transformToRARuntimeService(
198 blueprintRuntimeService: BluePrintRuntimeService<*>,
199 templateArtifactName: String
200 ): ResourceAssignmentRuntimeService {
202 val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(
203 blueprintRuntimeService.id(),
204 blueprintRuntimeService.bluePrintContext()
206 resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
207 resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
209 return resourceAssignmentRuntimeService
212 @Throws(BluePrintProcessorException::class)
214 raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
217 lateinit var type: String
220 checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
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)
232 @Throws(BluePrintProcessorException::class)
233 fun parseResponseNode(
234 responseNode: JsonNode, resourceAssignment: ResourceAssignment,
235 raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>
237 val metadata = resourceAssignment.property!!.metadata
239 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
240 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
242 val type = resourceAssignment.property!!.type
243 val valueToPrint = getValueToLog(metadata, responseNode)
245 logger.info("For template key (${resourceAssignment.name}) trying to get value from responseNode ($valueToPrint)")
247 in BluePrintTypes.validPrimitiveTypes() -> {
249 parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
251 in BluePrintTypes.validCollectionTypes() -> {
253 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
257 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
260 } catch (e: Exception) {
261 logger.error("Fail to parse response data, error message $e")
262 throw BluePrintProcessorException("${e.message}", e)
266 private fun parseResponseNodeForPrimitiveTypes(
267 responseNode: JsonNode,
268 outputKeyMapping: MutableMap<String, String>
270 // Return responseNode if is not a Complex Type
271 if (!responseNode.isComplexType()) {
275 val outputKey = outputKeyMapping.keys.firstOrNull()
276 var returnNode = if (responseNode is ArrayNode) {
277 val arrayNode = responseNode.toList()
278 if (outputKey.isNullOrEmpty()) {
281 arrayNode.firstOrNull { element ->
282 element.isComplexType() && element.has(outputKeyMapping[outputKey])
289 if (returnNode.isNullOrMissing() || returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[outputKey])) {
290 throw BluePrintProcessorException("Fail to find output key mapping ($outputKey) in the responseNode.")
292 return if (returnNode.isComplexType()) {
293 returnNode[outputKeyMapping[outputKey]]
299 private fun parseResponseNodeForCollection(
300 responseNode: JsonNode, resourceAssignment: ResourceAssignment,
301 raRuntimeService: ResourceAssignmentRuntimeService,
302 outputKeyMapping: MutableMap<String, String>
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)"
313 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
315 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
316 if (outputKeyMapping.isNotEmpty()) {
317 when (responseNode) {
319 val responseArrayNode = responseNode.toList()
320 for (responseSingleJsonNode in responseArrayNode) {
321 val arrayChildNode = parseSingleElementOfArrayResponseNode(
323 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata
325 arrayNode.add(arrayChildNode)
327 resultNode = arrayNode
330 val responseArrayNode = responseNode.rootFieldsToMap()
332 parseObjectResponseNode(entrySchemaType, outputKeyMapping, responseArrayNode, metadata)
335 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
339 when (responseNode) {
341 responseNode.forEach { elementNode ->
342 arrayNode.add(elementNode)
344 resultNode = arrayNode
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(
353 responseSingleJsonNode,
357 arrayNode.add(arrayChildNode)
359 resultNode = arrayNode
362 resultNode = responseNode
370 private fun parseSingleElementOfArrayResponseNode(
371 entrySchemaType: String, outputKeyMapping: MutableMap<String, String>,
372 raRuntimeService: ResourceAssignmentRuntimeService,
373 responseNode: JsonNode, metadata: MutableMap<String, String>?
375 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
376 when (entrySchemaType) {
377 in BluePrintTypes.validPrimitiveTypes() -> {
378 if (outputKeyMappingHasOnlyOneElement) {
379 val outputKeyMap = outputKeyMapping.entries.first()
380 return parseSingleElementNodeWithOneOutputKeyMapping(
388 throw BluePrintProcessorException("Expect one entry in output-key-mapping")
393 checkOutputKeyMappingAllElementsInDataTypeProperties(
398 parseSingleElementNodeWithAllOutputKeyMapping(
405 outputKeyMappingHasOnlyOneElement -> {
406 val outputKeyMap = outputKeyMapping.entries.first()
407 parseSingleElementNodeWithOneOutputKeyMapping(
416 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
423 private fun parseObjectResponseNode(
424 entrySchemaType: String, outputKeyMapping: MutableMap<String, String>,
425 responseArrayNode: MutableMap<String, JsonNode>, metadata: MutableMap<String, String>?
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
435 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
439 private fun parseSingleElementNodeWithOneOutputKeyMapping(
440 responseSingleJsonNode: JsonNode, outputKeyMappingKey:
441 String, outputKeyMappingValue: String, type: String, metadata: MutableMap<String, String>?
443 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
445 val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
446 responseSingleJsonNode.get(outputKeyMappingValue)
448 NullNode.getInstance()
451 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
452 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
454 return arrayChildNode
457 private fun parseSingleElementNodeWithAllOutputKeyMapping(
458 responseSingleJsonNode: JsonNode,
459 outputKeyMapping: MutableMap<String, String>,
460 type: String, metadata: MutableMap<String, String>?
462 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
463 outputKeyMapping.map {
464 val responseKeyValue = if (responseSingleJsonNode.has(it.value)) {
465 responseSingleJsonNode.get(it.value)
467 NullNode.getInstance()
470 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
471 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
473 return arrayChildNode
476 private fun parseObjectResponseNodeWithOneOutputKeyMapping(
477 responseArrayNode: MutableMap<String, JsonNode>,
478 outputKeyMappingKey: String, outputKeyMappingValue: String,
479 type: String, metadata: MutableMap<String, String>?
481 val objectNode = JacksonUtils.objectMapper.createObjectNode()
482 val responseSingleJsonNode = responseArrayNode.filterKeys { key ->
483 key == outputKeyMappingValue
484 }.entries.firstOrNull()
486 if (responseSingleJsonNode == null) {
487 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
488 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
490 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
491 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
496 private fun parseResponseNodeForComplexType(
497 responseNode: JsonNode, resourceAssignment: ResourceAssignment,
498 raRuntimeService: ResourceAssignmentRuntimeService,
499 outputKeyMapping: MutableMap<String, String>
501 val entrySchemaType = resourceAssignment.property!!.type
502 val dictionaryName = resourceAssignment.dictionaryName!!
503 val metadata = resourceAssignment.property!!.metadata
504 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
506 if (outputKeyMapping.isNotEmpty()) {
508 checkOutputKeyMappingAllElementsInDataTypeProperties(
513 parseSingleElementNodeWithAllOutputKeyMapping(
520 outputKeyMappingHasOnlyOneElement -> {
521 val outputKeyMap = outputKeyMapping.entries.first()
522 parseSingleElementNodeWithOneOutputKeyMapping(
523 responseNode, outputKeyMap.key, outputKeyMap.value,
524 entrySchemaType, metadata
528 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
532 val childNode = JacksonUtils.objectMapper.createObjectNode()
533 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
538 private fun checkOutputKeyMappingAllElementsInDataTypeProperties(
539 dataTypeName: String, outputKeyMapping: MutableMap<String, String>,
540 raRuntimeService: ResourceAssignmentRuntimeService
542 val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
543 val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
544 return result == null
547 private fun logKeyValueResolvedResource(
548 metadata: MutableMap<String, String>?,
553 val valueToPrint = getValueToLog(metadata, value)
556 "For List Type Resource: key ($key), value ($valueToPrint), " +
561 private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
562 return (outputKeyMapping.size == 1)
565 fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any {
566 return if (checkIfLogIsProtected(metadata)) {
567 "******REDACTED******"
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)
578 val protectedMetadata = metadata[ResourceResolutionConstants.RESOURCE_RESOLUTION_LOG_PROTECTED_METADATA]
579 if (protectedMetadata == "yes" || protectedMetadata == "y") {
580 checkProtected = true
583 return checkProtected