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