aboutsummaryrefslogtreecommitdiffstats
path: root/ms/blueprintsprocessor/functions/resource-resolution
diff options
context:
space:
mode:
Diffstat (limited to 'ms/blueprintsprocessor/functions/resource-resolution')
-rw-r--r--ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponent.kt130
-rw-r--r--ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponentTest.kt276
2 files changed, 406 insertions, 0 deletions
diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponent.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponent.kt
new file mode 100644
index 000000000..86bcb4c54
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponent.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2022 Bell Canada
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution
+
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE
+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.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
+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.stereotype.Component
+
+@Component("component-resource-deletion")
+@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+open class ResourceDeletionComponent(
+ private val resourceResolutionDBService: ResourceResolutionDBService,
+ private val templateResolutionService: TemplateResolutionService
+) : AbstractComponentFunction() {
+
+ companion object {
+ const val INPUT_LAST_N_OCCURRENCES = "last-n-occurrences"
+ const val INPUT_FAIL_ON_EMPTY = "fail-on-empty"
+ const val ATTRIBUTE_RESULT = "result"
+ const val ATTRIBUTE_SUCCESS = "success"
+ }
+
+ data class DeletionResult(val nDeletedTemplates: Int, val nDeletedResources: Int)
+
+ override suspend fun processNB(executionRequest: ExecutionServiceInput) {
+ bluePrintRuntimeService.setNodeTemplateAttributeValue(
+ nodeTemplateName, ATTRIBUTE_RESULT, emptyMap<String, Any>().asJsonNode()
+ )
+ bluePrintRuntimeService.setNodeTemplateAttributeValue(
+ nodeTemplateName, ATTRIBUTE_SUCCESS, false.asJsonPrimitive()
+ )
+
+ val resolutionKey = getOptionalOperationInput(RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY)?.textValue() ?: ""
+ val resourceId = getOptionalOperationInput(RESOURCE_RESOLUTION_INPUT_RESOURCE_ID)?.textValue() ?: ""
+ val resourceType = getOptionalOperationInput(RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE)?.textValue() ?: ""
+
+ val resultMap = when {
+ resolutionKey.isNotBlank() -> runDelete(byResolutionKey(resolutionKey))
+ resourceType.isNotBlank() && resourceId.isNotBlank() ->
+ runDelete(byResourceTypeAndId(resourceType, resourceId))
+ else -> throw BluePrintProcessorException(
+ "Please use resolution-key OR resource-type + resource-id. Values must not be blank"
+ )
+ }
+ bluePrintRuntimeService.setNodeTemplateAttributeValue(
+ nodeTemplateName, ATTRIBUTE_RESULT, resultMap.asObjectNode()
+ )
+
+ getOptionalOperationInput(INPUT_FAIL_ON_EMPTY)?.booleanValue().takeIf { it == true }?.let {
+ resultMap.all { it.value.nDeletedResources == 0 && it.value.nDeletedTemplates == 0 }
+ .takeIf { it }?.let {
+ throw BluePrintProcessorException("No templates or resources were deleted")
+ }
+ }
+
+ bluePrintRuntimeService.setNodeTemplateAttributeValue(
+ nodeTemplateName, ATTRIBUTE_SUCCESS, true.asJsonPrimitive()
+ )
+ }
+
+ private suspend fun runDelete(fn: suspend (String, String, String, Int?) -> DeletionResult):
+ Map<String, DeletionResult> {
+ val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+ val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
+ val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
+ val artifactPrefixNamesNode = getOperationInput(INPUT_ARTIFACT_PREFIX_NAMES)
+ val artifactPrefixNames = JacksonUtils.getListFromJsonNode(artifactPrefixNamesNode, String::class.java)
+ val lastN = getOptionalOperationInput(INPUT_LAST_N_OCCURRENCES)?.let {
+ if (it.isInt) it.intValue() else null
+ }
+
+ return artifactPrefixNames.associateWith { fn(blueprintName, blueprintVersion, it, lastN) }
+ }
+
+ private fun byResolutionKey(resolutionKey: String):
+ suspend (String, String, String, Int?) -> DeletionResult = {
+ bpName, bpVersion, artifactName, lastN ->
+ val nDeleteTemplates = templateResolutionService.deleteTemplates(
+ bpName, bpVersion, artifactName, resolutionKey, lastN
+ )
+ val nDeletedResources = resourceResolutionDBService.deleteResources(
+ bpName, bpVersion, artifactName, resolutionKey, lastN
+ )
+ DeletionResult(nDeleteTemplates, nDeletedResources)
+ }
+
+ private fun byResourceTypeAndId(resourceType: String, resourceId: String):
+ suspend (String, String, String, Int?) -> DeletionResult = {
+ bpName, bpVersion, artifactName, lastN ->
+ val nDeletedTemplates = templateResolutionService.deleteTemplates(
+ bpName, bpVersion, artifactName, resourceType, resourceId, lastN
+ )
+ val nDeletedResources = resourceResolutionDBService.deleteResources(
+ bpName, bpVersion, artifactName, resourceType, resourceId, lastN
+ )
+ DeletionResult(nDeletedTemplates, nDeletedResources)
+ }
+
+ override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+ addError(runtimeException.message ?: "Failed in ResourceDeletionComponent")
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponentTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponentTest.kt
new file mode 100644
index 000000000..fb2587347
--- /dev/null
+++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceDeletionComponentTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright © 2022 Bell Canada
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.node.NullNode
+import io.mockk.coEvery
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.slot
+import io.mockk.spyk
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceDeletionComponent.Companion.ATTRIBUTE_RESULT
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceDeletionComponent.Companion.ATTRIBUTE_SUCCESS
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceDeletionComponent.Companion.INPUT_FAIL_ON_EMPTY
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceDeletionComponent.Companion.INPUT_LAST_N_OCCURRENCES
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE
+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.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
+import org.onap.ccsdk.cds.controllerblueprints.core.data.ServiceTemplate
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+
+class ResourceDeletionComponentTest {
+
+ private val blueprintName = "testCBA"
+ private val blueprintVersion = "1.0.0"
+ private val artifactNames = listOf("artifact-a", "artifact-b")
+ private val resolutionKey = "resolutionKey"
+ private val resourceId = "1"
+ private val resourceType = "ServiceInstance"
+ private val nodetemplateName = "resource-deletion"
+ private val executionRequest = ExecutionServiceInput()
+
+ private lateinit var resourceResolutionDBService: ResourceResolutionDBService
+ private lateinit var templateResolutionService: TemplateResolutionService
+ private lateinit var resourceDeletionComponent: ResourceDeletionComponent
+ private lateinit var bluePrintRuntimeService: BluePrintRuntimeService<*>
+
+ private val props = mutableMapOf<String, JsonNode>()
+
+ private var success = slot<JsonNode>()
+ private var result = slot<JsonNode>()
+
+ @Before
+ fun setup() {
+ bluePrintRuntimeService = spyk()
+ every { bluePrintRuntimeService.bluePrintContext() }.returns(
+ BluePrintContext(
+ ServiceTemplate().apply {
+ this.metadata = mutableMapOf(
+ BluePrintConstants.METADATA_TEMPLATE_VERSION to blueprintVersion,
+ BluePrintConstants.METADATA_TEMPLATE_NAME to blueprintName
+ )
+ }
+ )
+ )
+ every { bluePrintRuntimeService.setNodeTemplateAttributeValue(nodetemplateName, ATTRIBUTE_SUCCESS, capture(success)) }
+ .answers { }
+ every { bluePrintRuntimeService.setNodeTemplateAttributeValue(nodetemplateName, ATTRIBUTE_RESULT, capture(result)) }
+ .answers { }
+
+ resourceResolutionDBService = mockk()
+ templateResolutionService = mockk()
+ resourceDeletionComponent = ResourceDeletionComponent(resourceResolutionDBService, templateResolutionService)
+ resourceDeletionComponent.bluePrintRuntimeService = bluePrintRuntimeService
+ resourceDeletionComponent.nodeTemplateName = nodetemplateName
+ resourceDeletionComponent.executionServiceInput = executionRequest
+ resourceDeletionComponent.processId = "12"
+ resourceDeletionComponent.workflowName = "workflow"
+ resourceDeletionComponent.stepName = "step"
+ resourceDeletionComponent.interfaceName = "interfaceName"
+ resourceDeletionComponent.operationName = "operationName"
+
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = NullNode.getInstance()
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = NullNode.getInstance()
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = NullNode.getInstance()
+ props[ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES] = artifactNames.asJsonType()
+ props[INPUT_FAIL_ON_EMPTY] = NullNode.getInstance()
+ props[INPUT_LAST_N_OCCURRENCES] = NullNode.getInstance()
+ resourceDeletionComponent.operationInputs = props
+ }
+
+ @Test
+ fun `using resolution-key`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive()
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(1)
+
+ coEvery {
+ resourceResolutionDBService.deleteResources(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(2)
+
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+
+ val expected = ResourceDeletionComponent.DeletionResult(1, 2).asJsonType()
+ val result: JsonNode = result.captured
+ assertEquals(expected, result[artifactNames[0]])
+ assertEquals(expected, result[artifactNames[1]])
+ assertEquals(true.asJsonPrimitive(), success.captured)
+ }
+
+ @Test
+ fun `using resource-type and resource-id`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId.asJsonPrimitive()
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType.asJsonPrimitive()
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resourceType, resourceId, null)
+ }.returns(2)
+
+ coEvery {
+ resourceResolutionDBService.deleteResources(blueprintName, blueprintVersion, any(), resourceType, resourceId, null)
+ }.returns(4)
+
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+
+ val expected = ResourceDeletionComponent.DeletionResult(2, 4).asJsonType()
+ val result: JsonNode = result.captured
+ assertEquals(expected, result[artifactNames[0]])
+ assertEquals(expected, result[artifactNames[1]])
+ assertEquals(true.asJsonPrimitive(), success.captured)
+ }
+
+ @Test(expected = BluePrintProcessorException::class)
+ fun `using resource-type missing resource-id`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType.asJsonPrimitive()
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+ }
+
+ @Test(expected = BluePrintProcessorException::class)
+ fun `using resource-id missing resource-type`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId.asJsonPrimitive()
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+ }
+
+ @Test
+ fun `attributes present when failing`() {
+ val threwException = runBlocking {
+ try {
+ resourceDeletionComponent.processNB(executionRequest)
+ false
+ } catch (e: Exception) {
+ true
+ }
+ }
+ assertTrue(threwException)
+ assertEquals(false.asJsonPrimitive(), success.captured)
+ assertEquals(emptyMap<String, Any>().asJsonNode(), result.captured)
+ }
+
+ @Test
+ fun `last-n-occurrences`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive()
+ props[INPUT_LAST_N_OCCURRENCES] = JacksonUtils.jsonNodeFromObject(3)
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resolutionKey, 3)
+ }.returns(3)
+
+ coEvery {
+ resourceResolutionDBService.deleteResources(blueprintName, blueprintVersion, any(), resolutionKey, 3)
+ }.returns(6)
+
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+
+ val expected = ResourceDeletionComponent.DeletionResult(3, 6).asJsonType()
+ val result: JsonNode = result.captured
+ assertEquals(expected, result[artifactNames[0]])
+ assertEquals(expected, result[artifactNames[1]])
+ assertEquals(true.asJsonPrimitive(), success.captured)
+ }
+
+ @Test
+ fun `fail-on-empty nothing deleted`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive()
+ props[INPUT_FAIL_ON_EMPTY] = true.asJsonPrimitive()
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(0)
+
+ coEvery {
+ resourceResolutionDBService.deleteResources(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(0)
+
+ val threwException = runBlocking {
+ try {
+ resourceDeletionComponent.processNB(executionRequest)
+ false
+ } catch (e: BluePrintProcessorException) {
+ true
+ }
+ }
+
+ val expected = ResourceDeletionComponent.DeletionResult(0, 0).asJsonType()
+ val result: JsonNode = result.captured
+ assertTrue(threwException)
+ assertEquals(expected, result[artifactNames[0]])
+ assertEquals(expected, result[artifactNames[1]])
+ assertEquals(false.asJsonPrimitive(), success.captured)
+ }
+
+ @Test
+ fun `fail-on-empty something deleted`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive()
+ props[INPUT_FAIL_ON_EMPTY] = true.asJsonPrimitive()
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(1)
+
+ coEvery {
+ resourceResolutionDBService.deleteResources(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.returns(1)
+
+ runBlocking { resourceDeletionComponent.processNB(executionRequest) }
+
+ val expected = ResourceDeletionComponent.DeletionResult(1, 1).asJsonType()
+ val result: JsonNode = result.captured
+ assertEquals(expected, result[artifactNames[0]])
+ assertEquals(expected, result[artifactNames[1]])
+ assertEquals(true.asJsonPrimitive(), success.captured)
+ }
+
+ @Test
+ fun `db throws exception`() {
+ props[RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive()
+
+ coEvery {
+ templateResolutionService.deleteTemplates(blueprintName, blueprintVersion, any(), resolutionKey, null)
+ }.throws(RuntimeException("DB failure!"))
+
+ val threwException = runBlocking {
+ try {
+ resourceDeletionComponent.processNB(executionRequest)
+ false
+ } catch (e: Exception) {
+ true
+ }
+ }
+
+ assertTrue(threwException)
+ assertEquals(false.asJsonPrimitive(), success.captured)
+ assertEquals(emptyMap<String, Any>().asJsonNode(), result.captured)
+ }
+}