Merge "Create REST API layer for resource resolution stored configlet"
authorDan Timoney <dtimoney@att.com>
Tue, 18 Jun 2019 15:16:39 +0000 (15:16 +0000)
committerGerrit Code Review <gerrit@onap.org>
Tue, 18 Jun 2019 15:16:39 +0000 (15:16 +0000)
ms/blueprintsprocessor/application/src/main/java/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintProcessorApplication.java
ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/resource/resolution/db/ResourceResolutionResultService.kt
ms/blueprintsprocessor/modules/inbounds/resource-api/pom.xml
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceController.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceExceptionHandler.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandler.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandlerTest.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/application-test.properties [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/logback.xml [new file with mode: 0644]
ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/test-cba.zip [new file with mode: 0644]

index 85268c7..2b6f8bc 100644 (file)
@@ -34,6 +34,11 @@ import org.springframework.context.annotation.ComponentScan;
 public class BlueprintProcessorApplication {
 
     public static void main(String[] args) {
+
+        // This is required for ResolutionResultsServiceController.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");
+
         SpringApplication.run(BlueprintProcessorApplication.class, args);
     }
 }
index cbc68bb..3cb9dec 100644 (file)
@@ -29,6 +29,21 @@ import java.util.*
 @Service
 class ResourceResolutionResultService(private val resourceResolutionRepository: ResourceResolutionRepository) {
 
+    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) {
 
@@ -50,18 +65,15 @@ class ResourceResolutionResultService(private val resourceResolutionRepository:
         }
     }
 
-    suspend fun read(bluePrintRuntimeService: BluePrintRuntimeService<*>, artifactPrefix: String,
-                     resolutionKey: String): String = withContext(Dispatchers.IO) {
+    suspend fun readByKey(resolutionResultId: String): String = withContext(Dispatchers.IO) {
 
-        val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+        resourceResolutionRepository.getOne(resolutionResultId).result!!
+    }
 
-        val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]
-        val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]
+    suspend fun deleteByKey(resolutionResultId: String): Unit = withContext(Dispatchers.IO) {
 
-        resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
-                resolutionKey,
-                blueprintName,
-                blueprintVersion,
-                artifactPrefix).result!!
+        val row = resourceResolutionRepository.getOne(resolutionResultId)
+        resourceResolutionRepository.delete(row)
+        resourceResolutionRepository.flush()
     }
 }
