CCSDK-3671 add workflows list for grpc 28/129328/2
authorOleg Mitsura <oleg.mitsura@amdocs.com>
Thu, 19 May 2022 15:05:13 +0000 (11:05 -0400)
committerOleg Mitsura <oleg.mitsura@amdocs.com>
Thu, 19 May 2022 15:20:21 +0000 (11:20 -0400)
Issue-ID: CCSDK-3671

1. added grpc 'getWorkflows' for BlueprintManagement
2. during CBA upload, the workflows get cached to BLUEPRINT_MODEL.workflows
3. reworked HTTP endpoint to use above "/workflows/blueprint-name/{name}/version/{version}"
4. If CDS is upgraded, with existing CBAs present, fallback by parsing the CBA instead of DB lookup

Signed-off-by: Oleg Mitsura <oleg.mitsura@amdocs.com>
Change-Id: I68bebfe23c0b16ea288512f1087bfe1ceef57686

components/model-catalog/proto-definition/proto/BluePrintManagement.proto
ms/blueprintsprocessor/application/src/main/resources/sql/schema.sql
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/domain/BlueprintModel.kt
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelRepository.kt
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintCatalogServiceImpl.kt
ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintProcessorCatalogServiceImpl.kt
ms/blueprintsprocessor/modules/commons/db-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImplTest.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BluePrintManagementGRPCHandler.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt
ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt

index a363e8a..ee8bd25 100644 (file)
@@ -42,6 +42,13 @@ message BluePrintManagementOutput {
     google.protobuf.Struct properties = 4;
 }
 
+// Get the list of workflows available for a given blueprintName/Version
+message BluePrintGetWorkflowsInput {
+    org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader commonHeader = 1;
+    string blueprintName = 2;
+    string blueprintVersion = 3;
+}
+
 message FileChunk {
     bytes chunk = 1;
 }
@@ -73,4 +80,5 @@ service BluePrintManagementService {
     rpc uploadBlueprint (BluePrintUploadInput) returns (BluePrintManagementOutput);
     rpc removeBlueprint (BluePrintRemoveInput) returns (BluePrintManagementOutput);
     rpc bootstrapBlueprint (BluePrintBootstrapInput) returns (BluePrintManagementOutput);
+    rpc getWorkflows(BluePrintGetWorkflowsInput) returns (BluePrintManagementOutput);
 }
index 851adcf..84cce28 100644 (file)
@@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS configurator.BLUEPRINT_MODEL (
   published                    varchar(1) not null,
   updated_by                   varchar(100) not null,
   tags                                 longtext null default null,
+  workflows   longtext null default null,
   primary key PK_BLUEPRINT_MODEL (blueprint_model_id),
   UNIQUE KEY UK_BLUEPRINT_MODEL (artifact_name , artifact_version)
 ) ENGINE=InnoDB;
index 9f4d32e..1feac8c 100755 (executable)
@@ -19,12 +19,17 @@ package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain
 import com.fasterxml.jackson.annotation.JsonFormat
 import io.swagger.annotations.ApiModelProperty
 import org.hibernate.annotations.Proxy
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
 import org.springframework.data.annotation.LastModifiedDate
 import org.springframework.data.jpa.domain.support.AuditingEntityListener
 import java.io.Serializable
 import java.util.Date
+import javax.persistence.AttributeConverter
 import javax.persistence.CascadeType
 import javax.persistence.Column
+import javax.persistence.Convert
+import javax.persistence.Converter
 import javax.persistence.Entity
 import javax.persistence.EntityListeners
 import javax.persistence.FetchType
