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 logger.info("Setting Resource Value ($value) for Resource Name " +
75 "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
76 "of type (${resourceProp.type})")
77 setResourceValue(resourceAssignment, raRuntimeService, value)
78 resourceAssignment.updatedDate = Date()
79 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
80 resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
82 } catch (e: Exception) {
83 throw BluePrintProcessorException("Failed in setting value for template key " +
84 "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
85 "type (${resourceProp.type}) with error message (${e.message})", e)
89 private fun setResourceValue(resourceAssignment: ResourceAssignment,
90 raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode) {
91 // TODO("See if Validation is needed wrt to type before storing")
92 raRuntimeService.putResolutionStore(resourceAssignment.name, value)
93 raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
94 resourceAssignment.property!!.value = value
97 fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
98 if (isNotEmpty(resourceAssignment.name)) {
99 resourceAssignment.updatedDate = Date()
100 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
101 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
102 resourceAssignment.message = message
106 @Throws(BluePrintProcessorException::class)
107 fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
108 val resourceProp = checkNotNull(resourceAssignment.property) {
109 "Failed to populate mandatory resource resource mapping $resourceAssignment"
111 if (resourceProp.required != null && resourceProp.required!!
112 && (resourceProp.value == null || resourceProp.value!!.returnNullIfMissing() == null)) {
113 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
114 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
118 @Throws(BluePrintProcessorException::class)
119 fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
122 val mapper = ObjectMapper()
123 val root: ObjectNode = mapper.createObjectNode()
125 assignments.forEach {
126 if (isNotEmpty(it.name) && it.property != null) {
128 val type = nullToEmpty(it.property?.type).toLowerCase()
129 val value = useDefaultValueIfNull(it, rName)
130 logger.info("Generating Resource name ($rName), type ($type), value ($value)")
131 root.set(rName, value)
134 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
135 logger.info("Generated Resource Param Data ($result)")
136 } catch (e: Exception) {
137 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
143 @Throws(BluePrintProcessorException::class)
144 fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
145 val data: MutableMap<String, JsonNode> = hashMapOf()
146 assignments.forEach {
147 if (isNotEmpty(it.name) && it.property != null) {
149 val type = nullToEmpty(it.property?.type).toLowerCase()
150 val value = useDefaultValueIfNull(it, rName)
151 logger.trace("Generating Resource name ($rName), type ($type), value ($value)")
158 private fun useDefaultValueIfNull(resourceAssignment: ResourceAssignment, resourceAssignmentName: String): JsonNode {
159 if (resourceAssignment.property?.value == null) {
160 val defaultValue = "\${$resourceAssignmentName}"
161 return TextNode(defaultValue)
163 return resourceAssignment.property!!.value!!
167 fun transformToRARuntimeService(blueprintRuntimeService: BluePrintRuntimeService<*>,
168 templateArtifactName: String): ResourceAssignmentRuntimeService {
170 val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(blueprintRuntimeService.id(),
171 blueprintRuntimeService.bluePrintContext())
172 resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
173 resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
175 return resourceAssignmentRuntimeService
178 @Throws(BluePrintProcessorException::class)
179 fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
180 propertyName: String): String {
181 lateinit var type: String
183 val dataTypeProps = checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
185 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
186 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
187 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
188 } catch (e: Exception) {
189 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
190 throw BluePrintProcessorException("${e.message}", e)
195 @Throws(BluePrintProcessorException::class)
196 fun parseResponseNode(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
197 raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>): JsonNode {
199 if ((resourceAssignment.property?.type).isNullOrEmpty()) {
200 throw BluePrintProcessorException("Couldn't get data dictionary type for dictionary name (${resourceAssignment.name})")
202 val type = resourceAssignment.property!!.type
204 in BluePrintTypes.validPrimitiveTypes() -> {
205 parseResponseNodeForPrimitiveTypes(responseNode, resourceAssignment, outputKeyMapping)
207 in BluePrintTypes.validCollectionTypes() -> {
209 parseResponseNodeForCollection(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
213 parseResponseNodeForComplexType(responseNode, resourceAssignment, raRuntimeService, outputKeyMapping)
216 } catch (e: Exception) {
217 logger.error("Fail to parse response data, error message $e")
218 throw BluePrintProcessorException("${e.message}", e)
222 private fun parseResponseNodeForPrimitiveTypes(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
223 outputKeyMapping: MutableMap<String, String>): JsonNode {
224 val dName = resourceAssignment.dictionaryName
225 logger.info("For template key (${resourceAssignment.name}) setting value as ($responseNode)")
227 var result: JsonNode? = responseNode
228 if (responseNode.isComplexType()) {
229 val key = outputKeyMapping.keys.firstOrNull()
230 var returnNode: JsonNode? = responseNode
231 if (responseNode is ArrayNode) {
232 val arrayNode = responseNode.toList()
233 val firstElement = if (key.isNullOrEmpty()) {
237 arrayNode.firstOrNull { element ->
238 element.isComplexType() && element.has(outputKeyMapping[key])
242 if (firstElement.isNull() || (firstElement!!.isComplexType() && !firstElement!!.has(outputKeyMapping[key]))
243 || (!result!!.isComplexType() && result is NullNode)) {
244 if (key.isNullOrEmpty()) {
245 throw BluePrintProcessorException("Fail to find mapping in the responseNode.")
248 throw BluePrintProcessorException("Fail to find response with output key mapping ($key) in result.")
251 returnNode = firstElement
253 result = if (returnNode!!.isComplexType()) {
254 returnNode[outputKeyMapping[key]]
263 private fun parseResponseNodeForCollection(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
264 raRuntimeService: ResourceAssignmentRuntimeService,
265 outputKeyMapping: MutableMap<String, String>): JsonNode {
266 val dName = resourceAssignment.dictionaryName
267 if ((resourceAssignment.property?.entrySchema?.type).isNullOrEmpty()) {
268 throw BluePrintProcessorException("Couldn't get data type for dictionary type " +
269 "(${resourceAssignment.property!!.type}) and dictionary name ($dName)")
271 val entrySchemaType = resourceAssignment.property!!.entrySchema!!.type
273 var arrayNode = JacksonUtils.objectMapper.createArrayNode()
275 if (outputKeyMapping.isNotEmpty()) {
276 when (responseNode) {
278 val responseArrayNode = responseNode.toList()
279 for (responseSingleJsonNode in responseArrayNode) {
280 val arrayChildNode = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseSingleJsonNode,
281 outputKeyMapping, entrySchemaType)
282 arrayNode.add(arrayChildNode)
286 val responseArrayNode = responseNode.rootFieldsToMap()
287 val arrayNodeResult = parseObjectNodeWithOutputKeyMapping(responseArrayNode, outputKeyMapping, entrySchemaType)
288 arrayNode.addAll(arrayNodeResult)
291 throw BluePrintProcessorException("Key-value response expected to match the responseNode.")
296 when (responseNode) {
298 responseNode.forEach { elementNode ->
299 arrayNode.add(elementNode)
303 val responseArrayNode = responseNode.rootFieldsToMap()
304 for ((key, responseSingleJsonNode) in responseArrayNode) {
305 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
306 JacksonUtils.populateJsonNodeValues(key, responseSingleJsonNode, entrySchemaType, arrayChildNode)
307 arrayNode.add(arrayChildNode)
311 arrayNode.add(responseNode)
316 logger.info("For template key (${resourceAssignment.name}) setting value as ($arrayNode)")
321 private fun parseResponseNodeForComplexType(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
322 raRuntimeService: ResourceAssignmentRuntimeService,
323 outputKeyMapping: MutableMap<String, String>): JsonNode {
324 val entrySchemaType = resourceAssignment.property!!.type
325 val dictionaryName = resourceAssignment.dictionaryName!!
327 var result: ObjectNode
328 if (checkOutputKeyMappingInDataTypeProperties(entrySchemaType, outputKeyMapping, raRuntimeService))
330 result = parseArrayNodeElementWithOutputKeyMapping(raRuntimeService, responseNode, outputKeyMapping, entrySchemaType)
333 val childNode = JacksonUtils.objectMapper.createObjectNode()
334 if (outputKeyMapping.isNotEmpty()) {
335 outputKeyMapping.map {
336 val responseKeyValue = if (responseNode.has(it.key)) {
337 responseNode.get(it.key)
340 NullNode.getInstance()
343 JacksonUtils.populateJsonNodeValues(it.value,
344 responseKeyValue, entrySchemaType, childNode)
348 JacksonUtils.populateJsonNodeValues(dictionaryName, responseNode, entrySchemaType, childNode)
355 private fun parseArrayNodeElementWithOutputKeyMapping(raRuntimeService: ResourceAssignmentRuntimeService,
356 responseSingleJsonNode: JsonNode, outputKeyMapping:
357 MutableMap<String, String>, entrySchemaType: String): ObjectNode {
358 val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
360 outputKeyMapping.map {
361 val responseKeyValue = if (responseSingleJsonNode.has(it.key)) {
362 responseSingleJsonNode.get(it.key)
365 NullNode.getInstance()
367 val propertyTypeForDataType = ResourceAssignmentUtils
368 .getPropertyType(raRuntimeService, entrySchemaType, it.key)
370 logger.info("For List Type Resource: key (${it.key}), value ($responseKeyValue), " +
371 "type ({$propertyTypeForDataType})")
373 JacksonUtils.populateJsonNodeValues(it.value,
374 responseKeyValue, propertyTypeForDataType, arrayChildNode)
377 return arrayChildNode
380 private fun parseObjectNodeWithOutputKeyMapping(responseArrayNode: MutableMap<String, JsonNode>,
381 outputKeyMapping: MutableMap<String, String>,
382 entrySchemaType: String): ArrayNode {
383 val arrayNode = JacksonUtils.objectMapper.createArrayNode()
384 outputKeyMapping.map {
385 val objectNode = JacksonUtils.objectMapper.createObjectNode()
386 val responseSingleJsonNode = responseArrayNode.filterKeys { key -> key == it.key }.entries.firstOrNull()
388 if (responseSingleJsonNode == null) {
389 JacksonUtils.populateJsonNodeValues(it.value, NullNode.getInstance(), entrySchemaType, objectNode)
393 JacksonUtils.populateJsonNodeValues(it.value, responseSingleJsonNode.value, entrySchemaType, objectNode)
395 arrayNode.add(objectNode)
401 private fun checkOutputKeyMappingInDataTypeProperties(dataTypeName: String, outputKeyMapping: MutableMap<String, String>,
402 raRuntimeService: ResourceAssignmentRuntimeService): Boolean {
403 val dataTypeProps = raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties
404 val result = outputKeyMapping.filterKeys { !dataTypeProps!!.containsKey(it) }.keys.firstOrNull()
405 return result == null