Mesh input-key-mapping using velocity 76/81976/2
authorAlexis de Talhouët <adetalhouet89@gmail.com>
Mon, 11 Mar 2019 19:53:12 +0000 (15:53 -0400)
committerAlexis de Talhouët <adetalhouet89@gmail.com>
Mon, 11 Mar 2019 20:01:00 +0000 (16:01 -0400)
Change-Id: Ic7190c86fc4e3f66fe7223c1c3575279c2c1d105
Issue-ID: CCSDK-1131
Signed-off-by: Alexis de Talhouët <adetalhouet89@gmail.com>
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceAssignmentProcessor.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/ResourceAssignmentProcessor.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/RestResourceResolutionProcessor.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/core/service/BluePrintTemplateService.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/core/utils/JacksonUtils.kt
ms/controllerblueprints/modules/resource-dict/src/main/kotlin/org/onap/ccsdk/apps/controllerblueprints/resource/dict/service/ResourceAssignmentValidationService.kt

index ca4d631..bac76e7 100644 (file)
@@ -37,7 +37,6 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory
 import org.springframework.context.annotation.Scope
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
 import org.springframework.stereotype.Service
-import java.util.*
 
 /**
  * DatabaseResourceAssignmentProcessor
@@ -77,12 +76,16 @@ open class DatabaseResourceAssignmentProcessor(private val dBLibGenericService:
                 val sql = checkNotNull(sourceProperties.query) { "failed to get request query for $dName under $dSource properties" }
                 val inputKeyMapping = checkNotNull(sourceProperties.inputKeyMapping) { "failed to get input-key-mappings for $dName under $dSource properties" }
 
-                logger.info("$dSource dictionary information : ($sql), ($inputKeyMapping), (${sourceProperties.outputKeyMapping})")
+                val resolvedInputKeyMapping = resolveInputKeyMappingVariables(inputKeyMapping)
+
+                val resolvedSql = resolveFromInputKeyMapping(sql, resolvedInputKeyMapping)
+
+                logger.info("$dSource dictionary information : ($resolvedSql), ($inputKeyMapping), (${sourceProperties.outputKeyMapping})")
                 val jdbcTemplate = blueprintDBLibService(sourceProperties)
 
-                val rows = jdbcTemplate.queryForList(sql, populateNamedParameter(inputKeyMapping))
+                val rows = jdbcTemplate.queryForList(resolvedSql, resolvedInputKeyMapping)
                 if (rows.isNullOrEmpty()) {
-                    logger.warn("Failed to get $dSource result for dictionary name ($dName) the query ($sql)")
+                    logger.warn("Failed to get $dSource result for dictionary name ($dName) the query ($resolvedSql)")
                 } else {
                     populateResource(resourceAssignment, sourceProperties, rows)
                 }
@@ -115,17 +118,6 @@ open class DatabaseResourceAssignmentProcessor(private val dBLibGenericService:
         }
     }
 
-    private fun populateNamedParameter(inputKeyMapping: Map<String, String>): Map<String, Any> {
-        val namedParameters = HashMap<String, Any>()
-        inputKeyMapping.forEach {
-            val expressionValue = raRuntimeService.getDictionaryStore(it.value).textValue()
-            logger.trace("Reference dictionary key (${it.key}) resulted in value ($expressionValue)")
-            namedParameters[it.key] = expressionValue
-        }
-        logger.info("Parameter information : ({})", namedParameters)
-        return namedParameters
-    }
-
     @Throws(BluePrintProcessorException::class)
     private fun populateResource(resourceAssignment: ResourceAssignment, sourceProperties: DatabaseResourceSource, rows: List<Map<String, Any>>) {
         val dName = resourceAssignment.dictionaryName
index 9b7c70a..3022ca5 100644 (file)
 
 package org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.processor
 
+import org.apache.commons.collections.MapUtils
 import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService
 import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException
 import org.onap.ccsdk.apps.controllerblueprints.core.interfaces.BlueprintFunctionNode
+import org.onap.ccsdk.apps.controllerblueprints.core.service.BluePrintTemplateService
+import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils
 import org.onap.ccsdk.apps.controllerblueprints.resource.dict.ResourceAssignment
 import org.onap.ccsdk.apps.controllerblueprints.resource.dict.ResourceDefinition
 import org.slf4j.LoggerFactory
+import java.util.*
 
 abstract class ResourceAssignmentProcessor : BlueprintFunctionNode<ResourceAssignment, ResourceAssignment> {
 
@@ -38,12 +42,32 @@ abstract class ResourceAssignmentProcessor : BlueprintFunctionNode<ResourceAssig
      */
     open fun <T> scriptPropertyInstanceType(name: String): T {
         return scriptPropertyInstances as? T
-                ?: throw BluePrintProcessorException("couldn't get script property instance ($name)")
+            ?: throw BluePrintProcessorException("couldn't get script property instance ($name)")
     }
 
     open fun resourceDefinition(name: String): ResourceDefinition {
         return resourceDictionaries[name]
-                ?: throw BluePrintProcessorException("couldn't get resource definition for ($name)")
+            ?: throw BluePrintProcessorException("couldn't get resource definition for ($name)")
+    }
+
+    open fun resolveInputKeyMappingVariables(inputKeyMapping: Map<String, String>): Map<String, Any> {
+        val resolvedInputKeyMapping = HashMap<String, Any>()
+        if (MapUtils.isNotEmpty(inputKeyMapping)) {
+            for ((key, value) in inputKeyMapping) {
+                val resultValue = raRuntimeService.getResolutionStore(value)
+                val expressionValue = JacksonUtils.getValue(resultValue)
+                log.trace("Reference dictionary key ({}), value ({})", key, expressionValue)
+                resolvedInputKeyMapping[key] = expressionValue
+            }
+        }
+        return resolvedInputKeyMapping
+    }
+
+    open fun resolveFromInputKeyMapping(valueToResolve: String, keyMapping: Map<String, Any>): String {
+        if (valueToResolve.isEmpty() || !valueToResolve.contains("$")) {
+            return valueToResolve
+        }
+        return BluePrintTemplateService.generateContent(valueToResolve, additionalContext = keyMapping)
     }
 
     override fun prepareRequest(resourceAssignment: ResourceAssignment): ResourceAssignment {
index 9bb1ea2..73ccfb2 100644 (file)
@@ -19,8 +19,6 @@ package org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.pr
 
 import com.fasterxml.jackson.databind.node.ArrayNode
 import com.fasterxml.jackson.databind.node.JsonNodeFactory
-import com.fasterxml.jackson.databind.node.ObjectNode
-import org.apache.commons.collections.MapUtils
 import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR
 import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.RestResourceSource
 import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
@@ -40,7 +38,6 @@ import org.slf4j.LoggerFactory
 import org.springframework.beans.factory.config.ConfigurableBeanFactory
 import org.springframework.context.annotation.Scope
 import org.springframework.stereotype.Service
-import java.util.*
 
 /**
  * RestResourceResolutionProcessor
@@ -81,7 +78,7 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS
                 val path = nullToEmpty(sourceProperties.path)
                 val inputKeyMapping =
                     checkNotNull(sourceProperties.inputKeyMapping) { "failed to get input-key-mappings for $dName under $dSource properties" }
-                val resolvedInputKeyMapping = populateInputKeyMappingVariables(inputKeyMapping)
+                val resolvedInputKeyMapping = resolveInputKeyMappingVariables(inputKeyMapping)
 
                 // Resolving content Variables
                 val payload = resolveFromInputKeyMapping(nullToEmpty(sourceProperties.payload), resolvedInputKeyMapping)
@@ -170,7 +167,9 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS
             }
             else -> {
                 // Complex Types
-                val objectNode = responseNode as ObjectNode
+                entrySchemaType =
+                    returnNotEmptyOrThrow(resourceAssignment.property?.entrySchema?.type) { "Entry schema is not defined for dictionary ($dName) info" }
+                val objectNode = JsonNodeFactory.instance.objectNode()
                 outputKeyMapping.map {
                     val responseKeyValue = responseNode.get(it.key)
                     val propertyTypeForDataType =
@@ -186,29 +185,6 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS
         }
     }
 
-    private fun populateInputKeyMappingVariables(inputKeyMapping: Map<String, String>): Map<String, Any> {
-        val resolvedInputKeyMapping = HashMap<String, Any>()
-        if (MapUtils.isNotEmpty(inputKeyMapping)) {
-            for ((key, value) in inputKeyMapping) {
-                val expressionValue = raRuntimeService.getResolutionStore(value).asText()
-                logger.trace("Reference dictionary key ({}), value ({})", key, expressionValue)
-                resolvedInputKeyMapping[key] = expressionValue
-            }
-        }
-        return resolvedInputKeyMapping
-    }
-
-    private fun resolveFromInputKeyMapping(valueToResolve: String, keyMapping: Map<String, Any>): String {
-        if (valueToResolve.isEmpty() || !valueToResolve.contains("$")) {
-            return valueToResolve
-        }
-        var res = valueToResolve
-        for (entry in keyMapping.entries) {
-            res = res.replace(("\\$" + entry.key).toRegex(), entry.value.toString())
-        }
-        return res
-    }
-
     @Throws(BluePrintProcessorException::class)
     private fun validate(resourceAssignment: ResourceAssignment) {
         checkNotEmptyOrThrow(resourceAssignment.name, "resource assignment template key is not defined")
index d175fdd..4fa69ca 100644 (file)
@@ -34,34 +34,32 @@ open class BluePrintTemplateService {
     companion object {
 
         /**
-         * Generate Content from Velocity Template and JSON Content.
+         * Generate Content from Velocity Template and JSON Content or property map.
          */
-        fun generateContent(template: String, json: String,
+        fun generateContent(template: String, json: String = "",
                             ignoreJsonNull: Boolean = false,
-                            additionalContext: MutableMap<String, Any> = hashMapOf()): String {
+                            additionalContext: Map<String, Any> = hashMapOf()): String {
             Velocity.init()
             val mapper = ObjectMapper()
             val nodeFactory = BluePrintJsonNodeFactory()
-            mapper.setNodeFactory(nodeFactory)
-
-            val jsonNode = mapper.readValue<JsonNode>(json, JsonNode::class.java)
-                    ?: throw BluePrintProcessorException("couldn't get json node from json")
-
-            if (ignoreJsonNull)
-                JacksonUtils.removeJsonNullNode(jsonNode)
+            mapper.nodeFactory = nodeFactory
 
             val velocityContext = VelocityContext()
             velocityContext.put("StringUtils", StringUtils::class.java)
             velocityContext.put("BooleanUtils", BooleanUtils::class.java)
-            /**
-             * Add the Custom Velocity Context API
-             */
+
+            // Add the Custom Velocity Context API
             additionalContext.forEach { name, value -> velocityContext.put(name, value) }
-            /**
-             * Add the JSON Data to the context
-             */
-            jsonNode.fields().forEach { entry ->
-                velocityContext.put(entry.key, entry.value)
+
+            // Add the JSON Data to the context
+            if (json.isNotEmpty()) {
+                val jsonNode = mapper.readValue<JsonNode>(json, JsonNode::class.java)
+                    ?: throw BluePrintProcessorException("couldn't get json node from json")
+                if (ignoreJsonNull)
+                    JacksonUtils.removeJsonNullNode(jsonNode)
+                jsonNode.fields().forEach { entry ->
+                    velocityContext.put(entry.key, entry.value)
+                }
             }
 
             val stringWriter = StringWriter()
index 932f0ed..e0341b8 100644 (file)
@@ -22,8 +22,13 @@ import com.fasterxml.jackson.annotation.JsonInclude
 import com.fasterxml.jackson.databind.JsonNode
 import com.fasterxml.jackson.databind.SerializationFeature
 import com.fasterxml.jackson.databind.node.ArrayNode
+import com.fasterxml.jackson.databind.node.BooleanNode
+import com.fasterxml.jackson.databind.node.DoubleNode
+import com.fasterxml.jackson.databind.node.FloatNode
+import com.fasterxml.jackson.databind.node.IntNode
 import com.fasterxml.jackson.databind.node.NullNode
 import com.fasterxml.jackson.databind.node.ObjectNode
+import com.fasterxml.jackson.databind.node.TextNode
 import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.async
@@ -46,7 +51,7 @@ class JacksonUtils {
     companion object {
         private val log: EELFLogger = EELFManager.getInstance().getLogger(this::class.toString())
         inline fun <reified T : Any> readValue(content: String): T =
-                jacksonObjectMapper().readValue(content, T::class.java)
+            jacksonObjectMapper().readValue(content, T::class.java)
 
         fun <T> readValue(content: String, valueType: Class<T>): T? {
             return jacksonObjectMapper().readValue(content, valueType)
@@ -84,7 +89,7 @@ class JacksonUtils {
             return runBlocking {
                 withContext(Dispatchers.Default) {
                     IOUtils.toString(JacksonUtils::class.java.classLoader
-                            .getResourceAsStream(fileName), Charset.defaultCharset())
+                        .getResourceAsStream(fileName), Charset.defaultCharset())
                 }
             }
         }
@@ -184,7 +189,7 @@ class JacksonUtils {
 
         fun <T> getInstanceFromMap(properties: MutableMap<String, JsonNode>, classType: Class<T>): T {
             return readValue(getJson(properties), classType)
-                    ?: throw BluePrintProcessorException("failed to transform content ($properties) to type ($classType)")
+                ?: throw BluePrintProcessorException("failed to transform content ($properties) to type ($classType)")
         }
 
         fun checkJsonNodeValueOfType(type: String, jsonNode: JsonNode): Boolean {
@@ -228,14 +233,35 @@ class JacksonUtils {
             }
         }
 
+        fun getValue(value: JsonNode): Any {
+            return when (value) {
+                is BooleanNode -> value.booleanValue()
+                is IntNode -> value.intValue()
+                is FloatNode -> value.floatValue()
+                is DoubleNode -> value.doubleValue()
+                is TextNode -> value.textValue()
+                else -> value
+            }
+        }
+
+        fun getValue(value: Any, type: String): Any {
+            return when (type.toLowerCase()) {
+                BluePrintConstants.DATA_TYPE_BOOLEAN -> (value as BooleanNode).booleanValue()
+                BluePrintConstants.DATA_TYPE_INTEGER -> (value as IntNode).intValue()
+                BluePrintConstants.DATA_TYPE_FLOAT -> (value as FloatNode).floatValue()
+                BluePrintConstants.DATA_TYPE_DOUBLE -> (value as DoubleNode).doubleValue()
+                BluePrintConstants.DATA_TYPE_STRING -> (value as TextNode).textValue()
+                else -> (value as JsonNode)
+            }
+        }
+
         fun populatePrimitiveValues(key: String, value: Any, primitiveType: String, objectNode: ObjectNode) {
             when (primitiveType.toLowerCase()) {
-                BluePrintConstants.DATA_TYPE_BOOLEAN -> objectNode.put(key, value as Boolean)
-                BluePrintConstants.DATA_TYPE_INTEGER -> objectNode.put(key, value as Int)
-                BluePrintConstants.DATA_TYPE_FLOAT -> objectNode.put(key, value as Float)
-                BluePrintConstants.DATA_TYPE_DOUBLE -> objectNode.put(key, value as Double)
-                BluePrintConstants.DATA_TYPE_TIMESTAMP -> objectNode.put(key, value as String)
-                else -> objectNode.put(key, value as String)
+                BluePrintConstants.DATA_TYPE_BOOLEAN -> objectNode.put(key, (value as BooleanNode).booleanValue())
+                BluePrintConstants.DATA_TYPE_INTEGER -> objectNode.put(key, (value as IntNode).intValue())
+                BluePrintConstants.DATA_TYPE_FLOAT -> objectNode.put(key, (value as FloatNode).floatValue())
+                BluePrintConstants.DATA_TYPE_DOUBLE -> objectNode.put(key, (value as DoubleNode).doubleValue())
+                else -> objectNode.put(key, (value as TextNode).textValue())
             }
         }
 
index b35bca7..cd1dd43 100644 (file)
@@ -76,14 +76,6 @@ open class ResourceAssignmentValidationServiceImpl : ResourceAssignmentValidatio
             validationMessage.appendln(String.format("Duplicate Assignment Template Keys (%s) is Present", duplicateKeyNames))
         }
 
-        // Check the Resource Assignment has Duplicate Dictionary Names
-        val duplicateDictionaryKeyNames = resourceAssignments.groupBy { it.dictionaryName }
-                .filter { it.value.size > 1 }
-                .map { it.key }
-        if (duplicateDictionaryKeyNames.isNotEmpty()) {
-            validationMessage.appendln(String.format("Duplicate Assignment Dictionary Keys (%s) is Present", duplicateDictionaryKeyNames))
-        }
-
         // Collect all the dependencies as a single list
         val dependenciesNames = resourceAssignments.mapNotNull { it.dependencies }.flatten()