Enforce resolutionKey or resourceId/resourceType 00/90700/10
authorAlexis de Talhouët <adetalhouet89@gmail.com>
Sat, 29 Jun 2019 04:28:40 +0000 (00:28 -0400)
committerAlexis de Talhouët <adetalhouet89@gmail.com>
Thu, 4 Jul 2019 19:53:42 +0000 (15:53 -0400)
There are three existing ways to perform the resolution:

    either we don't store the results at all, whether for resource or template
    either we store using the resolution key. The combination of blueprintName,
       blueprintVersion, artifactName and resolutionKey has to be unique. If it
       is re-used, it is considered as a new attempt for that specific resolution
       request, and process will only try to resolve resources not marked at SUCCESS
       in the database.
    either we store using the resourceId and resourceType. As previous point, the
       combination of blueprintName, blueprintVersion, artifactName and resolutionKey
       has to be unique. If it is re-used, it is considered as a new attempt for that
       specific resolution request, and process will only try to resolve resources not
       marked at SUCCESS in the database.

TBD: add uni tests

Issue-ID: CCSDK-1423
Change-Id: I6b7198453cf0002edfa7a0c9ea3179555211b5dc
Signed-off-by: Alexis de Talhouët <adetalhouet89@gmail.com>
17 files changed:
components/model-catalog/blueprint-model/test-blueprint/baseconfiguration/Definitions/node_types.json
ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolution.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolution.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionRepository.kt
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionService.kt
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImpl.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResolutionException.kt [moved from ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceException.kt with 90% similarity]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceExceptionHandler.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceControllerTest.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateControllerTest.kt

index ceb18b2..0af2cd9 100644 (file)
                   "required": false,
                   "type": "string"
                 },
+                "occurrence": {
+                  "description": "Number of time to perform the resolution.",
+                  "required": false,
+                  "default": 1,
+                  "type": "integer"
+                },
                 "store-result": {
                   "description": "Whether or not to store the output.",
                   "required": false,
                   "type": "string"
                 },
                 "artifact-prefix-names": {
-                  "description": "Template , Resource Assignment Artifact Prefix names",
                   "required": true,
+                  "description": "Template , Resource Assignment Artifact Prefix names",
                   "type": "list",
                   "entry_schema": {
                     "type": "string"
index 166d7b1..31aa7c7 100644 (file)
@@ -48,11 +48,12 @@ class ComponentRemotePythonExecutorTest {
 
             val componentRemotePythonExecutor = ComponentRemotePythonExecutor(remoteScriptExecutionService)
 
-            val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-activate-request.json",
+            val executionServiceInput =
+                JacksonUtils.readValueFromClassPathFile("payload/requests/sample-activate-request.json",
                     ExecutionServiceInput::class.java)!!
 
             val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("123456-1000",
-                    "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_scripts")
+                "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_scripts")
 
             val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
             stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "execute-remote-python")
@@ -76,14 +77,17 @@ class ComponentRemotePythonExecutorTest {
     fun testComponentRemotePythonExecutorProcessNB() {
         runBlocking {
             val remoteScriptExecutionService =
-                    MockRemoteScriptExecutionService()
+                MockRemoteScriptExecutionService()
             val componentRemotePythonExecutor = ComponentRemotePythonExecutor(
-                    remoteScriptExecutionService)
+                remoteScriptExecutionService)
             val bluePrintRuntime = mockk<DefaultBluePrintRuntimeService>(
-                    "123456-1000")
-            val input  = getMockedOutput(bluePrintRuntime)
+                "123456-1000")
+
+            every { bluePrintRuntime.setNodeTemplateAttributeValue(any(), any(), any()) } answers {}
+
+            val input = getMockedOutput(bluePrintRuntime)
             componentRemotePythonExecutor.bluePrintRuntimeService =
-                    bluePrintRuntime
+                bluePrintRuntime
             componentRemotePythonExecutor.applyNB(input)
         }
     }
@@ -96,13 +100,13 @@ class ComponentRemotePythonExecutorTest {
         val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
 
         stepMetaData.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE,
-                "execute-remote-python")
+            BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE,
+            "execute-remote-python")
         stepMetaData.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_INTERFACE,
-                "ComponentRemotePythonExecutor")
+            BluePrintConstants.PROPERTY_CURRENT_INTERFACE,
+            "ComponentRemotePythonExecutor")
         stepMetaData.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
+            BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
 
         val mapper = ObjectMapper()
         val rootNode = mapper.createObjectNode()
@@ -111,13 +115,13 @@ class ComponentRemotePythonExecutorTest {
 
         val operationalInputs: MutableMap<String, JsonNode> = hashMapOf()
         operationalInputs.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE,
-                "execute-remote-python")
+            BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE,
+            "execute-remote-python")
         operationalInputs.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_INTERFACE,
