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.DesignerApiDomains
28 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.BootstrapRequest
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.WorkFlowData
32 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.WorkFlowsResponse
33 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.load.BluePrintDatabaseLoadService
34 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.utils.BluePrintEnhancerUtils
35 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
36 import org.onap.ccsdk.cds.controllerblueprints.core.logger
37 import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException
38 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
39 import org.onap.ccsdk.cds.controllerblueprints.core.updateErrorMessage
40 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
41 import org.onap.ccsdk.cds.controllerblueprints.core.normalizedFile
42 import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir
43 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
44 import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType
45 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
46 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService
47 import org.onap.ccsdk.cds.controllerblueprints.core.scripts.BluePrintCompileCache
48 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
49 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintFileUtils
50 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
51 import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes
52 import org.onap.ccsdk.cds.error.catalog.core.utils.errorCauseOrDefault
53 import org.onap.ccsdk.cds.error.catalog.core.utils.errorMessageOrDefault
54 import org.springframework.core.io.ByteArrayResource
55 import org.springframework.core.io.Resource
56 import org.springframework.data.domain.Page
57 import org.springframework.data.domain.PageRequest
58 import org.springframework.data.domain.Pageable
59 import org.springframework.http.HttpHeaders
60 import org.springframework.http.MediaType
61 import org.springframework.http.ResponseEntity
62 import org.springframework.http.codec.multipart.FilePart
63 import org.springframework.stereotype.Service
64 import org.springframework.transaction.annotation.Transactional
65 import java.io.IOException
69 * BlueprintModelHandler Purpose: Handler service to handle the request from BlurPrintModelRest
71 * @author Brinda Santh
76 open class BluePrintModelHandler(
77 private val bluePrintDatabaseLoadService: BluePrintDatabaseLoadService,
78 private val blueprintsProcessorCatalogService: BluePrintCatalogService,
79 private val bluePrintLoadConfiguration: BluePrintLoadConfiguration,
80 private val blueprintModelSearchRepository: BlueprintModelSearchRepository,
81 private val blueprintModelRepository: BlueprintModelRepository,
82 private val blueprintModelContentRepository: BlueprintModelContentRepository,
83 private val bluePrintEnhancerService: BluePrintEnhancerService
86 private val log = logger(BluePrintModelHandler::class)
88 open suspend fun bootstrapBlueprint(bootstrapRequest: BootstrapRequest) {
90 "Bootstrap request with type load(${bootstrapRequest.loadModelType}), " +
91 "resource dictionary load(${bootstrapRequest.loadResourceDictionary}) and " +
92 "cba load(${bootstrapRequest.loadCBA})"
94 if (bootstrapRequest.loadModelType) {
95 bluePrintDatabaseLoadService.initModelTypes()
97 if (bootstrapRequest.loadResourceDictionary) {
98 bluePrintDatabaseLoadService.initResourceDictionary()
100 if (bootstrapRequest.loadCBA) {
101 bluePrintDatabaseLoadService.initBluePrintCatalog()
105 @Throws(BluePrintException::class)
106 open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest):
107 WorkFlowSpecResponse {
108 val basePath = blueprintsProcessorCatalogService.getFromDatabase(req
109 .blueprintName, req.version)
110 log.info("blueprint base path $basePath")
112 val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString())
113 val workFlow = blueprintContext.workflowByName(req.workflowName)
115 val wfRes = WorkFlowSpecResponse()
116 wfRes.blueprintName = req.blueprintName
117 wfRes.version = req.version
119 val workFlowData = WorkFlowData()
120 workFlowData.workFlowName = req.workflowName
121 workFlowData.inputs = workFlow.inputs
122 workFlowData.outputs = workFlow.outputs
124 for ((k, v) in workFlow.inputs!!) {
125 addDataType(v.type, blueprintContext, wfRes)
128 for ((k, v) in workFlow.outputs!!) {
129 addDataType(v.type, blueprintContext, wfRes)
131 wfRes.workFlowData = workFlowData
135 private fun addDataType(name: String, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
136 var data = ctx.dataTypeByName(name)
138 res.dataTypes?.put(name, data)
139 addParentDataType(data, ctx, res)
143 private fun addParentDataType(data: DataType, ctx: BluePrintContext, res: WorkFlowSpecResponse) {
144 for ((k, v) in data.properties!!) {
145 addDataType(v.type, ctx, res)
149 @Throws(BluePrintException::class)
150 open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse {
151 val basePath = blueprintsProcessorCatalogService.getFromDatabase(
153 log.info("blueprint base path $basePath")
155 var res = WorkFlowsResponse()
156 res.blueprintName = name
157 res.version = version
159 val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(
161 if (blueprintContext.workflows() != null) {
162 res.workflows = blueprintContext.workflows()!!.keys
168 * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
170 * @return List<BlueprintModelSearch> list of the controller blueprint archives
171 </BlueprintModelSearch> */
172 open fun allBlueprintModel(): List<BlueprintModelSearch> {
173 return blueprintModelSearchRepository.findAll()
177 * This is a getAllBlueprintModel method to retrieve all the BlueprintModel in Database
179 * @return List<BlueprintModelSearch> list of the controller blueprint archives
180 </BlueprintModelSearch> */
181 open fun allBlueprintModel(pageRequest: Pageable): Page<BlueprintModelSearch> {
182 return blueprintModelSearchRepository.findAll(pageRequest)
186 * This is a saveBlueprintModel method
188 * @param filePart filePart
189 * @return Mono<BlueprintModelSearch>
190 * @throws BluePrintException BluePrintException
191 </BlueprintModelSearch> */
192 @Throws(BluePrintException::class)
193 open suspend fun saveBlueprintModel(filePart: FilePart): BlueprintModelSearch {
195 return upload(filePart, false)
196 } catch (e: IOException) {
197 throw httpProcessorException(ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
198 "Error in Save CBA: ${e.message}", e.errorCauseOrDefault())
203 * This is a searchBlueprintModels method
206 * @return List<BlueprintModelSearch>
207 </BlueprintModelSearch> */
208 open fun searchBlueprintModels(tags: String): List<BlueprintModelSearch> {
209 return blueprintModelSearchRepository.findByTagsContainingIgnoreCase(tags)
213 * This is a getBlueprintModelSearchByNameAndVersion method
216 * @param version version
217 * @return BlueprintModelSearch
218 * @throws BluePrintException BluePrintException
220 @Throws(BluePrintException::class)
221 open fun getBlueprintModelSearchByNameAndVersion(name: String, version: String): BlueprintModelSearch? {
222 return blueprintModelSearchRepository.findByArtifactNameAndArtifactVersion(name, version)
223 /*?: throw BluePrintException(
224 ErrorCode.RESOURCE_NOT_FOUND.value,
225 String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
230 * This is a downloadBlueprintModelFileByNameAndVersion method to download a Blueprint by Name and Version
233 * @param version version
234 * @return ResponseEntity<Resource>
235 * @throws BluePrintException BluePrintException
237 @Throws(BluePrintException::class)
238 open fun downloadBlueprintModelFileByNameAndVersion(
241 ): ResponseEntity<Resource> {
243 val archiveByteArray = download(name, version)
244 val fileName = "${name}_$version.zip"
245 return prepareResourceEntity(fileName, archiveByteArray)
246 } catch (e: BluePrintProcessorException) {
247 e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
248 val errorMsg = "Error while downloading the CBA file by Blueprint Name ($name) and Version ($version)."
249 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg,
250 "Wrong resource definition or resolution failed.")
255 * This is a downloadBlueprintModelFile method to find the target file to download and return a file resource
257 * @return ResponseEntity<Resource>
258 * @throws BluePrintException BluePrintException
260 @Throws(BluePrintException::class)
261 open fun downloadBlueprintModelFile(id: String): ResponseEntity<Resource> {
262 val blueprintModel: BlueprintModel
264 blueprintModel = getBlueprintModel(id)
265 } catch (e: BluePrintException) {
266 throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
267 "Error while downloading the CBA file: couldn't get blueprint modelby ID ($id)",
268 e.errorCauseOrDefault())
271 val fileName = "${blueprintModel.artifactName}_${blueprintModel.artifactVersion}.zip"
272 val file = blueprintModel.blueprintModelContent?.content
273 ?: throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
274 "Error while downloading the CBA file: couldn't get model content")
275 return prepareResourceEntity(fileName, file)
279 * @return ResponseEntity<Resource>
281 private fun prepareResourceEntity(fileName: String, file: ByteArray): ResponseEntity<Resource> {
282 return ResponseEntity.ok()
283 .contentType(MediaType.parseMediaType("text/plain"))
284 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"$fileName\"")
285 .body(ByteArrayResource(file))
289 * This is a getBlueprintModel method
292 * @return BlueprintModel
293 * @throws BluePrintException BluePrintException
295 @Throws(BluePrintException::class)
296 open fun getBlueprintModel(id: String): BlueprintModel {
297 val blueprintModel: BlueprintModel
298 val dbBlueprintModel = blueprintModelRepository.findById(id)
299 if (dbBlueprintModel.isPresent) {
300 blueprintModel = dbBlueprintModel.get()
302 val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
303 throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
305 return blueprintModel
309 * This is a getBlueprintModelByNameAndVersion method
312 * @param version version
313 * @return BlueprintModel
314 * @throws BluePrintException BluePrintException
316 @Throws(BluePrintException::class)
317 open fun getBlueprintModelByNameAndVersion(name: String, version: String): BlueprintModel {
318 val blueprintModel = blueprintModelRepository
319 .findByArtifactNameAndArtifactVersion(name, version)
320 if (blueprintModel != null) {
321 return blueprintModel
323 val msg = String.format(BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG, name, version)
324 throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
329 * This is a getBlueprintModelSearch method
332 * @return BlueprintModelSearch
333 * @throws BluePrintException BluePrintException
335 @Throws(BluePrintException::class)
336 open fun getBlueprintModelSearch(id: String): BlueprintModelSearch {
337 return blueprintModelSearchRepository.findById(id)
338 ?: throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
339 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id))
343 * This is a searchBluePrintModelsByKeyWord method to retrieve specific BlueprintModel in Database
344 * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type
345 * @author Shaaban Ebrahim
348 * @return List<BlueprintModelSearch> list of the controller blueprint
349 </BlueprintModelSearch> */
350 open fun searchBluePrintModelsByKeyWord(keyWord: String): List<BlueprintModelSearch> {
351 return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
352 keyWord, keyWord, keyWord, keyWord, keyWord
357 * This is a searchBluePrintModelsByKeyWordPagebale method to retrieve specific BlueprintModel in Database
358 * where keyword equals updatedBy or tags or artifcat name or artifcat version or artifact type and pageable
359 * @author Shaaban Ebrahim
362 * @return List<BlueprintModelSearch> list of the controller blueprint
363 </BlueprintModelSearch> */
364 open fun searchBluePrintModelsByKeyWordPaged(keyWord: String, pageRequest: PageRequest): Page<BlueprintModelSearch> {
365 return blueprintModelSearchRepository.findByUpdatedByOrTagsOrOrArtifactNameOrOrArtifactVersionOrArtifactType(
376 * This is a deleteBlueprintModel method
379 * @throws BluePrintException BluePrintException
382 @Throws(BluePrintException::class)
383 open fun deleteBlueprintModel(id: String) {
384 val dbBlueprintModel = blueprintModelRepository.findById(id)
385 if (dbBlueprintModel != null && dbBlueprintModel.isPresent) {
386 blueprintModelContentRepository.deleteByBlueprintModel(dbBlueprintModel.get())
387 blueprintModelRepository.delete(dbBlueprintModel.get())
389 val msg = String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, id)
390 throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, msg)
394 open suspend fun deleteBlueprintModel(name: String, version: String) {
395 blueprintsProcessorCatalogService.deleteFromDatabase(name, version)
399 * This is a CBA enrichBlueprint method
400 * Save the Zip File in archive location and extract the cba content.
401 * Populate the Enhancement Location
402 * Enhance the CBA content
403 * Compress the Enhanced Content
404 * Return back the the compressed content back to the caller.
406 * @param filePart filePart
407 * @return ResponseEntity<Resource>
408 * @throws BluePrintException BluePrintException
410 @Throws(BluePrintException::class)
411 open suspend fun enrichBlueprint(filePart: FilePart): ResponseEntity<Resource> {
413 val enhancedByteArray = enrichBlueprintFileSource(filePart)
414 return BluePrintEnhancerUtils.prepareResourceEntity("enhanced-cba.zip", enhancedByteArray)
415 } catch (e: BluePrintProcessorException) {
416 e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
417 val errorMsg = "Error while enhancing the CBA package."
418 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg,
419 "Wrong CBA file provided, please verify and enrich Again.")
420 } catch (e: Exception) {
421 throw httpProcessorException(ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
422 "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault())
427 * This is a publishBlueprintModel method to change the status published to YES
429 * @param filePart filePart
430 * @return BlueprintModelSearch
431 * @throws BluePrintException BluePrintException
433 @Throws(BluePrintException::class)
434 open suspend fun publishBlueprint(filePart: FilePart): BlueprintModelSearch {
436 return upload(filePart, true)
437 } catch (e: BluePrintProcessorException) {
438 e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
439 val errorMsg = "Error in Publishing CBA."
440 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg,
441 "Wrong CBA provided, please verify and enrich your CBA.")
442 } catch (e: Exception) {
443 throw httpProcessorException(ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
444 "Error in Publishing CBA: ${e.message}", e.errorCauseOrDefault())
448 /** Common CBA Save and Publish function for RestController and GRPC Handler, the [fileSource] may be
449 * byteArray or File Part type.*/
450 open suspend fun upload(fileSource: Any, validate: Boolean): BlueprintModelSearch {
451 val saveId = UUID.randomUUID().toString()
452 val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, saveId)
453 val blueprintWorking = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId)
455 val compressedFile = normalizedFile(blueprintArchive, "cba.zip")
457 is FilePart -> BluePrintEnhancerUtils.filePartAsFile(fileSource, compressedFile)
458 is ByteArray -> BluePrintEnhancerUtils.byteArrayAsFile(fileSource, compressedFile)
460 // Save the Copied file to Database
461 val blueprintId = blueprintsProcessorCatalogService.saveToDatabase(saveId, compressedFile, validate)
463 return blueprintModelSearchRepository.findById(blueprintId)
464 ?: throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
465 String.format(BLUEPRINT_MODEL_ID_FAILURE_MSG, blueprintId))
466 } catch (e: BluePrintException) {
467 e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
468 val errorMsg = "Error in Upload CBA."
469 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg,
470 "Wrong enriched CBA.")
471 } catch (e: IOException) {
472 throw httpProcessorException(ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
473 "Error in Upload CBA: ${e.errorMessageOrDefault()}", e.errorCauseOrDefault())
475 // Clean blueprint script cache
476 val cacheKey = BluePrintFileUtils
477 .compileCacheKey(normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, saveId))
478 BluePrintCompileCache.cleanClassLoader(cacheKey)
479 deleteNBDir(blueprintArchive)
480 deleteNBDir(blueprintWorking)
484 /** Common CBA download function for RestController and GRPC Handler, the [fileSource] may be
485 * byteArray or File Part type.*/
486 open fun download(name: String, version: String): ByteArray {
488 val blueprintModel = getBlueprintModelByNameAndVersion(name, version)
489 return blueprintModel.blueprintModelContent?.content
490 ?: throw httpProcessorException(ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API,
491 "Error while downloading the CBA file: couldn't get model content")
492 } catch (e: BluePrintException) {
493 e.http(ErrorCatalogCodes.RESOURCE_NOT_FOUND)
494 val errorMsg = "Fail to get Blueprint Model content."
495 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg,
496 "Wrong name and version was provide.")
500 /** Common CBA Enrich function for RestController and GRPC Handler, the [fileSource] may be
501 * byteArray or File Part type.*/
502 open suspend fun enrichBlueprintFileSource(fileSource: Any): ByteArray {
503 val enhanceId = UUID.randomUUID().toString()
504 val blueprintArchive = normalizedPathName(bluePrintLoadConfiguration.blueprintArchivePath, enhanceId)
505 val blueprintWorkingDir = normalizedPathName(bluePrintLoadConfiguration.blueprintWorkingPath, enhanceId)
508 is FilePart -> BluePrintEnhancerUtils
509 .copyFilePartToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
510 is ByteArray -> BluePrintEnhancerUtils
511 .copyByteArrayToEnhanceDir(fileSource, blueprintArchive, blueprintWorkingDir)
512 } // Enhance the Blue Prints
513 bluePrintEnhancerService.enhance(blueprintWorkingDir)
515 return BluePrintEnhancerUtils.compressEnhanceDirAndReturnByteArray(blueprintWorkingDir, blueprintArchive)
516 } catch (e: BluePrintException) {
517 e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT)
518 val errorMsg = "Fail Enriching the CBA."
519 throw e.updateErrorMessage(DesignerApiDomains.DESIGNER_API, errorMsg)
520 } catch (e: IOException) {
521 throw httpProcessorException(ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API,
522 "Error while Enriching the CBA file.", e.errorCauseOrDefault())
524 BluePrintEnhancerUtils.cleanEnhancer(blueprintArchive, blueprintWorkingDir)
530 private const val BLUEPRINT_MODEL_ID_FAILURE_MSG = "failed to get blueprint model id(%s) from repo"
531 private const val BLUEPRINT_MODEL_NAME_VERSION_FAILURE_MSG = "failed to get blueprint model by name(%s)" + " and version(%s) from repo"