01cfd723ba6eeaf138bbf4103f6a29c4a5d500c2
[ccsdk/cds.git] /
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 IBM.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17
18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils
19
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.ObjectNode
24 import com.fasterxml.jackson.databind.node.TextNode
25 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
27 import org.onap.ccsdk.cds.controllerblueprints.core.*
28 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
29 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonReactorUtils
30 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
31 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
32 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
33 import org.slf4j.LoggerFactory
34 import java.util.*
35
36 class ResourceAssignmentUtils {
37     companion object {
38
39         private val logger = LoggerFactory.getLogger(ResourceAssignmentUtils::class.toString())
40
41         suspend fun resourceDefinitions(blueprintBasePath: String): MutableMap<String, ResourceDefinition> {
42             val dictionaryFile = normalizedFile(blueprintBasePath, BluePrintConstants.TOSCA_DEFINITIONS_DIR,
43                     ResourceResolutionConstants.FILE_NAME_RESOURCE_DEFINITION_TYPES)
44             checkFileExists(dictionaryFile) { "resource definition file(${dictionaryFile.absolutePath}) is missing" }
45             return JacksonReactorUtils.getMapFromFile(dictionaryFile, ResourceDefinition::class.java)
46         }
47
48         @Throws(BluePrintProcessorException::class)
49         fun setResourceDataValue(resourceAssignment: ResourceAssignment,
50                                  raRuntimeService: ResourceAssignmentRuntimeService, value: Any?) {
51             // TODO("See if Validation is needed in future with respect to conversion and Types")
52             return setResourceDataValue(resourceAssignment, raRuntimeService, value.asJsonType())
53         }
54
55         @Throws(BluePrintProcessorException::class)
56         fun setResourceDataValue(resourceAssignment: ResourceAssignment,
57                                  raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode) {
58             val resourceProp = checkNotNull(resourceAssignment.property) {
59                 "Failed in setting resource value for resource mapping $resourceAssignment"
60             }
61             checkNotEmpty(resourceAssignment.name) {
62                 "Failed in setting resource value for resource mapping $resourceAssignment"
63             }
64
65             if (resourceAssignment.dictionaryName.isNullOrEmpty()) {
66                 resourceAssignment.dictionaryName = resourceAssignment.name
67                 logger.warn("Missing dictionary key, setting with template key (${resourceAssignment.name}) " +
68                         "as dictionary key (${resourceAssignment.dictionaryName})")
69             }
70
71             try {
72                 if (resourceProp.type.isNotEmpty()) {
73                     logger.info("Setting Resource Value ($value) for Resource Name " +
74                             "(${resourceAssignment.name}), definition(${resourceAssignment.dictionaryName}) " +
75                             "of type (${resourceProp.type})")
76                     setResourceValue(resourceAssignment, raRuntimeService, value)
77                     resourceAssignment.updatedDate = Date()
78                     resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
79                     resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
80                 }
81             } catch (e: Exception) {
82                 throw BluePrintProcessorException("Failed in setting value for template key " +
83                         "(${resourceAssignment.name}) and dictionary key (${resourceAssignment.dictionaryName}) of " +
84                         "type (${resourceProp.type}) with error message (${e.message})", e)
85             }
86         }
87
88         private fun setResourceValue(resourceAssignment: ResourceAssignment,
89                                      raRuntimeService: ResourceAssignmentRuntimeService, value: JsonNode) {
90             // TODO("See if Validation is needed wrt to type before storing")
91             raRuntimeService.putResolutionStore(resourceAssignment.name, value)
92             raRuntimeService.putDictionaryStore(resourceAssignment.dictionaryName!!, value)
93             resourceAssignment.property!!.value = value
94         }
95
96         fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) {
97             if (isNotEmpty(resourceAssignment.name)) {
98                 resourceAssignment.updatedDate = Date()
99                 resourceAssignment.updatedBy = BluePrintConstants.USER_SYSTEM
100                 resourceAssignment.status = BluePrintConstants.STATUS_FAILURE
101                 resourceAssignment.message = message
102             }
103         }
104
105         @Throws(BluePrintProcessorException::class)
106         fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) {
107             val resourceProp = checkNotNull(resourceAssignment.property) {
108                 "Failed to populate mandatory resource resource mapping $resourceAssignment"
109             }
110             if (resourceProp.required != null && resourceProp.required!!
111                     && (resourceProp.value == null || resourceProp.value!!.returnNullIfMissing() == null)) {
112                 logger.error("failed to populate mandatory resource mapping ($resourceAssignment)")
113                 throw BluePrintProcessorException("failed to populate mandatory resource mapping ($resourceAssignment)")
114             }
115         }
116
117         @Throws(BluePrintProcessorException::class)
118         fun generateResourceDataForAssignments(assignments: List<ResourceAssignment>): String {
119             val result: String
120             try {
121                 val mapper = ObjectMapper()
122                 val root: ObjectNode = mapper.createObjectNode()
123
124                 assignments.forEach {
125                     if (isNotEmpty(it.name) && it.property != null) {
126                         val rName = it.name
127                         val type = nullToEmpty(it.property?.type).toLowerCase()
128                         val value = useDefaultValueIfNull(it, rName)
129                         logger.info("Generating Resource name ($rName), type ($type), value ($value)")
130                         root.set(rName, value)
131                     }
132                 }
133                 result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(root)
134                 logger.info("Generated Resource Param Data ($result)")
135             } catch (e: Exception) {
136                 throw BluePrintProcessorException("Resource Assignment is failed with $e.message", e)
137             }
138
139             return result
140         }
141
142         @Throws(BluePrintProcessorException::class)
143         fun generateResourceForAssignments(assignments: List<ResourceAssignment>): MutableMap<String, JsonNode> {
144             val data: MutableMap<String, JsonNode> = hashMapOf()
145             assignments.forEach {
146                 if (isNotEmpty(it.name) && it.property != null) {
147                     val rName = it.name
148                     val type = nullToEmpty(it.property?.type).toLowerCase()
149                     val value = useDefaultValueIfNull(it, rName)
150                     logger.trace("Generating Resource name ($rName), type ($type), value ($value)")
151                     data[rName] = value
152                 }
153             }
154             return data
155         }
156
157         private fun useDefaultValueIfNull(resourceAssignment: ResourceAssignment, resourceAssignmentName: String): JsonNode {
158             if (resourceAssignment.property?.value == null) {
159                 val defaultValue = "\${$resourceAssignmentName}"
160                 return TextNode(defaultValue)
161             } else {
162                 return resourceAssignment.property!!.value!!
163             }
164         }
165
166         fun transformToRARuntimeService(blueprintRuntimeService: BluePrintRuntimeService<*>,
167                                         templateArtifactName: String): ResourceAssignmentRuntimeService {
168
169             val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService(blueprintRuntimeService.id(),
170                     blueprintRuntimeService.bluePrintContext())
171             resourceAssignmentRuntimeService.createUniqueId(templateArtifactName)
172             resourceAssignmentRuntimeService.setExecutionContext(blueprintRuntimeService.getExecutionContext() as MutableMap<String, JsonNode>)
173
174             return resourceAssignmentRuntimeService
175         }
176
177         @Throws(BluePrintProcessorException::class)
178         fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String,
179                             propertyName: String): String {
180             lateinit var type: String
181             try {
182                 val dataTypeProps = checkNotNull(raRuntimeService.bluePrintContext().dataTypeByName(dataTypeName)?.properties)
183
184                 val propertyDefinition = checkNotNull(dataTypeProps[propertyName])
185                 type = checkNotEmpty(propertyDefinition.type) { "Couldn't get data type ($dataTypeName)" }
186                 logger.trace("Data type({})'s property ({}) is ({})", dataTypeName, propertyName, type)
187             } catch (e: Exception) {
188                 logger.error("couldn't get data type($dataTypeName)'s property ($propertyName), error message $e")
189                 throw BluePrintProcessorException("${e.message}", e)
190             }
191             return type
192         }
193
194         @Throws(BluePrintProcessorException::class)
195         fun parseResponseNode(responseNode: JsonNode, resourceAssignment: ResourceAssignment,
196                               raRuntimeService: ResourceAssignmentRuntimeService, outputKeyMapping: MutableMap<String, String>): JsonNode {
197             val dName = resourceAssignment.dictionaryName
198             val dSource = resourceAssignment.dictionarySource
199             val type = nullToEmpty(resourceAssignment.property?.type)
200             lateinit var entrySchemaType: String
201             when (type) {
202                 in BluePrintTypes.validPrimitiveTypes() -> {
203                     if (dSource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
204                         logger.info("For template key (${resourceAssignment.name}) setting value as ($responseNode)")
205                     val result = if (responseNode is ArrayNode)
206                         responseNode.get(0)
207                     else
208                         responseNode
209                     return if (result.isComplexType()) {
210                         check(result.has(outputKeyMapping[dName])) {
211                             "Fail to find output key mapping ($dName) in result."
212                         }
213                         result[outputKeyMapping[dName]]
214                     } else {
215                         result
216                     }
217                 }
218                 in BluePrintTypes.validCollectionTypes() -> {
219                     // Array Types
220                     entrySchemaType = checkNotEmpty(resourceAssignment.property?.entrySchema?.type) {
221                         "Entry schema is not defined for dictionary ($dName) info"
222                     }
223                     val arrayNode = JacksonUtils.objectMapper.createArrayNode()
224                     lateinit var responseValueNode: JsonNode
225                     lateinit var propertyType: String
226                     outputKeyMapping.map {
227                         val arrayChildNode = JacksonUtils.objectMapper.createObjectNode()
228                         val responseArrayNode = responseNode.rootFieldsToMap()
229                         outer@ for ((key, responseSingleJsonNode) in responseArrayNode) {
230                             if (key == it.key) {
231                                 if (entrySchemaType in BluePrintTypes.validPrimitiveTypes()) {
232                                     responseValueNode = responseSingleJsonNode
233                                     propertyType = entrySchemaType
234
235                                 } else {
236                                     responseValueNode = responseSingleJsonNode.get(it.key)
237                                     propertyType = getPropertyType(raRuntimeService, entrySchemaType, it.key)
238                                 }
239                                 if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
240                                     logger.info("For List Type Resource: key (${it.key}), value ($responseValueNode), " +
241                                             "type  ({$propertyType})")
242                                 JacksonUtils.populateJsonNodeValues(it.value,
243                                         responseValueNode, propertyType, arrayChildNode)
244                                 arrayNode.add(arrayChildNode)
245                                 break@outer
246                             }
247                         }
248                     }
249                     if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
250                         logger.info("For template key (${resourceAssignment.name}) setting value as ($arrayNode)")
251
252                     return arrayNode
253                 }
254                 else -> {
255                     // Complex Types
256                     entrySchemaType = checkNotEmpty(resourceAssignment.property?.type) {
257                         "Entry schema is not defined for dictionary ($dName) info"
258                     }
259                     val objectNode = JacksonUtils.objectMapper.createObjectNode()
260                     outputKeyMapping.map {
261                         val responseKeyValue = responseNode.get(it.key)
262                         val propertyTypeForDataType = ResourceAssignmentUtils
263                                 .getPropertyType(raRuntimeService, entrySchemaType, it.key)
264
265                         if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
266                             logger.info("For List Type Resource: key (${it.key}), value ($responseKeyValue), type  ({$propertyTypeForDataType})")
267                         JacksonUtils.populateJsonNodeValues(it.value, responseKeyValue, propertyTypeForDataType, objectNode)
268                     }
269
270                     if (resourceAssignment.dictionarySource !in ResourceResolutionConstants.DATA_DICTIONARY_SECRET_SOURCE_TYPES)
271                         logger.info("For template key (${resourceAssignment.name}) setting value as ($objectNode)")
272
273                     return objectNode
274                 }
275             }
276         }
277     }
278 }