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