diff options
Diffstat (limited to 'ms')
57 files changed, 2217 insertions, 952 deletions
diff --git a/ms/blueprintsprocessor/application/pom.xml b/ms/blueprintsprocessor/application/pom.xml index 0f9e08b5f..f9eead39e 100755 --- a/ms/blueprintsprocessor/application/pom.xml +++ b/ms/blueprintsprocessor/application/pom.xml @@ -32,6 +32,7 @@ <description>Blueprints Processor Application</description> <dependencies> + <dependency> <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId> <artifactId>blueprint-core</artifactId> diff --git a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplication.java b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplication.java index 2b6f8bcf1..3d5145a22 100644 --- a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplication.java +++ b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplication.java @@ -29,13 +29,12 @@ import org.springframework.context.annotation.ComponentScan; */ @SpringBootApplication @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) -@ComponentScan(basePackages = {"org.onap.ccsdk.cds.controllerblueprints", - "org.onap.ccsdk.cds.blueprintsprocessor"}) +@ComponentScan(basePackages = {"org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"}) public class BlueprintProcessorApplication { public static void main(String[] args) { - // This is required for ResolutionResultsServiceController.getStoredResult to accept a content-type value + // This is required for TemplateController.getStoredResult to accept a content-type value // as a request parameter, e.g. &format=application%2Fxml is accepted System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); diff --git a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/SwaggerConfig.java b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/SwaggerConfig.java index 8ad3c08f2..4df55ffde 100644 --- a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/SwaggerConfig.java +++ b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/SwaggerConfig.java @@ -17,8 +17,9 @@ package org.onap.ccsdk.cds.blueprintsprocessor; - +import io.swagger.annotations.Api; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; @@ -27,37 +28,36 @@ import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import java.util.Collections; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebFlux; /** * SwaggerConfig * * @author Brinda Santh 8/13/2018 */ -//@Configuration -//@EnableSwagger2 -@SuppressWarnings("unused") -@Deprecated +@Configuration +@EnableSwagger2WebFlux public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.any()) - .paths(PathSelectors.any()) - .build() - .apiInfo(apiInfo()); + .select() + .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) + .paths(PathSelectors.any()) + .build() + .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfo( - "Blueprints Processor API", - "Controller blueprints processor API for VNF Selfservice.", - "1.0.0", - "Terms of service", - new Contact("Brinda Santh", "www.onap.com", "bs2796@onap.com"), - "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", Collections.emptyList()); + "CDS Blueprints Processor APIs", + "Provide APIs to interact with CBA, their related resolved resources and templates.", + "0.5.0", + null, + new Contact("CCSDK Team", "www.onap.org", "onap-discuss@lists.onap.org"), + "Apache 2.0", + "http://www.apache.org/licenses/LICENSE-2.0", + Collections.emptyList()); } - - -} +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/WebConfig.java b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/WebConfig.java index b453b8581..32abff011 100644 --- a/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/WebConfig.java +++ b/ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/WebConfig.java @@ -27,7 +27,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.web.reactive.config.CorsRegistry; import org.springframework.web.reactive.config.ResourceHandlerRegistry; -import org.springframework.web.reactive.config.WebFluxConfigurationSupport; +import org.springframework.web.reactive.config.WebFluxConfigurer; /** * WebConfig @@ -35,7 +35,7 @@ import org.springframework.web.reactive.config.WebFluxConfigurationSupport; * @author Brinda Santh 8/13/2018 */ @Configuration -public class WebConfig extends WebFluxConfigurationSupport { +public class WebConfig implements WebFluxConfigurer { @Autowired private AuthenticationManager authenticationManager; @@ -45,7 +45,8 @@ public class WebConfig extends WebFluxConfigurationSupport { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("swagger-ui.html") + + registry.addResourceHandler("/swagger-ui.html**") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") diff --git a/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties b/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties index a94fdf390..fae1adb72 100755 --- a/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties +++ b/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties @@ -1,3 +1,4 @@ +<<<<<<< HEAD # # Copyright � 2017-2018 AT&T Intellectual Property. # @@ -73,3 +74,12 @@ blueprintsprocessor.cliExecutor.enabled=true ### If enabling remote python executor, set this value to true ### blueprintprocessor.remoteScriptCommand.enabled=true blueprintprocessor.remoteScriptCommand.enabled=false + +# Kafka-message-lib Configurations +blueprintsprocessor.messageclient.self-service-api.topic=producer.t +blueprintsprocessor.messageclient.self-service-api.type=kafka-basic-auth +blueprintsprocessor.messageclient.self-service-api.bootstrapServers=127.0.0.1:9092 +blueprintsprocessor.messageclient.self-service-api.consumerTopic=receiver.t +blueprintsprocessor.messageclient.self-service-api.groupId=receiver-id +blueprintsprocessor.messageclient.self-service-api.clientId=default-client-id +blueprintsprocessor.messageclient.self-service-api.kafkaEnable=false diff --git a/ms/blueprintsprocessor/application/src/main/resources/application.properties b/ms/blueprintsprocessor/application/src/main/resources/application.properties index 1319d9fb5..d6e7dc890 100755 --- a/ms/blueprintsprocessor/application/src/main/resources/application.properties +++ b/ms/blueprintsprocessor/application/src/main/resources/application.properties @@ -72,3 +72,11 @@ blueprintsprocessor.restclient.primary-aai-data.url=https://aai.onap:8443 blueprintsprocessor.restclient.primary-aai-data.username=aai@aai.onap.org blueprintsprocessor.restclient.primary-aai-data.password=demo123456! +# Kafka-message-lib Configuration +blueprintsprocessor.messageclient.self-service-api.topic=producer.t +blueprintsprocessor.messageclient.self-service-api.type=kafka-basic-auth +blueprintsprocessor.messageclient.self-service-api.bootstrapServers=127.0.0.1:9092 +blueprintsprocessor.messageclient.self-service-api.consumerTopic=receiver.t +blueprintsprocessor.messageclient.self-service-api.groupId=receiver-id +blueprintsprocessor.messageclient.self-service-api.clientId=default-client-id +blueprintsprocessor.messageclient.self-service-api.kafkaEnable=false diff --git a/ms/blueprintsprocessor/application/src/test/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplicationTest.java b/ms/blueprintsprocessor/application/src/test/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplicationTest.java index 90783d40f..fc36e6287 100644 --- a/ms/blueprintsprocessor/application/src/test/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplicationTest.java +++ b/ms/blueprintsprocessor/application/src/test/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplicationTest.java @@ -16,7 +16,6 @@ package org.onap.ccsdk.cds.blueprintsprocessor; - import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -34,7 +33,6 @@ import org.springframework.test.context.junit4.SpringRunner; * @author Brinda Santh * DATE : 8/14/2018 */ - @RunWith(SpringRunner.class) @ContextConfiguration(classes = {BlueprintProcessorApplication.class, BluePrintLoadConfiguration.class}) @SpringBootTest(classes = BlueprintProcessorApplication.class, diff --git a/ms/blueprintsprocessor/application/src/test/resources/application.properties b/ms/blueprintsprocessor/application/src/test/resources/application.properties index 09ee651a4..ea2f976a3 100644 --- a/ms/blueprintsprocessor/application/src/test/resources/application.properties +++ b/ms/blueprintsprocessor/application/src/test/resources/application.properties @@ -46,3 +46,13 @@ blueprintprocessor.netconfExecutor.enabled=true blueprintprocessor.restConfExecutor.enabled=true blueprintsprocessor.cliExecutor.enabled=true blueprintprocessor.remoteScriptCommand.enabled=false + + +# Kafka-message-lib Configuration +blueprintsprocessor.messageclient.self-service-api.topic=producer.t +blueprintsprocessor.messageclient.self-service-api.type=kafka-basic-auth +blueprintsprocessor.messageclient.self-service-api.bootstrapServers=127.0.0.1:9092 +blueprintsprocessor.messageclient.self-service-api.consumerTopic=receiver.t +blueprintsprocessor.messageclient.self-service-api.groupId=receiver-id +blueprintsprocessor.messageclient.self-service-api.clientId=default-client-id +blueprintsprocessor.messageclient.self-service-api.kafkaEnable=false diff --git a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt index 2de22424e..c45fb881f 100644 --- a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt +++ b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt @@ -16,6 +16,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.python.executor +import com.fasterxml.jackson.databind.JsonNode import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.* import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ExecutionServiceConstant @@ -75,8 +76,19 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic val dynamicProperties = getOptionalOperationInput(INPUT_DYNAMIC_PROPERTIES) val packages = getOptionalOperationInput(INPUT_PACKAGES)?.returnNullIfMissing() - val args = getOptionalOperationInput(INPUT_ARGUMENT_PROPERTIES)?.returnNullIfMissing() - ?.rootFieldsToMap()?.toSortedMap()?.values?.map { it.textValue() }?.joinToString(" ") + val argsNode = getOptionalOperationInput(INPUT_ARGUMENT_PROPERTIES)?.returnNullIfMissing() + + // This prevents unescaping values, as well as quoting the each parameter, in order to allow for spaces in values + var args = "" + argsNode?.fields()?.forEach { + if (it.value.isValueNode) { + args = "$args ${it.value}" + } else { + it.value.fields().forEach { item -> + args = "$args ${item.value}" + } + } + } val command = getOperationInput(INPUT_COMMAND).asText() var scriptCommand = command.replace(pythonScript.name, pythonScript.absolutePath) diff --git a/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt b/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt index 166d7b136..31aa7c7c1 100644 --- a/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt +++ b/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt @@ -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 { diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt index 7c2c11c06..ee7c31ad2 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponent.kt @@ -20,6 +20,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import com.fasterxml.jackson.databind.node.JsonNodeFactory import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput 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 @@ -34,24 +35,38 @@ open class ResourceResolutionComponent(private val resourceResolutionService: Re override suspend fun processNB(executionRequest: ExecutionServiceInput) { - val occurrence = getOperationInput(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE) - val key = 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_RESOLUTION_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] = key?.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_RESOLUTION_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 jsonResponse = JsonNodeFactory.instance.objectNode() - for (j in 1..occurrence.asInt()) { + for (j in 1..occurrence) { + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = j val response = resourceResolutionService.resolveResources(bluePrintRuntimeService, nodeTemplateName, @@ -59,11 +74,12 @@ open class ResourceResolutionComponent(private val resourceResolutionService: Re properties) // provide indexed result in output if we have multiple resolution - if (occurrence.asInt() != 1) { - jsonResponse.set(key?.asText() + "-" + j, response.asJsonNode()) + if (occurrence != 1) { + jsonResponse.set(Integer.toString(j), response.asJsonNode()) } else { jsonResponse.setAll(response.asObjectNode()) } + } // Set Output Attributes diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt index 929e9e8df..fb32aa78b 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionConstants.kt @@ -24,7 +24,7 @@ object ResourceResolutionConstants { const val INPUT_ARTIFACT_PREFIX_NAMES = "artifact-prefix-names" const val OUTPUT_ASSIGNMENT_PARAMS = "assignment-params" const val FILE_NAME_RESOURCE_DEFINITION_TYPES = "resources_definition_types.json" - const val RESOURCE_RESOLUTION_INPUT_KEY = "resolution-key" + const val RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY = "resolution-key" const val RESOURCE_RESOLUTION_INPUT_STORE_RESULT = "store-result" const val RESOURCE_RESOLUTION_INPUT_OCCURRENCE = "occurrence" const val RESOURCE_RESOLUTION_INPUT_RESOURCE_ID = "resource-id" 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 b1482934f..0e97267da 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 @@ -20,11 +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.ResourceResolutionResultService +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.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 @@ -58,7 +61,7 @@ interface ResourceResolutionService { @Service(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION) open class ResourceResolutionServiceImpl(private var applicationContext: ApplicationContext, - private var resolutionResultService: ResourceResolutionResultService, + private var templateResolutionDBService: TemplateResolutionService, private var blueprintTemplateService: BluePrintTemplateService, private var resourceResolutionDBService: ResourceResolutionDBService) : ResourceResolutionService { @@ -74,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 { @@ -109,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) @@ -126,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 @@ -147,20 +164,19 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica properties: Map<String, Any>) { val bulkSequenced = BulkResourceSequencingUtils.process(resourceAssignments) - val resourceAssignmentRuntimeService = - ResourceAssignmentUtils.transformToRARuntimeService(blueprintRuntimeService, artifactPrefix) + val resourceAssignmentRuntimeService = blueprintRuntimeService as ResourceAssignmentRuntimeService coroutineScope { bulkSequenced.forEach { batchResourceAssignments -> // Execute Non Dependent Assignments in parallel ( ie asynchronously ) - val deferred = batchResourceAssignments.filter { it.name != "*" && it.name != "start" } + val deferred = batchResourceAssignments + .filter { it.name != "*" && it.name != "start" } + .filter { it.status != BluePrintConstants.STATUS_SUCCESS } .map { resourceAssignment -> async { val dictionaryName = resourceAssignment.dictionaryName val dictionarySource = resourceAssignment.dictionarySource - /** - * Get the Processor name - */ + val processorName = processorName(dictionaryName!!, dictionarySource!!, resourceDefinitions) val resourceAssignmentProcessor = @@ -175,13 +191,12 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica // Invoke Apply Method resourceAssignmentProcessor.applyNB(resourceAssignment) - if (properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT) - && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean) { + if (isToStore(properties)) { resourceResolutionDBService.write(properties, blueprintRuntimeService, artifactPrefix, resourceAssignment) - log.info("resolution saved into database successfully : ($resourceAssignment)") + log.info("Resource resolution saved into database successfully : ($resourceAssignment)") } // Set errors from RA @@ -199,7 +214,6 @@ open class ResourceResolutionServiceImpl(private var applicationContext: Applica } - /** * If the Source instance is "input", then it is not mandatory to have source Resource Definition, So it can * derive the default input processor. @@ -230,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_RESOLUTION_KEY] as String + val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String + val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String + + if (resolutionKey.isNotEmpty()) { + val existingResourceAssignments = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence( + bluePrintRuntimeService, + resolutionKey, + occurrence, + artifactPrefix) + if (existingResourceAssignments.isNotEmpty()) { + log.info("Resolution with resolutionKey=($resolutionKey) already exist - will resolve all resources not already resolved.", + resolutionKey) + } + return existingResourceAssignments + } else if (resourceId.isNotEmpty() && resourceType.isNotEmpty()) { + val existingResourceAssignments = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence( + bluePrintRuntimeService, + resourceId, + resourceType, + + occurrence, + artifactPrefix) + if (existingResourceAssignments.isNotEmpty()) { + log.info("Resolution with resourceId=($resourceId) and resourceType=($resourceType) already exist - will resolve " + + "all resources not already resolved.") + } + return existingResourceAssignments + } + return emptyList() + } + + // Update the resource assignment list with the status of the resource that have already been resolved + private fun updateResourceAssignmentWithExisting(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) + } + } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolution.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolution.kt index 767a1feaf..baabfd913 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolution.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolution.kt @@ -17,13 +17,14 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db import com.fasterxml.jackson.annotation.JsonFormat -import com.fasterxml.jackson.annotation.JsonProperty +import io.swagger.annotations.ApiModelProperty import org.hibernate.annotations.Proxy import org.springframework.data.annotation.LastModifiedDate 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,47 +39,67 @@ import javax.persistence.TemporalType @Proxy(lazy = false) class ResourceResolution : Serializable { - @Id - @Column(name = "resource_resolution_id") - var id: String? = null - - @Column(name = "resolution_key", nullable = false) - var resolutionKey: String? = null - - @Column(name = "resource_type", nullable = false) - var resourceType: String? = null - - @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 + @get:ApiModelProperty(value = "Version of the CBA.", required = true) @Column(name = "blueprint_version", nullable = false) var blueprintVersion: String? = null + @get:ApiModelProperty(value = "Artifact name for which to retrieve a resolved resource.", required = true) @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 - @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 - @Column(name = "dictionary_vname", nullable = false) + @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) @Column(name = "dictionary_status", nullable = false) var dictionarySource: String? = null + @get:ApiModelProperty(value = "Version of the data dictionary used for the resolution.", required = true) @Column(name = "dictionary_version", nullable = false) var dictionaryVersion: Int = 0 - @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'") @LastModifiedDate @Temporal(TemporalType.TIMESTAMP) diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt index 81239be30..5335b14b1 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBService.kt @@ -24,7 +24,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeServ 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 +33,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, @@ -62,14 +105,15 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso suspend fun readWithResourceIdAndResourceType(blueprintName: String, blueprintVersion: String, resourceId: String, - resourceType: String): List<ResourceResolution> = withContext(Dispatchers.IO) { - - resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( - blueprintName, - blueprintVersion, - resourceId, - resourceType) - } + resourceType: String): List<ResourceResolution> = + withContext(Dispatchers.IO) { + + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( + blueprintName, + blueprintVersion, + resourceId, + resourceType) + } suspend fun write(properties: Map<String, Any>, bluePrintRuntimeService: BluePrintRuntimeService<*>, @@ -80,12 +124,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_RESOLUTION_KEY] as String + val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String + val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String + val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int write(blueprintName, blueprintVersion, @@ -93,7 +136,8 @@ class ResourceResolutionDBService(private val resourceResolutionRepository: Reso resourceId, resourceType, artifactPrefix, - resourceAssignment) + resourceAssignment, + occurrence) } suspend fun write(blueprintName: String, @@ -102,17 +146,23 @@ 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 - resourceResolution.value = JacksonUtils.getValue(resourceAssignment.property?.value!!).toString() + 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 resourceResolution.dictionaryVersion = resourceAssignment.version diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt index d2cbf8411..429041e14 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionRepository.kt @@ -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 diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultRepository.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultRepository.kt deleted file mode 100644 index efc130a84..000000000 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019 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.db - -import org.springframework.data.jpa.repository.JpaRepository - -interface ResourceResolutionResultRepository : JpaRepository<ResourceResolutionResult, String> { - - fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(key: String, blueprintName: String?, - blueprintVersion: String?, - artifactName: String): ResourceResolutionResult -} diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultService.kt deleted file mode 100644 index ebb34ba4a..000000000 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultService.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 Bell Canada. - * Modifications Copyright © 2019 IBM. - * - * 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.db - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants -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.springframework.dao.DataIntegrityViolationException -import org.springframework.stereotype.Service -import java.util.* - -@Service -class ResourceResolutionResultService(private val resourceResolutionRepository: ResourceResolutionResultRepository) { - - suspend fun read(bluePrintRuntimeService: BluePrintRuntimeService<*>, artifactPrefix: String, - resolutionKey: String): String = withContext(Dispatchers.IO) { - - val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! - - val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION] - val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME] - - resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( - resolutionKey, - blueprintName, - blueprintVersion, - artifactPrefix).result!! - } - - suspend fun write(properties: Map<String, Any>, result: String, bluePrintRuntimeService: BluePrintRuntimeService<*>, - artifactPrefix: String) = withContext(Dispatchers.IO) { - - val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! - - val resourceResolutionResult = ResourceResolutionResult() - resourceResolutionResult.id = UUID.randomUUID().toString() - resourceResolutionResult.artifactName = artifactPrefix - resourceResolutionResult.blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION] - resourceResolutionResult.blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME] - resourceResolutionResult.resolutionKey = - properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY].toString() - resourceResolutionResult.result = result - - try { - resourceResolutionRepository.saveAndFlush(resourceResolutionResult) - } catch (ex: DataIntegrityViolationException) { - throw BluePrintException("Failed to store resource resolution result.", ex) - } - } - - suspend fun readByKey(resolutionResultId: String): String = withContext(Dispatchers.IO) { - - resourceResolutionRepository.getOne(resolutionResultId).result!! - } - - suspend fun deleteByKey(resolutionResultId: String): Unit = withContext(Dispatchers.IO) { - - val row = resourceResolutionRepository.getOne(resolutionResultId) - resourceResolutionRepository.delete(row) - resourceResolutionRepository.flush() - } -}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResult.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolution.kt index c9b747e59..70aadb4b1 100755 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResult.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolution.kt @@ -17,6 +17,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db import com.fasterxml.jackson.annotation.JsonFormat +import io.swagger.annotations.ApiModelProperty import org.hibernate.annotations.Proxy import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener @@ -33,30 +34,50 @@ import javax.persistence.TemporalType @EntityListeners(AuditingEntityListener::class) @Entity -@Table(name = "RESOURCE_RESOLUTION_RESULT") +@Table(name = "TEMPLATE_RESOLUTION") @Proxy(lazy = false) -class ResourceResolutionResult : Serializable { - - @Id - @Column(name = "resource_resolution_result_id") - var id: String? = null - - @Column(name = "resolution_key", nullable = false) - var resolutionKey: String? = null +class TemplateResolution : Serializable { + @get:ApiModelProperty(value = "Name of the CBA.", required = true) @Column(name = "blueprint_name", nullable = false) var blueprintName: String? = null + @get:ApiModelProperty(value = "Version of the CBA.", required = true) @Column(name = "blueprint_version", nullable = false) var blueprintVersion: String? = null + @get:ApiModelProperty(value = "Artifact name for which to retrieve a resolved resource.", required = true) @Column(name = "artifact_name", nullable = false) var artifactName: String? = null + @get:ApiModelProperty(value = "Rendered template.", required = true) @Lob @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 @Temporal(TemporalType.TIMESTAMP) diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionRepository.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionRepository.kt new file mode 100644 index 000000000..440663f25 --- /dev/null +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionRepository.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 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.db + +import org.springframework.data.jpa.repository.JpaRepository +import javax.transaction.Transactional + +interface TemplateResolutionRepository : JpaRepository<TemplateResolution, String> { + + 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) +} diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionService.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionService.kt new file mode 100644 index 000000000..55f7e770b --- /dev/null +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionService.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2019 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.db + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants +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) { + + private val log = LoggerFactory.getLogger(TemplateResolutionService::class.toString()) + + suspend fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + bluePrintRuntimeService: BluePrintRuntimeService<*>, + artifactPrefix: String, + resolutionKey: String): String = + withContext(Dispatchers.IO) { + + val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + + val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!! + val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!! + + findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(blueprintName, + blueprintVersion, + artifactPrefix, + resolutionKey) + } + + 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<*>, + artifactPrefix: String): TemplateResolution = withContext(Dispatchers.IO) { + + val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + + val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!! + val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!! + val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] as String + val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String + val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String + val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int + + write(blueprintName, + blueprintVersion, + artifactPrefix, + result, + occurrence, + resolutionKey, + resourceId, + resourceType) + } + + 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 diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/CapabilityResourceResolutionProcessor.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/CapabilityResourceResolutionProcessor.kt index 7a8d6ec5c..49cb83e09 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/CapabilityResourceResolutionProcessor.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/CapabilityResourceResolutionProcessor.kt @@ -20,6 +20,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.pro import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.CapabilityResourceSource import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentFunctionScriptingService import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils @@ -82,6 +83,7 @@ open class CapabilityResourceResolutionProcessor(private val applicationContext: override suspend fun recoverNB(runtimeException: RuntimeException, resourceAssignment: ResourceAssignment) { raRuntimeService.getBluePrintError() .addError("Failed in CapabilityResourceResolutionProcessor : ${runtimeException.message}") + ResourceAssignmentUtils.setFailedResourceDataValue(resourceAssignment, runtimeException.message) } suspend fun scriptInstance(scriptType: String, scriptClassReference: String, instanceDependencies: List<String>) diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/ResourceAssignmentProcessor.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/ResourceAssignmentProcessor.kt index 39ffe5ea9..1abcea825 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/ResourceAssignmentProcessor.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/ResourceAssignmentProcessor.kt @@ -93,6 +93,7 @@ abstract class ResourceAssignmentProcessor : BlueprintFunctionNode<ResourceAssig } catch (runtimeException: RuntimeException) { log.error("failed in ${getName()} : ${runtimeException.message}", runtimeException) recoverNB(runtimeException, resourceAssignment) + return false } return true } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponentTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponentTest.kt index 3a30ae90e..39076b4f5 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponentTest.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/ResourceResolutionComponentTest.kt @@ -1,9 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. - * - * Modifications Copyright © 2018 IBM. - * - * Modifications Copyright © 2019 IBM, Bell Canada. + * Copyright © 2018 Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,98 +17,150 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import com.fasterxml.jackson.databind.JsonNode +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk import kotlinx.coroutines.runBlocking +import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith -import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties -import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertyConfiguration import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput -import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData -import org.onap.ccsdk.cds.blueprintsprocessor.core.utils.PayloadUtils -import org.onap.ccsdk.cds.blueprintsprocessor.db.BluePrintDBLibConfiguration -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor.* -import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants -import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode -import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration -import org.onap.ccsdk.cds.controllerblueprints.core.putJsonElement -import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintError +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.ComponentScan -import org.springframework.test.context.ContextConfiguration -import org.springframework.test.context.TestPropertySource -import org.springframework.test.context.junit4.SpringRunner - -@RunWith(SpringRunner::class) -@ContextConfiguration(classes = [ResourceResolutionServiceImpl::class, - InputResourceResolutionProcessor::class, DefaultResourceResolutionProcessor::class, - DatabaseResourceAssignmentProcessor::class, RestResourceResolutionProcessor::class, - CapabilityResourceResolutionProcessor::class, - BlueprintPropertyConfiguration::class, BluePrintProperties::class, - BluePrintDBLibConfiguration::class, BluePrintLoadConfiguration::class]) -@TestPropertySource(locations = ["classpath:application-test.properties"]) -@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) -@EnableAutoConfiguration +import java.lang.RuntimeException +import kotlin.test.assertEquals +import kotlin.test.fail + class ResourceResolutionComponentTest { - @Autowired - lateinit var resourceResolutionComponent: ResourceResolutionComponent + private val resourceResolutionService = mockk<ResourceResolutionService>() + private val resourceResolutionComponent = ResourceResolutionComponent(resourceResolutionService) - @Test - fun testProcess() { - runBlocking { + private val resolutionKey = "resolutionKey" + private val resourceId = "1" + private val resourceType = "ServiceInstance" + private val occurrence = 1 + private val props = mutableMapOf<String, JsonNode>() + private val bluePrintRuntimeService = mockk<BluePrintRuntimeService<*>>() + private val artifactNames = listOf("template") + private val nodeTemplateName = "nodeTemplateName" - val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", - "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + private val executionRequest = ExecutionServiceInput() - val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", - ExecutionServiceInput::class.java)!! - // Prepare Inputs - PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, executionServiceInput.payload, "resource-assignment") + @Before + fun setup() { - val stepMetaData: MutableMap<String, JsonNode> = hashMapOf() - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "resource-assignment") - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ResourceResolutionComponent") - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process") - bluePrintRuntimeService.put("resource-assignment-step-inputs", stepMetaData.asJsonNode()) + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = true.asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey.asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId.asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType.asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence.asJsonPrimitive() + props[ResourceResolutionConstants.INPUT_ARTIFACT_PREFIX_NAMES] = JacksonUtils.jsonNodeFromObject(artifactNames) - resourceResolutionComponent.bluePrintRuntimeService = bluePrintRuntimeService - val stepInputData = StepData().apply { - name = "resource-assignment" - properties = stepMetaData + resourceResolutionComponent.operationInputs = props + resourceResolutionComponent.bluePrintRuntimeService = bluePrintRuntimeService + resourceResolutionComponent.nodeTemplateName = nodeTemplateName + + resourceResolutionComponent.executionServiceInput = executionRequest + resourceResolutionComponent.processId = "12" + resourceResolutionComponent.workflowName = "workflow" + resourceResolutionComponent.stepName = "step" + resourceResolutionComponent.interfaceName = "interfaceName" + resourceResolutionComponent.operationName = "operationName" + } + + @Test + fun processNBWithResolutionKeyAndResourceIdAndResourceTypeTestException() { + runBlocking { + try { + resourceResolutionComponent.processNB(executionRequest) + } catch (e: BluePrintProcessorException) { + assertEquals("Can't proceed with the resolution: either provide resolution-key OR combination of resource-id and resource-type.", + e.message) + return@runBlocking } - executionServiceInput.stepData = stepInputData - resourceResolutionComponent.applyNB(executionServiceInput) + fail() } } @Test - fun testRecover() { + fun processNBWithResourceIdTestException() { + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "".asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = "".asJsonPrimitive() + + runBlocking { + try { + resourceResolutionComponent.processNB(executionRequest) + } catch (e: BluePrintProcessorException) { + assertEquals("Can't proceed with the resolution: both resource-id and resource-type should be provided, one of them is missing.", + e.message) + return@runBlocking + } + fail() + } + } + + @Test + fun processNBWithEmptyResourceTypeResourceIdResolutionKeyTestException() { + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "".asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = "".asJsonPrimitive() + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = "".asJsonPrimitive() + runBlocking { - val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", - "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + try { + resourceResolutionComponent.processNB(executionRequest) + } catch (e: BluePrintProcessorException) { + assertEquals("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.", + e.message) + return@runBlocking + } + fail() + } + } - val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", - ExecutionServiceInput::class.java)!! + @Test + fun processNBTest() { + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "".asJsonPrimitive() - // Prepare Inputs - PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, executionServiceInput.payload, "resource-assignment") + val properties = mutableMapOf<String, Any>() + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = true + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType + properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence - val stepMetaData: MutableMap<String, JsonNode> = hashMapOf() - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "resource-assignment") - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ResourceResolutionComponent") - stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process") - bluePrintRuntimeService.put("resource-assignment-step-inputs", stepMetaData.asJsonNode()) + coEvery { + resourceResolutionService.resolveResources(any<BluePrintRuntimeService<*>>(), + any<String>(), + any<List<String>>(), + any<MutableMap<String, Any>>()) + } returns mutableMapOf() + every { bluePrintRuntimeService.setNodeTemplateAttributeValue(any(), any(), any()) } returns Unit - resourceResolutionComponent.bluePrintRuntimeService = bluePrintRuntimeService - val stepInputData = StepData().apply { - name = "resource-assignment" - properties = stepMetaData - } - executionServiceInput.stepData = stepInputData - resourceResolutionComponent.recoverNB(RuntimeException("TEST PASSED"), executionServiceInput) + + runBlocking { + resourceResolutionComponent.processNB(executionRequest) + } + +// FIXME add verification +// coVerify { +// resourceResolutionService.resolveResources(eq(bluePrintRuntimeService), +// eq(nodeTemplateName), eq(artifactNames), eq(properties)) +// } + } + + @Test + fun testRecover() { + runBlocking { + val blueprintError = BluePrintError() + val exception = RuntimeException("message") + every { bluePrintRuntimeService.getBluePrintError() } returns blueprintError + resourceResolutionComponent.recoverNB(exception, executionRequest) + + assertEquals(1, blueprintError.errors.size) } } }
\ No newline at end of file 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 4a3e75cde..f1ad03054 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 @@ -20,6 +20,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution import kotlinx.coroutines.runBlocking import org.junit.Assert +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties @@ -28,6 +29,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInpu import org.onap.ccsdk.cds.blueprintsprocessor.core.utils.PayloadUtils import org.onap.ccsdk.cds.blueprintsprocessor.db.BluePrintDBLibConfiguration import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor.* +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils @@ -63,12 +65,27 @@ class ResourceResolutionServiceTest { @Autowired lateinit var resourceResolutionService: ResourceResolutionService + private val props = hashMapOf<String, Any>() + private val resolutionKey = "resolutionKey" + private val resourceId = "1" + private val resourceType = "ServiceInstance" + private val occurrence = 0 + + @Before + fun setup() { + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = true + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence + } + @Test fun testRegisteredSource() { val sources = resourceResolutionService.registeredResourceSources() assertNotNull(sources, "failed to get registered sources") assertTrue(sources.containsAll(arrayListOf("source-input", "source-default", "source-processor-db", - "source-rest")), "failed to get registered sources : $sources") + "source-rest")), "failed to get registered sources : $sources") } @Test @@ -79,15 +96,27 @@ class ResourceResolutionServiceTest { Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", - "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") - val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", ExecutionServiceInput::class.java)!! + + val resourceAssignmentRuntimeService = + ResourceAssignmentUtils.transformToRARuntimeService(bluePrintRuntimeService, + "testResolveResource") + + // Prepare Inputs - PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, executionServiceInput.payload, "resource-assignment") + PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment") - resourceResolutionService.resolveResources(bluePrintRuntimeService, "resource-assignment", "baseconfig", mapOf()) + resourceResolutionService.resolveResources(resourceAssignmentRuntimeService, + "resource-assignment", + "baseconfig", + props) } } @@ -99,17 +128,23 @@ class ResourceResolutionServiceTest { Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", - "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") - val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", ExecutionServiceInput::class.java)!! val artefactNames = listOf("baseconfig", "another") // Prepare Inputs - PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, executionServiceInput.payload, "resource-assignment") - - resourceResolutionService.resolveResources(bluePrintRuntimeService, "resource-assignment", artefactNames, mapOf()) + PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment") + + resourceResolutionService.resolveResources(bluePrintRuntimeService, + "resource-assignment", + artefactNames, + props) } } @@ -121,17 +156,61 @@ class ResourceResolutionServiceTest { Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", - "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") - val executionServiceInput = JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", ExecutionServiceInput::class.java)!! + val resourceAssignmentRuntimeService = + ResourceAssignmentUtils.transformToRARuntimeService(bluePrintRuntimeService, + "testResolveResourcesWithMappingAndTemplate") + val artifactPrefix = "another" // Prepare Inputs - PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, executionServiceInput.payload, "resource-assignment") + PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment") + + resourceResolutionService.resolveResources(resourceAssignmentRuntimeService, + "resource-assignment", + artifactPrefix, + props) + } + } + + + @Test + fun testResolveResourcesWithResourceIdAndResourceType() { - resourceResolutionService.resolveResources(bluePrintRuntimeService, "resource-assignment", artifactPrefix, mapOf<String, String>()) + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "" + + runBlocking { + Assert.assertNotNull("failed to create ResourceResolutionService", resourceResolutionService) + + val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234", + "./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration") + + val executionServiceInput = + JacksonUtils.readValueFromClassPathFile("payload/requests/sample-resourceresolution-request.json", + ExecutionServiceInput::class.java)!! + + val resourceAssignmentRuntimeService = + ResourceAssignmentUtils.transformToRARuntimeService(bluePrintRuntimeService, + "testResolveResourcesWithMappingAndTemplate") + + val artifactPrefix = "another" + + // Prepare Inputs + PayloadUtils.prepareInputsFromWorkflowPayload(bluePrintRuntimeService, + executionServiceInput.payload, + "resource-assignment") + + resourceResolutionService.resolveResources(resourceAssignmentRuntimeService, + "resource-assignment", + artifactPrefix, + props) } } } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBServiceTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBServiceTest.kt new file mode 100644 index 000000000..dcf2e64a5 --- /dev/null +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionDBServiceTest.kt @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2019 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.db + +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext +import org.onap.ccsdk.cds.controllerblueprints.core.service.DefaultBluePrintRuntimeService +import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment +import org.springframework.dao.EmptyResultDataAccessException +import kotlin.test.assertEquals + +open class ResourceResolutionDBServiceTest { + + private val resourceResolutionRepository = mockk<ResourceResolutionRepository>() + + private val resourceResolutionDBService = ResourceResolutionDBService(resourceResolutionRepository) + + private val resolutionKey = "resolutionKey" + private val resourceId = "1" + private val resourceType = "ServiceInstance" + private val occurrence = 0 + private val artifactPrefix = "template" + private val blueprintName = "blueprintName" + private val blueprintVersion = "1.0.0" + private val metadata = hashMapOf<String, String>() + private val props = hashMapOf<String, Any>() + private val bluePrintContext = mockk<BluePrintContext>() + private val bluePrintRuntimeService = mockk<DefaultBluePrintRuntimeService>() + + @Before + fun setup() { + metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION] = blueprintVersion + metadata[BluePrintConstants.METADATA_TEMPLATE_NAME] = blueprintName + + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence + + every { bluePrintContext.metadata } returns metadata + + every { bluePrintRuntimeService.bluePrintContext() } returns bluePrintContext + } + + @Test + fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrenceTest() { + + val rr1 = ResourceResolution() + val rr2 = ResourceResolution() + + val list = listOf(rr1, rr2) + every { + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence( + any(), any(), any(), any(), any()) + } returns list + runBlocking { + + val res = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence( + bluePrintRuntimeService, resolutionKey, occurrence, artifactPrefix) + + assertEquals(2, res.size) + } + } + + @Test + fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrenceTestException() { + every { + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence( + any(), any(), any(), any(), any()) + } throws EmptyResultDataAccessException(1) + runBlocking { + val res = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence( + bluePrintRuntimeService, resolutionKey, occurrence, artifactPrefix) + + assert(res.isEmpty()) + } + } + + @Test + fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrenceTest() { + + val rr1 = ResourceResolution() + val rr2 = ResourceResolution() + val list = listOf(rr1, rr2) + every { + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence( + any(), any(), any(), any(), any(), any()) + } returns list + runBlocking { + + val res = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence( + bluePrintRuntimeService, resourceId, resourceType, occurrence, artifactPrefix) + + assertEquals(2, res.size) + } + } + + @Test + fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrenceTestException() { + every { + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence( + any(), any(), any(), any(), any(), any()) + } throws EmptyResultDataAccessException(1) + runBlocking { + val res = + resourceResolutionDBService.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence( + bluePrintRuntimeService, resourceId, resourceType, occurrence, artifactPrefix) + + assert(res.isEmpty()) + } + } + + @Test + fun readValueTest() { + val rr = ResourceResolution() + rr.name = "bob" + rr.value = "testValue" + every { + resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndName( + any(), any(), any(), any(), any()) + } returns rr + runBlocking { + val res = + resourceResolutionDBService.readValue( + blueprintName, blueprintVersion, artifactPrefix, resolutionKey, "bob") + + assertEquals(rr.name, res.name) + assertEquals(rr.value, res.value) + } + } + + @Test + fun readWithResolutionKeyTest() { + val rr1 = ResourceResolution() + val rr2 = ResourceResolution() + val list = listOf(rr1, rr2) + every { + resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + any(), any(), any(), any()) + } returns list + runBlocking { + val res = + resourceResolutionDBService.readWithResolutionKey( + blueprintName, blueprintVersion, artifactPrefix, resolutionKey) + assertEquals(2, res.size) + } + } + + @Test + fun readWithResourceIdAndResourceTypeTest() { + val rr1 = ResourceResolution() + val rr2 = ResourceResolution() + val list = listOf(rr1, rr2) + every { + resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType( + any(), any(), any(), any()) + } returns list + runBlocking { + val res = + resourceResolutionDBService.readWithResourceIdAndResourceType( + blueprintName, blueprintVersion, resourceId, resourceType) + assertEquals(2, res.size) + } + } + + @Test + fun writeTest() { + val resourceResolution = ResourceResolution() + val resourceAssignment = ResourceAssignment() + resourceAssignment.property?.status = BluePrintConstants.STATUS_SUCCESS + resourceAssignment.property?.value = "result".asJsonPrimitive() + resourceAssignment.dictionarySource = "ddSource" + resourceAssignment.dictionaryName = "ddName" + resourceAssignment.version = 1 + resourceAssignment.name = "test" + every { + resourceResolutionRepository.saveAndFlush(any<ResourceResolution>()) + } returns resourceResolution + runBlocking { + val res = + resourceResolutionDBService.write( + props, bluePrintRuntimeService, artifactPrefix, resourceAssignment) + + assertEquals(resourceResolution, res) + } + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionServiceTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionServiceTest.kt new file mode 100644 index 000000000..48c6f02ef --- /dev/null +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/TemplateResolutionServiceTest.kt @@ -0,0 +1,137 @@ +package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext +import org.onap.ccsdk.cds.controllerblueprints.core.service.DefaultBluePrintRuntimeService +import org.springframework.dao.EmptyResultDataAccessException +import kotlin.test.assertEquals + +class TemplateResolutionServiceTest { + + private val templateResolutionRepository = mockk<TemplateResolutionRepository>() + + private val templateResolutionService = TemplateResolutionService(templateResolutionRepository) + + private val resolutionKey = "resolutionKey" + private val resourceId = "1" + private val resourceType = "ServiceInstance" + private val occurrence = 0 + private val artifactPrefix = "template" + private val blueprintName = "blueprintName" + private val blueprintVersion = "1.0.0" + private val result = "result" + private val metadata = hashMapOf<String, String>() + private val props = hashMapOf<String, Any>() + private val bluePrintContext = mockk<BluePrintContext>() + private val bluePrintRuntimeService = mockk<DefaultBluePrintRuntimeService>() + + @Before + fun setup() { + metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION] = blueprintVersion + metadata[BluePrintConstants.METADATA_TEMPLATE_NAME] = blueprintName + + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = resolutionKey + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = resourceId + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = resourceType + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = occurrence + + every { bluePrintContext.metadata } returns metadata + + every { bluePrintRuntimeService.bluePrintContext() } returns bluePrintContext + } + + @Test + fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameTest() { + val tr = TemplateResolution() + tr.result = "res" + runBlocking { + every { + templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any()) + } returns tr + val res = + templateResolutionService.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + bluePrintRuntimeService, artifactPrefix, resolutionKey) + assertEquals(tr.result, res) + } + } + + @Test(expected = EmptyResultDataAccessException::class) + fun findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameTestException() { + val tr = TemplateResolution() + runBlocking { + every { + templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any()) + } returns tr + templateResolutionService.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName( + bluePrintRuntimeService, artifactPrefix, resolutionKey) + } + } + + @Test + fun writeWithResolutionKeyTest() { + val tr = TemplateResolution() + runBlocking { + every { templateResolutionRepository.saveAndFlush(any<TemplateResolution>()) } returns tr + every { + templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any()) + } returns null + val res = templateResolutionService.write(props, result, bluePrintRuntimeService, artifactPrefix) + assertEquals(tr, res) + } + } + + @Test + fun writeWithResolutionKeyExistingTest() { + val tr = TemplateResolution() + runBlocking { + every { templateResolutionRepository.saveAndFlush(any<TemplateResolution>()) } returns tr + every { + templateResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any()) + } returns tr + every { + templateResolutionRepository.deleteByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any()) + } returns Unit + val res = templateResolutionService.write(props, result, bluePrintRuntimeService, artifactPrefix) + verify { + templateResolutionRepository.deleteByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + eq(resolutionKey), eq(blueprintName), eq(blueprintVersion), eq(artifactPrefix), eq(occurrence)) + } + assertEquals(tr, res) + } + } + + @Test + fun writeWithResourceIdResourceTypeExistingTest() { + props[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = "" + val tr = TemplateResolution() + runBlocking { + every { templateResolutionRepository.saveAndFlush(any<TemplateResolution>()) } returns tr + every { + templateResolutionRepository.findByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any(), any()) + } returns tr + every { + templateResolutionRepository.deleteByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + any(), any(), any(), any(), any(), any()) + } returns Unit + val res = templateResolutionService.write(props, result, bluePrintRuntimeService, artifactPrefix) + verify { + templateResolutionRepository.deleteByResourceIdAndResourceTypeAndBlueprintNameAndBlueprintVersionAndArtifactNameAndOccurrence( + eq(resourceId), eq(resourceType), eq(blueprintName), eq(blueprintVersion), eq(artifactPrefix), eq(occurrence)) + } + assertEquals(tr, res) + } + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/mock/MockRestResourceResolutionProcessor.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/mock/MockRestResourceResolutionProcessor.kt index be023307b..e80663094 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/mock/MockRestResourceResolutionProcessor.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/mock/MockRestResourceResolutionProcessor.kt @@ -98,7 +98,7 @@ class MockRestResourceResolutionProcessor(private val blueprintRestLibPropertySe } } catch (e: Exception) { ResourceAssignmentUtils.setFailedResourceDataValue(executionRequest, e.message) - throw BluePrintProcessorException("Failed in template key ($executionRequest) assignments with: ${e.message}", + throw BluePrintProcessorException("Failed in template resolutionKey ($executionRequest) assignments with: ${e.message}", e) } } diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceResolutionProcessorTest.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceResolutionProcessorTest.kt index f73197e0b..153e88937 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceResolutionProcessorTest.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceResolutionProcessorTest.kt @@ -71,34 +71,6 @@ class DatabaseResourceResolutionProcessorTest { val processorName = databaseResourceAssignmentProcessor.applyNB(resourceAssignment) assertNotNull(processorName, "couldn't get Database resource assignment processor name") - println(processorName) - } - } - - @Test - fun `test database resource resolution primary db`() { - runBlocking { - val bluePrintContext = BluePrintMetadataUtils.getBluePrintContext( - "./../../../../components/model-catalog/blueprint-model/test-blueprint/capability_python") - - val resourceAssignmentRuntimeService = ResourceAssignmentRuntimeService("1234", bluePrintContext) - - databaseResourceAssignmentProcessor.raRuntimeService = resourceAssignmentRuntimeService - databaseResourceAssignmentProcessor.resourceDictionaries = ResourceAssignmentUtils - .resourceDefinitions(bluePrintContext.rootPath) - - val resourceAssignment = ResourceAssignment().apply { - name = "service-instance-id" - dictionaryName = "service-instance-id" - dictionarySource = "primary-db" - property = PropertyDefinition().apply { - type = "string" - } - } - - val processorName = databaseResourceAssignmentProcessor.applyNB(resourceAssignment) - assertNotNull(processorName, "couldn't get Database resource assignment processor name") - println(processorName) } } }
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImpl.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImpl.kt index 452dd0f5c..3e1f0715b 100755 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImpl.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImpl.kt @@ -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) diff --git a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaBasicAuthMessageProducerService.kt b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaBasicAuthMessageProducerService.kt index 52ac346db..008e92437 100644 --- a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaBasicAuthMessageProducerService.kt +++ b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaBasicAuthMessageProducerService.kt @@ -16,6 +16,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.message.service +import org.apache.commons.lang.builder.ToStringBuilder import org.apache.kafka.clients.producer.ProducerConfig.* import org.apache.kafka.common.serialization.StringSerializer import org.onap.ccsdk.cds.blueprintsprocessor.message.KafkaBasicAuthMessageProducerProperties @@ -27,7 +28,6 @@ import org.springframework.kafka.core.ProducerFactory import org.springframework.kafka.support.SendResult import org.springframework.util.concurrent.ListenableFutureCallback - class KafkaBasicAuthMessageProducerService( private val messageProducerProperties: KafkaBasicAuthMessageProducerProperties) : BlueprintMessageProducerService { @@ -64,9 +64,8 @@ class KafkaBasicAuthMessageProducerService( return true } - private fun producerFactory(additionalConfig: Map<String, Any>? = null): ProducerFactory<String, Any> { - log.info("Client Properties : $messageProducerProperties") + log.info("Client Properties : ${ToStringBuilder.reflectionToString(messageProducerProperties)}") val configProps = hashMapOf<String, Any>() configProps[BOOTSTRAP_SERVERS_CONFIG] = messageProducerProperties.bootstrapServers configProps[KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt index c2698c026..c45ebc127 100644 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/api/data/BlueprintProcessorData.kt @@ -30,24 +30,32 @@ import java.util.* */ open class ExecutionServiceInput { - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Headers providing request context.") lateinit var commonHeader: CommonHeader - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Provide information about the action to execute.") lateinit var actionIdentifiers: ActionIdentifiers - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, + value = "Contain the information to be passed as input to the action." + + "The payload is constituted of two section: the workflow input which is the higher level block (xxx-request)" + + " and the input for resource resolution located within the xxx-request block, contained within xxx-properties") lateinit var payload: ObjectNode + @get:ApiModelProperty(hidden = true) var stepData: StepData? = null } open class ExecutionServiceOutput { - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Headers providing request context.") lateinit var commonHeader: CommonHeader - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Provide information about the action to execute.") lateinit var actionIdentifiers: ActionIdentifiers - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Status of the request.") lateinit var status: Status - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, + value = "Contain the information to be passed as input to the action." + + "The payload is constituted of two section: the workflow input which is the higher level block (xxx-request)" + + " and the input for resource resolution located within the xxx-request block, contained within xxx-properties") lateinit var payload: ObjectNode + @get:ApiModelProperty(hidden = true) var stepData: StepData? = null } @@ -55,53 +63,56 @@ const val ACTION_MODE_ASYNC = "async" const val ACTION_MODE_SYNC = "sync" open class ActionIdentifiers { - @get:ApiModelProperty(required = false) + @get:ApiModelProperty(required = false, value = "Name of the CBA.") lateinit var blueprintName: String - @get:ApiModelProperty(required = false) + @get:ApiModelProperty(required = false, value = "Version of the CBA.") lateinit var blueprintVersion: String - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Name of the workflow to execute.") lateinit var actionName: String - @get:ApiModelProperty(required = true, allowableValues = "sync, async") + @get:ApiModelProperty(required = true, + value = "Async processing is only supported for gRPC client.", + allowableValues = "sync, async") lateinit var mode: String } open class CommonHeader { - @get:ApiModelProperty(required = true, example = "2012-04-23T18:25:43.511Z") + @get:ApiModelProperty(required = true, value = "Date of the execution", example = "2012-04-23T18:25:43.511Z") @get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") var timestamp: Date = Date() - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Identify the system/person triggering the request.") lateinit var originatorId: String - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Uniquely identify a request.") lateinit var requestId: String - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Allow for fine-grain request identifier") lateinit var subRequestId: String - @get:ApiModelProperty(required = false) + @get:ApiModelProperty(required = false, hidden = true) var flags: Flags? = null } open class Flags { + @get:ApiModelProperty(value = "Whether or not to force the action.") var isForce: Boolean = false @get:ApiModelProperty(value = "3600") var ttl: Int = 3600 } open class Status { - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "HTTP status code equivalent.") var code: Int = 200 - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Type of the event being emitted by CDS.") var eventType: String = "" - @get:ApiModelProperty(required = true, example = "2012-04-23T18:25:43.511Z") + @get:ApiModelProperty(required = true, + value = "Time when the execution ended.", + example = "2012-04-23T18:25:43.511Z") @get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") var timestamp: Date = Date() - @get:ApiModelProperty(required = false) + @get:ApiModelProperty(required = false, value = "Error message when system failed") var errorMessage: String? = null - @get:ApiModelProperty(required = true) + @get:ApiModelProperty(required = true, value = "Message providing request status") var message: String = "success" } open class StepData { lateinit var name: String var properties: MutableMap<String, JsonNode> = mutableMapOf() -} - - +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceController.kt deleted file mode 100644 index 61a9541d3..000000000 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceController.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright © 2018-2019 Bell Canada Intellectual Property. - * - * 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.resolutionresults.api - -import io.swagger.annotations.ApiOperation -import kotlinx.coroutines.runBlocking -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.web.bind.annotation.* - -/** - * Exposes Resolution Results API to store and retrieve resource resolution results from external processes, - * like python or ansible scripts - * - * @author Serge Simard - * @version 1.0 - */ -@RestController -@RequestMapping("/api/v1/resolution-results") -open class ResolutionResultsServiceController { - - @Autowired - lateinit var resolutionResultsServiceHandler: ResolutionResultsServiceHandler - - @RequestMapping(path = ["/ping"], method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE]) - @ResponseBody - fun ping(): String = runBlocking { - "Success" - } - - @RequestMapping(path = ["/{resolution_result_id}"], method = [RequestMethod.GET], produces = [MediaType.TEXT_PLAIN_VALUE]) - @ApiOperation(value = "Fetch a stored result by ID", - notes = "Loads a stored result using the resolution_result_id primary key") - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun getStoredResultById(@PathVariable(value = "resolution_result_id") resolutionResultId: String) - : String = runBlocking { - resolutionResultsServiceHandler.loadStoredResultById(resolutionResultId) - } - - @RequestMapping(path = ["/"], method = [RequestMethod.GET], produces = [MediaType.TEXT_PLAIN_VALUE]) - @ApiOperation(value = "Fetch a stored result ", - notes = "Loads a stored result using the blueprint metadata, artifact name and resolution-key") - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun getStoredResult(@RequestParam(value = "bpName") bpName: String, - @RequestParam(value = "bpVersion") bpVersion: String, - @RequestParam(value = "artifactName") artifactName: String, - @RequestParam(value = "resolutionKey") resolutionKey: String, - @RequestParam(value = "format", required = false, defaultValue = "text/plain") format: String) - : ResponseEntity<String> = runBlocking { - - val payload = resolutionResultsServiceHandler.loadStoredResult(bpName, bpVersion, artifactName, resolutionKey) - - var expectedContentType = format - if (expectedContentType.indexOf('/') < 0) { - expectedContentType = "application/$expectedContentType" - } - val expectedMediaType : MediaType = MediaType.valueOf(expectedContentType) - - ResponseEntity.ok().contentType(expectedMediaType).body(payload) - } - - - @PostMapping("/{bpName}/{bpVersion}/{artifactName}/{resolutionKey}", produces = [MediaType.TEXT_PLAIN_VALUE]) - @ApiOperation(value = "Store result ", - notes = "Store result under resolution-key for the specified blueprint/version/artifact.") - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun putStoredResult(@PathVariable(value = "bpName") bpName: String, - @PathVariable(value = "bpVersion") bpVersion: String, - @PathVariable(value = "artifactName") artifactName: String, - @PathVariable(value = "resolutionKey") resolutionKey: String, - @RequestBody result : String): String? = runBlocking { - resolutionResultsServiceHandler.saveNewStoredResult(bpName, bpVersion, artifactName, resolutionKey, result).id - } - - - @DeleteMapping(path = ["/{resolution_result_id}"]) - @ApiOperation(value = "Deletes a stored result by ID", - notes = "Removes a stored result, using the resolution_result_id primary key") - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun deleteStoredResult(@PathVariable(value = "resolution_result_id") resolutionResultId: String) = runBlocking { - resolutionResultsServiceHandler.removeStoredResultById(resolutionResultId) - } - -} diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandler.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandler.kt deleted file mode 100644 index 1fb34d789..000000000 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandler.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2018-2019 Bell Canada Intellectual Property. - * - * 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.resolutionresults.api - -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionResult -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionResultService -import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService -import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils -import org.springframework.stereotype.Service -import java.util.* - -/** - * Process Resolution Results API request to store and retrieve resource resolution results using database acess layer - * ResourceResolutionResultService and corresponding entities - * - * @author Serge Simard - * @version 1.0 - */ -@Service -class ResolutionResultsServiceHandler(private val bluePrintCatalogService: BluePrintCatalogService, - private var resolutionResultService: ResourceResolutionResultService) { - - suspend fun loadStoredResultById(resolutionResultId: String): String { - - return resolutionResultService.readByKey(resolutionResultId) - } - - suspend fun loadStoredResult(blueprintName : String, blueprintVersion : String, artifactTemplate: String, - resolutionKey: String): String { - - val basePath = bluePrintCatalogService.getFromDatabase(blueprintName, blueprintVersion) - val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(UUID.randomUUID().toString(), - basePath.toString()) - - return resolutionResultService.read(blueprintRuntimeService, artifactTemplate, resolutionKey) - } - - suspend fun saveNewStoredResult(blueprintName : String, blueprintVersion : String, artifactTemplate: String, - resolutionKey: String, result: String): ResourceResolutionResult { - - val basePath = bluePrintCatalogService.getFromDatabase(blueprintName, blueprintVersion) - val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(UUID.randomUUID().toString(), - basePath.toString()) - - val properties = mapOf(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_KEY to resolutionKey) - - val resultStored = resolutionResultService.write(properties, result, blueprintRuntimeService, artifactTemplate) - - return resultStored - } - - suspend fun removeStoredResultById(resolutionResultId: String): Unit { - - return resolutionResultService.deleteByKey(resolutionResultId) - } -}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceController.kt deleted file mode 100644 index 40aa1a3e6..000000000 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceController.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright © 2019 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.resolutionresults.api - -import io.swagger.annotations.ApiOperation -import kotlinx.coroutines.runBlocking -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolution -import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionDBService -import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.web.bind.annotation.* - -@RestController -@RequestMapping("/api/v1/resources") -open class ResourceController(private var resourceResolutionDBService: ResourceResolutionDBService) { - - @RequestMapping(path = ["/ping"], method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE]) - @ResponseBody - fun ping(): String = runBlocking { - "Success" - } - - @RequestMapping(path = [""], - method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE]) - @ApiOperation(value = "Fetch all resource values associated to a resolution key. ", - notes = "Retrieve a stored resource value using the blueprint metadata, artifact name and the resolution-key.", - produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun getAllFromResolutionKeyOrFromResourceTypeAndId(@RequestParam(value = "bpName", required = true) bpName: String, - @RequestParam(value = "bpVersion", required = true) bpVersion: String, - @RequestParam(value = "artifactName", required = false, defaultValue = "") artifactName: String, - @RequestParam(value = "resolutionKey", required = false, defaultValue = "") resolutionKey: String, - @RequestParam(value = "resourceType", required = false, defaultValue = "") resourceType: String, - @RequestParam(value = "resourceId", required = false, defaultValue = "") resourceId: String) - : 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.") - } else if (resolutionKey.isNotEmpty() && artifactName.isNotEmpty()) { - ResponseEntity.ok() - .body(resourceResolutionDBService.readWithResolutionKey(bpName, bpVersion, artifactName, resolutionKey)) - } else if (resourceType.isNotEmpty() && resourceId.isNotEmpty()){ - ResponseEntity.ok() - .body(resourceResolutionDBService.readWithResourceIdAndResourceType(bpName, bpVersion, resourceId, resourceType)) - } else { - throw ResourceException("Missing param. Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.") - } - } - - @RequestMapping(path = ["/resource"], - method = [RequestMethod.GET], - produces = [MediaType.APPLICATION_JSON_VALUE]) - @ApiOperation(value = "Fetch a resource value using resolution key.", - notes = "Retrieve a stored resource value using the blueprint metadata, artifact name, resolution-key along with the name of the resource value to retrieve.", - produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - @PreAuthorize("hasRole('USER')") - fun getOneFromResolutionKey(@RequestParam(value = "bpName", required = true) bpName: String, - @RequestParam(value = "bpVersion", required = true) bpVersion: String, - @RequestParam(value = "artifactName", required = true) artifactName: String, - @RequestParam(value = "resolutionKey", required = true) resolutionKey: String, - @RequestParam(value = "name", required = true) name: String) - : ResponseEntity<ResourceResolution> = runBlocking { - - ResponseEntity.ok() - .body(resourceResolutionDBService.readValue(bpName, bpVersion, artifactName, resolutionKey, name)) - } -}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceException.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResolutionException.kt index 6815c05ef..62e89e260 100644 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceException.kt +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResolutionException.kt @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.onap.ccsdk.cds.blueprintsprocessor.resolutionresults.api +package org.onap.ccsdk.cds.blueprintsprocessor.resource.api -class ResourceException(message: String) : RuntimeException(message) { +class ResolutionException(message: String) : RuntimeException(message) { var code: Int = 404 } - diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt new file mode 100644 index 000000000..3a708a973 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt @@ -0,0 +1,111 @@ +/* + * Copyright © 2019 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.resource.api + +import com.fasterxml.jackson.databind.JsonNode +import io.swagger.annotations.Api +import io.swagger.annotations.ApiOperation +import io.swagger.annotations.ApiParam +import kotlinx.coroutines.runBlocking +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.utils.JacksonUtils +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/api/v1/resources") +@Api(value = "/api/v1/resources", + description = "Interaction with resolved resources.") +open class ResourceController(private var resourceResolutionDBService: ResourceResolutionDBService) { + + @RequestMapping(path = ["/health-check"], + method = [RequestMethod.GET], + produces = [MediaType.APPLICATION_JSON_VALUE]) + @ResponseBody + @ApiOperation(value = "Health Check", hidden = true) + fun resourceControllerHealthCheck(): JsonNode = runBlocking { + JacksonUtils.getJsonNode("Success") + } + + @RequestMapping(path = [""], + method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE]) + @ApiOperation(value = "Get all resolved resources using the resolution key. ", + notes = "Retrieve all stored resolved resources using the blueprint name, blueprint version, " + + "artifact name and the resolution-key.", + response = ResourceResolution::class, + responseContainer = "List", + produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun getAllFromResolutionKeyOrFromResourceTypeAndId( + @ApiParam(value = "Name of the CBA.", required = true) + @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) + @RequestParam(value = "artifactName", required = false, defaultValue = "") artifactName: String, + @ApiParam(value = "Resolution Key associated with the resolution.", required = false) + @RequestParam(value = "resolutionKey", required = false, defaultValue = "") 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) + : ResponseEntity<List<ResourceResolution>> = runBlocking { + + if ((resolutionKey.isNotEmpty() || artifactName.isNotEmpty()) && (resourceId.isNotEmpty() || resourceType.isNotEmpty())) { + 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)) + } else if (resourceType.isNotEmpty() && resourceId.isNotEmpty()) { + ResponseEntity.ok() + .body(resourceResolutionDBService.readWithResourceIdAndResourceType(bpName, + bpVersion, + resourceId, + resourceType)) + } else { + throw ResolutionException("Missing param. Either retrieve resolved value using artifact name and resolution-key OR using resource-id and resource-type.") + } + } + + @RequestMapping(path = ["/resource"], + method = [RequestMethod.GET], + produces = [MediaType.APPLICATION_JSON_VALUE]) + @ApiOperation(value = "Fetch a resource value using resolution key.", + notes = "Retrieve a stored resource value using the blueprint metadata, artifact name, resolution-key along with the name of the resource value to retrieve.", + produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun getOneFromResolutionKey(@ApiParam(value = "Name of the CBA.", required = true) + @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) + @RequestParam(value = "artifactName", required = true) artifactName: String, + @ApiParam(value = "Resolution Key associated with the resolution.", required = true) + @RequestParam(value = "resolutionKey", required = true) resolutionKey: String, + @ApiParam(value = "Name of the resource to retrieve.", required = true) + @RequestParam(value = "name", required = true) name: String) + : ResponseEntity<ResourceResolution> = runBlocking { + + ResponseEntity.ok() + .body(resourceResolutionDBService.readValue(bpName, bpVersion, artifactName, resolutionKey, name)) + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceExceptionHandler.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceExceptionHandler.kt index 7f8f7da79..42ff8016c 100644 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceExceptionHandler.kt +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceExceptionHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2018-2019 Bell Canada Intellectual Property. + * Copyright © 2019 Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.onap.ccsdk.cds.blueprintsprocessor.resolutionresults.api +package org.onap.ccsdk.cds.blueprintsprocessor.resource.api import com.fasterxml.jackson.annotation.JsonFormat import com.fasterxml.jackson.annotation.JsonInclude @@ -27,6 +27,7 @@ import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.orm.jpa.JpaObjectRetrievalFailureException import org.springframework.dao.EmptyResultDataAccessException +import org.springframework.dao.IncorrectResultSizeDataAccessException import org.springframework.web.server.ServerWebInputException import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestControllerAdvice @@ -34,61 +35,65 @@ import java.io.Serializable import java.util.* /** - * Handle exceptions in Resolution Results API and provide relevant HTTP status codes and messages + * Handle exceptions in Resolution API and provide relevant HTTP status codes and messages * * @author Serge Simard * @version 1.0 */ -@RestControllerAdvice("org.onap.ccsdk.cds.blueprintsprocessor.resolutionresults") -open class ResolutionResultsServiceExceptionHandler { +@RestControllerAdvice("org.onap.ccsdk.cds.blueprintsprocessor.resource.api") +open class ResourceExceptionHandler { - private val log = LoggerFactory.getLogger(ResolutionResultsServiceExceptionHandler::class.toString()) + private val log = LoggerFactory.getLogger(ExceptionHandler::class.toString()) - private val debugMsg = "ResolutionResultsService_Error_Message" + private val debugMsg = "Resolution_Service_Error_Message" @ExceptionHandler - fun ResolutionResultsServiceExceptionHandler(e: BluePrintProcessorException): ResponseEntity<ErrorMessage> { - log.error(e.message, e) + fun resolutionResultsServiceExceptionHandler(e: BluePrintProcessorException): ResponseEntity<ErrorMessage> { val errorCode = ErrorCode.BLUEPRINT_PATH_MISSING - val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg) - return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)) + return returnError(e, errorCode) } @ExceptionHandler - fun ResolutionResultsServiceExceptionHandler(e: ServerWebInputException): ResponseEntity<ErrorMessage> { - log.error(e.message, e) + fun resolutionResultsServiceExceptionHandler(e: ServerWebInputException): ResponseEntity<ErrorMessage> { val errorCode = ErrorCode.INVALID_REQUEST_FORMAT - val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg) - return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)) + return returnError(e, errorCode) } @ExceptionHandler - fun ResolutionResultsServiceExceptionHandler(e: EmptyResultDataAccessException): ResponseEntity<ErrorMessage> { - log.error(e.message, e) - var errorCode = ErrorCode.RESOURCE_NOT_FOUND - val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg) - return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)) + fun resolutionResultsServiceExceptionHandler(e: IncorrectResultSizeDataAccessException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.DUPLICATE_DATA + return returnError(e, errorCode) } @ExceptionHandler - fun ResolutionResultsServiceExceptionHandler(e: JpaObjectRetrievalFailureException): ResponseEntity<ErrorMessage> { - log.error(e.message, e) + fun resolutionResultsServiceExceptionHandler(e: EmptyResultDataAccessException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.RESOURCE_NOT_FOUND + return returnError(e, errorCode) + } - var errorCode = ErrorCode.RESOURCE_NOT_FOUND - val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg) - return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)) + @ExceptionHandler + fun resolutionResultsServiceExceptionHandler(e: JpaObjectRetrievalFailureException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.RESOURCE_NOT_FOUND + return returnError(e, errorCode) } @ExceptionHandler - fun ResolutionResultsServiceExceptionHandler(e: Exception): ResponseEntity<ErrorMessage> { + fun resolutionResultsServiceExceptionHandler(e: Exception): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.GENERIC_FAILURE + return returnError(e, errorCode) + } + + fun returnError(e: Exception, errorCode: ErrorCode): ResponseEntity<ErrorMessage> { log.error(e.message, e) - var errorCode = ErrorCode.GENERIC_FAILURE - val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg) - return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)) + val errorMessage = + ErrorMessage(errorCode.message(e.message!!), + errorCode.value, + debugMsg) + return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)!!) } @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)) } diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt new file mode 100644 index 000000000..de5843a66 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt @@ -0,0 +1,169 @@ +/* + * Copyright © 2019 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.resource.api + +import com.fasterxml.jackson.databind.JsonNode +import io.swagger.annotations.Api +import io.swagger.annotations.ApiOperation +import io.swagger.annotations.ApiParam +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 +import org.springframework.web.bind.annotation.* + +/** + * Exposes Template Resolution API to store and retrieve rendered template results. + * + * @author Serge Simard + * @version 1.0 + */ +@RestController +@RequestMapping("/api/v1/template") +@Api(value = "/api/v1/template", + description = "Interaction with resolved template.") +open class TemplateController(private val templateResolutionService: TemplateResolutionService) { + + @RequestMapping(path = ["/health-check"], + method = [RequestMethod.GET], + produces = [MediaType.APPLICATION_JSON_VALUE]) + @ResponseBody + @ApiOperation(value = "Health Check", hidden = true) + fun templateControllerHealthCheck(): JsonNode = runBlocking { + JacksonUtils.getJsonNode("Success") + } + + @RequestMapping(path = [""], + method = [RequestMethod.GET], + produces = [MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE]) + @ApiOperation(value = "Retrieve a resolved template.", + notes = "Retrieve a config template for a given CBA's action, identified by its blueprint name, blueprint version, " + + "artifact name and resolution key. An extra 'format' parameter can be passed to tell what content-type" + + " to expect in return") + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun get( + @ApiParam(value = "Name of the CBA.", required = true) + @RequestParam(value = "bpName") bpName: String, + @ApiParam(value = "Version of the CBA.", required = true) + @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 = 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 { + + 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) { + expectedContentType = "application/$expectedContentType" + } + val expectedMediaType: MediaType = MediaType.valueOf(expectedContentType) + + ResponseEntity.ok().contentType(expectedMediaType).body(result) + } + + + @PostMapping("/{bpName}/{bpVersion}/{artifactName}/{resolutionKey}", produces = [MediaType.APPLICATION_JSON_VALUE]) + @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 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, artifactName, result, resourceId = resourceId, resourceType = resourceType) + + ResponseEntity.ok().body(resultStored) + } +} diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandlerTest.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandlerTest.kt deleted file mode 100644 index 813c900d7..000000000 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandlerTest.kt +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright © 2019 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.resolutionresults.api - -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith -import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintCoreConfiguration -import org.onap.ccsdk.cds.controllerblueprints.core.deleteDir -import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService -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.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 java.util.* -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.assertTrue - -@RunWith(SpringRunner::class) -@WebFluxTest -@ContextConfiguration(classes = [ResolutionResultsServiceHandler::class, BluePrintCoreConfiguration::class, - BluePrintCatalogService::class, SecurityProperties::class]) -@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) -@TestPropertySource(locations = ["classpath:application-test.properties"]) -class ResolutionResultsServiceHandlerTest { - - private val log = LoggerFactory.getLogger(ResolutionResultsServiceHandlerTest::class.toString()) - - @Autowired - lateinit var blueprintCatalog: BluePrintCatalogService - @Autowired - lateinit var webTestClient: WebTestClient - - var resolutionKey = "7cafa9f3-bbc8-49ec-8f25-fcaa6ac3ff08" - val blueprintName = "baseconfiguration" - val blueprintVersion = "1.0.0" - val templatePrefix = "activate" - val payloadDummyTemplateData = "PAYLOAD DATA" - - @BeforeTest - fun init() { - runBlocking { - deleteDir("target", "blueprints") - blueprintCatalog.saveToDatabase(UUID.randomUUID().toString(), loadTestCbaFile()) - } - } - - @AfterTest - fun cleanDir() { - deleteDir("target", "blueprints") - } - - @Test - fun `ping return Success`() { - runBlocking { - - webTestClient.get().uri("/api/v1/resolution-results/ping") - .exchange() - .expectStatus().isOk - .expectBody().equals("Success") - } - } - - @Test - fun `store-retrieve-delete result by path or UUID`() { - runBlocking { - createRetrieveDelete() - } - } - - @Test - fun `get returns requested JSON content-type`() { - runBlocking { - createRetrieveDelete("json") - } - } - - @Test - fun `get returns requested XML content-type`() { - runBlocking { - createRetrieveDelete("xml") - } - } - - private fun createRetrieveDelete(expectedType : String? = null): WebTestClient.ResponseSpec { - var uuid = "MISSING" - - // Store new result for blueprint/artifact/resolutionkey - webTestClient - .post() - .uri("/api/v1/resolution-results/$blueprintName/$blueprintVersion/$templatePrefix/$resolutionKey/") - .body(BodyInserters.fromObject(payloadDummyTemplateData)) - .exchange() - .expectStatus().is2xxSuccessful - .expectBody() - .consumeWith { - uuid = String(it.responseBody) - log.info("Stored result under UUID $uuid") - } - // Retrieve same payload - var requestArguments = "bpName=$blueprintName&bpVersion=$blueprintVersion" + - "&artifactName=$templatePrefix&resolutionKey=$resolutionKey" - if (expectedType != null) { - requestArguments = "$requestArguments&format=$expectedType" - webTestClient - .get() - .uri("/api/v1/resolution-results/?$requestArguments") - .exchange() - .expectStatus().is2xxSuccessful - .expectHeader().contentType(MediaType.valueOf("application/$expectedType")) - .expectBody().equals(payloadDummyTemplateData) - } else { - webTestClient - .get() - .uri("/api/v1/resolution-results/?$requestArguments") - .exchange() - .expectStatus().is2xxSuccessful - .expectHeader().contentType(MediaType.TEXT_PLAIN) - .expectBody().equals(payloadDummyTemplateData) - } - // And delete by UUID - return webTestClient - .delete() - .uri("/api/v1/resolution-results/$uuid/") - .exchange() - .expectStatus().is2xxSuccessful - } - - /* - * Error cases - */ - @Test - fun `get returns 400 error if missing arg`() { - runBlocking { - val arguments = "bpBADName=$blueprintName" + - "&bpBADVersion=$blueprintVersion" + - "&artifactName=$templatePrefix" + - "&resolutionKey=$resolutionKey" - - webTestClient.get().uri("/api/v1/resolution-results/?$arguments") - .exchange() - .expectStatus().isBadRequest - } - } - - @Test - fun `get returns 503 error if Blueprint not found`() { - runBlocking { - val arguments = "bpName=BAD_BP_NAME" + - "&bpVersion=BAD_BP_VERSION" + - "&artifactName=$templatePrefix" + - "&resolutionKey=$resolutionKey" - - webTestClient.get().uri("/api/v1/resolution-results/?$arguments") - .exchange() - .expectStatus().isEqualTo(HttpStatus.SERVICE_UNAVAILABLE) - } - } - - @Test - fun `get returns 404 if entry not found`() { - runBlocking { - - webTestClient - .get() - .uri("/api/v1/resolution-results/?bpName=$blueprintName&bpVersion=$blueprintVersion" + - "&artifactName=$templatePrefix&resolutionKey=$resolutionKey") - .exchange() - .expectStatus().isNotFound - } - } - - @Test - fun `get returns 404 if UUID not found`() { - runBlocking { - - webTestClient - .get() - .uri("/api/v1/resolution-results/234234234234/") - .exchange() - .expectStatus().isNotFound - } - } - - private fun loadTestCbaFile(): File { - val testCbaFile = Paths.get("./src/test/resources/test-cba.zip").toFile() - assertTrue(testCbaFile.exists(), "couldn't get file ${testCbaFile.absolutePath}") - return testCbaFile - } -}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceControllerTest.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceControllerTest.kt index fa8bf4459..85ac7bddd 100644 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResourceControllerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceControllerTest.kt @@ -14,44 +14,28 @@ * limitations under the License. */ -package org.onap.ccsdk.cds.blueprintsprocessor.resolutionresults.api +package org.onap.ccsdk.cds.blueprintsprocessor.resource.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 java.lang.reflect.Array +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants @RunWith(SpringRunner::class) @@ -75,7 +59,7 @@ class ResourceControllerTest { @Test fun `ping return Success`() { runBlocking { - webTestClient.get().uri("/api/v1/resources/ping") + webTestClient.get().uri("/api/v1/resources/health-check") .exchange() .expectStatus().isOk .expectBody() @@ -231,7 +215,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 } diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateControllerTest.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateControllerTest.kt new file mode 100644 index 000000000..c3a718e11 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateControllerTest.kt @@ -0,0 +1,175 @@ +/* + * Copyright © 2019 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.resource.api + +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintCoreConfiguration +import org.onap.ccsdk.cds.controllerblueprints.core.deleteDir +import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService +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.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 kotlin.test.AfterTest + +@RunWith(SpringRunner::class) +@WebFluxTest +@ContextConfiguration(classes = [BluePrintCoreConfiguration::class, + BluePrintCatalogService::class, SecurityProperties::class]) +@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) +@TestPropertySource(locations = ["classpath:application-test.properties"]) +class TemplateControllerTest { + + private val log = LoggerFactory.getLogger(TemplateControllerTest::class.toString()) + + @Autowired + lateinit var webTestClient: WebTestClient + + var resolutionKey = "7cafa9f3-bbc8-49ec-8f25-fcaa6ac3ff08" + val blueprintName = "baseconfiguration" + val blueprintVersion = "1.0.0" + val templatePrefix = "activate" + val payloadDummyTemplateData = "PAYLOAD DATA" + + var requestArguments = "bpName=$blueprintName&bpVersion=$blueprintVersion" + + "&artifactName=$templatePrefix&resolutionKey=$resolutionKey" + + @AfterTest + fun cleanDir() { + deleteDir("target", "blueprints") + } + + @Test + fun `ping return Success`() { + runBlocking { + webTestClient.get().uri("/api/v1/template/health-check") + .exchange() + .expectStatus().isOk + .expectBody() + .equals("Success") + } + } + + @Test + fun `store same value and tries to retrieve - duplicate entry execption`() { + runBlocking { + + resolutionKey = "1" + + post(resolutionKey) + post(resolutionKey) + + webTestClient + .get() + .uri("/api/v1/template?$requestArguments") + .exchange() + .expectStatus().is4xxClientError + .expectBody().equals(payloadDummyTemplateData) + } + } + + @Test + fun `get returns requested JSON content-type`() { + runBlocking { + resolutionKey = "2" + post(resolutionKey) + get("json", resolutionKey) + } + } + + @Test + fun `get returns requested XML content-type`() { + runBlocking { + resolutionKey = "3" + post(resolutionKey) + get("xml", resolutionKey) + } + } + + @Test + fun `get returns 400 error if missing arg`() { + runBlocking { + val arguments = "bpBADName=$blueprintName" + + "&bpBADVersion=$blueprintVersion" + + "&artifactName=$templatePrefix" + + "&resolutionKey=$resolutionKey" + + webTestClient.get().uri("/api/v1/template?$arguments") + .exchange() + .expectStatus().isBadRequest + } + } + + @Test + fun `get returns 404 if entry not found`() { + runBlocking { + + webTestClient + .get() + .uri("/api/v1/template?bpName=$blueprintName&bpVersion=$blueprintVersion" + + "&artifactName=$templatePrefix&resolutionKey=notFound") + .exchange() + .expectStatus().isNotFound + } + } + + private fun post(resKey: String) { + webTestClient + .post() + .uri("/api/v1/template/$blueprintName/$blueprintVersion/$templatePrefix/$resKey") + .body(BodyInserters.fromObject(payloadDummyTemplateData)) + .exchange() + .expectStatus().is2xxSuccessful + .expectBody() + .consumeWith { + log.info("Stored result under UUID ${it.responseBody}") + } + } + + private fun get(expectedType: String, resKey: String) { + var requestArguments = "bpName=$blueprintName&bpVersion=$blueprintVersion" + + "&artifactName=$templatePrefix&resolutionKey=$resKey" + + if (expectedType.isNotEmpty()) { + requestArguments = "$requestArguments&format=$expectedType" + webTestClient + .get() + .uri("/api/v1/template?$requestArguments") + .exchange() + .expectStatus().is2xxSuccessful + .expectHeader().contentType(MediaType.valueOf("application/$expectedType")) + .expectBody().equals(payloadDummyTemplateData) + } else { + webTestClient + .get() + .uri("/api/v1/template?$requestArguments") + .exchange() + .expectStatus().is2xxSuccessful + .expectHeader().contentType(MediaType.TEXT_PLAIN) + .expectBody().equals(payloadDummyTemplateData) + } + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/pom.xml b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/pom.xml index 340f2c618..ece1b0ac2 100755 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/pom.xml +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/pom.xml @@ -42,6 +42,7 @@ <artifactId>proto-definition</artifactId> <version>${project.version}</version> </dependency> + <dependency> <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId> <artifactId>blueprint-core</artifactId> @@ -59,6 +60,35 @@ <artifactId>h2</artifactId> <scope>test</scope> </dependency> - </dependencies> + <!-- For Message libraries --> + <dependency> + <groupId>org.onap.ccsdk.cds.blueprintsprocessor</groupId> + <artifactId>message-lib</artifactId> + </dependency> + + <!-- For spring-kafka --> + <dependency> + <groupId>org.springframework.kafka</groupId> + <artifactId>spring-kafka</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.kafka</groupId> + <artifactId>spring-kafka-test</artifactId> + <scope>test</scope> + </dependency> + + <!-- Apache Kafka --> + <dependency> + <groupId>org.apache.kafka</groupId> + <artifactId>kafka_2.11</artifactId> + <version>${kafka.version}</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + </exclusions> + </dependency> + </dependencies> </project> diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt index eff977348..60016fb98 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt @@ -17,15 +17,17 @@ package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api +import com.fasterxml.jackson.databind.JsonNode +import io.swagger.annotations.Api import io.swagger.annotations.ApiOperation +import io.swagger.annotations.ApiParam import kotlinx.coroutines.runBlocking import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ACTION_MODE_ASYNC import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput import org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api.utils.determineHttpStatusCode -import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.http.codec.multipart.FilePart @@ -34,43 +36,59 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping("/api/v1/execution-service") +@Api(value = "/api/v1/execution-service", + description = "Interaction with CBA.") open class ExecutionServiceController { @Autowired lateinit var executionServiceHandler: ExecutionServiceHandler - @RequestMapping(path = ["/ping"], method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE]) + @RequestMapping(path = ["/health-check"], + method = [RequestMethod.GET], + produces = [MediaType.APPLICATION_JSON_VALUE]) @ResponseBody - fun ping(): String = runBlocking { - "Success" + @ApiOperation(value = "Health Check", hidden = true) + fun executionServiceControllerHealthCheck(): JsonNode = runBlocking { + JacksonUtils.getJsonNode("Success") } @PostMapping(path = ["/upload"], consumes = [MediaType.MULTIPART_FORM_DATA_VALUE]) - @ApiOperation(value = "Upload CBA", notes = "Takes a File and load it in the runtime database") @ResponseBody @PreAuthorize("hasRole('USER')") - fun upload(@RequestPart("file") filePart: FilePart): String = runBlocking { - executionServiceHandler.upload(filePart) + @ApiOperation(value = "Upload a CBA", + notes = "Upload the CBA package. This will also run validation on the CBA.", + produces = MediaType.APPLICATION_JSON_VALUE) + fun upload(@ApiParam(value = "The ZIP file containing the overall CBA package.", required = true) + @RequestPart("file") filePart: FilePart): JsonNode = runBlocking { + JacksonUtils.getJsonNode(executionServiceHandler.upload(filePart)) } @DeleteMapping("/name/{name}/version/{version}") - @Throws(BluePrintException::class) + @ApiOperation(value = "Delete a CBA", + notes = "Delete the CBA package identified by its name and version.", + produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasRole('USER')") - fun deleteBlueprint(@PathVariable(value = "name") name: String, + fun deleteBlueprint(@ApiParam(value = "Name of the CBA.", required = true) + @PathVariable(value = "name") name: String, + @ApiParam(value = "Version of the CBA.", required = true) @PathVariable(value = "version") version: String) = runBlocking { executionServiceHandler.remove(name, version) } @RequestMapping(path = ["/process"], method = [RequestMethod.POST], produces = [MediaType.APPLICATION_JSON_VALUE]) - @ApiOperation(value = "Resolve Resource Mappings", - notes = "Takes the blueprint information and process as per the payload") + @ApiOperation(value = "Execute a CBA workflow (action)", + notes = "Execute the appropriate CBA's action based on the ExecutionServiceInput object passed as input.", + produces = MediaType.APPLICATION_JSON_VALUE, + response = ExecutionServiceOutput::class) @ResponseBody @PreAuthorize("hasRole('USER')") - fun process(@RequestBody executionServiceInput: ExecutionServiceInput): ResponseEntity<ExecutionServiceOutput> = runBlocking { - if (executionServiceInput.actionIdentifiers.mode == ACTION_MODE_ASYNC) { - throw IllegalStateException("Can't process async request through the REST endpoint. Use gRPC for async processing.") + fun process(@ApiParam(value = "ExecutionServiceInput payload.", required = true) + @RequestBody executionServiceInput: ExecutionServiceInput): ResponseEntity<ExecutionServiceOutput> = + runBlocking { + if (executionServiceInput.actionIdentifiers.mode == ACTION_MODE_ASYNC) { + throw IllegalStateException("Can't process async request through the REST endpoint. Use gRPC for async processing.") + } + val processResult = executionServiceHandler.doProcess(executionServiceInput) + ResponseEntity(processResult, determineHttpStatusCode(processResult.status.code)) } - val processResult = executionServiceHandler.doProcess(executionServiceInput) - ResponseEntity(processResult, determineHttpStatusCode(processResult.status.code)) - } } diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt new file mode 100644 index 000000000..a04a79921 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt @@ -0,0 +1,47 @@ +package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api + +import org.apache.kafka.clients.CommonClientConfigs +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.kafka.annotation.EnableKafka +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory +import org.springframework.kafka.core.ConsumerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.support.serializer.JsonDeserializer + +@Configuration +open class MessagingConfig { + + @Value("\${blueprintsprocessor.messageclient.self-service-api.groupId}") + lateinit var groupId: String + + @Value("\${blueprintsprocessor.messageclient.self-service-api.bootstrapServers}") + lateinit var bootstrapServers: String + + open fun consumerFactory(): ConsumerFactory<String, ExecutionServiceInput>? { + val configProperties = hashMapOf<String, Any>() + configProperties[CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + configProperties[ConsumerConfig.GROUP_ID_CONFIG] = groupId + configProperties[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java.name + configProperties[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java.name + configProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + + return DefaultKafkaConsumerFactory(configProperties, StringDeserializer(), JsonDeserializer(ExecutionServiceInput::class.java)) + } + + /** + * Creation of a Kafka MessageListener Container + * + * @return KafkaListener instance. + */ + @Bean + open fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, ExecutionServiceInput> { + val factory = ConcurrentKafkaListenerContainerFactory<String, ExecutionServiceInput>() + factory.consumerFactory = consumerFactory() + return factory + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt new file mode 100644 index 000000000..1d219a83e --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt @@ -0,0 +1,74 @@ +/* + * Copyright © 2019 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.selfservice.api + +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking +import org.apache.commons.lang3.builder.ToStringBuilder +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BluePrintMessageLibPropertyService +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.kafka.annotation.KafkaListener +import org.springframework.stereotype.Service + +@ConditionalOnProperty(name = ["blueprintsprocessor.messageclient.self-service-api.kafkaEnable"], havingValue = "true") +@Service +open class MessagingController(private val propertyService: BluePrintMessageLibPropertyService, + private val executionServiceHandler: ExecutionServiceHandler) { + + private val log = LoggerFactory.getLogger(MessagingController::class.java)!! + + companion object { + // TODO PREFIX should be retrieved from model or from request. + const val PREFIX = "self-service-api" + const val EXECUTION_STATUS = 200 + } + + @KafkaListener(topics = ["\${blueprintsprocessor.messageclient.self-service-api.consumerTopic}"]) + open fun receive(input: ExecutionServiceInput) { + + log.info("Successfully received a message: {}", ToStringBuilder.reflectionToString(input)) + + runBlocking { + log.info("Successfully received a message: {}", ToStringBuilder.reflectionToString(input)) + + // Process the message. + async { + processMessage(input) + } + } + } + + private suspend fun processMessage(executionServiceInput: ExecutionServiceInput) { + + val executionServiceOutput = executionServiceHandler.doProcess(executionServiceInput) + + if (executionServiceOutput.status.code == EXECUTION_STATUS) { + val bluePrintMessageClientService = propertyService + .blueprintMessageClientService(PREFIX) + + val payload = executionServiceOutput.payload + + log.info("The payload to publish is {}", payload) + + bluePrintMessageClientService.sendMessage(payload) + } + else { + log.error("Fail to process the given event due to {}", executionServiceOutput.status.errorMessage) + } + } +} diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintManagementGRPCHandlerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintManagementGRPCHandlerTest.kt index fd764d78f..e084c60cf 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintManagementGRPCHandlerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintManagementGRPCHandlerTest.kt @@ -23,6 +23,8 @@ import io.grpc.testing.GrpcServerRule import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api.messaginglib.MessagingControllerTest +import org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api.messaginglib.ProducerConfiguration import org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader import org.onap.ccsdk.cds.controllerblueprints.core.deleteDir import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile @@ -33,6 +35,7 @@ import org.onap.ccsdk.cds.controllerblueprints.management.api.FileChunk import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.FilterType import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit4.SpringRunner @@ -44,7 +47,9 @@ import kotlin.test.assertTrue @RunWith(SpringRunner::class) @EnableAutoConfiguration @DirtiesContext -@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) +@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"], + excludeFilters = [ComponentScan.Filter(value = [MessagingConfig::class, MessagingController::class, ProducerConfiguration::class, + MessagingControllerTest.ConsumerConfiguration::class, MessagingControllerTest::class], type = FilterType.ASSIGNABLE_TYPE)]) @TestPropertySource(locations = ["classpath:application-test.properties"]) class BluePrintManagementGRPCHandlerTest { diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingGRPCHandlerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingGRPCHandlerTest.kt index a84bf5b5f..ce5acd400 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingGRPCHandlerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingGRPCHandlerTest.kt @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.FilterType import org.springframework.test.annotation.DirtiesContext import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit4.SpringRunner @@ -43,7 +44,8 @@ import kotlin.test.BeforeTest @RunWith(SpringRunner::class) @DirtiesContext @EnableAutoConfiguration -@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) +@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"], + excludeFilters =arrayOf(ComponentScan.Filter(value = [(MessagingController::class)], type = FilterType.ASSIGNABLE_TYPE))) @TestPropertySource(locations = ["classpath:application-test.properties"]) class BluePrintProcessingGRPCHandlerTest { private val log = LoggerFactory.getLogger(BluePrintProcessingGRPCHandlerTest::class.java) diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt index 9cbd898dc..65b41262b 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt @@ -30,6 +30,7 @@ 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.context.annotation.FilterType import org.springframework.core.io.ByteArrayResource import org.springframework.http.client.MultipartBodyBuilder import org.springframework.test.context.ContextConfiguration @@ -49,7 +50,8 @@ import kotlin.test.assertTrue @RunWith(SpringRunner::class) @WebFluxTest @ContextConfiguration(classes = [ExecutionServiceHandler::class, BluePrintCoreConfiguration::class, BluePrintCatalogService::class, SecurityProperties::class]) -@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) +@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"], + excludeFilters =arrayOf(ComponentScan.Filter(value = [(MessagingController::class)], type = FilterType.ASSIGNABLE_TYPE))) @TestPropertySource(locations = ["classpath:application-test.properties"]) class ExecutionServiceHandlerTest { diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt new file mode 100644 index 000000000..f7459f522 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt @@ -0,0 +1,211 @@ +/* + * Copyright © 2019 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.selfservice.api.messaginglib + +import com.fasterxml.jackson.databind.node.ObjectNode +import kotlinx.coroutines.reactive.awaitSingle +import kotlinx.coroutines.runBlocking +import org.apache.commons.lang.builder.ToStringBuilder +import org.apache.kafka.clients.CommonClientConfigs +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ActionIdentifiers +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.CommonHeader +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData +import org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api.MessagingController +import org.onap.ccsdk.cds.controllerblueprints.core.deleteDir +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.autoconfigure.security.SecurityProperties +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration +import org.springframework.core.io.ByteArrayResource +import org.springframework.http.client.MultipartBodyBuilder +import org.springframework.kafka.annotation.EnableKafka +import org.springframework.kafka.annotation.KafkaListener +import org.springframework.kafka.annotation.PartitionOffset +import org.springframework.kafka.annotation.TopicPartition +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory +import org.springframework.kafka.core.ConsumerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.support.serializer.JsonDeserializer +import org.springframework.kafka.test.context.EmbeddedKafka +import org.springframework.test.annotation.DirtiesContext +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.test.web.reactive.server.returnResult +import org.springframework.web.reactive.function.BodyInserters +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import kotlin.test.assertNotNull + +@RunWith(SpringRunner::class) +@EnableAutoConfiguration +@ContextConfiguration(classes = [MessagingControllerTest::class, SecurityProperties::class]) +@ComponentScan(basePackages = ["org.onap.ccsdk.cds.blueprintsprocessor", "org.onap.ccsdk.cds.controllerblueprints"]) +@TestPropertySource(locations = ["classpath:application-test.properties"]) +@DirtiesContext +@EmbeddedKafka(ports = [9092]) +@WebFluxTest +class MessagingControllerTest { + + private val log = LoggerFactory.getLogger(MessagingControllerTest::class.java)!! + + @Autowired + lateinit var controller: MessagingController + + @Value("\${blueprintsprocessor.messageclient.self-service-api.consumerTopic}") + lateinit var topicUsedForConsumer: String + + @Autowired + lateinit var kt: KafkaTemplate<String, ExecutionServiceInput> + + @Autowired + lateinit var webTestClient: WebTestClient + + var receivedEvent: String? = null + + @Before + fun setup() { + deleteDir("target", "blueprints") + uploadBluePrint() + } + + @After + fun clean() { + deleteDir("target", "blueprints") + } + + @Test + fun testReceive() { + val samplePayload = "{\n" + + " \"resource-assignment-request\": {\n" + + " \"artifact-name\": [\"hostname\"],\n" + + " \"store-result\": true,\n" + + " \"resource-assignment-properties\" : {\n" + + " \"hostname\": \"demo123\"\n" + + " }\n" + + " }\n" + + " }" + + kt.defaultTopic = topicUsedForConsumer + + val input = ExecutionServiceInput().apply { + commonHeader = CommonHeader().apply { + originatorId = "1" + requestId = "1234" + subRequestId = "1234-1234" + } + + actionIdentifiers = ActionIdentifiers().apply { + blueprintName = "golden" + blueprintVersion = "1.0.0" + actionName = "resource-assignment" + mode = "sync" + } + + stepData = StepData().apply { + name = "resource-assignment" + } + + payload = JacksonUtils.jsonNode(samplePayload) as ObjectNode + } + + kt.sendDefault(input) + log.info("test-sender sent message='{}'", ToStringBuilder.reflectionToString(input)) + + Thread.sleep(1000) + } + + @KafkaListener(topicPartitions = [TopicPartition(topic = "\${blueprintsprocessor.messageclient.self-service-api.topic}", partitionOffsets = [PartitionOffset(partition = "0", initialOffset = "0")])]) + fun receivedEventFromBluePrintProducer(event: ExecutionServiceInput) { + assertNotNull(event) + } + + private fun uploadBluePrint() { + runBlocking { + val body = MultipartBodyBuilder().apply { + part("file", object : ByteArrayResource(Files.readAllBytes(loadCbaArchive().toPath())) { + override fun getFilename(): String { + return "test-cba.zip" + } + }) + }.build() + + webTestClient + .post() + .uri("/api/v1/execution-service/upload") + .body(BodyInserters.fromMultipartData(body)) + .exchange() + .expectStatus().isOk + .returnResult<String>() + .responseBody + .awaitSingle() + } + } + + private fun loadCbaArchive():File { + return Paths.get("./src/test/resources/cba-for-kafka-integration.zip").toFile() + } + + @Configuration + @EnableKafka + open class ConsumerConfiguration { + + @Value("\${blueprintsprocessor.messageclient.self-service-api.bootstrapServers}") + lateinit var bootstrapServers: String + + @Value("\${blueprintsprocessor.messageclient.self-service-api.groupId}") + lateinit var groupId:String + + @Bean + open fun consumerFactory2(): ConsumerFactory<String, ExecutionServiceInput>? { + val configProperties = hashMapOf<String, Any>() + configProperties[CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + configProperties[ConsumerConfig.GROUP_ID_CONFIG] = groupId + configProperties[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java.name + configProperties[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java.name + configProperties[ConsumerConfig.AUTO_OFFSET_RESET_CONFIG] = "earliest" + configProperties[ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG] = 1000 + + return DefaultKafkaConsumerFactory(configProperties, StringDeserializer(), + JsonDeserializer(ExecutionServiceInput::class.java)) + } + + @Bean + open fun listenerFactory(): ConcurrentKafkaListenerContainerFactory<String, ExecutionServiceInput> { + val factory = ConcurrentKafkaListenerContainerFactory<String, ExecutionServiceInput>() + factory.consumerFactory = consumerFactory2() + return factory + } + } +} + + diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/ProducerConfiguration.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/ProducerConfiguration.kt new file mode 100644 index 000000000..dc1f38a63 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/ProducerConfiguration.kt @@ -0,0 +1,48 @@ +/* + * Copyright © 2019 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.selfservice.api.messaginglib + +import org.apache.kafka.clients.producer.ProducerConfig +import org.apache.kafka.common.serialization.StringSerializer +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.kafka.annotation.EnableKafka +import org.springframework.kafka.core.DefaultKafkaProducerFactory +import org.springframework.kafka.core.KafkaTemplate +import org.springframework.kafka.core.ProducerFactory +import org.springframework.kafka.support.serializer.JsonSerializer + +@Configuration +open class ProducerConfiguration { + + @Value("\${blueprintsprocessor.messageclient.self-service-api.bootstrapServers}") + lateinit var bootstrapServers: String + + open fun kpf(): ProducerFactory<String, ExecutionServiceInput> { + val configs = HashMap<String, Any>() + configs[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers + configs[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java + configs[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java + return DefaultKafkaProducerFactory(configs) + } + + @Bean + open fun kt(): KafkaTemplate<String, ExecutionServiceInput> { + return KafkaTemplate<String, ExecutionServiceInput>(kpf()) + } +}
\ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/application-test.properties b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/application-test.properties index 6705523df..d532b1582 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/application-test.properties +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/application-test.properties @@ -31,3 +31,12 @@ blueprintsprocessor.blueprintArchivePath=./target/blueprints/archive # Python executor blueprints.processor.functions.python.executor.executionPath=./../../../../components/scripts/python/ccsdk_blueprints blueprints.processor.functions.python.executor.modulePaths=./../../../../components/scripts/python/ccsdk_blueprints + +# Kafka-message-lib Configuration +blueprintsprocessor.messageclient.self-service-api.kafkaEnable=true +blueprintsprocessor.messageclient.self-service-api.topic=producer.t +blueprintsprocessor.messageclient.self-service-api.type=kafka-basic-auth +blueprintsprocessor.messageclient.self-service-api.bootstrapServers=127.0.0.1:9092 +blueprintsprocessor.messageclient.self-service-api.consumerTopic=receiver.t +blueprintsprocessor.messageclient.self-service-api.groupId=receiver-id +blueprintsprocessor.messageclient.self-service-api.clientId=default-client-id diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip Binary files differnew file mode 100644 index 000000000..23070380c --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip diff --git a/ms/blueprintsprocessor/parent/pom.xml b/ms/blueprintsprocessor/parent/pom.xml index b1d288979..686be49fd 100755 --- a/ms/blueprintsprocessor/parent/pom.xml +++ b/ms/blueprintsprocessor/parent/pom.xml @@ -14,7 +14,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.onap.ccsdk.cds</groupId> @@ -43,7 +44,9 @@ <sli.version>${ccsdk.sli.core.version}</sli.version> <guava.version>27.0.1-jre</guava.version> <jython.version>2.7.1</jython.version> - <springfox.swagger2.version>2.9.2</springfox.swagger2.version> + <!-- Should be using released artifact as soon as available:--> + <!-- https://github.com/springfox/springfox/milestone/44--> + <springfox.swagger2.version>3.0.0-SNAPSHOT</springfox.swagger2.version> <h2database.version>1.4.197</h2database.version> <onap.logger.slf4j>1.2.2</onap.logger.slf4j> <powermock.version>1.7.4</powermock.version> @@ -86,6 +89,11 @@ <artifactId>springfox-swagger-ui</artifactId> <version>${springfox.swagger2.version}</version> </dependency> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-spring-webflux</artifactId> + <version>${springfox.swagger2.version}</version> + </dependency> <!-- Common Utils Dependencies --> <dependency> @@ -477,6 +485,13 @@ <scope>test</scope> </dependency> + <!-- message-lib dependency --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>message-lib</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> </dependencyManagement> @@ -523,6 +538,10 @@ </dependency> <dependency> <groupId>io.springfox</groupId> + <artifactId>springfox-spring-webflux</artifactId> + </dependency> + <dependency> + <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <dependency> @@ -584,6 +603,14 @@ </dependency> </dependencies> + <repositories> + <repository> + <id>spring-libs-milestone</id> + <name>Spring Milestone Maven Repository</name> + <url>http://oss.jfrog.org/artifactory/oss-snapshot-local/</url> + </repository> + </repositories> + <build> <plugins> <plugin> diff --git a/ms/command-executor/src/main/python/command_executor_handler.py b/ms/command-executor/src/main/python/command_executor_handler.py index 365c00188..305c83e17 100644 --- a/ms/command-executor/src/main/python/command_executor_handler.py +++ b/ms/command-executor/src/main/python/command_executor_handler.py @@ -163,9 +163,16 @@ class CommandExecutorHandler(): def activate_venv(self): self.logger.info("{} - Activate Python Virtual Environment".format(self.blueprint_id)) + # Fix: The python generated activate_this.py script concatenates the env bin dir to PATH on every call + # eventually this process PATH variable was so big (128Kb) that no child process could be spawn + # This script will remove all duplicates; while keeping the order of the PATH folders + fixpathenvvar = "os.environ['PATH']=os.pathsep.join(list(dict.fromkeys(os.environ['PATH'].split(':'))))" + path = "%s/bin/activate_this.py" % self.venv_home try: exec (open(path).read(), {'__file__': path}) + exec (fixpathenvvar) + self.logger.info("Running with PATH : {}".format(os.environ['PATH'])) return True except Exception as err: self.logger.info( diff --git a/ms/command-executor/src/main/python/command_executor_server.py b/ms/command-executor/src/main/python/command_executor_server.py index 3596a0ec5..6266141d9 100644 --- a/ms/command-executor/src/main/python/command_executor_server.py +++ b/ms/command-executor/src/main/python/command_executor_server.py @@ -53,5 +53,5 @@ class CommandExecutorServer(CommandExecutor_pb2_grpc.CommandExecutorServiceServi if not handler.execute_command(request, results): self.logger.info("{} - Failed to executeCommand. {}".format(blueprint_id, results)) return utils.build_response(request, results, False) - self.logger.info("{} - Execute command logs: {}".format(blueprint_id, results)) + self.logger.info("{} - Execution finished successfully.".format(blueprint_id)) return utils.build_response(request, results) diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BlueprintErrorCode.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BlueprintErrorCode.kt index 79ef395e3..51d088ee1 100644 --- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BlueprintErrorCode.kt +++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BlueprintErrorCode.kt @@ -76,6 +76,11 @@ enum class ErrorCode (val value: Int, val httpCode: Int) { override fun message(detailMsg: String): String { return "Duplicated entry while saving Blueprint. Details : {$detailMsg}" } + }, + DUPLICATE_DATA(11, 409) { + override fun message(detailMsg: String): String { + return "Duplicated data - was expecting one result, got more than one. Details : {$detailMsg}" + } }; abstract fun message(detailMsg: String): String |