Merge "ResourceResolution should fail if required resource did not resolve"
diff --git a/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt b/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt
index 76b7fae..23d47dc 100644
--- a/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt
+++ b/ms/blueprintsprocessor/functions/k8s-profile-upload/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/profile/upload/K8sProfileUploadComponent.kt
@@ -25,6 +25,7 @@
 import org.apache.commons.io.FileUtils
 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionService
 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
@@ -213,7 +214,7 @@
             properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = ""
             properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = 1
             properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_SUMMARY] = false
-            val resolutionResult: Pair<String, JsonNode> = resourceResolutionService.resolveResources(
+            val resolutionResult: Pair<String, MutableList<ResourceAssignment>> = resourceResolutionService.resolveResources(
                 bluePrintRuntimeService,
                 nodeTemplateName,
                 ks8ProfileSource,
@@ -222,6 +223,10 @@
             val tempMainPath: File = createTempDir("k8s-profile-", "")
             val tempProfilePath: File = createTempDir("content-", "", tempMainPath)
 
+            val resolvedJsonContent = resolutionResult.second
+                .associateBy({ it.name }, { it.property?.value })
+                .asJsonNode()
+
             try {
                 val manifestFiles: ArrayList<File>? = readManifestFiles(
                     profileSourceFileFolderPath.toFile(),
@@ -229,7 +234,7 @@
                 )
                 if (manifestFiles != null) {
                     templateLocation(
-                        profileSourceFileFolderPath.toFile(), resolutionResult.second,
+                        profileSourceFileFolderPath.toFile(), resolvedJsonContent,
                         tempProfilePath, manifestFiles
                     )
                 } else
diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt
index 07eb15d..38c5c99 100644
--- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt
+++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionService.kt
@@ -73,7 +73,7 @@
         nodeTemplateName: String,
         artifactPrefix: String,
         properties: Map<String, Any>
-    ): Pair<String, JsonNode>
+    ): Pair<String, MutableList<ResourceAssignment>>
 
     /** Resolve resources for all the sources defined in a particular resource Definition[resolveDefinition]
      * with other [resourceDefinitions] dependencies for the sources [sources]
@@ -137,12 +137,22 @@
         val templateMap: MutableMap<String, String> = hashMapOf()
         val assignmentMap: MutableMap<String, JsonNode> = hashMapOf()
         artifactNames.forEach { artifactName ->
-            val (resolvedStringContent, resolvedJsonContent) = resolveResources(
+            val (resolvedStringContent, resourceAssignmentList) = resolveResources(
                 resourceAssignmentRuntimeService, nodeTemplateName,
                 artifactName, properties
             )
+            val resolvedJsonContent = resourceAssignmentList
+                .associateBy({ it.name }, { it.property?.value })
+                .asJsonNode()
+
             templateMap[artifactName] = resolvedStringContent
             assignmentMap[artifactName] = resolvedJsonContent
+
+            val failedResolution = resourceAssignmentList.filter { it.status != "success" && it.property?.required == true }.map { it.name }
+            if (failedResolution.isNotEmpty()) {
+                log.error("Failed to resolve required resources($failedResolution)")
+                bluePrintRuntimeService.setBluePrintError(resourceAssignmentRuntimeService.getBluePrintError())
+            }
         }
         return ResourceResolutionResult(templateMap, assignmentMap)
     }
@@ -152,7 +162,7 @@
         nodeTemplateName: String,
         artifactPrefix: String,
         properties: Map<String, Any>
-    ): Pair<String, JsonNode> {
+    ): Pair<String, MutableList<ResourceAssignment>> {
 
         // Template Artifact Definition Name
         val artifactTemplate = "$artifactPrefix-template"
@@ -196,9 +206,7 @@
             ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_SUMMARY,
             false
         ) as Boolean
-        val assignmentMap = resourceAssignments
-            .associateBy({ it.name }, { it.property?.value })
-            .asJsonNode()
+
         val resolvedParamJsonContent =
             ResourceAssignmentUtils.generateResourceDataForAssignments(resourceAssignments.toList())
         val artifactTemplateDefinition =
@@ -229,7 +237,7 @@
             log.info("Template resolution saved into database successfully : ($properties)")
         }
 
-        return Pair(resolvedContent, assignmentMap)
+        return Pair(resolvedContent, resourceAssignments)
     }
 
     override suspend fun resolveResourceDefinition(
diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt
index a22a73f..db8ca48 100644
--- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt
+++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionServiceTest.kt
@@ -29,11 +29,10 @@
 import org.onap.ccsdk.cds.blueprintsprocessor.core.utils.PayloadUtils
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor.MockCapabilityScriptRA
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintError
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
@@ -137,15 +136,20 @@
                 "baseconfig",
                 props
             )
-        }.let { (templateMap, assignmentMap) ->
+        }.let { (templateMap, assignmentList) ->
             assertEquals("This is Sample Velocity Template", templateMap)
 
-            val expectedAssignmentMap = hashMapOf(
+            val expectedAssignmentList = mutableListOf(
                 "service-instance-id" to "siid_1234",
                 "vnf-id" to "vnf_1234",
                 "vnf_name" to "temp_vnf"
-            ).asJsonType()
-            assertEquals(expectedAssignmentMap, assignmentMap)
+            )
+            assertEquals(expectedAssignmentList.size, assignmentList.size)
+
+            val areEqual = expectedAssignmentList.zip(assignmentList).all { (it1, it2) ->
+                it1.first == it2.name && it1.second == it2.property?.value?.asText() ?: null
+            }
+            assertEquals(true, areEqual)
         }
     }
 
@@ -327,7 +331,9 @@
                 """.trimIndent(),
                 it.first
             )
-            assertEquals("siid_1234", it.second["service-instance-id"].asText())
+            val areEqual = it.second.first().name == "service-instance-id" &&
+                "siid_1234" == it.second.first().property?.value?.asText() ?: null
+            assertEquals(true, areEqual)
         }
     }