Refactoring BP Code with ErrorCatalog
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / inbounds / designer-api / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / designer / api / BluePrintManagementGRPCHandler.kt
index b7badb5..7600979 100644 (file)
 
 package org.onap.ccsdk.cds.blueprintsprocessor.designer.api
 
-import io.grpc.StatusException
+import com.google.protobuf.ByteString
+import com.google.protobuf.util.JsonFormat
 import io.grpc.stub.StreamObserver
 import kotlinx.coroutines.runBlocking
+import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.handler.BluePrintModelHandler
 import org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader
 import org.onap.ccsdk.cds.controllerblueprints.common.api.Status
-import org.onap.ccsdk.cds.controllerblueprints.core.*
-import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
-import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
-import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
-import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonString
+import org.onap.ccsdk.cds.controllerblueprints.core.emptyTONull
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.currentTimestamp
-import org.onap.ccsdk.cds.controllerblueprints.management.api.*
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintBootstrapInput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintDownloadInput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementOutput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementServiceGrpc
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintRemoveInput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintUploadInput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.DownloadAction
+import org.onap.ccsdk.cds.controllerblueprints.management.api.FileChunk
+import org.onap.ccsdk.cds.controllerblueprints.management.api.RemoveAction
+import org.onap.ccsdk.cds.controllerblueprints.management.api.UploadAction
+import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes
+import org.onap.ccsdk.cds.error.catalog.core.GrpcErrorCodes
+import org.onap.ccsdk.cds.error.catalog.core.utils.errorMessageOrDefault
+import org.onap.ccsdk.cds.error.catalog.services.ErrorCatalogService
 import org.slf4j.LoggerFactory
 import org.springframework.security.access.prepost.PreAuthorize
 import org.springframework.stereotype.Service
-import java.io.File
-import java.util.*
 
+// TODO("Convert to coroutines handler")
 @Service
-open class BluePrintManagementGRPCHandler(private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
-                                          private val blueprintsProcessorCatalogService: BluePrintCatalogService)
-    : BluePrintManagementServiceGrpc.BluePrintManagementServiceImplBase() {
+open class BluePrintManagementGRPCHandler(
+    private val bluePrintModelHandler: BluePrintModelHandler,
+    private val errorCatalogService: ErrorCatalogService
+) :
+    BluePrintManagementServiceGrpc.BluePrintManagementServiceImplBase() {
 
     private val log = LoggerFactory.getLogger(BluePrintManagementGRPCHandler::class.java)
 
     @PreAuthorize("hasRole('USER')")
-    override fun uploadBlueprint(request: BluePrintUploadInput, responseObserver:
-    StreamObserver<BluePrintManagementOutput>) {
-        runBlocking {
+    override fun uploadBlueprint(
+        request: BluePrintUploadInput,
+        responseObserver: StreamObserver<BluePrintManagementOutput>
+    ) {
 
+        runBlocking {
+            // TODO("catch if request id is missing")
             log.info("request(${request.commonHeader.requestId})")
-            val uploadId = UUID.randomUUID().toString()
-            val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, uploadId)
-            val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, uploadId)
             try {
-                val cbaFile = normalizedFile(blueprintArchive, "cba.zip")
-
-                saveToDisk(request, cbaFile)
-
+                /** Get the file byte array */
+                val byteArray = request.fileChunk.chunk.toByteArray()
+                /** Get the Upload Action */
                 val uploadAction = request.actionIdentifiers?.actionName.emptyTONull()
-                        ?: UploadAction.DRAFT.toString()
+                    ?: UploadAction.DRAFT.toString()
 
                 when (uploadAction) {
                     UploadAction.DRAFT.toString() -> {
-                        val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(uploadId, cbaFile, false)
-                        responseObserver.onNext(successStatus("Successfully uploaded CBA($blueprintId)...",
-                                request.commonHeader))
+                        val blueprintModel = bluePrintModelHandler.upload(byteArray, false)
+                        responseObserver.onNext(successStatus(request.commonHeader, blueprintModel.asJsonString()))
                     }
                     UploadAction.PUBLISH.toString() -> {
-                        val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(uploadId, cbaFile, true)
-                        responseObserver.onNext(successStatus("Successfully uploaded CBA($blueprintId)...",
-                                request.commonHeader))
+                        val blueprintModel = bluePrintModelHandler.upload(byteArray, true)
+                        responseObserver.onNext(successStatus(request.commonHeader, blueprintModel.asJsonString()))
                     }
                     UploadAction.VALIDATE.toString() -> {
-                        //TODO("Not Implemented")
-                        responseObserver.onError(failStatus("Not Implemented",
-                                BluePrintProcessorException("Not Implemented")))
+                        // TODO("Not Implemented")
+                        responseObserver.onNext(
+                            failStatus(
+                                request.commonHeader,
+                                "Upload action($uploadAction) not implemented",
+                                BluePrintProcessorException("Not Implemented")
+                            )
+                        )
                     }
                     UploadAction.ENRICH.toString() -> {
-                        //TODO("Not Implemented")
-                        responseObserver.onError(failStatus("Not Implemented",
-                                BluePrintProcessorException("Not Implemented")))
+                        val enrichedByteArray = bluePrintModelHandler.enrichBlueprintFileSource(byteArray)
+                        responseObserver.onNext(outputWithFileBytes(request.commonHeader, enrichedByteArray))
+                    }
+                    else -> {
+                        responseObserver.onNext(
+                            failStatus(
+                                request.commonHeader,
+                                "Upload action($uploadAction) not implemented",
+                                BluePrintProcessorException("Not implemented")
+                            )
+                        )
                     }
                 }
+            } catch (e: Exception) {
+                responseObserver.onNext(
+                    failStatus(
+                        request.commonHeader,
+                        "request(${request.commonHeader.requestId}): Failed to upload CBA", e
+                    )
+                )
+            } finally {
                 responseObserver.onCompleted()
+            }
+        }
+    }
+
+    @PreAuthorize("hasRole('USER')")
+    override fun downloadBlueprint(
+        request: BluePrintDownloadInput,
+        responseObserver: StreamObserver<BluePrintManagementOutput>
+    ) {
+        runBlocking {
+            val blueprintName = request.actionIdentifiers.blueprintName
+            val blueprintVersion = request.actionIdentifiers.blueprintVersion
+            val blueprint = "blueprint $blueprintName:$blueprintVersion"
+
+            /** Get the Search Action */
+            val searchAction = request.actionIdentifiers?.actionName.emptyTONull()
+                ?: DownloadAction.SEARCH.toString()
+
+            log.info("request(${request.commonHeader.requestId}): Received download $blueprint")
+            try {
+                when (searchAction) {
+                    DownloadAction.SEARCH.toString() -> {
+                        val downloadByteArray = bluePrintModelHandler.download(blueprintName, blueprintVersion)
+                        responseObserver.onNext(outputWithFileBytes(request.commonHeader, downloadByteArray))
+                    }
+                    else -> {
+                        responseObserver.onNext(
+                            failStatus(
+                                request.commonHeader,
+                                "Search action($searchAction) not implemented",
+                                BluePrintProcessorException("Not implemented")
+                            )
+                        )
+                    }
+                }
             } catch (e: Exception) {
-                responseObserver.onError(failStatus("request(${request.commonHeader.requestId}): Failed to upload CBA", e))
+                responseObserver.onNext(
+                    failStatus(
+                        request.commonHeader,
+                        "request(${request.commonHeader.requestId}): Failed to delete $blueprint", e
+                    )
+                )
             } finally {
-                // Clean blueprint script cache
-                val cacheKey = BluePrintFileUtils
-                        .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, uploadId))
-                BluePrintCompileCache.cleanClassLoader(cacheKey)
-                deleteNBDir(blueprintArchive)
-                deleteNBDir(blueprintWorking)
+                responseObserver.onCompleted()
             }
         }
     }
 
     @PreAuthorize("hasRole('USER')")
