Merge "virtualenv lib changed. need to pin to older version."
[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.WorkFlowData
29 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowSpecRequest
30 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowSpecResponse
31 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowsResponse
32 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.load.BluePrintDatabaseLoadService
33 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.utils.BluePrintEnhancerUtils
34 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
35 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
36 import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
37 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
38 import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir
39 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
40 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
41 import org.onap.ccsdk.cds.controllerblueprints.core.logger
42 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
43 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
44 import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
45 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
46 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
47 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
48 import org.springframework.core.io.ByteArrayResource
49 import org.springframework.core.io.Resource
50 import org.springframework.data.domain.Page
51 import org.springframework.data.domain.PageRequest
52 import org.springframework.data.domain.Pageable
53 import org.springframework.http.HttpHeaders
54 import org.springframework.http.MediaType
55 import org.springframework.http.ResponseEntity
56 import org.springframework.http.codec.multipart.FilePart
57 import org.springframework.stereotype.Service
58 import org.springframework.transaction.annotation.Transactional
59 import java.io.IOException
60 import java.util.UUID
61
62 /**
63  * BlueprintModelHandler Purpose: Handler service to handle the request from BlurPrintModelRest
64  *
65  * @author Brinda Santh
66  * @version 1.0
67  */
68
69 @Service
70 open class BluePrintModelHandler(
71     private val bluePrintDatabaseLoadService: BluePrintDatabaseLoadService,
72     private val blueprintsProcessorCatalogService: BluePrintCatalogService,
73     private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
74     private val blueprintModelSearchRepository: BlueprintModelSearchRepository,
75     private val blueprintModelRepository: BlueprintModelRepository,
76     private val blueprintModelContentRepository: BlueprintModelContentRepository,
77     private val bluePrintEnhancerService: BluePrintEnhancerService
78 ) {
79
80     private val log = logger(BluePrintModelHandler::class)
81
82     open suspend fun bootstrapBlueprint(bootstrapRequest: BootstrapRequest) {
83         log.info(
84             "Bootstrap request with type load(${bootstrapRequest.loadModelType}), " +
85                     "resource dictionary load(${bootstrapRequest.loadResourceDictionary}) and " +
86                     "cba load(${bootstrapRequest.loadCBA})"
87         )
88         if (bootstrapRequest.loadModelType) {
89             bluePrintDatabaseLoadService.initModelTypes()
90         }
91         if (bootstrapRequest.loadResourceDictionary) {
92             bluePrintDatabaseLoadService.initResourceDictionary()
93         }
94         if (bootstrapRequest.loadCBA) {
95             bluePrintDatabaseLoadService.initBluePrintCatalog()
96         }
97     }
98
99     @Throws(BluePrintException::class)
100     open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest):
101             WorkFlowSpecResponse {
102         val basePath = blueprintsProcessorCatalogService.getFromDatabase(req
103                 .blueprintName, req.version)
104         log.info("blueprint base path $basePath")
105
106         val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
107         val workFlow = blueprintContext.workflowByName(req.workflowName)
108
109         val wfRes = WorkFlowSpecResponse()
110         wfRes.blueprintName = req.blueprintName
111         wfRes.version = req.version
112
113         val workFlowData = WorkFlowData()
114         workFlowData.workFlowName = req.workflowName
115         workFlowData.inputs = workFlow.inputs
116         workFlowData.outputs = workFlow.outputs
117
118         for ((k, v) in workFlow.inputs!!) {
119             addDataType(v.type, blueprintContext, wfRes)
120         }
121
122         for ((k, v) in workFlow.outputs!!) {
123             addDataType(v.type, blueprintContext, wfRes)
124         }
125         wfRes.workFlowData = workFlowData
126         return wfRes
127     }
128
129     private fun addDataType(name: String, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
130         var data = ctx.dataTypeByName(name)
131         if (data != null) {
132             res.dataTypes?.put(name, data)
133             addParentDataType(data, ctx, res)
134         }
135     }
136
137     private fun addParentDataType(data: DataType, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
138         for ((k, v) in data.properties!!) {
139             addDataType(v.type, ctx, res)
140         }
141     }
142
143     @Throws(BluePrintException::class)
144     open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse {
145         val basePath = blueprintsProcessorCatalogService.getFromDatabase(
146                 name, version)
147         log.info("blueprint base path $basePath")
148
149         var res = WorkFlowsResponse()
150         res.blueprintName = name
151         res.version = version
152
153         val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(
154                 basePath.toString())
155         if (blueprintContext.workflows() != null) {
156             res.workflows = blueprintContext.workflows()!!.keys
157         }
158         return res
159     }
160
161     /**
162      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
163      *
164      * @return List<BlueprintModelSearch> list of the controller blueprint archives
165     </BlueprintModelSearch> */
166     open fun allBlueprintModel(): List<BlueprintModelSearch> {
167         return blueprintModelSearchRepository.findAll()
168     }
169
170     /**
171      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
172      *
173      * @return List<BlueprintModelSearch> list of the controller blueprint archives
174     </BlueprintModelSearch> */
175     open fun allBlueprintModel(pageRequest: Pageable): Page<BlueprintModelSearch> {
176         return blueprintModelSearchRepository.findAll(pageRequest)
177     }
178
179     /**
180      * This is a saveBlueprintModel method
181      *
182      * @param filePart filePart
183      * @return Mono<BlueprintModelSearch>
184      * @throws BluePrintException BluePrintException
185     </BlueprintModelSearch> */
186     @Throws(BluePrintException::class)
187     open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
188         try {
189             return upload(filePart, false)
190         } catch (e: IOException) {
191             throw BluePrintException(
192                 ErrorCode.IO_FILE_INTERRUPT.value,
193                 "Error in Save CBA: ${e.message}", e
194             )
195         }
196     }
197
198     /**
199      * This is a searchBlueprintModels method
200      *
201      * @param tags tags
202      * @return List<BlueprintModelSearch>
203     </BlueprintModelSearch> */
204     open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
205         return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
206     }
207
208     /**
209      * This is a getBlueprintModelSearchByNameAndVersion method
210      *
211      * @param name name
212      * @param version version
213      * @return BlueprintModelSearch
214      * @throws BluePrintException BluePrintException
215      */
216     @Throws(BluePrintException::class)
217     open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch? {
218         return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
219             /*?: throw BluePrintException(
220                 ErrorCode.RESOURCE_NOT_FOUND.value,
221                 String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
222             )*/
223     }
224
225     /**
226      * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
227      *
228      * @param name name
229      * @param version version
230      * @return ResponseEntity<Resource>
231      * @throws BluePrintException BluePrintException
232     </Resource> */
233     @Throws(BluePrintException::class)
234     open fun downloadBlueprintModelFileByNameAndVersion(
235         name: String,
236         version: String
237     ): ResponseEntity<Resource> {
238         try {
239             val archiveByteArray = download(name, version)
240             val fileName = "${name}_$version.zip"
241             return prepareResourceEntity(fileName, archiveByteArray)
242         } catch (e: BluePrintException) {
243             throw BluePrintException(
244                 ErrorCode.RESOURCE_NOT_FOUND.value,
245                 String.format("Error while " + "downloading the CBA file: %s", e.message), e
246             )
247         }
248     }
249
250     /**
251      * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
252      *
253      * @return ResponseEntity<Resource>
254      * @throws BluePrintException BluePrintException
255     </Resource> */
256     @Throws(BluePrintException::class)
257     open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
258         val blueprintModel: BlueprintModel
259         try {
260             blueprintModel = getBlueprintModel(id)
261         } catch (e: BluePrintException) {
262             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, String.format("Error while " + "downloading the CBA file: %s", e.message), e)
263         }
264
265         val fileName = "${blueprintModel.artifactName}_${blueprintModel.artifactVersion}.zip"
266         val file = blueprintModel.blueprintModelContent?.content
267             ?: throw BluePrintException(
268                 ErrorCode.RESOURCE_NOT_FOUND.value,
269                 String.format("Error while downloading the CBA file: couldn't get model content")
270             )
271         return prepareResourceEntity(fileName, file)
272     }
273
274     /**
275      * @return ResponseEntity<Resource>
276     </Resource> */
277     private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
278         return ResponseEntity.ok()
279             .contentType(MediaType.parseMediaType("text/plain"))
280             .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
281             .body(ByteArrayResource(file))
282     }
283
284     /**
285      * This is a getBlueprintModel method
286      *
287      * @param id id
288      * @return BlueprintModel
289      * @throws BluePrintException BluePrintException
290      */
291     @Throws(BluePrintException::class)
292     open fun getBlueprintModel(id: String): BlueprintModel {
293         val blueprintModel: BlueprintModel
294         val dbBlueprintModel = blueprintModelRepository.findById(id)
295         if (dbBlueprintModel.isPresent) {
296             blueprintModel = dbBlueprintModel.get()
297         } else {
298             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
299             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
300         }
301         return blueprintModel
302     }
303
304     /**
305      * This is a getBlueprintModelByNameAndVersion method
306      *
307      * @param name name
308      * @param version version
309      * @return BlueprintModel
310      * @throws BluePrintException BluePrintException
311      */
312     @Throws(BluePrintException::class)
313     open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
314         val blueprintModel = blueprintModelRepository
315             .findByArtifactNameAndArtifactVersion(name, version)
316         if (blueprintModel != null) {
317             return blueprintModel
318         } else {
319             val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
320             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
321         }
322     }
323
324     /**
325      * This is a getBlueprintModelSearch method
326      *
327      * @param id id
328      * @return BlueprintModelSearch
329      * @throws BluePrintException BluePrintException
330      */
331     @Throws(BluePrintException::class)
332     open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
333         return blueprintModelSearchRepository.findById(id)
334             ?: throw BluePrintException(
335                 ErrorCode.RESOURCE_NOT_FOUND.value,
336                 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
337             )
338     }
339
340     /**
341      * This is a searchBluePrintModelsByKeyWord method to retrieve specific  BlueprintModel in Database
342      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type
343      * @author Shaaban Ebrahim
344      * @param keyWord
345      *
346      * @return List<BlueprintModelSearch> list of the controller blueprint
347     </BlueprintModelSearch> */
348     open fun searchBluePrintModelsByKeyWord(keyWord: String): List<BlueprintModelSearch> {
349         return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
350             keyWord, keyWord, keyWord, keyWord, keyWord
351         )
352     }
353
354     /**
355      * This is a searchBluePrintModelsByKeyWordPagebale method to retrieve specific  BlueprintModel in Database
356      * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type and pageable
357      * @author Shaaban Ebrahim
358      * @param keyWord
359      *
360      * @return List<BlueprintModelSearch> list of the controller blueprint
361     </BlueprintModelSearch> */
362     open fun searchBluePrintModelsByKeyWordPaged(keyWord: String, pageRequest: PageRequest): Page<BlueprintModelSearch> {
363         return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
364             keyWord,
365             keyWord,
366             keyWord,
367             keyWord,
368             keyWord,
369             pageRequest
370         )
371     }
372
373     /**
374      * This is a deleteBlueprintModel method
375      *
376      * @param id id
377      * @throws BluePrintException BluePrintException
378      */
379     @Transactional
380     @Throws(BluePrintException::class)
381     open fun deleteBlueprintModel(id: String) {
382         val dbBlueprintModel = blueprintModelRepository.findById(id)
383         if (dbBlueprintModel != null && dbBlueprintModel.isPresent) {
384             blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
385             blueprintModelRepository.delete(dbBlueprintModel.get())
386         } else {
387             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
388             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
389         }
390     }
391
392     open suspend fun deleteBlueprintModel(name: String, version: String) {
393         blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
394     }
395
396     /**
397      * This is a CBA enrichBlueprint method
398      * Save the Zip File in archive location and extract the cba content.
399      * Populate the Enhancement Location
400      * Enhance the CBA content
401      * Compress the Enhanced Content
402      * Return back the the compressed content back to the caller.
403      *
404      * @param filePart filePart
405      * @return ResponseEntity<Resource>
406      * @throws BluePrintException BluePrintException
407      */
408     @Throws(BluePrintException::class)
409     open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
410         try {
411             val enhancedByteArray = enrichBlueprintFileSource(filePart)
412             return BluePrintEnhancerUtils.prepareResourceEntity("enhanced-cba.zip", enhancedByteArray)
413         } catch (e: IOException) {
414             throw BluePrintException(
415                 ErrorCode.IO_FILE_INTERRUPT.value,
416                 "Error in Enriching CBA: ${e.message}", e
417             )
418         }
419     }
420
421     /**
422      * This is a publishBlueprintModel method to change the status published to YES
423      *
424      * @param filePart filePart
425      * @return BlueprintModelSearch
426      * @throws BluePrintException BluePrintException
427      */
428     @Throws(BluePrintException::class)
429     open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
430         try {
431             return upload(filePart, true)
432         } catch (e: Exception) {
433             throw BluePrintException(
434                 ErrorCode.IO_FILE_INTERRUPT.value,
435                 "Error in Publishing CBA: ${e.message}", e
436             )
437         }
438     }
439
440     /** Common CBA Save and Publish function for RestController and GRPC Handler, the [fileSource] may be
441      * byteArray or File Part type.*/
442     open suspend fun upload(fileSource: Any, validate: Boolean): BlueprintModelSearch {
443         val saveId = UUID.randomUUID().toString()
444         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
445         val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
446         try {
447             val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
448             when (fileSource) {
449                 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
450                 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
451             }
452             // Save the Copied file to Database
453             val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
454
455             return blueprintModelSearchRepository.findById(blueprintId)
456                 ?: throw BluePrintException(
457                     ErrorCode.RESOURCE_NOT_FOUND.value,
458                     String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId)
459                 )
460         } catch (e: IOException) {
461             throw BluePrintException(
462                 ErrorCode.IO_FILE_INTERRUPT.value,
463                 "Error in Upload CBA: ${e.message}", e
464             )
465         } finally {
466             // Clean blueprint script cache
467             val cacheKey = BluePrintFileUtils
468                 .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
469             BluePrintCompileCache.cleanClassLoader(cacheKey)
470             deleteNBDir(blueprintArchive)
471             deleteNBDir(blueprintWorking)
472         }
473     }
474
475     /** Common CBA download function for RestController and GRPC Handler, the [fileSource] may be
476      * byteArray or File Part type.*/
477     open fun download(name: String, version: String): ByteArray {
478         try {
479             val blueprintModel = getBlueprintModelByNameAndVersion(name, version)
480             return blueprintModel.blueprintModelContent?.content
481                 ?: throw BluePrintException(
482                     ErrorCode.RESOURCE_NOT_FOUND.value,
483                     String.format("Error while downloading the CBA file: couldn't get model content")
484                 )
485         } catch (e: BluePrintException) {
486             throw BluePrintException(
487                 ErrorCode.RESOURCE_NOT_FOUND.value,
488                 String.format("Error while " + "downloading the CBA file: %s", e.message), e
489             )
490         }
491     }
492
493     /** Common CBA Enrich function for RestController and GRPC Handler, the [fileSource] may be
494      * byteArray or File Part type.*/
495     open suspend fun enrichBlueprintFileSource(fileSource: Any): ByteArray {
496         val enhanceId = UUID.randomUUID().toString()
497         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
498         val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
499         try {
500             when (fileSource) {
501                 is FilePart -> BluePrintEnhancerUtils
502                     .copyFilePartToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
503                 is ByteArray -> BluePrintEnhancerUtils
504                     .copyByteArrayToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
505             } // Enhance the Blue Prints
506             bluePrintEnhancerService.enhance(blueprintWorkingDir)
507
508             return BluePrintEnhancerUtils.compressEnhanceDirAndReturnByteArray(blueprintWorkingDir, blueprintArchive)
509         } catch (e: IOException) {
510             throw BluePrintException(
511                 ErrorCode.IO_FILE_INTERRUPT.value,
512                 "Error in Enriching CBA: ${e.message}", e
513             )
514         } finally {
515             BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
516         }
517     }
518
519     companion object {
520
521         private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
522         private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"
523     }
524 }