@@ -123,8 +128,26 @@ class BlueprintModel : Serializable {
     @OneToOne(mappedBy = "blueprintModel", fetch = FetchType.EAGER, orphanRemoval = true, cascade = [CascadeType.ALL])
     var blueprintModelContent: BlueprintModelContent? = null
 
+    // will be populated with workflow specs for each workflow (JSON object)
+    @Lob
+    @Convert(converter = WorkflowsConverter::class)
+    @Column(name = "workflows", nullable = false)
+    lateinit var workflows: Map<String, Workflow>
+
     companion object {
 
         private const val serialVersionUID = 1L
     }
+
+    @Converter
+    class WorkflowsConverter : AttributeConverter<Map<String, Workflow>, String> {
+        override fun convertToDatabaseColumn(node: Map<String, Workflow>): String {
+            return JacksonUtils.getJson(node, true)
+        }
+
+        override fun convertToEntityAttribute(dbData: String): Map<String, Workflow> {
+            if (dbData == null || "".equals(dbData)) return emptyMap()
+            return JacksonUtils.getMapFromJson(dbData, Workflow::class.java)
+        }
+    }
 }
index a6f0da1..67869d1 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository
 
+import com.fasterxml.jackson.databind.JsonNode
 import org.jetbrains.annotations.NotNull
 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModel
 import org.springframework.data.jpa.repository.JpaRepository
