Merge "Resource resolution should return a string"
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / resource-resolution / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / resource / resolution / ResourceResolutionService.kt
index 5106225..641175c 100644 (file)
 
 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution
 
+import com.fasterxml.jackson.databind.JsonNode
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.coroutineScope
-import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionResultService
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolution
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionDBService
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.TemplateResolutionService
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor.ResourceAssignmentProcessor
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
-import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
-import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceDefinitionUtils.createResourceAssignments
+import org.onap.ccsdk.cds.controllerblueprints.core.*
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintTemplateService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
@@ -34,6 +37,7 @@ import org.onap.ccsdk.cds.controllerblueprints.resource.dict.utils.BulkResourceS
 import org.slf4j.LoggerFactory
 import org.springframework.context.ApplicationContext
 import org.springframework.stereotype.Service
+import java.util.*
 
 interface ResourceResolutionService {
 
@@ -46,20 +50,28 @@ interface ResourceResolutionService {
                                  artifactNames: List<String>, properties: Map<String, Any>): MutableMap<String, String>
 
     suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
-                                               artifactPrefix: String, properties: Map<String, Any>): String
+                                 artifactPrefix: String, properties: Map<String, Any>): String
 
-    suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
-                                                  artifactMapping: String, artifactTemplate: String?): String
+    /** Resolve resources for all the sources defined in a particular resource Definition[resolveDefinition]
+     * with other [resourceDefinitions] dependencies for the sources [sources]
+     * Used to get the same resource values from multiple sources. **/
+    suspend fun resolveResourceDefinition(blueprintRuntimeService: BluePrintRuntimeService<*>,
+                                          resourceDefinitions: MutableMap<String, ResourceDefinition>,
+                                          resolveDefinition: String, sources: List<String>)
+            : MutableMap<String, JsonNode>
 
     suspend fun resolveResourceAssignments(blueprintRuntimeService: BluePrintRuntimeService<*>,
                                            resourceDefinitions: MutableMap<String, ResourceDefinition>,
                                            resourceAssignments: MutableList<ResourceAssignment>,
-                                           identifierName: String)
+                                           artifactPrefix: String,
+                                           properties: Map<String, Any>)
 }
 
 @Service(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION)
 open class ResourceResolutionServiceImpl(private var applicationContext: ApplicationContext,
-                                         private var resolutionResultService: ResourceResolutionResultService) :
+                                         private var templateResolutionDBService: TemplateResolutionService,
+                                         private var blueprintTemplateService: BluePrintTemplateService,
+                                         private var resourceResolutionDBService: ResourceResolutionDBService) :
         ResourceResolutionService {
 
     private val log = LoggerFactory.getLogger(ResourceResolutionService::class.java)
@@ -73,50 +85,41 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
     override suspend fun resolveFromDatabase(bluePrintRuntimeService: BluePrintRuntimeService<*>,
                                              artifactTemplate: String,
                                              resolutionKey: String): String {
-        return resolutionResultService.read(bluePrintRuntimeService, artifactTemplate, resolutionKey)
+        return templateResolutionDBService.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
+                bluePrintRuntimeService,
+                artifactTemplate,
+                resolutionKey)
     }
 
     override suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
-                                          artifactNames: List<String>, properties: Map<String, Any>): MutableMap<String, String> {
+                                          artifactNames: List<String>,
+                                          properties: Map<String, Any>): MutableMap<String, String> {
+
+        val resourceAssignmentRuntimeService =
+                ResourceAssignmentUtils.transformToRARuntimeService(bluePrintRuntimeService, artifactNames.toString())
 
         val resolvedParams: MutableMap<String, String> = hashMapOf()
         artifactNames.forEach { artifactName ->
-            val resolvedContent = resolveResources(bluePrintRuntimeService, nodeTemplateName,
+            val resolvedContent = resolveResources(resourceAssignmentRuntimeService, nodeTemplateName,
                     artifactName, properties)
+
             resolvedParams[artifactName] = resolvedContent
         }
         return resolvedParams
     }
 
+
     override suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
-                                                        artifactPrefix: String, properties: Map<String, Any>): String {
+                                          artifactPrefix: String, properties: Map<String, Any>): String {
 
         // Velocity Artifact Definition Name
         val artifactTemplate = "$artifactPrefix-template"
         // Resource Assignment Artifact Definition Name
         val artifactMapping = "$artifactPrefix-mapping"
 
-        val result = resolveResources(bluePrintRuntimeService, nodeTemplateName,
-                artifactMapping, artifactTemplate)
-
-        if (properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
-                && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean) {
-            resolutionResultService.write(properties, result, bluePrintRuntimeService, artifactPrefix)
-            log.info("resolution saved into database successfully : ($properties)")
-        }
-
-        return result
-    }
-
-
-    override suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
-                                                           artifactMapping: String, artifactTemplate: String?): String {
-
         val resolvedContent: String
         log.info("Resolving resource for template artifact($artifactTemplate) with resource assignment artifact($artifactMapping)")
 
-        val identifierName = artifactTemplate ?: "no-template"
-
         val resourceAssignmentContent =
                 bluePrintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, artifactMapping)
 