-    override fun removeBlueprint(request: BluePrintRemoveInput, responseObserver:
-    StreamObserver<BluePrintManagementOutput>) {
+    override fun removeBlueprint(
+        request: BluePrintRemoveInput,
+        responseObserver:
+        StreamObserver<BluePrintManagementOutput>
+    ) {
 
         runBlocking {
-            val blueprintName = request.blueprintName
-            val blueprintVersion = request.blueprintVersion
+            val blueprintName = request.actionIdentifiers.blueprintName
+            val blueprintVersion = request.actionIdentifiers.blueprintVersion
             val blueprint = "blueprint $blueprintName:$blueprintVersion"
 
             log.info("request(${request.commonHeader.requestId}): Received delete $blueprint")
 
+            /** Get the Remove Action */
+            val removeAction = request.actionIdentifiers?.actionName.emptyTONull()
+                ?: RemoveAction.DEFAULT.toString()
 
             try {
-                blueprintsProcessorCatalogService.deleteFromDatabase(blueprintName, blueprintVersion)
-                responseObserver.onNext(successStatus("Successfully deleted $blueprint", request.commonHeader))
-                responseObserver.onCompleted()
+                when (removeAction) {
+                    RemoveAction.DEFAULT.toString() -> {
+                        bluePrintModelHandler.deleteBlueprintModel(blueprintName, blueprintVersion)
+                        responseObserver.onNext(successStatus(request.commonHeader))
+                    }
+                    else -> {
+                        responseObserver.onNext(
+                            failStatus(
+                                request.commonHeader,
+                                "Remove action($removeAction) not implemented",
+                                BluePrintProcessorException("Not implemented")
+                            )
+                        )
+                    }
+                }
             } catch (e: Exception) {
-                responseObserver.onError(failStatus("request(${request.commonHeader.requestId}): Failed to delete $blueprint", e))
+                responseObserver.onNext(
+                    failStatus(
+                        request.commonHeader,
+                        "request(${request.commonHeader.requestId}): Failed to delete $blueprint", e
+                    )
+                )
+            } finally {
+                responseObserver.onCompleted()
             }
         }
     }
 
-    private fun saveToDisk(request: BluePrintUploadInput, cbaFile: File) {
-        log.info("request(${request.commonHeader.requestId}): Writing CBA File under :${cbaFile.absolutePath}")
+    override fun bootstrapBlueprint(
+        request: BluePrintBootstrapInput,
+        responseObserver: StreamObserver<BluePrintManagementOutput>
+    ) {
+        runBlocking {
+            try {
+                log.info("request(${request.commonHeader.requestId}): Received bootstrap request")
+                val bootstrapRequest = BootstrapRequest().apply {
+                    loadModelType = request.loadModelType
+                    loadResourceDictionary = request.loadResourceDictionary
+                    loadCBA = request.loadCBA
+                }
+                /** Perform bootstrap of Model Types, Resource Definitions and CBA */
+                bluePrintModelHandler.bootstrapBlueprint(bootstrapRequest)
+                responseObserver.onNext(successStatus(request.commonHeader))
+            } catch (e: Exception) {
+                responseObserver.onNext(
+                    failStatus(
+                        request.commonHeader,
+                        "request(${request.commonHeader.requestId}): Failed to bootstrap", e
+                    )
+                )
+            } finally {
+                responseObserver.onCompleted()
+            }
+        }
+    }
 
-        // Recreate Folder
-        cbaFile.parentFile.reCreateDirs()
+    private fun outputWithFileBytes(header: CommonHeader, byteArray: ByteArray): BluePrintManagementOutput =
+        BluePrintManagementOutput.newBuilder()
+            .setCommonHeader(header)
+            .setFileChunk(FileChunk.newBuilder().setChunk(ByteString.copyFrom(byteArray)))
+            .setStatus(
+                Status.newBuilder()
+                    .setTimestamp(currentTimestamp())
+                    .setMessage(BluePrintConstants.STATUS_SUCCESS)
+                    .setCode(200)
+                    .build()
+            )
+            .build()
 
-        // Write the File
-        cbaFile.writeBytes(request.fileChunk.chunk.toByteArray()).apply {
-            log.info("request(${request.commonHeader.requestId}): CBA file(${cbaFile.absolutePath} written successfully")
+    private fun successStatus(header: CommonHeader, propertyContent: String? = null): BluePrintManagementOutput {
+        // Populate Response Payload
+        val propertiesBuilder = BluePrintManagementOutput.newBuilder().propertiesBuilder
+        propertyContent?.let {
+            JsonFormat.parser().merge(propertyContent, propertiesBuilder)
         }
+        return BluePrintManagementOutput.newBuilder()
+            .setCommonHeader(header)
+            .setProperties(propertiesBuilder.build())
+            .setStatus(
+                Status.newBuilder()
+                    .setTimestamp(currentTimestamp())
+                    .setMessage(BluePrintConstants.STATUS_SUCCESS)
+                    .setCode(200)
+                    .build()
+            )
+            .build()
+    }
 
+    private fun failStatus(header: CommonHeader, message: String, e: Exception): BluePrintManagementOutput {
+        log.error(message, e)
+        return if (e is BluePrintProcessorException) onErrorCatalog(header, message, e) else onError(header, message, e)
     }
 
-    private fun successStatus(message: String, header: CommonHeader): BluePrintManagementOutput =
-            BluePrintManagementOutput.newBuilder()
-                    .setCommonHeader(header)
-                    .setStatus(Status.newBuilder()
-                            .setTimestamp(currentTimestamp())
-                            .setMessage(message)
-                            .setCode(200)
-                            .build())
-                    .build()
+    private fun onError(header: CommonHeader, message: String, error: Exception): BluePrintManagementOutput {
+        val code = GrpcErrorCodes.code(ErrorCatalogCodes.GENERIC_FAILURE)
+        return BluePrintManagementOutput.newBuilder()
+                .setCommonHeader(header)
+                .setStatus(
+                        Status.newBuilder()
+                                .setTimestamp(currentTimestamp())
+                                .setMessage(BluePrintConstants.STATUS_FAILURE)
+                                .setErrorMessage("Error : $message \n Details: ${error.errorMessageOrDefault()}")
+                                .setCode(code)
+                                .build()
+                )
+                .build()
+    }
 
-    private fun failStatus(message: String, e: Exception): StatusException {
-        log.error(message, e)
-        return io.grpc.Status.INTERNAL
-                .withDescription(message)
-                .withCause(e)
-                .asException()
+    private fun onErrorCatalog(header: CommonHeader, message: String, error: BluePrintProcessorException):
+            BluePrintManagementOutput {
+        val err = if (error.protocol == "") {
+            error.grpc(ErrorCatalogCodes.GENERIC_FAILURE)
+        } else {
+            error.convertToGrpc()
+        }
+        val errorPayload = errorCatalogService.errorPayload(err.addErrorPayloadMessage(message))
+        return BluePrintManagementOutput.newBuilder()
+                .setCommonHeader(header)
+                .setStatus(
+                        Status.newBuilder()
+                                .setTimestamp(currentTimestamp())
+                                .setMessage(BluePrintConstants.STATUS_FAILURE)
+                                .setErrorMessage("Error : ${errorPayload.message}")
+                                .setCode(errorPayload.code)
+                                .build()
+                )
+                .build()
     }
 }