@@ -59,6 +60,15 @@ interface BlueprintModelRepository : JpaRepository<BlueprintModel, String> {
     @Query("SELECT m.id FROM BlueprintModel m WHERE m.artifactName = :artifactName AND m.artifactVersion = :artifactVersion")
     fun findIdByArtifactNameAndArtifactVersion(@Param("artifactName") artifactName: String, @Param("artifactVersion") artifactVersion: String): String?
 
+    /**
+     * Find the workflows for a given blueprint name/version
+     * @param artifactName artifactName
+     * @param artifactVersion artifactVersion
+     * @return String?
+     */
+    @Query("SELECT m.workflows from BlueprintModel m WHERE m.artifactName = :artifactName AND m.artifactVersion = :artifactVersion")
+    fun findWorkflowsByArtifactNameAndArtifactVersion(@Param("artifactName") artifactName: String, @Param("artifactVersion") artifactVersion: String): JsonNode?
+
     /**
      * This is a findTopByArtifactNameOrderByArtifactIdDesc method
      *
index 9d18263..0637634 100644 (file)
@@ -21,6 +21,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.service
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
 import org.onap.ccsdk.cds.controllerblueprints.core.deCompress
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintValidatorService
@@ -71,10 +72,11 @@ abstract class BlueprintCatalogServiceImpl(
 
         val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(processingId, workingDir!!)
         val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+        val workflows = bluePrintRuntimeService.bluePrintContext().workflows()!!
         metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = processingId
         metadata[BluePrintConstants.PROPERTY_BLUEPRINT_VALID] = valid
 
-        save(metadata, archiveFile)
+        save(metadata, archiveFile, workflows)
 
         return processingId
     }
@@ -87,7 +89,7 @@ abstract class BlueprintCatalogServiceImpl(
 
     override suspend fun deleteFromDatabase(name: String, version: String) = delete(name, version)
 
-    abstract suspend fun save(metadata: MutableMap<String, String>, archiveFile: File)
+    abstract suspend fun save(metadata: MutableMap<String, String>, archiveFile: File, workflows: Map<String, Workflow>)
     abstract suspend fun get(name: String, version: String, extract: Boolean): Path?
     abstract suspend fun delete(name: String, version: String)
 }
index 9c007da..c45a28a 100755 (executable)
@@ -29,6 +29,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
 import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
 import org.onap.ccsdk.cds.controllerblueprints.core.deCompress
 import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintValidatorService
@@ -120,7 +121,7 @@ class BlueprintProcessorCatalogServiceImpl(
         }
     }
 
-    override suspend fun save(metadata: MutableMap<String, String>, archiveFile: File) {
+    override suspend fun save(metadata: MutableMap<String, String>, archiveFile: File, workflows: Map<String, Workflow>) {
         val artifactName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]
         val artifactVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]
 
@@ -152,9 +153,8 @@ class BlueprintProcessorCatalogServiceImpl(
         blueprintModel.artifactVersion = artifactVersion
         blueprintModel.updatedBy = metadata[BluePrintConstants.METADATA_TEMPLATE_AUTHOR]!!
         blueprintModel.tags = metadata[BluePrintConstants.METADATA_TEMPLATE_TAGS]!!
-        val description =
-            if (null != metadata[BluePrintConstants.METADATA_TEMPLATE_DESCRIPTION]) metadata[BluePrintConstants.METADATA_TEMPLATE_DESCRIPTION] else ""
-        blueprintModel.artifactDescription = description
+        blueprintModel.artifactDescription = "Controller Blueprint for $artifactName:$artifactVersion"
+        blueprintModel.workflows = workflows
 
         val blueprintModelContent = BlueprintModelContent()
         blueprintModelContent.id = metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID]
index b7b1f78..5f1d095 100644 (file)
@@ -102,10 +102,11 @@ class BlueprintProcessorCatalogServiceImplTest {
         runBlocking {
             val file = normalizedFile("./target/blueprints/generated-cba.zip")
             assertTrue(file.exists(), "couldnt get file ${file.absolutePath}")
-            val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+            val ctx = bluePrintRuntimeService.bluePrintContext()
+            val metadata = ctx.metadata!!
             metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = blueprintId
 
-            blueprintsProcessorCatalogService.save(metadata, file)
+            blueprintsProcessorCatalogService.save(metadata, file, ctx.workflows()!!)
         }
     }
 
@@ -114,10 +115,12 @@ class BlueprintProcessorCatalogServiceImplTest {
         runBlocking {
             val file = normalizedFile("./target/blueprints/generated-cba.zip")
             assertTrue(file.exists(), "couldnt get file ${file.absolutePath}")
-            val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
+
+            val ctx = bluePrintRuntimeService.bluePrintContext()
+            val metadata = ctx.metadata!!
             metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = blueprintId
 
-            blueprintsProcessorCatalogService.save(metadata, file)
+            blueprintsProcessorCatalogService.save(metadata, file, ctx.workflows()!!)
             blueprintsProcessorCatalogService.get("baseconfiguration", "1.0.0", true)
         }
 
index 54f8dbc..0dc0941 100644 (file)
@@ -29,10 +29,12 @@ import org.onap.ccsdk.cds.controllerblueprints.common.api.Status
 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.asJsonType
 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.BluePrintBootstrapInput
 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintDownloadInput
+import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintGetWorkflowsInput
 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
@@ -241,6 +243,33 @@ open class BluePrintManagementGRPCHandler(
         }
     }
 
+    @PreAuthorize("hasRole('USER')")
+    override fun getWorkflows(
+        request: BluePrintGetWorkflowsInput,
+        responseObserver: StreamObserver<BluePrintManagementOutput>
+    ) {
+        runBlocking {
+            val blueprintName = request.blueprintName
+            val blueprintVersion = request.blueprintVersion
+            val blueprint = "blueprint $blueprintName:$blueprintVersion"
+
+            log.info("request(${request.commonHeader.requestId}): Received getWorkflows $blueprint")
+            try {
+                val workflowsSetJson = bluePrintModelHandler.getWorkflowNamesFromRepository(blueprintName, blueprintVersion)
+                responseObserver.onNext(successStatus(request.commonHeader, mapOf("workflows" to workflowsSetJson).asJsonType().toString()))
+            } catch (e: Exception) {
+                responseObserver.onNext(
+                    failStatus(
+                        request.commonHeader,
+                        "request(${request.commonHeader.requestId}): Failed to get workflows for ($blueprint)", e
+                    )
+                )
+            } finally {
+                responseObserver.onCompleted()
+            }
+        }
+    }
+
     private fun outputWithFileBytes(header: CommonHeader, byteArray: ByteArray): BluePrintManagementOutput =
         BluePrintManagementOutput.newBuilder()
             .setCommonHeader(header)
@@ -258,6 +287,7 @@ open class BluePrintManagementGRPCHandler(
     private fun successStatus(header: CommonHeader, propertyContent: String? = null): BluePrintManagementOutput {
         // Populate Response Payload
         val propertiesBuilder = BluePrintManagementOutput.newBuilder().propertiesBuilder
+        // propertyContent is expected to have a string which contains a JSON map
         propertyContent?.let {
             JsonFormat.parser().merge(propertyContent, propertiesBuilder)
         }
index 66d4b0e..777a214 100644 (file)
@@ -393,10 +393,7 @@ open class BlueprintModelController(private val bluePrintModelHandler: BluePrint
 
     @PostMapping(
         path = arrayOf("/workflow-spec"),
-        produces = arrayOf(
-            MediaType
-                .APPLICATION_JSON_VALUE
-        ),
+        produces = arrayOf(MediaType.APPLICATION_JSON_VALUE),
         consumes = arrayOf(MediaType.APPLICATION_JSON_VALUE)
     )
     @ApiOperation(
@@ -418,9 +415,7 @@ open class BlueprintModelController(private val bluePrintModelHandler: BluePrint
         }
 
     @GetMapping(
-        path = arrayOf(
-            "/workflows/blueprint-name/{name}/version/{version}"
-        ),
+        path = arrayOf("/workflows/blueprint-name/{name}/version/{version}"),
         produces = arrayOf(MediaType.APPLICATION_JSON_VALUE)
     )
     @ApiOperation(
index 7bbaa8c..a5fcd32 100644 (file)
@@ -39,6 +39,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
 import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
 import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir
 import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
 import org.onap.ccsdk.cds.controllerblueprints.core.logger
@@ -107,42 +108,78 @@ open class BluePrintModelHandler(
     }
 
     @Throws(BluePrintException::class)
-    open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest):
-        WorkFlowSpecResponse {
-            val basePath = blueprintsProcessorCatalogService.getFromDatabase(
-                req
-                    .blueprintName,
-                req.version
-            )
-            log.info("blueprint base path $basePath")
+    private suspend fun getBlueprintCtxByNameAndVersion(blueprintName: String, version: String): BluePrintContext {
+        val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, version)
+        log.info("blueprint base path $basePath for blueprint: $blueprintName version:$version")
+        return BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
+    }
+
+    @Throws(BluePrintException::class)
+    /**
+     * Try to get workflows cached from the BLUEPRINT_MODEL table first,
+     * failing that, load the CBA from the filesystem and extract workflows.
+     * The second case is possible during update scenario - current CDS DB already contains desired CBAs and reupload
+     * is not feasible.
+     */
+    open suspend fun getWorkflowsFromRepository(name: String, version: String): Map<String, Workflow> {
+        var workflowsFromCache: Map<String, Workflow>
+        try {
+            workflowsFromCache = blueprintModelRepository.findByArtifactNameAndArtifactVersion(name, version)?.workflows!!
+            if (workflowsFromCache.isEmpty()) {
+                log.info("findByArtifactNameAndArtifactVersion did not return list of workflows for blueprintName:($name) version:($version), falling back to loading CBA from filesystem.")
+                workflowsFromCache = getBlueprintCtxByNameAndVersion(name, version).workflows()!!
+                // TODO: does it make sense to update the BLUEPRINT_MODEL workflows in this case or just wait for CBA reupload?
+            }
+        } catch (e: Exception) {
+            throw BluePrintException("Failed to get workflows from DB cache or by reading CBA", e)
+        }
+        return workflowsFromCache
+    }
+
+    // lookup workflows list from field.
+    open suspend fun getWorkflowNamesFromRepository(name: String, version: String): Set<String> {
+        return getWorkflowsFromRepository(name, version).keys
+    }
 
-            val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
-            val workFlow = blueprintContext.workflowByName(req.workflowName)
+    @Throws(BluePrintException::class)
+    open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest): WorkFlowSpecResponse {
+        val basePath = blueprintsProcessorCatalogService.getFromDatabase(req.blueprintName, req.version)
+        log.info("blueprint base path $basePath")
 
-            val wfRes = WorkFlowSpecResponse()
-            wfRes.blueprintName = req.blueprintName
-            wfRes.version = req.version
+        val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
+        val workFlow = blueprintContext.workflowByName(req.workflowName)
 
-            val workFlowData = WorkFlowData()
-            workFlowData.workFlowName = req.workflowName
-            workFlowData.inputs = workFlow.inputs
-            workFlowData.outputs = workFlow.outputs
-            wfRes.workFlowData = workFlowData
+        val wfRes = WorkFlowSpecResponse()
+        wfRes.blueprintName = req.blueprintName
+        wfRes.version = req.version
 
-            if (workFlow.inputs != null) {
-                for ((k, v) in workFlow.inputs!!) {
-                    addPropertyInfo(k, v, blueprintContext, wfRes)
-                }
+        val workFlowData = WorkFlowData()
+        workFlowData.workFlowName = req.workflowName
+        workFlowData.inputs = workFlow.inputs
+        workFlowData.outputs = workFlow.outputs
+
+        if (workFlow.inputs != null) {
+            for ((k, v) in workFlow.inputs!!) {
+                addPropertyInfo(v, blueprintContext, wfRes)
             }
+        }
 
-            if (workFlow.outputs != null) {
-                for ((k, v) in workFlow.outputs!!) {
-                    addPropertyInfo(k, v, blueprintContext, wfRes)
-                }
+        if (workFlow.outputs != null) {
+            for ((k, v) in workFlow.outputs!!) {
+                addPropertyInfo(k, v, blueprintContext, wfRes)
             }
+        }
 
-            return wfRes
+        wfRes.workFlowData = workFlowData
+        return wfRes
+    }
+
+    private fun addPropertyInfo(prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
+        addDataType(prop.type, ctx, res)
+        if (prop.entrySchema != null && prop.entrySchema!!.type != null) {
+            addDataType(prop.entrySchema!!.type, ctx, res)
         }
+    }
 
     private fun addPropertyInfo(propName: String, prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
         updatePropertyInfo(propName, prop, ctx, res)
@@ -192,23 +229,19 @@ open class BluePrintModelHandler(
         }
     }
 
+    // wrap CBA workflows list in WorkflowsResponse object
     @Throws(BluePrintException::class)
     open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse {
-        val basePath = blueprintsProcessorCatalogService.getFromDatabase(
-            name, version
+        var workflows = getWorkflowsFromRepository(name, version)
+        if (workflows == null) throw httpProcessorException(
+            ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
+            "Failed to find workflows list for blueprint: ($name) version: ($version) from filesystem."
         )
-        log.info("blueprint base path $basePath")
 
         var res = WorkFlowsResponse()
         res.blueprintName = name
         res.version = version
-
-        val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(
-            basePath.toString()
-        )
-        if (blueprintContext.workflows() != null) {
-            res.workflows = blueprintContext.workflows()!!.keys
-        }
+        res.workflows = workflows.keys.toMutableSet()
         return res
     }
 
@@ -242,6 +275,7 @@ open class BluePrintModelHandler(
         try {
             return upload(filePart, false)
         } catch (e: IOException) {
+            log.error("saveBlueprintModel fails ${e.message}", e)
             throw httpProcessorException(
                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
                 "Error in Save CBA: ${e.message}", e.errorCauseOrDefault()
@@ -527,13 +561,15 @@ open class BluePrintModelHandler(
             val enhancedByteArray = enrichBlueprintFileSource(filePart)
             return upload(enhancedByteArray, true)
         } catch (e: BluePrintProcessorException) {
-            e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
             val errorMsg = "Error while enhancing and uploading the CBA package."
+            log.error(errorMsg, e)
+            e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
             throw e.updateErrorMessage(
                 DesignerApiDomains.DESIGNER_API, errorMsg,
                 "Wrong CBA file provided, please verify the source CBA."
             )
         } catch (e: Exception) {
+            log.error("Error enriching/uploading CBA", e)
             throw httpProcessorException(
                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
                 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault()