Fix inconsistency introduced during commit b4cbb0ee9
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / inbounds / designer-api / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / designer / api / handler / BluePrintModelHandler.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 Bell Canada.
4  * Modifications Copyright © 2019 IBM.
5  * Modifications Copyright © 2019 Orange.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 package org.onap.ccsdk.cds.blueprintsprocessor.designer.api.handler
21
22 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModel
23 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModelSearch
24 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelContentRepository
25 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelRepository
26 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelSearchRepository
27 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.BootstrapRequest
28 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.DesignerApiDomains
29 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowData
30 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowSpecRequest
31 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowSpecResponse
32 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowsResponse
33 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.load.BluePrintDatabaseLoadService
34 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.utils.BluePrintEnhancerUtils
35 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
36 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
37 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
38 import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
39 import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
40 import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir
41 import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException
42 import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
43 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
44 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
45 import org.onap.ccsdk.cds.controllerblueprints.core.logger
46 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
47 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
48 import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
49 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
50 import org.onap.ccsdk.cds.controllerblueprints.core.updateErrorMessage
51 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
52 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
53 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
54 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
55 import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes
56 import org.onap.ccsdk.cds.error.catalog.core.utils.errorCauseOrDefault
57 import org.onap.ccsdk.cds.error.catalog.core.utils.errorMessageOrDefault
58 import org.springframework.core.io.ByteArrayResource
59 import org.springframework.core.io.Resource
60 import org.springframework.data.domain.Page
61 import org.springframework.data.domain.PageRequest
62 import org.springframework.data.domain.Pageable
63 import org.springframework.http.HttpHeaders
64 import org.springframework.http.MediaType
65 import org.springframework.http.ResponseEntity
66 import org.springframework.http.codec.multipart.FilePart
67 import org.springframework.stereotype.Service
68 import org.springframework.transaction.annotation.Transactional
69 import java.io.File
70 import java.io.IOException
71 import java.util.UUID
72
73 /**
74  * BlueprintModelHandler Purpose: Handler service to handle the request from BlurPrintModelRest
75  *
76  * @author Brinda Santh
77  * @version 1.0
78  */
79
80 @Service
81 open class BluePrintModelHandler(
82     private val bluePrintDatabaseLoadService: BluePrintDatabaseLoadService,
83     private val blueprintsProcessorCatalogService: BluePrintCatalogService,
84     private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
85     private val blueprintModelSearchRepository: BlueprintModelSearchRepository,
86     private val blueprintModelRepository: BlueprintModelRepository,
87     private val blueprintModelContentRepository: BlueprintModelContentRepository,
88     private val bluePrintEnhancerService: BluePrintEnhancerService
89 ) {
90
91     private val log = logger(BluePrintModelHandler::class)
92
93     open suspend fun bootstrapBlueprint(bootstrapRequest: BootstrapRequest) {
94         log.info(
95             "Bootstrap request with type load(${bootstrapRequest.loadModelType}), " +
96                 "resource dictionary load(${bootstrapRequest.loadResourceDictionary}) and " +
97                 "cba load(${bootstrapRequest.loadCBA})"
98         )
99         if (bootstrapRequest.loadModelType) {
100             bluePrintDatabaseLoadService.initModelTypes()
101         }
102         if (bootstrapRequest.loadResourceDictionary) {
103             bluePrintDatabaseLoadService.initResourceDictionary()
104         }
105         if (bootstrapRequest.loadCBA) {
106             bluePrintDatabaseLoadService.initBluePrintCatalog()
107         }
108     }
109
110     @Throws(BluePrintException::class)
111     private suspend fun getBlueprintCtxByNameAndVersion(blueprintName: String, version: String): BluePrintContext {
112         val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, version)
113         log.info("blueprint base path $basePath for blueprint: $blueprintName version:$version")
114         return BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
115     }
116
117     @Throws(BluePrintException::class)
118     /**
119      * Try to get workflows cached from the BLUEPRINT_MODEL table first,
120      * failing that, load the CBA from the filesystem and extract workflows.
121      * The second case is possible during update scenario - current CDS DB already contains desired CBAs and reupload
122      * is not feasible.
123      */
124     open suspend fun getWorkflowsFromRepository(name: String, version: String): Map<String, Workflow> {
125         var workflowsFromCache: Map<String, Workflow>
126         try {
127             workflowsFromCache = blueprintModelRepository.findByArtifactNameAndArtifactVersion(name, version)?.workflows!!
128             if (workflowsFromCache.isEmpty()) {
129                 log.info("findByArtifactNameAndArtifactVersion did not return list of workflows for blueprintName:($name) version:($version), falling back to loading CBA from filesystem.")
130                 workflowsFromCache = getBlueprintCtxByNameAndVersion(name, version).workflows()!!
131                 // TODO: does it make sense to update the BLUEPRINT_MODEL workflows in this case or just wait for CBA reupload?
132             }
133         } catch (e: Exception) {
134             throw BluePrintException("Failed to get workflows from DB cache or by reading CBA", e)
135         }
136         return workflowsFromCache
137     }
138
139     // lookup workflows list from field.
140     open suspend fun getWorkflowNamesFromRepository(name: String, version: String): Set<String> {
141         return getWorkflowsFromRepository(name, version).keys
142     }
143
144     @Throws(BluePrintException::class)
145     open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest): WorkFlowSpecResponse {
146         val basePath = blueprintsProcessorCatalogService.getFromDatabase(req.blueprintName, req.version)
147         log.info("blueprint base path $basePath")
148
149         val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
150         val workFlow = blueprintContext.workflowByName(req.workflowName)
151
152         val wfRes = WorkFlowSpecResponse()
153         wfRes.blueprintName = req.blueprintName
154         wfRes.version = req.version
155
156         val workFlowData = WorkFlowData()
157         workFlowData.workFlowName = req.workflowName
158         workFlowData.inputs = workFlow.inputs
159         workFlowData.outputs = workFlow.outputs
160
161         if (workFlow.inputs != null) {
162             for ((k, v) in workFlow.inputs!!) {
163                 addPropertyInfo(k, v, blueprintContext, wfRes)
164             }
165         }
166
167         if (workFlow.outputs != null) {
168             for ((k, v) in workFlow.outputs!!) {
169                 addPropertyInfo(k, v, blueprintContext, wfRes)
170             }
171         }
172
173         wfRes.workFlowData = workFlowData
174         return wfRes
175     }
176
177     private fun addPropertyInfo(propName: String, prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
178         updatePropertyInfo(propName, prop, ctx, res)
179         addDataType(prop.type, ctx, res)
180         if (prop.entrySchema != null && prop.entrySchema!!.type != null) {
181             addDataType(prop.entrySchema!!.type, ctx, res)
182         }
183     }
184
185     private fun updatePropertyInfo(name: String, prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
186         if (prop.inputparam == null || prop.inputparam == false) {
187             var workflow = ctx.workflowByName(res.workFlowData.workFlowName)
188             for ((k, v) in workflow.steps!!) {
189                 var arts = ctx.nodeTemplateArtifacts(v.target!!)
190                 if (arts != null) {
191                     for ((k, v) in arts.entries!!) {
192                         if (v.type == "artifact-mapping-resource") {
193                             val file: String = v.file
194                             val completePath = ctx.rootPath.plus(File.separator).plus(file)
195                             val resourceAssignment = JacksonUtils.getListFromFile(completePath, ResourceAssignment::class.java)
196                             for (res in resourceAssignment) {
197                                 if (res.name == name && res.inputParameter) {
198                                     prop.inputparam = true
199                                     return
200                                 }
201                             }
202                         }
203                     }
204                 }
205             }
206         }
207     }
208
209     private fun addDataType(name: String, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
210         var data = ctx.dataTypeByName(name)
211         if (data != null) {
212             res.dataTypes?.put(name, data)
213             addParentDataType(data, ctx, res)
214         }
215     }
216
217     private fun addParentDataType(data: DataType, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
218         if (data.properties != null) {
219             for ((k, v) in data.properties!!) {
220                 addPropertyInfo(k, v, ctx, res)
221             }
222         }
223     }
224
225     // wrap CBA workflows list in WorkflowsResponse object
226     @Throws(BluePrintException::class)
227     open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse {
228         var workflows = getWorkflowsFromRepository(name, version)
229         if (workflows == null) throw httpProcessorException(
230             ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
231             "Failed to find workflows list for blueprint: ($name) version: ($version) from filesystem."
232         )
233
234         var res = WorkFlowsResponse()
235         res.blueprintName = name
236         res.version = version
237         res.workflows = workflows.keys.toMutableSet()
238         return res
239     }
240
241     /**
242      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
243      *
244      * @return List<BlueprintModelSearch> list of the controller blueprint archives
245      </BlueprintModelSearch> */
246     open fun allBlueprintModel(): List<BlueprintModelSearch> {
247         return blueprintModelSearchRepository.findAll()
248     }
249
250     /**
251      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
252      *
253      * @return List<BlueprintModelSearch> list of the controller blueprint archives
254      </BlueprintModelSearch> */
255     open fun allBlueprintModel(pageRequest: Pageable): Page<BlueprintModelSearch> {
256         return blueprintModelSearchRepository.findAll(pageRequest)
257     }
258
259     /**
260      * This is a saveBlueprintModel method
261      *
262      * @param filePart filePart
263      * @return Mono<BlueprintModelSearch>
264      * @throws BluePrintException BluePrintException
265      </BlueprintModelSearch> */
266     @Throws(BluePrintException::class)
267     open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
268         try {
269             return upload(filePart, false)
270         } catch (e: IOException) {
271             log.error("saveBlueprintModel fails ${e.message}", e)
272             throw httpProcessorException(
273                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
274                 "Error in Save CBA: ${e.message}", e.errorCauseOrDefault()
275             )
276         }
277     }
278
279     /**
280      * This is a searchBlueprintModels method
281      *
282      * @param tags tags
283      * @return List<BlueprintModelSearch>
284      </BlueprintModelSearch> */
285     open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
286         return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
287     }
288
289     /**
290      * This is a getBlueprintModelSearchByNameAndVersion method
291      *
292      * @param name name
293      * @param version version
294      * @return BlueprintModelSearch
295      * @throws BluePrintException BluePrintException
296      */
297     @Throws(BluePrintException::class)
298     open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch? {
299         return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
300         /*?: throw BluePrintException(
301             ErrorCode.RESOURCE_NOT_FOUND.value,
302             String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
303         )*/
304     }
305
306     /**
307      * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
308      *
309      * @param name name
310      * @param version version
311      * @return ResponseEntity<Resource>
312      * @throws BluePrintException BluePrintException
313      </Resource> */
314     @Throws(BluePrintException::class)
315     open fun downloadBlueprintModelFileByNameAndVersion(
316         name: String,
317         version: String
318     ): ResponseEntity<Resource> {
319         try {
320             val archiveByteArray = download(name, version)
321             val fileName = "${name}_$version.zip"
322             return prepareResourceEntity(fileName, archiveByteArray)
323         } catch (e: BluePrintProcessorException) {
324             e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
325             val errorMsg = "Error while downloading the CBA file by Blueprint Name ($name) and Version ($version)."
326             throw e.updateErrorMessage(
327                 DesignerApiDomains.DESIGNER_API, errorMsg,
328                 "Wrong resource definition or resolution failed."
329             )
330         }
331     }
332
333     /**
334      * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
335      *
336      * @return ResponseEntity<Resource>
337      * @throws BluePrintException BluePrintException
338      </Resource> */
339     @Throws(BluePrintException::class)
340     open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
341         val blueprintModel: BlueprintModel
342         try {
343             blueprintModel = getBlueprintModel(id)
344         } catch (e: BluePrintException) {
345             throw httpProcessorException(
346                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
347                 "Error while downloading the CBA file: couldn't get blueprint modelby ID ($id)",
348                 e.errorCauseOrDefault()
349             )
350         }
351
352         val fileName = "${blueprintModel.artifactName}_${blueprintModel.artifactVersion}.zip"
353         val file = blueprintModel.blueprintModelContent?.content
354             ?: throw httpProcessorException(
355                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
356                 "Error while downloading the CBA file: couldn't get model content"
357             )
358         return prepareResourceEntity(fileName, file)
359     }
360
361     /**
362      * @return ResponseEntity<Resource>
363      </Resource> */
364     private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
365         return ResponseEntity.ok()
366             .contentType(MediaType.parseMediaType("text/plain"))
367             .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
368             .body(ByteArrayResource(file))
369     }
370
371     /**
372      * This is a getBlueprintModel method
373      *
374      * @param id id
375      * @return BlueprintModel
376      * @throws BluePrintException BluePrintException
377      */
378     @Throws(BluePrintException::class)
379     open fun getBlueprintModel(id: String): BlueprintModel {
380         val blueprintModel: BlueprintModel
381         val dbBlueprintModel = blueprintModelRepository.findById(id)
382         if (dbBlueprintModel.isPresent) {
383             blueprintModel = dbBlueprintModel.get()
384         } else {
385             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
386             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
387         }
388         return blueprintModel
389     }
390
391     /**
392      * This is a getBlueprintModelByNameAndVersion method
393      *
394      * @param name name
395      * @param version version
396      * @return BlueprintModel
397      * @throws BluePrintException BluePrintException
398      */
399     @Throws(BluePrintException::class)
400     open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
401         val blueprintModel = blueprintModelRepository
402             .findByArtifactNameAndArtifactVersion(name, version)
403         if (blueprintModel != null) {
404             return blueprintModel
405         } else {
406             val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
407             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
408         }
409     }
410
411     /**
412      * This is a getBlueprintModelSearch method
413      *
414      * @param id id
415      * @return BlueprintModelSearch
416      * @throws BluePrintException BluePrintException
417      */
418     @Throws(BluePrintException::class)
419     open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
420         return blueprintModelSearchRepository.findById(id)
421             ?: throw httpProcessorException(
422                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
423                 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
424             )
425     }
426
427     /**
428      * This is a searchBluePrintModelsByKeyWord method to retrieve specific  BlueprintModel in Database
429      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type
430      * @author Shaaban Ebrahim
431      * @param keyWord
432      *
433      * @return List<BlueprintModelSearch> list of the controller blueprint
434      </BlueprintModelSearch> */
435     open fun searchBluePrintModelsByKeyWord(keyWord: String): List<BlueprintModelSearch> {
436         return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
437             keyWord, keyWord, keyWord, keyWord, keyWord
438         )
439     }
440
441     /**
442      * This is a searchBluePrintModelsByKeyWordPagebale method to retrieve specific  BlueprintModel in Database
443      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type and pageable
444      * @author Shaaban Ebrahim
445      * @param keyWord
446      *
447      * @return List<BlueprintModelSearch> list of the controller blueprint
448      </BlueprintModelSearch> */
449     open fun searchBluePrintModelsByKeyWordPaged(keyWord: String, pageRequest: PageRequest): Page<BlueprintModelSearch> {
450         return blueprintModelSearchRepository.findByUpdatedByContainingIgnoreCaseOrTagsContainingIgnoreCaseOrArtifactNameContainingIgnoreCaseOrArtifactVersionContainingIgnoreCaseOrArtifactTypeContainingIgnoreCase(
451             keyWord,
452             keyWord,
453             keyWord,
454             keyWord,
455             keyWord,
456             pageRequest
457         )
458     }
459
460     /**
461      * This is a deleteBlueprintModel method
462      *
463      * @param id id
464      * @throws BluePrintException BluePrintException
465      */
466     @Transactional
467     @Throws(BluePrintException::class)
468     open fun deleteBlueprintModel(id: String) {
469         val dbBlueprintModel = blueprintModelRepository.findById(id)
470         if (dbBlueprintModel.isPresent) {
471             blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
472             blueprintModelRepository.delete(dbBlueprintModel.get())
473         } else {
474             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
475             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
476         }
477     }
478
479     open suspend fun deleteBlueprintModel(name: String, version: String) {
480         blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
481     }
482
483     /**
484      * This is a CBA enrichBlueprint method
485      * Save the Zip File in archive location and extract the cba content.
486      * Populate the Enhancement Location
487      * Enhance the CBA content
488      * Compress the Enhanced Content
489      * Return back the the compressed content back to the caller.
490      *
491      * @param filePart filePart
492      * @return ResponseEntity<Resource>
493      * @throws BluePrintException BluePrintException
494      */
495     @Throws(BluePrintException::class)
496     open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
497         try {
498             val enhancedByteArray = enrichBlueprintFileSource(filePart)
499             return BluePrintEnhancerUtils.prepareResourceEntity("enhanced-cba.zip", enhancedByteArray)
500         } catch (e: BluePrintProcessorException) {
501             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
502             val errorMsg = "Error while enhancing the CBA package."
503             throw e.updateErrorMessage(
504                 DesignerApiDomains.DESIGNER_API, errorMsg,
505                 "Wrong CBA file provided, please verify and enrich Again."
506             )
507         } catch (e: Exception) {
508             throw httpProcessorException(
509                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
510                 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault()
511             )
512         }
513     }
514
515     /**
516      * This is a publishBlueprintModel method to change the status published to YES
517      * NOTE: this method is meant for enriched blueprints only.
518      *
519      * @param filePart filePart
520      * @return BlueprintModelSearch
521      * @throws BluePrintException BluePrintException
522      */
523     @Throws(BluePrintException::class)
524     open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
525         try {
526             return upload(filePart, true)
527         } catch (e: BluePrintProcessorException) {
528             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
529             val errorMsg = "Error in Publishing CBA."
530             throw e.updateErrorMessage(
531                 DesignerApiDomains.DESIGNER_API, errorMsg,
532                 "Wrong CBA provided, please verify and enrich your CBA."
533             )
534         } catch (e: Exception) {
535             throw httpProcessorException(
536                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
537                 "Error in Publishing CBA: ${e.message}", e.errorCauseOrDefault()
538             )
539         }
540     }
541
542     /**
543      * Enrich and publish the blueprint.
544      * NOTE: this method is meant for the unenriched vs publishBlueprint(filePart)
545      *       which is used for enriched blueprints.
546      *
547      * @param filePart filePart
548      * @return BlueprintModelSearch
549      * @throws BluePrintException BluePrintException
550      */
551     @Throws(BluePrintException::class)
552     open suspend fun enrichAndPublishBlueprint(filePart: FilePart): BlueprintModelSearch {
553         try {
554             val enhancedByteArray = enrichBlueprintFileSource(filePart)
555             return upload(enhancedByteArray, true)
556         } catch (e: BluePrintProcessorException) {
557             val errorMsg = "Error while enhancing and uploading the CBA package."
558             log.error(errorMsg, e)
559             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
560             throw e.updateErrorMessage(
561                 DesignerApiDomains.DESIGNER_API, errorMsg,
562                 "Wrong CBA file provided, please verify the source CBA."
563             )
564         } catch (e: Exception) {
565             log.error("Error enriching/uploading CBA", e)
566             throw httpProcessorException(
567                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
568                 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault()
569             )
570         }
571     }
572
573     /** Common CBA Save and Publish function for RestController and GRPC Handler, the [fileSource] may be
574      * byteArray or File Part type.*/
575     open suspend fun upload(fileSource: Any, validate: Boolean): BlueprintModelSearch {
576         val saveId = UUID.randomUUID().toString()
577         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
578         val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
579         try {
580             val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
581             when (fileSource) {
582                 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
583                 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
584             }
585             // Save the Copied file to Database
586             val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
587
588             return blueprintModelSearchRepository.findById(blueprintId)
589                 ?: throw httpProcessorException(
590                     ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
591                     String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId)
592                 )
593         } catch (e: BluePrintException) {
594             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
595             val errorMsg = "Error in Upload CBA."
596             throw e.updateErrorMessage(
597                 DesignerApiDomains.DESIGNER_API, errorMsg,
598                 "Wrong enriched CBA."
599             )
600         } catch (e: IOException) {
601             throw httpProcessorException(
602                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
603                 "Error in Upload CBA: ${e.errorMessageOrDefault()}", e.errorCauseOrDefault()
604             )
605         } finally {
606             // Clean blueprint script cache
607             val cacheKey = BluePrintFileUtils
608                 .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
609             BluePrintCompileCache.cleanClassLoader(cacheKey)
610             deleteNBDir(blueprintArchive)
611             deleteNBDir(blueprintWorking)
612         }
613     }
614
615     /** Common CBA download function for RestController and GRPC Handler, the [fileSource] may be
616      * byteArray or File Part type.*/
617     open fun download(name: String, version: String): ByteArray {
618         try {
619             val blueprintModel = getBlueprintModelByNameAndVersion(name, version)
620             return blueprintModel.blueprintModelContent?.content
621                 ?: throw httpProcessorException(
622                     ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
623                     "Error while downloading the CBA file: couldn't get model content"
624                 )
625         } catch (e: BluePrintException) {
626             e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
627             val errorMsg = "Fail to get Blueprint Model content."
628             throw e.updateErrorMessage(
629                 DesignerApiDomains.DESIGNER_API, errorMsg,
630                 "Wrong name and version was provide."
631             )
632         }
633     }
634
635     /** Common CBA Enrich function for RestController and GRPC Handler, the [fileSource] may be
636      * byteArray or File Part type.*/
637     open suspend fun enrichBlueprintFileSource(fileSource: Any): ByteArray {
638         val enhanceId = UUID.randomUUID().toString()
639         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
640         val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
641         try {
642             when (fileSource) {
643                 is FilePart ->
644                     BluePrintEnhancerUtils
645                         .copyFilePartToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
646                 is ByteArray ->
647                     BluePrintEnhancerUtils
648                         .copyByteArrayToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
649             } // Enhance the Blue Prints
650             bluePrintEnhancerService.enhance(blueprintWorkingDir)
651
652             return BluePrintEnhancerUtils.compressEnhanceDirAndReturnByteArray(blueprintWorkingDir, blueprintArchive)
653         } catch (e: BluePrintException) {
654             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
655             val errorMsg = "Fail Enriching the CBA."
656             throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg)
657         } catch (e: IOException) {
658             throw httpProcessorException(
659                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
660                 "Error while Enriching the CBA file.", e.errorCauseOrDefault()
661             )
662         } finally {
663             BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
664         }
665     }
666
667     companion object {
668
669         private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
670         private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"
671     }
672 }