2 * Copyright © 2017-2018 AT&T Intellectual Property.
3 * Modifications Copyright © 2019 Bell Canada.
4 * Modifications Copyright © 2019 IBM.
5 * Modifications Copyright © 2019 Orange.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 package org.onap.ccsdk.cds.blueprintsprocessor.designer.api.handler
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
63 * BlueprintModelHandler Purpose: Handler service to handle the request from BlurPrintModelRest
65 * @author Brinda Santh
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
80 private val log = logger(BluePrintModelHandler::class)
82 open suspend fun bootstrapBlueprint(bootstrapRequest: BootstrapRequest) {
84 "Bootstrap request with type load(${bootstrapRequest.loadModelType}), " +
85 "resource dictionary load(${bootstrapRequest.loadResourceDictionary}) and " +
86 "cba load(${bootstrapRequest.loadCBA})"
88 if (bootstrapRequest.loadModelType) {
89 bluePrintDatabaseLoadService.initModelTypes()
91 if (bootstrapRequest.loadResourceDictionary) {
92 bluePrintDatabaseLoadService.initResourceDictionary()
94 if (bootstrapRequest.loadCBA) {
95 bluePrintDatabaseLoadService.initBluePrintCatalog()
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")
106 val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
107 val workFlow = blueprintContext.workflowByName(req.workflowName)
109 val wfRes = WorkFlowSpecResponse()
110 wfRes.blueprintName = req.blueprintName
111 wfRes.version = req.version
113 val workFlowData = WorkFlowData()
114 workFlowData.workFlowName = req.workflowName
115 workFlowData.inputs = workFlow.inputs
116 workFlowData.outputs = workFlow.outputs
118 for ((k, v) in workFlow.inputs!!) {
119 addDataType(v.type, blueprintContext, wfRes)
122 for ((k, v) in workFlow.outputs!!) {
123 addDataType(v.type, blueprintContext, wfRes)
125 wfRes.workFlowData = workFlowData
129 private fun addDataType(name: String, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
130 var data = ctx.dataTypeByName(name)
132 res.dataTypes?.put(name, data)
133 addParentDataType(data, ctx, res)
137 private fun addParentDataType(data: DataType, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
138 for ((k, v) in data.properties!!) {
139 addDataType(v.type, ctx, res)
143 @Throws(BluePrintException::class)
144 open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse {
145 val basePath = blueprintsProcessorCatalogService.getFromDatabase(
147 log.info("blueprint base path $basePath")
149 var res = WorkFlowsResponse()
150 res.blueprintName = name
151 res.version = version
153 val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(
155 if (blueprintContext.workflows() != null) {
156 res.workflows = blueprintContext.workflows()!!.keys
162 * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
164 * @return List<BlueprintModelSearch> list of the controller blueprint archives
165 </BlueprintModelSearch> */
166 open fun allBlueprintModel(): List<BlueprintModelSearch> {
167 return blueprintModelSearchRepository.findAll()
171 * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
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)
180 * This is a saveBlueprintModel method
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 {
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
199 * This is a searchBlueprintModels method
202 * @return List<BlueprintModelSearch>
203 </BlueprintModelSearch> */
204 open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
205 return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
209 * This is a getBlueprintModelSearchByNameAndVersion method
212 * @param version version
213 * @return BlueprintModelSearch
214 * @throws BluePrintException BluePrintException
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)
226 * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
229 * @param version version
230 * @return ResponseEntity<Resource>
231 * @throws BluePrintException BluePrintException
233 @Throws(BluePrintException::class)
234 open fun downloadBlueprintModelFileByNameAndVersion(
237 ): ResponseEntity<Resource> {
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
251 * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
253 * @return ResponseEntity<Resource>
254 * @throws BluePrintException BluePrintException
256 @Throws(BluePrintException::class)
257 open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
258 val blueprintModel: BlueprintModel
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)
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")
271 return prepareResourceEntity(fileName, file)
275 * @return ResponseEntity<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))
285 * This is a getBlueprintModel method
288 * @return BlueprintModel
289 * @throws BluePrintException BluePrintException
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()
298 val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
299 throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
301 return blueprintModel
305 * This is a getBlueprintModelByNameAndVersion method
308 * @param version version
309 * @return BlueprintModel
310 * @throws BluePrintException BluePrintException
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
319 val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
320 throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
325 * This is a getBlueprintModelSearch method
328 * @return BlueprintModelSearch
329 * @throws BluePrintException BluePrintException
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)
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
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
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
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(
374 * This is a deleteBlueprintModel method
377 * @throws BluePrintException BluePrintException
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())
387 val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
388 throw BluePrintException(ErrorCode.RESOURCE_NOT_FOUND.value, msg)
392 open suspend fun deleteBlueprintModel(name: String, version: String) {
393 blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
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.
404 * @param filePart filePart
405 * @return ResponseEntity<Resource>
406 * @throws BluePrintException BluePrintException
408 @Throws(BluePrintException::class)
409 open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
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
422 * This is a publishBlueprintModel method to change the status published to YES
424 * @param filePart filePart
425 * @return BlueprintModelSearch
426 * @throws BluePrintException BluePrintException
428 @Throws(BluePrintException::class)
429 open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
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
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)
447 val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
449 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
450 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
452 // Save the Copied file to Database
453 val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
455 return blueprintModelSearchRepository.findById(blueprintId)
456 ?: throw BluePrintException(
457 ErrorCode.RESOURCE_NOT_FOUND.value,
458 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId)
460 } catch (e: IOException) {
461 throw BluePrintException(
462 ErrorCode.IO_FILE_INTERRUPT.value,
463 "Error in Upload CBA: ${e.message}", e
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)
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 {
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")
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
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)
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)
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
515 BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
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"