diff options
Diffstat (limited to 'ms/blueprintsprocessor/modules/inbounds')
21 files changed, 964 insertions, 549 deletions
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 |