da6311d00625e91070cfa344e51a53f69ccc2a70
[ccsdk/cds.git] /
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     open fun allBlueprintModelByPublished(published: Boolean, pageRequest: Pageable): Page<BlueprintModelSearch> {
260         val dbPublished = if (published) "Y" else "N"
261         return blueprintModelSearchRepository.findByPublished(dbPublished, pageRequest)
262     }
263
264     /**
265      * This is a saveBlueprintModel method
266      *
267      * @param filePart filePart
268      * @return Mono<BlueprintModelSearch>
269      * @throws BluePrintException BluePrintException
270      </BlueprintModelSearch> */
271     @Throws(BluePrintException::class)
272     open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
273         try {
274             return upload(filePart, false)
275         } catch (e: IOException) {
276             log.error("saveBlueprintModel fails ${e.message}", e)
277             throw httpProcessorException(
278                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
279                 "Error in Save CBA: ${e.message}", e.errorCauseOrDefault()
280             )
281         }
282     }
283
284     /**
285      * This is a searchBlueprintModels method
286      *
287      * @param tags tags
288      * @return List<BlueprintModelSearch>
289      </BlueprintModelSearch> */
290     open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
291         return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
292     }
293
294     /**
295      * This is a getBlueprintModelSearchByNameAndVersion method
296      *
297      * @param name name
298      * @param version version
299      * @return BlueprintModelSearch
300      * @throws BluePrintException BluePrintException
301      */
302     @Throws(BluePrintException::class)
303     open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch? {
304         return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
305         /*?: throw BluePrintException(
306             ErrorCode.RESOURCE_NOT_FOUND.value,
307             String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
308         )*/
309     }
310
311     /**
312      * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
313      *
314      * @param name name
315      * @param version version
316      * @return ResponseEntity<Resource>
317      * @throws BluePrintException BluePrintException
318      </Resource> */
319     @Throws(BluePrintException::class)
320     open fun downloadBlueprintModelFileByNameAndVersion(
321         name: String,
322         version: String
323     ): ResponseEntity<Resource> {
324         try {
325             val archiveByteArray = download(name, version)
326             val fileName = "${name}_$version.zip"
327             return prepareResourceEntity(fileName, archiveByteArray)
328         } catch (e: BluePrintProcessorException) {
329             e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
330             val errorMsg = "Error while downloading the CBA file by Blueprint Name ($name) and Version ($version)."
331             throw e.updateErrorMessage(
332                 DesignerApiDomains.DESIGNER_API, errorMsg,
333                 "Wrong resource definition or resolution failed."
334             )
335         }
336     }
337
338     /**
339      * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
340      *
341      * @return ResponseEntity<Resource>
342      * @throws BluePrintException BluePrintException
343      </Resource> */
344     @Throws(BluePrintException::class)
345     open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
346         val blueprintModel: BlueprintModel
347         try {
348             blueprintModel = getBlueprintModel(id)
349         } catch (e: BluePrintException) {
350             throw httpProcessorException(
351                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
352                 "Error while downloading the CBA file: couldn't get blueprint modelby ID ($id)",
353                 e.errorCauseOrDefault()
354             )
355         }
356
357         val fileName = "${blueprintModel.artifactName}_${blueprintModel.artifactVersion}.zip"
358         val file = blueprintModel.blueprintModelContent?.content
359             ?: throw httpProcessorException(
360                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
361                 "Error while downloading the CBA file: couldn't get model content"
362             )
363         return prepareResourceEntity(fileName, file)
364     }
365
366     /**
367      * @return ResponseEntity<Resource>
368      </Resource> */
369     private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
370         return ResponseEntity.ok()
371             .contentType(MediaType.parseMediaType("text/plain"))
372             .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
373             .body(ByteArrayResource(file))
374     }
375
376     /**
377      * This is a getBlueprintModel method
378      *
379      * @param id id
380      * @return BlueprintModel
381      * @throws BluePrintException BluePrintException
382      */
383     @Throws(BluePrintException::class)
384     open fun getBlueprintModel(id: String): BlueprintModel {
385         val blueprintModel: BlueprintModel
386         val dbBlueprintModel = blueprintModelRepository.findById(id)
387         if (dbBlueprintModel.isPresent) {
388             blueprintModel = dbBlueprintModel.get()
389         } else {
390             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
391             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
392         }
393         return blueprintModel
394     }
395
396     /**
397      * This is a getBlueprintModelByNameAndVersion method
398      *
399      * @param name name
400      * @param version version
401      * @return BlueprintModel
402      * @throws BluePrintException BluePrintException
403      */
404     @Throws(BluePrintException::class)
405     open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
406         val blueprintModel = blueprintModelRepository
407             .findByArtifactNameAndArtifactVersion(name, version)
408         if (blueprintModel != null) {
409             return blueprintModel
410         } else {
411             val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
412             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
413         }
414     }
415
416     /**
417      * This is a getBlueprintModelSearch method
418      *
419      * @param id id
420      * @return BlueprintModelSearch
421      * @throws BluePrintException BluePrintException
422      */
423     @Throws(BluePrintException::class)
424     open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
425         return blueprintModelSearchRepository.findById(id)
426             ?: throw httpProcessorException(
427                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
428                 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
429             )
430     }
431
432     /**
433      * This is a searchBluePrintModelsByKeyWord method to retrieve specific  BlueprintModel in Database
434      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type
435      * @author Shaaban Ebrahim
436      * @param keyWord
437      *
438      * @return List<BlueprintModelSearch> list of the controller blueprint
439      </BlueprintModelSearch> */
440     open fun searchBluePrintModelsByKeyWord(keyWord: String): List<BlueprintModelSearch> {
441         return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
442             keyWord, keyWord, keyWord, keyWord, keyWord
443         )
444     }
445
446     /**
447      * This is a searchBluePrintModelsByKeyWordPagebale method to retrieve specific  BlueprintModel in Database
448      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type and pageable
449      * @author Shaaban Ebrahim
450      * @param keyWord
451      *
452      * @return List<BlueprintModelSearch> list of the controller blueprint
453      </BlueprintModelSearch> */
454     open fun searchBluePrintModelsByKeyWordPaged(keyWord: String, pageRequest: PageRequest): Page<BlueprintModelSearch> {
455         return blueprintModelSearchRepository.findByUpdatedByContainingIgnoreCaseOrTagsContainingIgnoreCaseOrArtifactNameContainingIgnoreCaseOrArtifactVersionContainingIgnoreCaseOrArtifactTypeContainingIgnoreCase(
456             keyWord,
457             keyWord,
458             keyWord,
459             keyWord,
460             keyWord,
461             pageRequest
462         )
463     }
464
465     /**
466      * This is a deleteBlueprintModel method
467      *
468      * @param id id
469      * @throws BluePrintException BluePrintException
470      */
471     @Transactional
472     @Throws(BluePrintException::class)
473     open fun deleteBlueprintModel(id: String) {
474         val dbBlueprintModel = blueprintModelRepository.findById(id)
475         if (dbBlueprintModel.isPresent) {
476             blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
477             blueprintModelRepository.delete(dbBlueprintModel.get())
478         } else {
479             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
480             throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
481         }
482     }
483
484     open suspend fun deleteBlueprintModel(name: String, version: String) {
485         blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
486     }
487
488     /**
489      * This is a CBA enrichBlueprint method
490      * Save the Zip File in archive location and extract the cba content.
491      * Populate the Enhancement Location
492      * Enhance the CBA content
493      * Compress the Enhanced Content
494      * Return back the the compressed content back to the caller.
495      *
496      * @param filePart filePart
497      * @return ResponseEntity<Resource>
498      * @throws BluePrintException BluePrintException
499      */
500     @Throws(BluePrintException::class)
501     open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
502         try {
503             val enhancedByteArray = enrichBlueprintFileSource(filePart)
504             return BluePrintEnhancerUtils.prepareResourceEntity("enhanced-cba.zip", enhancedByteArray)
505         } catch (e: BluePrintProcessorException) {
506             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
507             val errorMsg = "Error while enhancing the CBA package."
508             throw e.updateErrorMessage(
509                 DesignerApiDomains.DESIGNER_API, errorMsg,
510                 "Wrong CBA file provided, please verify and enrich Again."
511             )
512         } catch (e: Exception) {
513             throw httpProcessorException(
514                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
515                 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault()
516             )
517         }
518     }
519
520     /**
521      * This is a publishBlueprintModel method to change the status published to YES
522      * NOTE: this method is meant for enriched blueprints only.
523      *
524      * @param filePart filePart
525      * @return BlueprintModelSearch
526      * @throws BluePrintException BluePrintException
527      */
528     @Throws(BluePrintException::class)
529     open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
530         try {
531             return upload(filePart, true)
532         } catch (e: BluePrintProcessorException) {
533             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
534             val errorMsg = "Error in Publishing CBA."
535             throw e.updateErrorMessage(
536                 DesignerApiDomains.DESIGNER_API, errorMsg,
537                 "Wrong CBA provided, please verify and enrich your CBA."
538             )
539         } catch (e: Exception) {
540             throw httpProcessorException(
541                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
542                 "Error in Publishing CBA: ${e.message}", e.errorCauseOrDefault()
543             )
544         }
545     }
546
547     /**
548      * Enrich and publish the blueprint.
549      * NOTE: this method is meant for the unenriched vs publishBlueprint(filePart)
550      *       which is used for enriched blueprints.
551      *
552      * @param filePart filePart
553      * @return BlueprintModelSearch
554      * @throws BluePrintException BluePrintException
555      */
556     @Throws(BluePrintException::class)
557     open suspend fun enrichAndPublishBlueprint(filePart: FilePart): BlueprintModelSearch {
558         try {
559             val enhancedByteArray = enrichBlueprintFileSource(filePart)
560             return upload(enhancedByteArray, true)
561         } catch (e: BluePrintProcessorException) {
562             val errorMsg = "Error while enhancing and uploading the CBA package."
563             log.error(errorMsg, e)
564             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
565             throw e.updateErrorMessage(
566                 DesignerApiDomains.DESIGNER_API, errorMsg,
567                 "Wrong CBA file provided, please verify the source CBA."
568             )
569         } catch (e: Exception) {
570             log.error("Error enriching/uploading CBA", e)
571             throw httpProcessorException(
572                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
573                 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault()
574             )
575         }
576     }
577
578     /** Common CBA Save and Publish function for RestController and GRPC Handler, the [fileSource] may be
579      * byteArray or File Part type.*/
580     open suspend fun upload(fileSource: Any, validate: Boolean): BlueprintModelSearch {
581         val saveId = UUID.randomUUID().toString()
582         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
583         val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
584         try {
585             val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
586             when (fileSource) {
587                 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
588                 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
589             }
590             // Save the Copied file to Database
591             val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
592
593             return blueprintModelSearchRepository.findById(blueprintId)
594                 ?: throw httpProcessorException(
595                     ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
596                     String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId)
597                 )
598         } catch (e: BluePrintException) {
599             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
600             val errorMsg = "Error in Upload CBA."
601             throw e.updateErrorMessage(
602                 DesignerApiDomains.DESIGNER_API, errorMsg,
603                 "Wrong enriched CBA."
604             )
605         } catch (e: IOException) {
606             throw httpProcessorException(
607                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
608                 "Error in Upload CBA: ${e.errorMessageOrDefault()}", e.errorCauseOrDefault()
609             )
610         } finally {
611             // Clean blueprint script cache
612             val cacheKey = BluePrintFileUtils
613                 .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
614             BluePrintCompileCache.cleanClassLoader(cacheKey)
615             deleteNBDir(blueprintArchive)
616             deleteNBDir(blueprintWorking)
617         }
618     }
619
620     /** Common CBA download function for RestController and GRPC Handler, the [fileSource] may be
621      * byteArray or File Part type.*/
622     open fun download(name: String, version: String): ByteArray {
623         try {
624             val blueprintModel = getBlueprintModelByNameAndVersion(name, version)
625             return blueprintModel.blueprintModelContent?.content
626                 ?: throw httpProcessorException(
627                     ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
628                     "Error while downloading the CBA file: couldn't get model content"
629                 )
630         } catch (e: BluePrintException) {
631             e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
632             val errorMsg = "Fail to get Blueprint Model content."
633             throw e.updateErrorMessage(
634                 DesignerApiDomains.DESIGNER_API, errorMsg,
635                 "Wrong name and version was provide."
636             )
637         }
638     }
639
640     /** Common CBA Enrich function for RestController and GRPC Handler, the [fileSource] may be
641      * byteArray or File Part type.*/
642     open suspend fun enrichBlueprintFileSource(fileSource: Any): ByteArray {
643         val enhanceId = UUID.randomUUID().toString()
644         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
645         val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
646         try {
647             when (fileSource) {
648                 is FilePart ->
649                     BluePrintEnhancerUtils
650                         .copyFilePartToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
651                 is ByteArray ->
652                     BluePrintEnhancerUtils
653                         .copyByteArrayToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
654             } // Enhance the Blue Prints
655             bluePrintEnhancerService.enhance(blueprintWorkingDir)
656
657             return BluePrintEnhancerUtils.compressEnhanceDirAndReturnByteArray(blueprintWorkingDir, blueprintArchive)
658         } catch (e: BluePrintException) {
659             e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
660             val errorMsg = "Fail Enriching the CBA."
661             throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg)
662         } catch (e: IOException) {
663             throw httpProcessorException(
664                 ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
665                 "Error while Enriching the CBA file.", e.errorCauseOrDefault()
666             )
667         } finally {
668             BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
669         }
670     }
671
672     companion object {
673
674         private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
675         private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"
676     }
677 }