-                "ComponentRemotePythonExecutor")
+            BluePrintConstants.PROPERTY_CURRENT_INTERFACE,
+            "ComponentRemotePythonExecutor")
         operationalInputs.putJsonElement(
-                BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
+            BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
         operationalInputs.putJsonElement("endpoint-selector", "aai")
         operationalInputs.putJsonElement("dynamic-properties", rootNode)
         operationalInputs.putJsonElement("command", "./run.sh")
@@ -125,8 +129,8 @@ class ComponentRemotePythonExecutorTest {
 
         every {
             svc.resolveNodeTemplateInterfaceOperationInputs(
-                    "execute-remote-python",
-                    "ComponentRemotePythonExecutor", "process")
+                "execute-remote-python",
+                "ComponentRemotePythonExecutor", "process")
         } returns operationalInputs
 
         val stepInputData = StepData().apply {
@@ -135,42 +139,42 @@ class ComponentRemotePythonExecutorTest {
         }
 
         val executionServiceInput = JacksonUtils
-                .readValueFromClassPathFile(
-                        "payload/requests/sample-remote-python-request.json",
-                        ExecutionServiceInput::class.java)!!
+            .readValueFromClassPathFile(
+                "payload/requests/sample-remote-python-request.json",
+                ExecutionServiceInput::class.java)!!
         executionServiceInput.stepData = stepInputData
 
         val operationOutputs = hashMapOf<String, JsonNode>()
         every {
             svc.resolveNodeTemplateInterfaceOperationOutputs(
-                    "execute-remote-python",
-                    "ComponentRemotePythonExecutor", "process")
+                "execute-remote-python",
+                "ComponentRemotePythonExecutor", "process")
         } returns operationOutputs
         val bluePrintRuntimeService = BluePrintMetadataUtils
-                .getBluePrintRuntime("123456-1000",
-                        "./../../../../components/model-" +
-                                "catalog/blueprint-model/test-blueprint/" +
-                                "remote_scripts")
+            .getBluePrintRuntime("123456-1000",
+                "./../../../../components/model-" +
+                        "catalog/blueprint-model/test-blueprint/" +
+                        "remote_scripts")
         every {
             svc.resolveNodeTemplateArtifactDefinition(
-                    "execute-remote-python", "component-script")
-        } returns bluePrintRuntimeService.resolveNodeTemplateArtifactDefinition(
                 "execute-remote-python", "component-script")
+        } returns bluePrintRuntimeService.resolveNodeTemplateArtifactDefinition(
+            "execute-remote-python", "component-script")
         every {
             svc.setNodeTemplateAttributeValue(
-                    "execute-remote-python", "prepare-environment-logs",
-                    "prepared successfully".asJsonPrimitive())
+                "execute-remote-python", "prepare-environment-logs",
+                "prepared successfully".asJsonPrimitive())
         } returns Unit
         every {
             svc.setNodeTemplateAttributeValue(
-                    "execute-remote-python",
-                    "execute-command-logs", "N/A".asJsonPrimitive())
+                "execute-remote-python",
+                "execute-command-logs", "N/A".asJsonPrimitive())
         } returns Unit
         every {
             svc.setNodeTemplateAttributeValue(
-                    "execute-remote-python",
-                    "execute-command-logs",
-                    "processed successfully".asJsonPrimitive())
+                "execute-remote-python",
+                "execute-command-logs",
+                "processed successfully".asJsonPrimitive())
         } returns Unit
 
         every {
index 2039d2e..fd14cc8 100644 (file)
@@ -21,11 +21,13 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode
 import org.onap.ccsdk.cds.controllerblueprints.core.asObjectNode
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 import org.springframework.beans.factory.config.ConfigurableBeanFactory
 import org.springframework.context.annotation.Scope
+import org.springframework.http.ResponseEntity
 import org.springframework.stereotype.Component
 
 @Component("component-resource-resolution")
@@ -35,41 +37,51 @@ open class ResourceResolutionComponent(private val resourceResolutionService: Re
 
     override suspend fun processNB(executionRequest: ExecutionServiceInput) {
 
-        val occurrence = getOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE)
-        val resolutionKey = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY)
-        val storeResult = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
-        val resourceId = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID)
-        val resourceType = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE)
-
+        val occurrence = getOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE).asInt()
+        val resolutionKey = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY)?.asText() ?: ""
+        val storeResult = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)?.asBoolean() ?: false
+        val resourceId = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID)?.asText() ?: ""
+        val resourceType = getOptionalOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE)?.asText() ?: ""
 
         val properties: MutableMap<String, Any> = mutableMapOf()
-        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = storeResult?.asBoolean() ?: false
-        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY] = resolutionKey?.asText() ?: ""
-        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId?.asText() ?: ""
-        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType?.asText() ?: ""
+        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = storeResult
+        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY] = resolutionKey
+        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId
+        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType
+        properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence
+
+        val jsonResponse = JsonNodeFactory.instance.objectNode()
+
+        // validate inputs if we need to store the resource and template resolution.
+        if (storeResult) {
+            if (resolutionKey.isNotEmpty() && (resourceId.isNotEmpty() || resourceType.isNotEmpty())) {
+                throw BluePrintProcessorException("Can't proceed with the resolution: either provide resolution-key OR combination of resource-id and resource-type.")
+            } else if ((resourceType.isNotEmpty() && resourceId.isEmpty()) || (resourceType.isEmpty() && resourceId.isNotEmpty())) {
+                throw BluePrintProcessorException("Can't proceed with the resolution: both resource-id and resource-type should be provided, one of them is missing.")
+            } else if (resourceType.isEmpty() && resourceId.isEmpty() && resolutionKey.isEmpty()) {
+                throw BluePrintProcessorException("Can't proceed with the resolution: can't persist resolution without a correlation key. " +
+                        "Either provide a resolution-key OR combination of resource-id and resource-type OR set `storeResult` to false.")
+            }
+        }
 
         val artifactPrefixNamesNode = getOperationInput(ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES)
         val artifactPrefixNames = JacksonUtils.getListFromJsonNode(artifactPrefixNamesNode, String::class.java)
 
