Move GRPC management api to designer api.
[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 kotlinx.coroutines.reactive.awaitSingle
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.utils.BluePrintEnhancerUtils
28 import org.onap.ccsdk.cds.controllerblueprints.core.*
29 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
30 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
31 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
32 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
33 import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
34 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
35 import org.slf4j.LoggerFactory
36 import org.springframework.core.io.ByteArrayResource
37 import org.springframework.core.io.Resource
38 import org.springframework.http.HttpHeaders
39 import org.springframework.http.MediaType
40 import org.springframework.http.ResponseEntity
41 import org.springframework.http.codec.multipart.FilePart
42 import org.springframework.stereotype.Service
43 import org.springframework.transaction.annotation.Transactional
44 import java.io.File
45 import java.io.IOException
46 import java.util.*
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 = LoggerFactory.getLogger(BluePrintModelHandler::class.java)!!
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 saveBlueprintModel method
76      *
77      * @param filePart filePart
78      * @return Mono<BlueprintModelSearch>
79      * @throws BluePrintException BluePrintException
80     </BlueprintModelSearch> */
81     @Throws(BluePrintException::class)
82     open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
83         try {
84             val blueprintId = upload(filePart, false)
85             // Check and Return the Saved File
86             val blueprintModelSearch = blueprintModelSearchRepository.findById(blueprintId)
87                     ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
88                             String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId))
89
90             log.info("Save successful for blueprint(${blueprintModelSearch.artifactName}) " +
91                     "version(${blueprintModelSearch.artifactVersion})")
92             return blueprintModelSearch
93         } catch (e: IOException) {
94             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
95                     "Error in Save CBA: ${e.message}", e)
96         }
97     }
98
99
100     /**
101      * This is a searchBlueprintModels method
102      *
103      * @param tags tags
104      * @return List<BlueprintModelSearch>
105     </BlueprintModelSearch> */
106     open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
107         return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
108     }
109
110     /**
111      * This is a getBlueprintModelSearchByNameAndVersion method
112      *
113      * @param name name
114      * @param version version
115      * @return BlueprintModelSearch
116      * @throws BluePrintException BluePrintException
117      */
118     @Throws(BluePrintException::class)
119     open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch {
120         return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
121                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
122                         String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version))
123
124     }
125
126     /**
127      * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
128      *
129      * @param name name
130      * @param version version
131      * @return ResponseEntity<Resource>
132      * @throws BluePrintException BluePrintException
133     </Resource> */
134     @Throws(BluePrintException::class)
135     open fun downloadBlueprintModelFileByNameAndVersion(name: String,
136                                                         version: String): ResponseEntity<Resource> {
137         val blueprintModel: BlueprintModel
138         try {
139             blueprintModel = getBlueprintModelByNameAndVersion(name, version)
140         } catch (e: BluePrintException) {
141             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
142                     String.format("Error while " + "downloading the CBA file: %s", e.message), e)
143         }
144
145         val fileName = blueprintModel.id + ".zip"
146         val file = blueprintModel.blueprintModelContent?.content
147                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
148                         String.format("Error while downloading the CBA file: couldn't get model content"))
149         return prepareResourceEntity(fileName, file)
150     }
151
152     /**
153      * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
154      *
155      * @return ResponseEntity<Resource>
156      * @throws BluePrintException BluePrintException
157     </Resource> */
158     @Throws(BluePrintException::class)
159     open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
160         val blueprintModel: BlueprintModel
161         try {
162             blueprintModel = getBlueprintModel(id)
163         } catch (e: BluePrintException) {
164             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, String.format("Error while " + "downloading the CBA file: %s", e.message), e)
165         }
166
167         val fileName = blueprintModel.id + ".zip"
168         val file = blueprintModel.blueprintModelContent?.content
169                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
170                         String.format("Error while downloading the CBA file: couldn't get model content"))
171         return prepareResourceEntity(fileName, file)
172     }
173
174     /**
175      * @return ResponseEntity<Resource>
176     </Resource> */
177     private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
178         return ResponseEntity.ok()
179                 .contentType(MediaType.parseMediaType("text/plain"))
180                 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
181                 .body(ByteArrayResource(file))
182     }
183
184     /**
185      * This is a getBlueprintModel method
186      *
187      * @param id id
188      * @return BlueprintModel
189      * @throws BluePrintException BluePrintException
190      */
191     @Throws(BluePrintException::class)
192     open fun getBlueprintModel(id: String): BlueprintModel {
193         val blueprintModel: BlueprintModel
194         val dbBlueprintModel = blueprintModelRepository.findById(id)
195         if (dbBlueprintModel.isPresent) {
196             blueprintModel = dbBlueprintModel.get()
197         } else {
198             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
199             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
200         }
201         return blueprintModel
202     }
203
204     /**
205      * This is a getBlueprintModelByNameAndVersion method
206      *
207      * @param name name
208      * @param version version
209      * @return BlueprintModel
210      * @throws BluePrintException BluePrintException
211      */
212     @Throws(BluePrintException::class)
213     open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
214         val blueprintModel = blueprintModelRepository
215                 .findByArtifactNameAndArtifactVersion(name, version)
216         if (blueprintModel != null) {
217             return blueprintModel
218         } else {
219             val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
220             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
221         }
222     }
223
224     /**
225      * This is a getBlueprintModelSearch method
226      *
227      * @param id id
228      * @return BlueprintModelSearch
229      * @throws BluePrintException BluePrintException
230      */
231     @Throws(BluePrintException::class)
232     open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
233         return blueprintModelSearchRepository.findById(id)
234                 ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
235                         String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id))
236     }
237
238     /**
239      * This is a deleteBlueprintModel method
240      *
241      * @param id id
242      * @throws BluePrintException BluePrintException
243      */
244     @Transactional
245     @Throws(BluePrintException::class)
246     open fun deleteBlueprintModel(id: String) {
247         val dbBlueprintModel = blueprintModelRepository.findById(id)
248         if (dbBlueprintModel != null && dbBlueprintModel.isPresent) {
249             blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
250             blueprintModelRepository.delete(dbBlueprintModel.get())
251         } else {
252             val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
253             throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
254         }
255     }
256
257     open suspend fun deleteBlueprintModel(name: String, version: String) {
258         blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
259     }
260
261     /**
262      * This is a CBA enrichBlueprint method
263      * Save the Zip File in archive location and extract the cba content.
264      * Populate the Enhancement Location
265      * Enhance the CBA content
266      * Compress the Enhanced Content
267      * Return back the the compressed content back to the caller.
268      *
269      * @param filePart filePart
270      * @return ResponseEntity<Resource>
271      * @throws BluePrintException BluePrintException
272      */
273     @Throws(BluePrintException::class)
274     open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
275         val enhanceId = UUID.randomUUID().toString()
276         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
277         val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
278         try {
279             BluePrintEnhancerUtils.decompressFilePart(filePart, blueprintArchive, blueprintWorkingDir)
280
281             // Enhance the Blue Prints
282             bluePrintEnhancerService.enhance(blueprintWorkingDir)
283
284             return BluePrintEnhancerUtils.compressToFilePart(blueprintWorkingDir, blueprintArchive)
285
286         } catch (e: IOException) {
287             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
288                     "Error in Enriching CBA: ${e.message}", e)
289         } finally {
290             BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
291         }
292     }
293
294     /**
295      * This is a publishBlueprintModel method to change the status published to YES
296      *
297      * @param filePart filePart
298      * @return BlueprintModelSearch
299      * @throws BluePrintException BluePrintException
300      */
301     @Throws(BluePrintException::class)
302     open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
303         try {
304             val blueprintId = upload(filePart, true)
305
306             return blueprintModelSearchRepository.findById(blueprintId)
307                     ?: throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value,
308                             String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId))
309
310         } catch (e: Exception) {
311             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
312                     "Error in Publishing CBA: ${e.message}", e)
313         }
314     }
315
316     //TODO("Combine Rest and GRPC Handler")
317     suspend fun upload(filePart: FilePart, validate: Boolean): String {
318         val saveId = UUID.randomUUID().toString()
319         val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
320         val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
321         try {
322             val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
323             compressedFile.parentFile.reCreateNBDirs()
324             // Copy the File Part to Local File
325             copyFromFilePart(filePart, compressedFile)
326             // Save the Copied file to Database
327             return blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
328         } catch (e: IOException) {
329             throw BluePrintException(ErrorCode.IO_FILE_INTERRUPT.value,
330                     "Error in Upload CBA: ${e.message}", e)
331         } finally {
332             // Clean blueprint script cache
333             val cacheKey = BluePrintFileUtils
334                     .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
335             BluePrintCompileCache.cleanClassLoader(cacheKey)
336             deleteNBDir(blueprintArchive)
337             deleteNBDir(blueprintWorking)
338         }
339     }
340
341     private suspend fun copyFromFilePart(filePart: FilePart, targetFile: File): File {
342         return filePart.transferTo(targetFile)
343                 .thenReturn(targetFile)
344                 .awaitSingle()
345     }
346
347     companion object {
348
349         private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
350         private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"
351     }
352 }