@@ -125,31 +128,54 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
                         as? MutableList<ResourceAssignment>
                         ?: throw BluePrintProcessorException("couldn't get Dictionary Definitions")
 
+        if (isToStore(properties)) {
+            val existingResourceResolution = isNewResolution(bluePrintRuntimeService, properties, artifactPrefix)
+            if (existingResourceResolution.isNotEmpty()) {
+                updateResourceAssignmentWithExisting(bluePrintRuntimeService as ResourceAssignmentRuntimeService,
+                    existingResourceResolution, resourceAssignments)
+            }
+        }
+
         // Get the Resource Dictionary Name
         val resourceDefinitions: MutableMap<String, ResourceDefinition> = ResourceAssignmentUtils
                 .resourceDefinitions(bluePrintRuntimeService.bluePrintContext().rootPath)
 
         // Resolve resources
-        resolveResourceAssignments(bluePrintRuntimeService, resourceDefinitions, resourceAssignments, identifierName)
+        resolveResourceAssignments(bluePrintRuntimeService,
+                resourceDefinitions,
+                resourceAssignments,
+                artifactPrefix,
+                properties)
 
         val resolvedParamJsonContent =
                 ResourceAssignmentUtils.generateResourceDataForAssignments(resourceAssignments.toList())
 
-        // Check Template is there
-        if (artifactTemplate != null) {
-            val blueprintTemplateService = BluePrintTemplateService(bluePrintRuntimeService, nodeTemplateName, artifactTemplate)
-            val templateContent =
-                    bluePrintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, artifactTemplate)
-
-            resolvedContent = blueprintTemplateService.generateContent(templateContent, resolvedParamJsonContent)
+        resolvedContent = blueprintTemplateService.generateContent(bluePrintRuntimeService, nodeTemplateName,
+                artifactTemplate, resolvedParamJsonContent)
 
-        } else {
-            resolvedContent = resolvedParamJsonContent
+        if (isToStore(properties)) {
+            templateResolutionDBService.write(properties, resolvedContent, bluePrintRuntimeService, artifactPrefix)
+            log.info("Template resolution saved into database successfully : ($properties)")
         }
 
         return resolvedContent
     }
 
+    override suspend fun resolveResourceDefinition(blueprintRuntimeService: BluePrintRuntimeService<*>,
+                                                   resourceDefinitions: MutableMap<String, ResourceDefinition>,
+                                                   resolveDefinition: String, sources: List<String>)
+            : MutableMap<String, JsonNode> {
+
+        // Populate Dummy Resource Assignments
+        val resourceAssignments = createResourceAssignments(resourceDefinitions, resolveDefinition, sources)
+
+        resolveResourceAssignments(blueprintRuntimeService, resourceDefinitions, resourceAssignments,
+                UUID.randomUUID().toString(), hashMapOf())
+
+        // Get the data from Resource Assignments
+        return ResourceAssignmentUtils.generateResourceForAssignments(resourceAssignments)
+    }
+
     /**
      * Iterate the Batch, get the Resource Assignment, dictionary Name, Look for the Resource definition for the
      * name, then get the type of the Resource Definition, Get the instance for the Resource Type and process the
@@ -158,23 +184,30 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
     override suspend fun resolveResourceAssignments(blueprintRuntimeService: BluePrintRuntimeService<*>,
                                                     resourceDefinitions: MutableMap<String, ResourceDefinition>,
                                                     resourceAssignments: MutableList<ResourceAssignment>,
-                                                    identifierName: String) {
+                                                    artifactPrefix: String,
+                                                    properties: Map<String, Any>) {
 
         val bulkSequenced = BulkResourceSequencingUtils.process(resourceAssignments)
-        val resourceAssignmentRuntimeService =
-                ResourceAssignmentUtils.transformToRARuntimeService(blueprintRuntimeService, identifierName)
+
+        // Check the BlueprintRuntime Service Should be ResourceAssignmentRuntimeService
+        val resourceAssignmentRuntimeService = if (blueprintRuntimeService !is ResourceAssignmentRuntimeService) {
+            ResourceAssignmentUtils.transformToRARuntimeService(blueprintRuntimeService, artifactPrefix)
+        } else {
+            blueprintRuntimeService
+        }
+
 
         coroutineScope {
             bulkSequenced.forEach { batchResourceAssignments ->
                 // Execute Non Dependent Assignments in parallel ( ie asynchronously )
-                val deferred = batchResourceAssignments.filter { it.name != "*" && it.name != "start" }
+                val deferred = batchResourceAssignments
+                        .filter { it.name != "*" && it.name != "start" }
+                        .filter { it.status != BluePrintConstants.STATUS_SUCCESS }
                         .map { resourceAssignment ->
                             async {
                                 val dictionaryName = resourceAssignment.dictionaryName
                                 val dictionarySource = resourceAssignment.dictionarySource
-                                /**
-                                 * Get the Processor name
-                                 */
+
                                 val processorName = processorName(dictionaryName!!, dictionarySource!!, resourceDefinitions)
 
                                 val resourceAssignmentProcessor =
