add get blueprints API with pagination and sorting
[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  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 package org.onap.ccsdk.cds.blueprintsprocessor.designer.api.handler
20
21 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModel
22 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModelSearch
23 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelContentRepository
24 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelRepository
25 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository.BlueprintModelSearchRepository
26 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.utils.BluePrintEnhancerUtils
27 import org.onap.ccsdk.cds.controllerblueprints.core.*
28 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
29 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
30 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
31 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
32 import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
33 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
34 import org.springframework.core.io.ByteArrayResource
35 import org.springframework.core.io.Resource
36 import org.springframework.data.domain.Page
37 import org.springframework.http.HttpHeaders
38 import org.springframework.http.MediaType
39 import org.springframework.http.ResponseEntity
40 import org.springframework.http.codec.multipart.FilePart
41 import org.springframework.stereotype.Service
42 import org.springframework.transaction.annotation.Transactional
43 import java.io.IOException
44 import java.util.*
45 import org.springframework.data.domain.Pageable
46
47
48 /**
49  * BlueprintModelHandler Purpose: Handler service to handle the request from BlurPrintModelRest
50  *
51  * @author Brinda Santh
52  * @version 1.0
53  */
54
55 @Service
56 open class BluePrintModelHandler(private val blueprintsProcessorCatalogService: BluePrintCatalogService,
57                                  private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
58                                  private val blueprintModelSearchRepository: BlueprintModelSearchRepository,
59                                  private val blueprintModelRepository: BlueprintModelRepository,
60                                  private val blueprintModelContentRepository: BlueprintModelContentRepository,
61                                  private val bluePrintEnhancerService: BluePrintEnhancerService) {
62
63     private val log = logger(BluePrintModelHandler::class)
64
65     /**
66      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
67      *
68      * @return List<BlueprintModelSearch> list of the controller blueprint archives
69     </BlueprintModelSearch> */
70     open fun allBlueprintModel(): List<BlueprintModelSearch> {
71         return blueprintModelSearchRepository.findAll()
72     }
73
74     /**
75      * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
76      *
77      * @return List<BlueprintModelSearch> list of the controller blueprint archives
78     </BlueprintModelSearch> */
79     open fun allBlueprintModel(pageRequest: Pageable): Page<BlueprintModelSearch> {
80         return blueprintModelSearchRepository.findAll(pageRequest)
81     }
82
83     /**
84      * This is a saveBlueprintModel method
85      *
86      * @param filePart filePart
87      * @return Mono<BlueprintModelSearch>
88      * @throws BluePrintException BluePrintException
89     </BlueprintModelSearch> */
90     @Throws(BluePrintException::class)
91     open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
92         try {
93             return upload(filePart, false)
94         } catch (e: IOException) {
95             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
96                     "Error in Save CBA: ${e.message}", e)
97         }
98     }
99
100
101     /**
102      * This is a searchBlueprintModels method
103      *
104      * @param tags tags
105      * @return List<BlueprintModelSearch>
106     </BlueprintModelSearch> */
107     open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
108         return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
109     }
110
111     /**
112      * This is a getBlueprintModelSearchByNameAndVersion method
113      *
114      * @param name name
115      * @param version version
116      * @return BlueprintModelSearch
117      * @throws BluePrintException BluePrintException
118      */
119     @Throws(BluePrintException::class)
120     open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch {
121         return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
122                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
123                         String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version))
124
125     }
126
127     /**
128      * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
129      *
130      * @param name name
131      * @param version version
132      * @return ResponseEntity<Resource>
133      * @throws BluePrintException BluePrintException
134     </Resource> */
135     @Throws(BluePrintException::class)
136     open fun downloadBlueprintModelFileByNameAndVersion(name: String,
137                                                         version: String): ResponseEntity<Resource> {
138         try {
139             val archiveByteArray = download(name, version)
140             val fileName = "${name}_$version.zip"
141             return prepareResourceEntity(fileName, archiveByteArray)
142         } catch (e: BluePrintException) {
143             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
144                     String.format("Error while " + "downloading the CBA file: %s", e.message), e)
145         }
146     }
147
148     /**
149      * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
150      *
151      * @return ResponseEntity<Resource>
152      * @throws BluePrintException BluePrintException
153     </Resource> */
154     @Throws(BluePrintException::class)
155     open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
156         val blueprintModel: BlueprintModel
157         try {
158             blueprintModel = getBlueprintModel(id)
159         } catch (e: BluePrintException) {
160             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, String.format("Error while " + "downloading the CBA file: %s", e.message), e)
161         }
162
163         val fileName = "${blueprintModel.artifactName}_${blueprintModel.artifactVersion}.zip"
164         val file = blueprintModel.blueprintModelContent?.content
165                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
166                         String.format("Error while downloading the CBA file: couldn't get model content"))
167         return prepareResourceEntity(fileName, file)
168     }
169
170     /**
171      * @return ResponseEntity<Resource>
172     </Resource> */
173     private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
174         return ResponseEntity.ok()
175                 .contentType(MediaType.parseMediaType("text/plain"))
176                 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
177                 .body(ByteArrayResource(file))
178     }
179
180     /**
181      * This is a getBlueprintModel method
182      *
183      * @param id id
184      * @return BlueprintModel
185      * @throws BluePrintException BluePrintException
186      */
187     @Throws(BluePrintException::class)
188     open fun getBlueprintModel(id: String): BlueprintModel {
189         val blueprintModel: BlueprintModel
190         val dbBlueprintModel = blueprintModelRepository.findById(id)
191         if (dbBlueprintModel.isPresent) {
192             blueprintModel = dbBlueprintModel.get()
193         } else {
194             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
195             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
196         }
197         return blueprintModel
198     }
199
200     /**
201      * This is a getBlueprintModelByNameAndVersion method
202      *
203      * @param name name
204      * @param version version
205      * @return BlueprintModel
206      * @throws BluePrintException BluePrintException
207      */
208     @Throws(BluePrintException::class)
209     open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
210         val blueprintModel = blueprintModelRepository
211                 .findByArtifactNameAndArtifactVersion(name, version)
212         if (blueprintModel != null) {
213             return blueprintModel
214         } else {
215             val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
216             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
217         }
218     }
219
220     /**
221      * This is a getBlueprintModelSearch method
222      *
223      * @param id id
224      * @return BlueprintModelSearch
225      * @throws BluePrintException BluePrintException
226      */
227     @Throws(BluePrintException::class)
228     open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
229         return blueprintModelSearchRepository.findById(id)
230                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
231                         String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id))
232     }
233
234     /**
235      * This is a deleteBlueprintModel method
236      *
237      * @param id id
238      * @throws BluePrintException BluePrintException
239      */
240     @Transactional
241     @Throws(BluePrintException::class)
242     open fun deleteBlueprintModel(id: String) {
243         val dbBlueprintModel = blueprintModelRepository.findById(id)
244         if (dbBlueprintModel != null && dbBlueprintModel.isPresent) {
245             blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
246             blueprintModelRepository.delete(dbBlueprintModel.get())
247         } else {
248             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
249             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
250         }
251     }
252
253     open suspend fun deleteBlueprintModel(name: String, version: String) {
254         blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
255     }
256
257     /**
258      * This is a CBA enrichBlueprint method
259      * Save the Zip File in archive location and extract the cba content.
260      * Populate the Enhancement Location
261      * Enhance the CBA content
262      * Compress the Enhanced Content
263      * Return back the the compressed content back to the caller.
264      *
265      * @param filePart filePart
266      * @return ResponseEntity<Resource>
267      * @throws BluePrintException BluePrintException
268      */
269     @Throws(BluePrintException::class)
270     open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
271         try {
272             val enhancedByteArray = enrichBlueprintFileSource(filePart)
273             return BluePrintEnhancerUtils.prepareResourceEntity("enhanced-cba.zip", enhancedByteArray)
274         } catch (e: IOException) {
275             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
276                     "Error in Enriching CBA: ${e.message}", e)
277         }
278     }
279
280     /**
281      * This is a publishBlueprintModel method to change the status published to YES
282      *
283      * @param filePart filePart
284      * @return BlueprintModelSearch
285      * @throws BluePrintException BluePrintException
286      */
287     @Throws(BluePrintException::class)
288     open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
289         try {
290             return upload(filePart, true)
291         } catch (e: Exception) {
292             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
293                     "Error in Publishing CBA: ${e.message}", e)
294         }
295     }
296
297     /** Common CBA Save and Publish function for RestController and GRPC Handler, the [fileSource] may be
298      * byteArray or File Part type.*/
299     open suspend fun upload(fileSource: Any, validate: Boolean): BlueprintModelSearch {
300         val saveId = UUID.randomUUID().toString()
301         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
302         val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
303         try {
304             val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
305             when (fileSource) {
306                 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
307                 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
308             }
309             // Save the Copied file to Database
310             val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
311
312             return blueprintModelSearchRepository.findById(blueprintId)
313                     ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
314                             String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId))
315
316         } catch (e: IOException) {
317             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
318                     "Error in Upload CBA: ${e.message}", e)
319         } finally {
320             // Clean blueprint script cache
321             val cacheKey = BluePrintFileUtils
322                     .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
323             BluePrintCompileCache.cleanClassLoader(cacheKey)
324             deleteNBDir(blueprintArchive)
325             deleteNBDir(blueprintWorking)
326         }
327     }
328
329     /** Common CBA download function for RestController and GRPC Handler, the [fileSource] may be
330      * byteArray or File Part type.*/
331     open fun download(name: String, version: String): ByteArray {
332         try {
333             val blueprintModel = getBlueprintModelByNameAndVersion(name, version)
334             return blueprintModel.blueprintModelContent?.content
335                     ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
336                             String.format("Error while downloading the CBA file: couldn't get model content"))
337         } catch (e: BluePrintException) {
338             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
339                     String.format("Error while " + "downloading the CBA file: %s", e.message), e)
340         }
341     }
342
343     /** Common CBA Enrich function for RestController and GRPC Handler, the [fileSource] may be
344      * byteArray or File Part type.*/
345     open suspend fun enrichBlueprintFileSource(fileSource: Any): ByteArray {
346         val enhanceId = UUID.randomUUID().toString()
347         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
348         val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
349         try {
350             when (fileSource) {
351                 is FilePart -> BluePrintEnhancerUtils
352                         .copyFilePartToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
353                 is ByteArray -> BluePrintEnhancerUtils
354                         .copyByteArrayToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
355             }            // Enhance the Blue Prints
356             bluePrintEnhancerService.enhance(blueprintWorkingDir)
357
358             return BluePrintEnhancerUtils.compressEnhanceDirAndReturnByteArray(blueprintWorkingDir, blueprintArchive)
359
360         } catch (e: IOException) {
361             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
362                     "Error in Enriching CBA: ${e.message}", e)
363         } finally {
364             BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
365         }
366     }
367
368     companion object {
369
370         private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
371         private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"
372     }
373 }