\ No newline at end of file
index d5acf4f..5dbbc71 100644 (file)
     <name>Blueprints Processor Resource API</name>
     <description>Blueprints Processor Resource API</description>
 
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId>
+            <artifactId>blueprint-core</artifactId>
+        </dependency>
+        <!--dependency>
+            <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId>
+            <artifactId>db-resources</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId>
+            <artifactId>blueprint-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.ccsdk.cds.controllerblueprints</groupId>
+            <artifactId>blueprint-scripts</artifactId>
+        </dependency-->
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 </project>
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
new file mode 100644 (file)
index 0000000..61a9541
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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/ResolutionResultsServiceExceptionHandler.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceExceptionHandler.kt
new file mode 100644 (file)
index 0000000..69641c6
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonFormat
+import com.fasterxml.jackson.annotation.JsonInclude
+import com.fasterxml.jackson.annotation.JsonTypeInfo
+import com.fasterxml.jackson.annotation.JsonTypeName
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
+import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.orm.jpa.JpaObjectRetrievalFailureException
+import org.springframework.dao.EmptyResultDataAccessException
+import org.springframework.web.server.ServerWebInputException
+import org.springframework.web.bind.annotation.ExceptionHandler
+import org.springframework.web.bind.annotation.RestControllerAdvice
+import java.io.Serializable
+import java.util.*
+
+/**
+ * Handle exceptions in Resolution Results 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 {
+
+    private val log = LoggerFactory.getLogger(ResolutionResultsServiceExceptionHandler::class.toString())
+
+    private val debugMsg = "ResolutionResultsService_Error_Message"
+
+    @ExceptionHandler
+    fun ResolutionResultsServiceExceptionHandler(e: BluePrintProcessorException): ResponseEntity<ErrorMessage> {
+        log.error(e.message)
+        val errorCode = ErrorCode.BLUEPRINT_PATH_MISSING
+        val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg)
+        return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode))
+    }
+
+    @ExceptionHandler
+    fun ResolutionResultsServiceExceptionHandler(e: ServerWebInputException): ResponseEntity<ErrorMessage> {
+        log.error(e.message)
+        val errorCode = ErrorCode.INVALID_REQUEST_FORMAT
+        val errorMessage = ErrorMessage(errorCode.message(e.message!!), errorCode.value, debugMsg)
+        return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode))
+    }
+
+    @ExceptionHandler
+    fun ResolutionResultsServiceExceptionHandler(e: EmptyResultDataAccessException): ResponseEntity<ErrorMessage> {
+        log.error(e.message)
+        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> {
+        log.error(e.message)
+
+        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: Exception): 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))
+    }
+}
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonTypeName("errorMessage")
+@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
+class ErrorMessage(var message: String?, var code: Int?, var debugMessage: String?) : Serializable {
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
+    var timestamp = Date()
+}
\ 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/ResolutionResultsServiceHandler.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resolutionresults/api/ResolutionResultsServiceHandler.kt
new file mode 100644 (file)
index 0000000..1fb34d7
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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/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
new file mode 100644 (file)
index 0000000..813c900
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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/resources/application-test.properties b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/application-test.properties
new file mode 100644 (file)
index 0000000..ebd5dc8
--- /dev/null
@@ -0,0 +1,33 @@
+#\r
+# Copyright © 2017-2018 AT&T Intellectual Property.\r
+#\r
+# Modifications Copyright © 2019 IBM, Bell Canada.\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the "License");\r
+# you may not use this file except in compliance with the License.\r
+# You may obtain a copy of the License at\r
+#\r
+#     http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+# See the License for the specific language governing permissions and\r
+# limitations under the License.\r
+#\r
+blueprintsprocessor.db.primary.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1\r
+blueprintsprocessor.db.primary.username=sa\r
+blueprintsprocessor.db.primary.password=\r
+blueprintsprocessor.db.primary.driverClassName=org.h2.Driver\r
+blueprintsprocessor.db.primary.hibernateHbm2ddlAuto=create-drop\r
+blueprintsprocessor.db.primary.hibernateDDLAuto=update\r
+blueprintsprocessor.db.primary.hibernateNamingStrategy=org.hibernate.cfg.ImprovedNamingStrategy\r
+blueprintsprocessor.db.primary.hibernateDialect=org.hibernate.dialect.H2Dialect\r
+# Controller Blueprints Core Configuration\r
+blueprintsprocessor.blueprintDeployPath=./target/blueprints/deploy\r
+blueprintsprocessor.blueprintWorkingPath=./target/blueprints/work\r
+blueprintsprocessor.blueprintArchivePath=./target/blueprints/archive\r
+\r
+# Python executor\r
+blueprints.processor.functions.python.executor.executionPath=./../../../../components/scripts/python/ccsdk_blueprints\r
+blueprints.processor.functions.python.executor.modulePaths=./../../../../components/scripts/python/ccsdk_blueprints\r
diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/logback.xml b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/logback.xml
new file mode 100644 (file)
index 0000000..56b0774
--- /dev/null
@@ -0,0 +1,35 @@
+<!--\r
+  ~ Copyright © 2017-2018 AT&T Intellectual Property.\r
+  ~\r
+  ~ Licensed under the Apache License, Version 2.0 (the "License");\r
+  ~ you may not use this file except in compliance with the License.\r
+  ~ You may obtain a copy of the License at\r
+  ~\r
+  ~     http://www.apache.org/licenses/LICENSE-2.0\r
+  ~\r
+  ~ Unless required by applicable law or agreed to in writing, software\r
+  ~ distributed under the License is distributed on an "AS IS" BASIS,\r
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+  ~ See the License for the specific language governing permissions and\r
+  ~ limitations under the License.\r
+  -->\r
+\r
+<configuration>\r
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">\r
+        <!-- encoders are assigned the type\r
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\r
+        <encoder>\r
+            <pattern>%d{HH:mm:ss.SSS} %-5level %logger{100} - %msg%n</pattern>\r
+        </encoder>\r
+    </appender>\r
+\r
+\r
+    <logger name="org.springframework" level="warn"/>\r
+    <logger name="org.hibernate" level="info"/>\r
+    <logger name="org.onap.ccsdk.cds.blueprintsprocessor" level="info"/>\r
+\r
+    <root level="info">\r
+        <appender-ref ref="STDOUT"/>\r
+    </root>\r
+\r
+</configuration>\r
diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/test-cba.zip b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/test-cba.zip
new file mode 100644 (file)
index 0000000..785ec6c
Binary files /dev/null and b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/test/resources/test-cba.zip differ