@@ -188,9 +221,19 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
                                     resourceAssignmentProcessor.resourceDictionaries = resourceDefinitions
                                     // Invoke Apply Method
                                     resourceAssignmentProcessor.applyNB(resourceAssignment)
+
+                                    if (isToStore(properties)) {
+                                        resourceResolutionDBService.write(properties,
+                                                blueprintRuntimeService,
+                                                artifactPrefix,
+                                                resourceAssignment)
+                                        log.info("Resource resolution saved into database successfully : ($resourceAssignment)")
+                                    }
+
                                     // Set errors from RA
                                     blueprintRuntimeService.setBluePrintError(resourceAssignmentRuntimeService.getBluePrintError())
                                 } catch (e: RuntimeException) {
+                                    log.error("Fail in processing ${resourceAssignment.name}", e)
                                     throw BluePrintProcessorException(e)
                                 }
                             }
@@ -202,7 +245,6 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
 
     }
 
-
     /**
      * If the Source instance is "input", then it is not mandatory to have source Resource Definition, So it can
      *  derive the default input processor.
@@ -233,4 +275,77 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
         return processorName
 
     }
+
+    // Check whether to store or not the resolution of resource and template
+    private fun isToStore(properties: Map<String, Any>): Boolean {
+        return properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
+                && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean
+    }
+
+    // Check whether resolution already exist in the database for the specified resolution-key or resourceId/resourceType
+    private suspend fun isNewResolution(bluePrintRuntimeService: BluePrintRuntimeService<*>,
+                                        properties: Map<String, Any>,
+                                        artifactPrefix: String): List<ResourceResolution> {
+        val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int
+        val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] as String
+        val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String
+        val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String
+
+        if (resolutionKey.isNotEmpty()) {
+            val existingResourceAssignments =
+                    resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
+                            bluePrintRuntimeService,
+                            resolutionKey,
+                            occurrence,
+                            artifactPrefix)
+            if (existingResourceAssignments.isNotEmpty()) {
+                log.info("Resolution with resolutionKey=($resolutionKey) already exist - will resolve all resources not already resolved.",
+                        resolutionKey)
+            }
+            return existingResourceAssignments
+        } else if (resourceId.isNotEmpty() && resourceType.isNotEmpty()) {
+            val existingResourceAssignments =
+                    resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
+                            bluePrintRuntimeService,
+                            resourceId,
+                            resourceType,
+
+                            occurrence,
+                            artifactPrefix)
+            if (existingResourceAssignments.isNotEmpty()) {
+                log.info("Resolution with resourceId=($resourceId) and resourceType=($resourceType) already exist - will resolve " +
+                        "all resources not already resolved.")
+            }
+            return existingResourceAssignments
+        }
+        return emptyList()
+    }
+
+    // Update the resource assignment list with the status of the resource that have already been resolved
+    private fun updateResourceAssignmentWithExisting(raRuntimeService : ResourceAssignmentRuntimeService,
+                                                     resourceResolutionList: List<ResourceResolution>,
+                                                     resourceAssignmentList: MutableList<ResourceAssignment>) {
+        resourceResolutionList.forEach { resourceResolution ->
+            if (resourceResolution.status == BluePrintConstants.STATUS_SUCCESS) {
+                resourceAssignmentList.forEach {
+                    if (compareOne(resourceResolution, it)) {
+                        log.info("Resource ({}) already resolve: value=({})", it.name, resourceResolution.value)
+                        val value = resourceResolution.value!!.asJsonPrimitive()
+                        it.property!!.value = value
+                        it.status = resourceResolution.status
+                        ResourceAssignmentUtils.setResourceDataValue(it, raRuntimeService, value)
+                    }
+                }
+            }
+        }
+    }
+
+    // Comparision between what we have in the database vs what we have to assign.
+    private fun compareOne(resourceResolution: ResourceResolution, resourceAssignment: ResourceAssignment): Boolean {
+        return (resourceResolution.name == resourceAssignment.name
+                && resourceResolution.dictionaryName == resourceAssignment.dictionaryName
+                && resourceResolution.dictionarySource == resourceAssignment.dictionarySource
+                && resourceResolution.dictionaryVersion == resourceAssignment.version)
+    }
+
 }