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