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!!
128 && (resourceProp.value == null || resourceProp.value!!.returnNullIfMissing() == null)
130 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
131 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
135 @Throws(BluePrintProcessorException::class)
136 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
139 val mapper = ObjectMapper()
140 val root: ObjectNode = mapper.createObjectNode()
142 var containsLogProtected = false
143 assignments.forEach {
144 if (isNotEmpty(it.name) && it.property != null) {
146 val metadata = it.property!!.metadata
147 val type = nullToEmpty(it.property?.type).toLowerCase()
148 val value = useDefaultValueIfNull(it, rName)
149 val valueToPrint = getValueToLog(metadata, value)
150 if (checkIfLogIsProtected(metadata)) {
151 containsLogProtected = true
153 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
154 root.set(rName, value)
157 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
159 if (!containsLogProtected) {
160 logger.info("Generated Resource Param Data ($result)")
162 } catch (e: Exception) {
163 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
169 @Throws(BluePrintProcessorException::class)
170 fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
171 val data: MutableMap<String, JsonNode> = hashMapOf()
172 assignments.forEach {
173 if (isNotEmpty(it.name) && it.property != null) {
175 val metadata = it.property!!.metadata
176 val type = nullToEmpty(it.property?.type).toLowerCase()
177 val value = useDefaultValueIfNull(it, rName)
178 val valueToPrint = getValueToLog(metadata, value)
180 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
187 private fun useDefaultValueIfNull(
188 resourceAssignment: ResourceAssignment,
189 resourceAssignmentName: String
191 if (resourceAssignment.property?.value == null) {
192 val defaultValue = "\${$resourceAssignmentName}"
193 return TextNode(defaultValue)
195 return resourceAssignment.property!!.value!!
199 fun transformToRARuntimeService(blueprintRuntimeService: BluePrintRuntimeService<*>,
200 templateArtifactName: String): 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)
213 fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
214 propertyName: String): String {
215 lateinit var type: String
218 checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
220 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
221 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
222 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
223 } catch (e: Exception) {
224 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
225 throw BluePrintProcessorException("${e.message}", e)
230 @Throws(BluePrintProcessorException::class)
231 fun parseResponseNode(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
232 raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>): JsonNode {
233 val metadata = resourceAssignment.property!!.metadata
235 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
236 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
238 val type = resourceAssignment.property!!.type
239 val valueToPrint = getValueToLog(metadata, responseNode)
241 logger.info("For template key (${resourceAssignment.name}) setting value as ($valueToPrint)")
243 in BluePrintTypes.validPrimitiveTypes() -> {
244 parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
246 in BluePrintTypes.validCollectionTypes() -> {
248 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
252 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
255 } catch (e: Exception) {
256 logger.error("Fail to parse response data, error message $e")
257 throw BluePrintProcessorException("${e.message}", e)
261 //TODO: Need to Refactor
262 private fun parseResponseNodeForPrimitiveTypes(responseNode: JsonNode,
263 outputKeyMapping: MutableMap<String, String>): JsonNode {
264 var result: JsonNode? = responseNode
266 if (responseNode.isComplexType()) {
267 val key = outputKeyMapping.keys.firstOrNull()
268 var returnNode: JsonNode?
269 if (responseNode is ArrayNode) {
270 val arrayNode = responseNode.toList()
271 val firstElement = if (key.isNullOrEmpty()) {
274 arrayNode.firstOrNull { element ->
275 element.isComplexType() && element.has(outputKeyMapping[key])
278 returnNode = firstElement
280 returnNode = responseNode
283 if (returnNode.isNull() || (returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[key]))) {
284 if (key.isNullOrEmpty()) {
285 throw BluePrintProcessorException("Fail to find mapping in the responseNode.")
287 throw BluePrintProcessorException("Fail to find response with output key mapping ($key) in result.")
290 result = if (returnNode.isComplexType()) {
291 returnNode[outputKeyMapping[key]]
296 if (outputKeyMapping.isNotEmpty()) {
297 throw BluePrintProcessorException("Fail to find key-value in response node to map output-key-mapping.")
303 private fun parseResponseNodeForCollection(
304 responseNode: JsonNode, resourceAssignment: ResourceAssignment,
305 raRuntimeService: ResourceAssignmentRuntimeService,
306 outputKeyMapping: MutableMap<String, String>
308 val dName = resourceAssignment.dictionaryName
309 val metadata = resourceAssignment.property!!.metadata
310 var resultNode: JsonNode
311 if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
312 throw BluePrintProcessorException(
313 "Couldn't get data type for dictionary type " +
314 "(${resourceAssignment.property!!.type}) and dictionary name ($dName)"
317 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
319 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
320 if (outputKeyMapping.isNotEmpty()) {
321 when (responseNode) {
323 val responseArrayNode = responseNode.toList()
324 for (responseSingleJsonNode in responseArrayNode) {
325 val arrayChildNode = parseSingleElementOfArrayResponseNode(
327 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata
329 arrayNode.add(arrayChildNode)
331 resultNode = arrayNode
334 val responseArrayNode = responseNode.rootFieldsToMap()
336 parseObjectResponseNode(entrySchemaType, outputKeyMapping, responseArrayNode, metadata)
339 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
343 when (responseNode) {
345 responseNode.forEach { elementNode ->
346 arrayNode.add(elementNode)
348 resultNode = arrayNode
351 val responseArrayNode = responseNode.rootFieldsToMap()
352 for ((key, responseSingleJsonNode) in responseArrayNode) {
353 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
354 logKeyValueResolvedResource(metadata, key, responseSingleJsonNode, entrySchemaType)
355 JacksonUtils.populateJsonNodeValues(
357 responseSingleJsonNode,
361 arrayNode.add(arrayChildNode)
363 resultNode = arrayNode
366 resultNode = responseNode
374 private fun parseSingleElementOfArrayResponseNode(
375 entrySchemaType: String, outputKeyMapping: MutableMap<String, String>,
376 raRuntimeService: ResourceAssignmentRuntimeService,
377 responseNode: JsonNode, metadata: MutableMap<String, String>?
379 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
380 when (entrySchemaType) {
381 in BluePrintTypes.validPrimitiveTypes() -> {
382 if (outputKeyMappingHasOnlyOneElement) {
383 val outputKeyMap = outputKeyMapping.entries.first()
384 return parseSingleElementNodeWithOneOutputKeyMapping(
392 throw BluePrintProcessorException("Expect one entry in output-key-mapping")
397 checkOutputKeyMappingAllElementsInDataTypeProperties(
402 parseSingleElementNodeWithAllOutputKeyMapping(
409 outputKeyMappingHasOnlyOneElement -> {
410 val outputKeyMap = outputKeyMapping.entries.first()
411 parseSingleElementNodeWithOneOutputKeyMapping(
420 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
427 private fun parseObjectResponseNode(
428 entrySchemaType: String, outputKeyMapping: MutableMap<String, String>,
429 responseArrayNode: MutableMap<String, JsonNode>, metadata: MutableMap<String, String>?
431 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
432 if (outputKeyMappingHasOnlyOneElement) {
433 val outputKeyMap = outputKeyMapping.entries.first()
434 return parseObjectResponseNodeWithOneOutputKeyMapping(
435 responseArrayNode, outputKeyMap.key, outputKeyMap.value,
436 entrySchemaType, metadata
439 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
443 private fun parseSingleElementNodeWithOneOutputKeyMapping(
444 responseSingleJsonNode: JsonNode, outputKeyMappingKey:
445 String, outputKeyMappingValue: String, type: String, metadata: MutableMap<String, String>?
447 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
449 val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
450 responseSingleJsonNode.get(outputKeyMappingValue)
452 NullNode.getInstance()
455 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
456 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
458 return arrayChildNode
461 private fun parseSingleElementNodeWithAllOutputKeyMapping(
462 responseSingleJsonNode: JsonNode,
463 outputKeyMapping: MutableMap<String, String>,
464 type: String, metadata: MutableMap<String, String>?
466 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
467 outputKeyMapping.map {
468 val responseKeyValue = if (responseSingleJsonNode.has(it.value)) {
469 responseSingleJsonNode.get(it.value)
471 NullNode.getInstance()
474 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
475 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
477 return arrayChildNode
480 private fun parseObjectResponseNodeWithOneOutputKeyMapping(
481 responseArrayNode: MutableMap<String, JsonNode>,
482 outputKeyMappingKey: String, outputKeyMappingValue: String,
483 type: String, metadata: MutableMap<String, String>?
485 val objectNode = JacksonUtils.objectMapper.createObjectNode()
486 val responseSingleJsonNode = responseArrayNode.filterKeys { key ->
487 key == outputKeyMappingValue
488 }.entries.firstOrNull()
490 if (responseSingleJsonNode == null) {
491 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
492 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
494 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
495 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
500 private fun parseResponseNodeForComplexType(
501 responseNode: JsonNode, resourceAssignment: ResourceAssignment,
502 raRuntimeService: ResourceAssignmentRuntimeService,
503 outputKeyMapping: MutableMap<String, String>
505 val entrySchemaType = resourceAssignment.property!!.type
506 val dictionaryName = resourceAssignment.dictionaryName!!
507 val metadata = resourceAssignment.property!!.metadata
508 val outputKeyMappingHasOnlyOneElement = checkIfOutputKeyMappingProvideOneElement(outputKeyMapping)
510 if (outputKeyMapping.isNotEmpty()) {
512 checkOutputKeyMappingAllElementsInDataTypeProperties(
517 parseSingleElementNodeWithAllOutputKeyMapping(
524 outputKeyMappingHasOnlyOneElement -> {
525 val outputKeyMap = outputKeyMapping.entries.first()
526 parseSingleElementNodeWithOneOutputKeyMapping(
527 responseNode, outputKeyMap.key, outputKeyMap.value,
528 entrySchemaType, metadata
532 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
536 val childNode = JacksonUtils.objectMapper.createObjectNode()
537 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
542 private fun checkOutputKeyMappingAllElementsInDataTypeProperties(
543 dataTypeName: String, outputKeyMapping: MutableMap<String, String>,
544 raRuntimeService: ResourceAssignmentRuntimeService
546 val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
547 val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
548 return result == null
551 private fun logKeyValueResolvedResource(
552 metadata: MutableMap<String, String>?,
557 val valueToPrint = getValueToLog(metadata, value)
560 "For List Type Resource: key ($key), value ($valueToPrint), " +
565 private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
566 return (outputKeyMapping.size == 1)
569 fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any {
570 return if (checkIfLogIsProtected(metadata)) {
577 private fun checkIfLogIsProtected(metadata: MutableMap<String, String>?): Boolean {
578 var checkProtected = false
579 if (metadata != null &&
580 metadata.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_LOG_PROTECTED_METADATA)
582 val protectedMetadata = metadata[ResourceResolutionConstants.RESOURCE_RESOLUTION_LOG_PROTECTED_METADATA]
583 if (protectedMetadata == "yes" || protectedMetadata == "y") {
584 checkProtected = true
587 return checkProtected