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(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)
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())
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"
62 checkNotEmpty(resourceAssignment.name) {
63 "Failed in setting resource value for resource mapping $resourceAssignment"
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})")
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
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)
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
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
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)")
114 ResourceAssignmentUtils.setResourceDataValue(resourceAssignment, raRuntimeService, value)
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)
124 @Throws(BluePrintProcessorException::class)
125 fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
126 val resourceProp = checkNotNull(resourceAssignment.property) {
127 "Failed to populate mandatory resource resource mapping $resourceAssignment"
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)")
136 @Throws(BluePrintProcessorException::class)
137 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
140 val mapper = ObjectMapper()
141 val root: ObjectNode = mapper.createObjectNode()
143 var containsLogProtected = false
144 assignments.forEach {
145 if (isNotEmpty(it.name) && it.property != null) {
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
154 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
155 root.set(rName, value)
158 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
160 if (!containsLogProtected) {
161 logger.info("Generated Resource Param Data ($result)")
163 } catch (e: Exception) {
164 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
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) {
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)
181 logger.trace("Generating Resource name ($rName), type ($type), value ($valueToPrint)")
188 private fun useDefaultValueIfNull(resourceAssignment: ResourceAssignment, resourceAssignmentName: String): JsonNode {
189 if (resourceAssignment.property?.value == null) {
190 val defaultValue = "\${$resourceAssignmentName}"
191 return TextNode(defaultValue)
193 return resourceAssignment.property!!.value!!
197 fun transformToRARuntimeService(blueprintRuntimeService: BluePrintRuntimeService<*>,
198 templateArtifactName: String): ResourceAssignmentRuntimeService {
200 val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(blueprintRuntimeService.id(),
201 blueprintRuntimeService.bluePrintContext())
202 resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
203 resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
205 return resourceAssignmentRuntimeService
208 @Throws(BluePrintProcessorException::class)
209 fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
210 propertyName: String): String {
211 lateinit var type: String
213 val dataTypeProps = checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
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)
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
230 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
231 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
233 val type = resourceAssignment.property!!.type
235 val valueToPrint = getValueToLog(metadata, responseNode)
237 logger.info("For template key (${resourceAssignment.name}) setting value as ($valueToPrint)")
240 in BluePrintTypes.validPrimitiveTypes() -> {
241 parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
243 in BluePrintTypes.validCollectionTypes() -> {
245 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
249 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
252 } catch (e: Exception) {
253 logger.error("Fail to parse response data, error message $e")
254 throw BluePrintProcessorException("${e.message}", e)
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()) {
270 arrayNode.firstOrNull { element ->
271 element.isComplexType() && element.has(outputKeyMapping[key])
275 returnNode = firstElement
278 returnNode = responseNode
281 if (returnNode.isNull() || (returnNode!!.isComplexType() && !returnNode.has(outputKeyMapping[key]))) {
282 if (key.isNullOrEmpty()) {
283 throw BluePrintProcessorException("Fail to find mapping in the responseNode.")
286 throw BluePrintProcessorException("Fail to find response with output key mapping ($key) in result.")
289 result = if (returnNode.isComplexType()) {
290 returnNode[outputKeyMapping[key]]
297 if (outputKeyMapping.isNotEmpty()) {
298 throw BluePrintProcessorException("Fail to find key-value in response node to map output-key-mapping.")
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)")
314 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
316 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
317 if (outputKeyMapping.isNotEmpty()) {
318 when (responseNode) {
320 val responseArrayNode = responseNode.toList()
321 for (responseSingleJsonNode in responseArrayNode) {
322 val arrayChildNode = parseSingleElementOfArrayResponseNode(entrySchemaType,
323 outputKeyMapping, raRuntimeService, responseSingleJsonNode, metadata)
324 arrayNode.add(arrayChildNode)
326 resultNode = arrayNode
329 val responseArrayNode = responseNode.rootFieldsToMap()
330 resultNode = parseObjectResponseNode(entrySchemaType, outputKeyMapping, responseArrayNode, metadata)
333 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
338 when (responseNode) {
340 responseNode.forEach { elementNode ->
341 arrayNode.add(elementNode)
343 resultNode = arrayNode
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)
353 resultNode = arrayNode
356 resultNode = responseNode
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)
375 throw BluePrintProcessorException("Expect one entry in output-key-mapping")
380 checkOutputKeyMappingAllElementsInDataTypeProperties(entrySchemaType, outputKeyMapping, raRuntimeService) -> {
381 parseSingleElementNodeWithAllOutputKeyMapping(responseNode, outputKeyMapping, entrySchemaType, metadata)
383 outputKeyMappingHasOnlyOneElement -> {
384 val outputKeyMap = outputKeyMapping.entries.first()
385 parseSingleElementNodeWithOneOutputKeyMapping(responseNode, outputKeyMap.key, outputKeyMap.value, entrySchemaType, metadata)
388 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
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)
404 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
408 private fun parseSingleElementNodeWithOneOutputKeyMapping(responseSingleJsonNode: JsonNode, outputKeyMappingKey:
409 String, outputKeyMappingValue: String, type: String, metadata: MutableMap<String, String>?): ObjectNode {
410 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
412 val responseKeyValue = if (responseSingleJsonNode.has(outputKeyMappingValue)) {
413 responseSingleJsonNode.get(outputKeyMappingValue)
416 NullNode.getInstance()
419 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseKeyValue, type)
420 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseKeyValue, type, arrayChildNode)
422 return arrayChildNode
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)
433 NullNode.getInstance()
436 logKeyValueResolvedResource(metadata, it.key, responseKeyValue, type)
437 JacksonUtils.populateJsonNodeValues(it.key, responseKeyValue, type, arrayChildNode)
439 return arrayChildNode
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()
449 if (responseSingleJsonNode == null) {
450 logKeyValueResolvedResource(metadata, outputKeyMappingKey, NullNode.getInstance(), type)
451 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, NullNode.getInstance(), type, objectNode)
455 logKeyValueResolvedResource(metadata, outputKeyMappingKey, responseSingleJsonNode.value, type)
456 JacksonUtils.populateJsonNodeValues(outputKeyMappingKey, responseSingleJsonNode.value, type, objectNode)
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)
469 if (outputKeyMapping.isNotEmpty()) {
471 checkOutputKeyMappingAllElementsInDataTypeProperties(entrySchemaType, outputKeyMapping, raRuntimeService) -> {
472 parseSingleElementNodeWithAllOutputKeyMapping(responseNode, outputKeyMapping, entrySchemaType, metadata)
474 outputKeyMappingHasOnlyOneElement -> {
475 val outputKeyMap = outputKeyMapping.entries.first()
476 parseSingleElementNodeWithOneOutputKeyMapping(responseNode, outputKeyMap.key, outputKeyMap.value,
477 entrySchemaType, metadata)
480 throw BluePrintProcessorException("Output-key-mapping do not map the Data Type $entrySchemaType")
485 val childNode = JacksonUtils.objectMapper.createObjectNode()
486 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
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
498 private fun logKeyValueResolvedResource(metadata: MutableMap<String, String>?, key: String, value: JsonNode, type: String) {
499 val valueToPrint = getValueToLog(metadata, value)
501 logger.info("For List Type Resource: key ($key), value ($valueToPrint), " +
505 private fun checkIfOutputKeyMappingProvideOneElement(outputKeyMapping: MutableMap<String, String>): Boolean {
506 return (outputKeyMapping.size == 1)
509 fun getValueToLog(metadata: MutableMap<String, String>?, value: Any): Any {
510 return if (checkIfLogIsProtected(metadata)) {
518 fun checkIfInputWasProvided(resourceAssignment: ResourceAssignment, value: JsonNode) : Boolean{
519 val defaultValueNode = resourceAssignment.property!!.defaultValue.asJsonType()
520 return (value.returnNullIfMissing() != null && value != defaultValueNode)
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
532 return checkProtected