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 if (resourceAssignment.dictionarySource in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES) {
75 logger.info("Setting Resource Value (*********) for Resource Name " +
76 "(${resourceAssignment.name}) of type (${resourceProp.type})")
79 logger.info("Setting Resource Value ($value) for Resource Name " +
80 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
81 "of type (${resourceProp.type})")
83 setResourceValue(resourceAssignment, raRuntimeService, value)
84 resourceAssignment.updatedDate = Date()
85 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
86 resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
88 } catch (e: Exception) {
89 throw BluePrintProcessorException("Failed in setting value for template key " +
90 "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
91 "type (${resourceProp.type}) with error message (${e.message})", e)
95 private fun setResourceValue(resourceAssignment: ResourceAssignment,
96 raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode) {
97 // TODO("See if Validation is needed wrt to type before storing")
98 raRuntimeService.putResolutionStore(resourceAssignment.name, value)
99 raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
100 resourceAssignment.property!!.value = value
103 fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
104 if (isNotEmpty(resourceAssignment.name)) {
105 resourceAssignment.updatedDate = Date()
106 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
107 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
108 resourceAssignment.message = message
112 @Throws(BluePrintProcessorException::class)
113 fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
114 val resourceProp = checkNotNull(resourceAssignment.property) {
115 "Failed to populate mandatory resource resource mapping $resourceAssignment"
117 if (resourceProp.required != null && resourceProp.required!!
118 && (resourceProp.value == null || resourceProp.value!!.returnNullIfMissing() == null)) {
119 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
120 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
124 @Throws(BluePrintProcessorException::class)
125 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
128 val mapper = ObjectMapper()
129 val root: ObjectNode = mapper.createObjectNode()
131 var containsSecret = false
132 assignments.forEach {
133 if (isNotEmpty(it.name) && it.property != null) {
135 val type = nullToEmpty(it.property?.type).toLowerCase()
136 val value = useDefaultValueIfNull(it, rName)
137 if (it.dictionarySource in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES) {
138 logger.info("Generating Resource name ($rName), type ($type), value (************)")
139 containsSecret = true
142 logger.info("Generating Resource name ($rName), type ($type), value ($value)")
144 root.set(rName, value)
147 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
148 if (containsSecret) {
149 logger.info("Generated Resource Param Data (***********)")
152 logger.info("Generated Resource Param Data ($result)")
154 } catch (e: Exception) {
155 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
161 @Throws(BluePrintProcessorException::class)
162 fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
163 val data: MutableMap<String, JsonNode> = hashMapOf()
164 assignments.forEach {
165 if (isNotEmpty(it.name) && it.property != null) {
167 val type = nullToEmpty(it.property?.type).toLowerCase()
168 val value = useDefaultValueIfNull(it, rName)
169 if (it.dictionarySource in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES) {
170 logger.trace("Generating Resource name ($rName), type ($type), value (************)")
173 logger.trace("Generating Resource name ($rName), type ($type), value ($value)")
181 private fun useDefaultValueIfNull(resourceAssignment: ResourceAssignment, resourceAssignmentName: String): JsonNode {
182 if (resourceAssignment.property?.value == null) {
183 val defaultValue = "\${$resourceAssignmentName}"
184 return TextNode(defaultValue)
186 return resourceAssignment.property!!.value!!
190 fun transformToRARuntimeService(blueprintRuntimeService: BluePrintRuntimeService<*>,
191 templateArtifactName: String): ResourceAssignmentRuntimeService {
193 val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(blueprintRuntimeService.id(),
194 blueprintRuntimeService.bluePrintContext())
195 resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
196 resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
198 return resourceAssignmentRuntimeService
201 @Throws(BluePrintProcessorException::class)
202 fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
203 propertyName: String): String {
204 lateinit var type: String
206 val dataTypeProps = checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
208 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
209 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
210 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
211 } catch (e: Exception) {
212 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
213 throw BluePrintProcessorException("${e.message}", e)
218 @Throws(BluePrintProcessorException::class)
219 fun parseResponseNode(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
220 raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>): JsonNode {
222 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
223 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
225 val type = resourceAssignment.property!!.type
227 if (resourceAssignment.dictionarySource in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES) {
228 logger.info("For template key (${resourceAssignment.name}) setting value as (***************)")
231 logger.info("For template key (${resourceAssignment.name}) setting value as ($responseNode)")
235 in BluePrintTypes.validPrimitiveTypes() -> {
236 parseResponseNodeForPrimitiveTypes(responseNode, outputKeyMapping)
238 in BluePrintTypes.validCollectionTypes() -> {
240 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
244 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
247 } catch (e: Exception) {
248 logger.error("Fail to parse response data, error message $e")
249 throw BluePrintProcessorException("${e.message}", e)
253 private fun parseResponseNodeForPrimitiveTypes(responseNode: JsonNode,
254 outputKeyMapping: MutableMap<String, String>): JsonNode {
255 var result: JsonNode? = responseNode
256 if (responseNode.isComplexType()) {
257 val key = outputKeyMapping.keys.firstOrNull()
258 var returnNode: JsonNode? = responseNode
259 if (responseNode is ArrayNode) {
260 val arrayNode = responseNode.toList()
261 val firstElement = if (key.isNullOrEmpty()) {
265 arrayNode.firstOrNull { element ->
266 element.isComplexType() && element.has(outputKeyMapping[key])
270 if (firstElement.isNull() || (firstElement!!.isComplexType() && !firstElement!!.has(outputKeyMapping[key]))
271 || (!result!!.isComplexType() && result is NullNode)) {
272 if (key.isNullOrEmpty()) {
273 throw BluePrintProcessorException("Fail to find mapping in the responseNode.")
276 throw BluePrintProcessorException("Fail to find response with output key mapping ($key) in result.")
279 returnNode = firstElement
281 result = if (returnNode!!.isComplexType()) {
282 returnNode[outputKeyMapping[key]]
291 private fun parseResponseNodeForCollection(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
292 raRuntimeService: ResourceAssignmentRuntimeService,
293 outputKeyMapping: MutableMap<String, String>): JsonNode {
294 val dName = resourceAssignment.dictionaryName
295 val dSource = resourceAssignment.dictionarySource
296 if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
297 throw BluePrintProcessorException("Couldn't get data type for dictionary type " +
298 "(${resourceAssignment.property!!.type}) and dictionary name ($dName)")
300 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
302 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
304 if (outputKeyMapping.isNotEmpty()) {
305 when (responseNode) {
307 val responseArrayNode = responseNode.toList()
308 for (responseSingleJsonNode in responseArrayNode) {
309 val arrayChildNode = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseSingleJsonNode,
310 outputKeyMapping, entrySchemaType, dSource!!)
311 arrayNode.add(arrayChildNode)
315 val responseArrayNode = responseNode.rootFieldsToMap()
316 val arrayNodeResult = parseObjectNodeWithOutputKeyMapping(responseArrayNode,
317 outputKeyMapping, entrySchemaType, dSource!!)
318 arrayNode.addAll(arrayNodeResult)
321 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
326 when (responseNode) {
328 responseNode.forEach { elementNode ->
329 arrayNode.add(elementNode)
333 val responseArrayNode = responseNode.rootFieldsToMap()
334 for ((key, responseSingleJsonNode) in responseArrayNode) {
335 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
336 JacksonUtils.populateJsonNodeValues(key, responseSingleJsonNode, entrySchemaType, arrayChildNode)
337 arrayNode.add(arrayChildNode)
341 arrayNode.add(responseNode)
348 private fun parseResponseNodeForComplexType(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
349 raRuntimeService: ResourceAssignmentRuntimeService,
350 outputKeyMapping: MutableMap<String, String>): JsonNode {
351 val entrySchemaType = resourceAssignment.property!!.type
352 val dictionarySource = resourceAssignment.dictionarySource!!
353 val dictionaryName = resourceAssignment.dictionaryName!!
355 var result: ObjectNode
356 if (checkOutputKeyMappingInDataTypeProperties(entrySchemaType, outputKeyMapping, raRuntimeService))
358 result = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseNode, outputKeyMapping, entrySchemaType, dictionarySource!!)
361 val childNode = JacksonUtils.objectMapper.createObjectNode()
362 if (outputKeyMapping.isNotEmpty()) {
363 outputKeyMapping.map {
364 val responseKeyValue = if (responseNode.has(it.key)) {
365 responseNode.get(it.key)
368 NullNode.getInstance()
371 logKeyValueResolvedResource(it.key, responseKeyValue, entrySchemaType, dictionarySource)
373 JacksonUtils.populateJsonNodeValues(it.value,
374 responseKeyValue, entrySchemaType, childNode)
378 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
385 private fun parseArrayNodeElementWithOutputKeyMapping(raRuntimeService: ResourceAssignmentRuntimeService,
386 responseSingleJsonNode: JsonNode, outputKeyMapping:
387 MutableMap<String, String>, entrySchemaType: String,
388 dictionarySource: String): ObjectNode {
389 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
391 outputKeyMapping.map {
392 val responseKeyValue = if (responseSingleJsonNode.has(it.key)) {
393 responseSingleJsonNode.get(it.key)
396 NullNode.getInstance()
398 val propertyTypeForDataType = ResourceAssignmentUtils
399 .getPropertyType(raRuntimeService, entrySchemaType, it.key)
401 logKeyValueResolvedResource(it.key, responseKeyValue, propertyTypeForDataType, dictionarySource)
403 JacksonUtils.populateJsonNodeValues(it.value,
404 responseKeyValue, propertyTypeForDataType, arrayChildNode)
406 return arrayChildNode
409 private fun parseObjectNodeWithOutputKeyMapping(responseArrayNode: MutableMap<String, JsonNode>,
410 outputKeyMapping: MutableMap<String, String>,
411 entrySchemaType: String,
412 dictionarySource: String): ArrayNode {
413 val arrayNode = JacksonUtils.objectMapper.createArrayNode()
414 outputKeyMapping.map {
415 val objectNode = JacksonUtils.objectMapper.createObjectNode()
416 val responseSingleJsonNode = responseArrayNode.filterKeys { key -> key == it.key }.entries.firstOrNull()
418 val responseNode = responseSingleJsonNode?.value ?: NullNode.getInstance()
420 logKeyValueResolvedResource(it.key, responseNode, entrySchemaType, dictionarySource)
422 JacksonUtils.populateJsonNodeValues(it.value, responseNode, entrySchemaType, objectNode)
424 arrayNode.add(objectNode)
430 private fun checkOutputKeyMappingInDataTypeProperties(dataTypeName: String, outputKeyMapping: MutableMap<String, String>,
431 raRuntimeService: ResourceAssignmentRuntimeService): Boolean {
432 val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
433 val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
434 return result == null
437 private fun logKeyValueResolvedResource(key: String, value: JsonNode, type: String, dictionarySource: String) {
438 if (dictionarySource in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES) {
439 logger.info("For List Type Resource: key ($key), value (****************), " +
443 logger.info("For List Type Resource: key ($key), value ($value), " +