-        val resourceAssignmentRuntimeService =
-            ResourceAssignmentUtils.transformToRARuntimeService(bluePrintRuntimeService, artifactPrefixNames.toString())
-
-        val jsonResponse = JsonNodeFactory.instance.objectNode()
-        for (j in 1..occurrence.asInt()) {
-            val key = resolutionKey?.asText() + "-" + j
-            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY] = key
+        for (j in 1..occurrence) {
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = j
 
-            val response = resourceResolutionService.resolveResources(resourceAssignmentRuntimeService,
+            val response = resourceResolutionService.resolveResources(bluePrintRuntimeService,
                 nodeTemplateName,
                 artifactPrefixNames,
                 properties)
 
             // provide indexed result in output if we have multiple resolution
-            if (occurrence.asInt() != 1) {
-                jsonResponse.set(key, response.asJsonNode())
+            if (occurrence != 1) {
+                jsonResponse.set(Integer.toString(j), response.asJsonNode())
             } else {
                 jsonResponse.setAll(response.asObjectNode())
             }
+
         }
 
         // Set Output Attributes
index 1270298..e08ac52 100644 (file)
@@ -20,13 +20,14 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.coroutineScope
+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.BluePrintConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
 import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintTemplateService
@@ -60,7 +61,7 @@ interface ResourceResolutionService {
 
 @Service(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION)
 open class ResourceResolutionServiceImpl(private var applicationContext: ApplicationContext,
-                                         private var resolutionResultService: TemplateResolutionService,
+                                         private var templateResolutionDBService: TemplateResolutionService,
                                          private var blueprintTemplateService: BluePrintTemplateService,
                                          private var resourceResolutionDBService: ResourceResolutionDBService) :
     ResourceResolutionService {
@@ -76,22 +77,30 @@ 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> {
 
+
+        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 {
 
@@ -111,6 +120,13 @@ 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(existingResourceResolution, resourceAssignments)
+            }
+        }
+
         // Get the Resource Dictionary Name
         val resourceDefinitions: MutableMap<String, ResourceDefinition> = ResourceAssignmentUtils
             .resourceDefinitions(bluePrintRuntimeService.bluePrintContext().rootPath)
@@ -128,10 +144,9 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
         resolvedContent = blueprintTemplateService.generateContent(bluePrintRuntimeService, nodeTemplateName,
             artifactTemplate, resolvedParamJsonContent)
 
-        if (properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
-            && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean) {
-            resolutionResultService.write(properties, resolvedContent, bluePrintRuntimeService, artifactPrefix)
-            log.info("template resolution saved into database successfully : ($properties)")
+        if (isToStore(properties)) {
+            templateResolutionDBService.write(properties, resolvedContent, bluePrintRuntimeService, artifactPrefix)
+            log.info("Template resolution saved into database successfully : ($properties)")
         }
 
         return resolvedContent
@@ -154,7 +169,9 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
         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
@@ -197,12 +214,6 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica
 
     }
 
-    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
-    }
-
-
     /**
      * 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 +244,74 @@ 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_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(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)
+                        it.property!!.value = resourceResolution.value!!.asJsonPrimitive()
+                        it.status = resourceResolution.status
+                    }
+                }
+            }
+        }
+    }
+
+    // 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)
+    }
+
 }
index fa922cd..baabfd9 100644 (file)
@@ -24,6 +24,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener
 import java.io.Serializable
 import java.util.*
 import javax.persistence.Column
+import javax.persistence.ElementCollection
 import javax.persistence.Entity
 import javax.persistence.EntityListeners
 import javax.persistence.Id
@@ -38,23 +39,6 @@ import javax.persistence.TemporalType
 @Proxy(lazy = false)
 class ResourceResolution : Serializable {
 
-    @Id
-    @Column(name = "resource_resolution_id")
-    var id: String? = null
-
-    @get:ApiModelProperty(value = "Resolution Key uniquely identifying the resolution of a given artifact within a CBA.",
-        required = true)
-    @Column(name = "resolution_key", nullable = false)
-    var resolutionKey: String? = null
-
-    @get:ApiModelProperty(value = "Resolution type.", required = true, example = "ServiceInstance, VfModule, VNF")
-    @Column(name = "resource_type", nullable = false)
-    var resourceType: String? = null
-
-    @get:ApiModelProperty(value = "ID associated with the resolution type in the inventory system.", required = true)
-    @Column(name = "resource_id", nullable = false)
-    var resourceId: String? = null
-
     @get:ApiModelProperty(value = "Name of the CBA.", required = true)
     @Column(name = "blueprint_name", nullable = false)
     var blueprintName: String? = null
@@ -67,19 +51,43 @@ class ResourceResolution : Serializable {
     @Column(name = "artifact_name", nullable = false)
     var artifactName: String? = null
 
+    @get:ApiModelProperty(value = "Name of the resource.", required = true)
+    @Column(name = "name", nullable = false)
+    var name: String? = null
+
+    @get:ApiModelProperty(value = "Value of the resolution.", required = true)
+    @Lob
+    @Column(name = "value", nullable = false)
+    var value: String? = null
+
     @get:ApiModelProperty(value = "Whether success of failure.", required = true)
     @Column(name = "status", nullable = false)
     var status: String? = null
 
-    @get:ApiModelProperty(value = "Name of the resource.", required = true)
-    @Column(name = "name", nullable = false)
-    var name: String? = null
+    @get:ApiModelProperty(value = "Resolution Key uniquely identifying the resolution of a given artifact within a CBA.",
+        required = true)
+    @Column(name = "resolution_key", nullable = false)
+    var resolutionKey: String? = null
+
+    @get:ApiModelProperty(value = "Resolution type.", required = true, example = "ServiceInstance, VfModule, VNF")
+    @Column(name = "resource_type", nullable = false)
+    var resourceType: String? = null
+
+    @get:ApiModelProperty(value = "ID associated with the resolution type in the inventory system.", required = true)
+    @Column(name = "resource_id", nullable = false)
+    var resourceId: String? = null
+
+    @get:ApiModelProperty(value = "If resolution occurred multiple time, this field provides the index.",
+        required = true)
+    @Column(name = "occurrence", nullable = false)
+    var occurrence: Int = 0
 
     @get:ApiModelProperty(value = "Name of the data dictionary used for the resolution.", required = true)
     @Column(name = "dictionary_name", nullable = false)
     var dictionaryName: String? = null
 
-    @get:ApiModelProperty(value = "Source associated with the data dictionary used for the resolution.", required = true)
+    @get:ApiModelProperty(value = "Source associated with the data dictionary used for the resolution.",
+        required = true)
     @Column(name = "dictionary_status", nullable = false)
     var dictionarySource: String? = null
 
@@ -87,10 +95,9 @@ class ResourceResolution : Serializable {
     @Column(name = "dictionary_version", nullable = false)
     var dictionaryVersion: Int = 0
 
-    @get:ApiModelProperty(value = "Value of the resolution.", required = true)
-    @Lob
-    @Column(name = "value", nullable = false)
-    var value: String? = null
+    @Id
+    @Column(name = "resource_resolution_id")
+    var id: String? = null
 
     @get:ApiModelProperty(value = "Creation date of the resolution.", required = true)
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
index c6ffde7..e9679ee 100644 (file)
@@ -25,6 +25,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
 import org.slf4j.LoggerFactory
 import org.springframework.dao.DataIntegrityViolationException
+import org.springframework.dao.EmptyResultDataAccessException
 import org.springframework.stereotype.Service
 import java.util.*
 
@@ -33,6 +34,49 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso
 
     private val log = LoggerFactory.getLogger(ResourceResolutionDBService::class.toString())
 
+    suspend fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
+        bluePrintRuntimeService: BluePrintRuntimeService<*>, key: String,
+        occurrence: Int, artifactPrefix: String): List<ResourceResolution> {
+        return try {
+            val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+
+            val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
+            val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
+
+            resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
+                blueprintName,
+                blueprintVersion,
+                artifactPrefix,
+                key,
+                occurrence)
+        } catch (e: EmptyResultDataAccessException) {
+            emptyList()
+        }
+    }
+
+    suspend fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
+        bluePrintRuntimeService: BluePrintRuntimeService<*>, resourceId: String,
+        resourceType: String, occurrence: Int,
+        artifactPrefix: String): List<ResourceResolution> {
+        return try {
+
+            val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+
+            val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
+            val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
+
+            resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
+                blueprintName,
+                blueprintVersion,
+                artifactPrefix,
+                resourceId,
+                resourceType,
+                occurrence)
+        } catch (e: EmptyResultDataAccessException) {
+            emptyList()
+        }
+    }
+
     suspend fun readValue(blueprintName: String,
                           blueprintVersion: String,
                           artifactPrefix: String,
@@ -81,12 +125,11 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso
 
         val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
         val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
-        val resolutionKey =
-            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY].toString()
-        val resourceType =
-            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE].toString()
-        val resourceId =
-            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID].toString()
+
+        val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_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
+        val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int
 
         write(blueprintName,
             blueprintVersion,
@@ -94,7 +137,8 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso
             resourceId,
             resourceType,
             artifactPrefix,
-            resourceAssignment)
+            resourceAssignment,
+            occurrence)
     }
 
     suspend fun write(blueprintName: String,
@@ -103,20 +147,22 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso
                       resourceId: String,
                       resourceType: String,
                       artifactPrefix: String,
-                      resourceAssignment: ResourceAssignment): ResourceResolution = withContext(Dispatchers.IO) {
+                      resourceAssignment: ResourceAssignment,
+                      occurrence: Int = 0): ResourceResolution = withContext(Dispatchers.IO) {
 
         val resourceResolution = ResourceResolution()
         resourceResolution.id = UUID.randomUUID().toString()
         resourceResolution.artifactName = artifactPrefix
+        resourceResolution.occurrence = occurrence
         resourceResolution.blueprintVersion = blueprintVersion
         resourceResolution.blueprintName = blueprintName
         resourceResolution.resolutionKey = resolutionKey
         resourceResolution.resourceType = resourceType
         resourceResolution.resourceId = resourceId
-        if (BluePrintConstants.STATUS_FAILURE == resourceAssignment.status) {
-            resourceResolution.value = ""
-        } else {
+        if (BluePrintConstants.STATUS_SUCCESS == resourceAssignment.status) {
             resourceResolution.value = JacksonUtils.getValue(resourceAssignment.property?.value!!).toString()
+        } else {
+            resourceResolution.value = ""
         }
         resourceResolution.name = resourceAssignment.name
         resourceResolution.dictionaryName = resourceAssignment.dictionaryName
index d2cbf84..429041e 100644 (file)
@@ -34,4 +34,19 @@ interface ResourceResolutionRepository : JpaRepository<ResourceResolution, Strin
                                                                            blueprintVersion: String,
                                                                            resourceId: String,
                                                                            resourceType: String): List<ResourceResolution>
+
+    fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        resolutionKey: String,
+        occurrence: Int): List<ResourceResolution>
+
+    fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        resourceId: String,
+        resourceType: String,
+        occurrence: Int): List<ResourceResolution>
 }
\ No newline at end of file
index ae24a9a..70aadb4 100755 (executable)
@@ -38,15 +38,6 @@ import javax.persistence.TemporalType
 @Proxy(lazy = false)
 class TemplateResolution : Serializable {
 
-    @Id
-    @Column(name = "template_resolution_id")
-    var id: String? = null
-
-    @get:ApiModelProperty(value = "Resolution Key uniquely identifying the resolution of a given artifact within a CBA.",
-        required = true)
-    @Column(name = "resolution_key", nullable = false)
-    var resolutionKey: String? = null
-
     @get:ApiModelProperty(value = "Name of the CBA.", required = true)
     @Column(name = "blueprint_name", nullable = false)
     var blueprintName: String? = null
@@ -64,6 +55,28 @@ class TemplateResolution : Serializable {
     @Column(name = "result", nullable = false)
     var result: String? = null
 
+    @get:ApiModelProperty(value = "Resolution Key uniquely identifying the resolution of a given artifact within a CBA.",
+        required = true)
+    @Column(name = "resolution_key", nullable = false)
+    var resolutionKey: String? = null
+
+    @get:ApiModelProperty(value = "Resolution type.", required = true, example = "ServiceInstance, VfModule, VNF")
+    @Column(name = "resource_type", nullable = false)
+    var resourceType: String? = null
+
+    @get:ApiModelProperty(value = "ID associated with the resolution type in the inventory system.", required = true)
+    @Column(name = "resource_id", nullable = false)
+    var resourceId: String? = null
+
+    @get:ApiModelProperty(value = "If resolution occurred multiple time, this field provides the index.",
+        required = true)
+    @Column(name = "occurrence", nullable = false)
+    var occurrence: Int = 0
+
+    @Id
+    @Column(name = "template_resolution_id")
+    var id: String? = null
+
     @get:ApiModelProperty(value = "Creation date of the resolution.", required = true)
     @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
     @LastModifiedDate
index 136b5e9..440663f 100644 (file)
 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db
 
 import org.springframework.data.jpa.repository.JpaRepository
+import javax.transaction.Transactional
 
 interface TemplateResolutionRepository : JpaRepository<TemplateResolution, String> {
 
-    fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(key: String, blueprintName: String?,
-                                                                              blueprintVersion: String?,
-                                                                              artifactName: String): TemplateResolution
+    fun findByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+        resourceId: String,
+        resourceType: String,
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        occurrence: Int): TemplateResolution ?
+
+    fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+        key: String,
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        occurrence: Int): TemplateResolution ?
+
+    @Transactional
+    fun deleteByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+        resourceId: String,
+        resourceType: String,
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        occurrence: Int)
+
+    @Transactional
+    fun deleteByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+        key: String,
+        blueprintName: String?,
+        blueprintVersion: String?,
+        artifactName: String,
+        occurrence: Int)
 }
index c4e3640..4466296 100644 (file)
@@ -21,36 +21,65 @@ import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.Reso
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
+import org.slf4j.LoggerFactory
 import org.springframework.dao.DataIntegrityViolationException
+import org.springframework.dao.EmptyResultDataAccessException
 import org.springframework.stereotype.Service
 import java.util.*
 
 @Service
 class TemplateResolutionService(private val templateResolutionRepository: TemplateResolutionRepository) {
 
-    suspend fun read(bluePrintRuntimeService: BluePrintRuntimeService<*>,
-                     artifactPrefix: String,
-                     resolutionKey: String): String = withContext(Dispatchers.IO) {
+    private val log = LoggerFactory.getLogger(TemplateResolutionService::class.toString())
 
-        val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+    suspend fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
+        bluePrintRuntimeService: BluePrintRuntimeService<*>,
+        artifactPrefix: String,
+        resolutionKey: String): String =
+        withContext(Dispatchers.IO) {
 
-        val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
-        val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
+            val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
 
-        read(blueprintName, blueprintVersion, artifactPrefix, resolutionKey)
-    }
+            val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
+            val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
 
-    suspend fun read(blueprintName: String,
-                     blueprintVersion: String,
-                     artifactPrefix: String,
-                     resolutionKey: String): String = withContext(Dispatchers.IO) {
+            findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(blueprintName,
+                blueprintVersion,
+                artifactPrefix,
+                resolutionKey)
+        }
 
-        templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
-            resolutionKey,
-            blueprintName,
-            blueprintVersion,
-            artifactPrefix).result!!
-    }
+    suspend fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(blueprintName: String,
+                                                                                      blueprintVersion: String,
+                                                                                      artifactPrefix: String,
+                                                                                      resolutionKey: String,
+                                                                                      occurrence: Int = 0): String =
+        withContext(Dispatchers.IO) {
+
+            templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                resolutionKey,
+                blueprintName,
+                blueprintVersion,
+                artifactPrefix,
+                occurrence)?.result ?: throw EmptyResultDataAccessException(1)
+        }
+
+    suspend fun findByResoureIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactName(blueprintName: String,
+                                                                                                 blueprintVersion: String,
+                                                                                                 artifactPrefix: String,
+                                                                                                 resourceId: String,
+                                                                                                 resourceType: String,
+                                                                                                 occurrence: Int = 0): String =
+        withContext(Dispatchers.IO) {
+
+            templateResolutionRepository.findByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                resourceId,
+                resourceType,
+                blueprintName,
+                blueprintVersion,
+                artifactPrefix,
+                occurrence)?.result!!
+        }
 
     suspend fun write(properties: Map<String, Any>,
                       result: String, bluePrintRuntimeService: BluePrintRuntimeService<*>,
@@ -60,30 +89,68 @@ class TemplateResolutionService(private val templateResolutionRepository: Templa
 
         val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
         val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
-        val resolutionKey =
-            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY].toString()
+        val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_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
+        val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int
 
-        write(blueprintName, blueprintVersion, resolutionKey, artifactPrefix, result)
+        write(blueprintName,
+            blueprintVersion,
+            artifactPrefix,
+            result,
+            occurrence,
+            resolutionKey,
+            resourceId,
+            resourceType)
     }
 
-    suspend fun write(blueprintName: String,
-                      blueprintVersion: String,
-                      resolutionKey: String,
-                      artifactPrefix: String,
-                      template: String): TemplateResolution = withContext(Dispatchers.IO) {
-
-        val resourceResolutionResult = TemplateResolution()
-        resourceResolutionResult.id = UUID.randomUUID().toString()
-        resourceResolutionResult.artifactName = artifactPrefix
-        resourceResolutionResult.blueprintVersion = blueprintVersion
-        resourceResolutionResult.blueprintName = blueprintName
-        resourceResolutionResult.resolutionKey = resolutionKey
-        resourceResolutionResult.result = template
-
-        try {
-            templateResolutionRepository.saveAndFlush(resourceResolutionResult)
-        } catch (ex: DataIntegrityViolationException) {
-            throw BluePrintException("Failed to store resource api result.", ex)
+    suspend fun write(blueprintName: String, blueprintVersion: String, artifactPrefix: String,
+                      template: String, occurrence: Int = 0, resolutionKey: String = "", resourceId: String = "",
+                      resourceType: String = ""): TemplateResolution =
+        withContext(Dispatchers.IO) {
+
+            val resourceResolutionResult = TemplateResolution()
+            resourceResolutionResult.id = UUID.randomUUID().toString()
+            resourceResolutionResult.artifactName = artifactPrefix
+            resourceResolutionResult.blueprintVersion = blueprintVersion
+            resourceResolutionResult.blueprintName = blueprintName
+            resourceResolutionResult.resolutionKey = resolutionKey
+            resourceResolutionResult.resourceId = resourceId
+            resourceResolutionResult.resourceType = resourceType
+            resourceResolutionResult.result = template
+            resourceResolutionResult.occurrence = occurrence
+
+            // Overwrite template resolution-key of resourceId/resourceType already existant
+            if (resolutionKey.isNotEmpty()) {
+                templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                    resolutionKey, blueprintName, blueprintVersion, artifactPrefix, occurrence)?.let {
+                    log.info("Overwriting template resolution for blueprintName=($blueprintVersion), blueprintVersion=($blueprintName), " +
+                            "artifactName=($artifactPrefix) and resolutionKey=($resolutionKey)")
+                    templateResolutionRepository.deleteByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                        resolutionKey,
+                        blueprintName,
+                        blueprintVersion,
+                        artifactPrefix,
+                        occurrence)
+                }
+            } else if (resourceId.isNotEmpty() && resourceType.isNotEmpty()) {
+                templateResolutionRepository.findByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                    resourceId, resourceType, blueprintName, blueprintVersion, artifactPrefix, occurrence)?.let {
+                    log.info("Overwriting template resolution for blueprintName=($blueprintVersion), blueprintVersion=($blueprintName), " +
+                            "artifactName=($artifactPrefix), resourceId=($resourceId) and resourceType=($resourceType)")
+                    templateResolutionRepository.deleteByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence(
+                        resourceId,
+                        resourceType,
+                        blueprintName,
+                        blueprintVersion,
+                        artifactPrefix,
+                        occurrence)
+                }
+            }
+            try {
+                templateResolutionRepository.saveAndFlush(resourceResolutionResult)
+            } catch (ex: DataIntegrityViolationException) {
+                throw BluePrintException("Failed to store resource api result.", ex)
+            }
         }
-    }
 }
\ No newline at end of file
index 452dd0f..3e1f071 100755 (executable)
@@ -45,11 +45,6 @@ class BlueprintProcessorCatalogServiceImpl(bluePrintRuntimeValidatorService: Blu
 
     private val log = LoggerFactory.getLogger(BlueprintProcessorCatalogServiceImpl::class.toString())
 
-    init {
-
-        log.info("BlueprintProcessorCatalogServiceImpl initialized")
-    }
-
     override suspend fun delete(name: String, version: String) {
         // Cleaning Deployed Blueprint
         deleteNBDir(bluePrintPathConfiguration.blueprintDeployPath, name, version)
index 4c8fc19..3a708a9 100644 (file)
@@ -70,7 +70,7 @@ open class ResourceController(private var resourceResolutionDBService: ResourceR
             : ResponseEntity<List<ResourceResolution>> = runBlocking {
 
         if ((resolutionKey.isNotEmpty() || artifactName.isNotEmpty()) && (resourceId.isNotEmpty() || resourceType.isNotEmpty())) {
-            throw ResourceException("Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.")
+            throw ResolutionException("Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.")
         } else if (resolutionKey.isNotEmpty() && artifactName.isNotEmpty()) {
             ResponseEntity.ok()
                 .body(resourceResolutionDBService.readWithResolutionKey(bpName, bpVersion, artifactName, resolutionKey))
@@ -81,7 +81,7 @@ open class ResourceController(private var resourceResolutionDBService: ResourceR
                     resourceId,
                     resourceType))
         } else {
-            throw ResourceException("Missing param. Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.")
+            throw ResolutionException("Missing param. Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.")
         }
     }
 
@@ -97,8 +97,7 @@ open class ResourceController(private var resourceResolutionDBService: ResourceR
                                 @RequestParam(value = "bpName", required = true) bpName: String,
                                 @ApiParam(value = "Version of the CBA.", required = true)
                                 @RequestParam(value = "bpVersion", required = true) bpVersion: String,
-                                @ApiParam(value = "Artifact name for which to retrieve a resolved resource.",
-                                    required = true)
+                                @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true)
                                 @RequestParam(value = "artifactName", required = true) artifactName: String,
                                 @ApiParam(value = "Resolution Key associated with the resolution.", required = true)
                                 @RequestParam(value = "resolutionKey", required = true) resolutionKey: String,
index f01dccf..42ff801 100644 (file)
@@ -93,7 +93,7 @@ open class ResourceExceptionHandler {
     }
 
     @ExceptionHandler
-    fun ResolutionResultsServiceExceptionHandler(e: ResourceException): ResponseEntity<ErrorMessage> {
+    fun ResolutionResultsServiceExceptionHandler(e: ResolutionException): ResponseEntity<ErrorMessage> {
         log.error(e.message, e)
         return ResponseEntity(ErrorMessage(e.message, e.code, debugMsg), HttpStatus.resolve(e.code))
     }
index 83e8130..de5843a 100644 (file)
@@ -24,6 +24,7 @@ import kotlinx.coroutines.runBlocking
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.TemplateResolution
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.TemplateResolutionService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+import org.springframework.dao.EmptyResultDataAccessException
 import org.springframework.http.MediaType
 import org.springframework.http.ResponseEntity
 import org.springframework.security.access.prepost.PreAuthorize
@@ -66,15 +67,40 @@ open class TemplateController(private val templateResolutionService: TemplateRes
         @RequestParam(value = "bpVersion") bpVersion: String,
         @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true)
         @RequestParam(value = "artifactName") artifactName: String,
-        @ApiParam(value = "Resolution Key associated with the resolution.", required = true)
+        @ApiParam(value = "Resolution Key associated with the resolution.", required = false)
         @RequestParam(value = "resolutionKey") resolutionKey: String,
+        @ApiParam(value = "Resource Type associated with the resolution.", required = false)
+        @RequestParam(value = "resourceType", required = false, defaultValue = "") resourceType: String,
+        @ApiParam(value = "Resource Id associated with the resolution.", required = false)
+        @RequestParam(value = "resourceId", required = false, defaultValue = "") resourceId: String,
         @ApiParam(value = "Expected format of the template being retrieved.",
             defaultValue = MediaType.TEXT_PLAIN_VALUE,
             required = true)
         @RequestParam(value = "format", required = false, defaultValue = MediaType.TEXT_PLAIN_VALUE) format: String)
             : ResponseEntity<String> = runBlocking {
 
-        val result = templateResolutionService.read(bpName, bpVersion, artifactName, resolutionKey)
+        var result = ""
+
+        if ((resolutionKey.isNotEmpty() || artifactName.isNotEmpty()) && (resourceId.isNotEmpty() || resourceType.isNotEmpty())) {
+            throw ResolutionException("Either retrieve resolved template using artifact name and resolution-key OR using resource-id and resource-type.")
+        } else if (resolutionKey.isNotEmpty() && artifactName.isNotEmpty()) {
+            result = templateResolutionService.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
+                bpName,
+                bpVersion,
+                artifactName,
+                resolutionKey)
+        } else if (resourceType.isNotEmpty() && resourceId.isNotEmpty()) {
+            result =
+                templateResolutionService.findByResoureIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactName(
+                    bpName,
+                    bpVersion,
+                    artifactName,
+                    resourceId,
+                    resourceType)
+        } else {
+            throw ResolutionException("Missing param. Either retrieve resolved template using artifact name and resolution-key OR using resource-id and resource-type.")
+        }
+
 
         var expectedContentType = format
         if (expectedContentType.indexOf('/') < 0) {
@@ -87,26 +113,56 @@ open class TemplateController(private val templateResolutionService: TemplateRes
 
 
     @PostMapping("/{bpName}/{bpVersion}/{artifactName}/{resolutionKey}", produces = [MediaType.APPLICATION_JSON_VALUE])
-    @ApiOperation(value = "Store a resolved template",
+    @ApiOperation(value = "Store a resolved template w/ resolution-key",
         notes = "Store a template for a given CBA's action, identified by its blueprint name, blueprint version, " +
                 "artifact name and resolution key.",
         response = TemplateResolution::class,
         produces = MediaType.APPLICATION_JSON_VALUE)
     @ResponseBody
     @PreAuthorize("hasRole('USER')")
-    fun post(@ApiParam(value = "Name of the CBA.", required = true)
-             @PathVariable(value = "bpName") bpName: String,
-             @ApiParam(value = "Version of the CBA.", required = true)
-             @PathVariable(value = "bpVersion") bpVersion: String,
-             @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true)
-             @PathVariable(value = "artifactName") artifactName: String,
-             @ApiParam(value = "Resolution Key associated with the resolution.", required = true)
-             @PathVariable(value = "resolutionKey") resolutionKey: String,
-             @ApiParam(value = "Template to store.", required = true)
-             @RequestBody result: String): ResponseEntity<TemplateResolution> = runBlocking {
+    fun postWithResolutionKey(
+        @ApiParam(value = "Name of the CBA.", required = true)
+        @PathVariable(value = "bpName") bpName: String,
+        @ApiParam(value = "Version of the CBA.", required = true)
+        @PathVariable(value = "bpVersion") bpVersion: String,
+        @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true)
+        @PathVariable(value = "artifactName") artifactName: String,
+        @ApiParam(value = "Resolution Key associated with the resolution.", required = true)
+        @PathVariable(value = "resolutionKey") resolutionKey: String,
+        @ApiParam(value = "Template to store.", required = true)
+        @RequestBody result: String): ResponseEntity<TemplateResolution> = runBlocking {
+
+        val resultStored =
+            templateResolutionService.write(bpName, bpVersion, artifactName, result, resolutionKey = resolutionKey)
+
+        ResponseEntity.ok().body(resultStored)
+    }
+
+    @PostMapping("/{bpName}/{bpVersion}/{artifactName}/{resourceType}/{resourceId}",
+        produces = [MediaType.APPLICATION_JSON_VALUE])
+    @ApiOperation(value = "Store a resolved template w/ resourceId and resourceType",
+        notes = "Store a template for a given CBA's action, identified by its blueprint name, blueprint version, " +
+                "artifact name, resourceId and resourceType.",
+        response = TemplateResolution::class,
+        produces = MediaType.APPLICATION_JSON_VALUE)
+    @ResponseBody
+    @PreAuthorize("hasRole('USER')")
+    fun postWithResourceIdAndResourceType(
+        @ApiParam(value = "Name of the CBA.", required = true)
+        @PathVariable(value = "bpName") bpName: String,
+        @ApiParam(value = "Version of the CBA.", required = true)
+        @PathVariable(value = "bpVersion") bpVersion: String,
+        @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true)
+        @PathVariable(value = "artifactName") artifactName: String,
+        @ApiParam(value = "Resource Type associated with the resolution.", required = false)
+        @PathVariable(value = "resourceType", required = true) resourceType: String,
+        @ApiParam(value = "Resource Id associated with the resolution.", required = false)
+        @PathVariable(value = "resourceId", required = true) resourceId: String,
+        @ApiParam(value = "Template to store.", required = true)
+        @RequestBody result: String): ResponseEntity<TemplateResolution> = runBlocking {
 
         val resultStored =
-            templateResolutionService.write(bpName, bpVersion, resolutionKey, artifactName, result)
+            templateResolutionService.write(bpName, bpVersion, artifactName, result, resourceId = resourceId, resourceType = resourceType)
 
         ResponseEntity.ok().body(resultStored)
     }
index daab7b3..9ed97df 100644 (file)
 
 package org.onap.ccsdk.cds.blueprintsprocessor.resolutionresults.api
 
-import com.fasterxml.jackson.core.type.TypeReference
-import com.fasterxml.jackson.module.kotlin.readValue
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintCoreConfiguration
 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.controllerblueprints.core.asJsonPrimitive
 import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
-import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
-import org.python.jline.console.internal.ConsoleRunner.property
 import org.slf4j.LoggerFactory
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.autoconfigure.security.SecurityProperties
 import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
 import org.springframework.context.annotation.ComponentScan
-import org.springframework.http.HttpHeaders
-import org.springframework.http.HttpStatus
-import org.springframework.http.MediaType
 import org.springframework.test.context.ContextConfiguration
 import org.springframework.test.context.TestPropertySource
 import org.springframework.test.context.junit4.SpringRunner
 import org.springframework.test.web.reactive.server.WebTestClient
-import org.springframework.web.reactive.function.BodyInserters
-import java.util.function.Consumer
-import kotlin.test.BeforeTest
-import org.h2.value.DataType.readValue
-import java.util.*
-import org.h2.value.DataType.readValue
-import org.python.bouncycastle.asn1.x500.style.RFC4519Style.l
-import org.h2.value.DataType.readValue
 import org.onap.ccsdk.cds.blueprintsprocessor.resource.api.ErrorMessage
 import org.onap.ccsdk.cds.blueprintsprocessor.resource.api.ResourceController
-import java.lang.reflect.Array
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 
 
 @RunWith(SpringRunner::class)
@@ -233,7 +217,7 @@ class ResourceControllerTest {
         resourceAssignment.dictionaryName = "dd$prefix"
         resourceAssignment.dictionarySource = "source$prefix"
         resourceAssignment.version = 2
-        resourceAssignment.status = "SUCCESS"
+        resourceAssignment.status = BluePrintConstants.STATUS_SUCCESS
         resourceAssignment.property = property
         return resourceAssignment
     }
index 649f6b5..c3a718e 100644 (file)
 package org.onap.ccsdk.cds.blueprintsprocessor.resource.api
 
 import kotlinx.coroutines.runBlocking
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintCoreConfiguration
-import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.TemplateResolutionService
 import org.onap.ccsdk.cds.controllerblueprints.core.deleteDir
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
 import org.slf4j.LoggerFactory
@@ -29,18 +27,13 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.autoconfigure.security.SecurityProperties
 import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
 import org.springframework.context.annotation.ComponentScan
-import org.springframework.http.HttpStatus
 import org.springframework.http.MediaType
 import org.springframework.test.context.ContextConfiguration
 import org.springframework.test.context.TestPropertySource
 import org.springframework.test.context.junit4.SpringRunner
 import org.springframework.test.web.reactive.server.WebTestClient
 import org.springframework.web.reactive.function.BodyInserters
-import java.io.File
-import java.nio.file.Paths
 import kotlin.test.AfterTest
-import kotlin.test.BeforeTest
-import kotlin.test.assertTrue
 
 @RunWith(SpringRunner::class)
 @WebFluxTest
@@ -137,7 +130,7 @@ class TemplateControllerTest {
             webTestClient
                 .get()
                 .uri("/api/v1/template?bpName=$blueprintName&bpVersion=$blueprintVersion" +
-                        "&artifactName=$templatePrefix&resolutionKey=bob")
+                        "&artifactName=$templatePrefix&resolutionKey=notFound")
                 .exchange()
                 .expectStatus().isNotFound
         }