diff options
Diffstat (limited to 'ms/blueprintsprocessor/modules')
12 files changed, 288 insertions, 62 deletions
diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/domain/BlueprintModel.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/domain/BlueprintModel.kt index 9f4d32e7a..1feac8cd2 100755 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/domain/BlueprintModel.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/domain/BlueprintModel.kt @@ -19,12 +19,17 @@ package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain import com.fasterxml.jackson.annotation.JsonFormat import io.swagger.annotations.ApiModelProperty import org.hibernate.annotations.Proxy +import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.io.Serializable import java.util.Date +import javax.persistence.AttributeConverter import javax.persistence.CascadeType import javax.persistence.Column +import javax.persistence.Convert +import javax.persistence.Converter import javax.persistence.Entity import javax.persistence.EntityListeners import javax.persistence.FetchType @@ -123,8 +128,26 @@ class BlueprintModel : Serializable { @OneToOne(mappedBy = "blueprintModel", fetch = FetchType.EAGER, orphanRemoval = true, cascade = [CascadeType.ALL]) var blueprintModelContent: BlueprintModelContent? = null + // will be populated with workflow specs for each workflow (JSON object) + @Lob + @Convert(converter = WorkflowsConverter::class) + @Column(name = "workflows", nullable = false) + lateinit var workflows: Map<String, Workflow> + companion object { private const val serialVersionUID = 1L } + + @Converter + class WorkflowsConverter : AttributeConverter<Map<String, Workflow>, String> { + override fun convertToDatabaseColumn(node: Map<String, Workflow>): String { + return JacksonUtils.getJson(node, true) + } + + override fun convertToEntityAttribute(dbData: String): Map<String, Workflow> { + if (dbData == null || "".equals(dbData)) return emptyMap() + return JacksonUtils.getMapFromJson(dbData, Workflow::class.java) + } + } } diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelRepository.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelRepository.kt index a6f0da1cc..67869d11f 100644 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelRepository.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/repository/BlueprintModelRepository.kt @@ -17,6 +17,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.repository +import com.fasterxml.jackson.databind.JsonNode import org.jetbrains.annotations.NotNull import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.domain.BlueprintModel import org.springframework.data.jpa.repository.JpaRepository @@ -60,6 +61,15 @@ interface BlueprintModelRepository : JpaRepository<BlueprintModel, String> { fun findIdByArtifactNameAndArtifactVersion(@Param("artifactName") artifactName: String, @Param("artifactVersion") artifactVersion: String): String? /** + * Find the workflows for a given blueprint name/version + * @param artifactName artifactName + * @param artifactVersion artifactVersion + * @return String? + */ + @Query("SELECT m.workflows from BlueprintModel m WHERE m.artifactName = :artifactName AND m.artifactVersion = :artifactVersion") + fun findWorkflowsByArtifactNameAndArtifactVersion(@Param("artifactName") artifactName: String, @Param("artifactVersion") artifactVersion: String): JsonNode? + + /** * This is a findTopByArtifactNameOrderByArtifactIdDesc method * * @param artifactName artifactName diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintCatalogServiceImpl.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintCatalogServiceImpl.kt index 9d1826395..06376346c 100644 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintCatalogServiceImpl.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintCatalogServiceImpl.kt @@ -21,6 +21,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.db.primary.service import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration +import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow import org.onap.ccsdk.cds.controllerblueprints.core.deCompress import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintValidatorService @@ -71,10 +72,11 @@ abstract class BlueprintCatalogServiceImpl( val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(processingId, workingDir!!) val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + val workflows = bluePrintRuntimeService.bluePrintContext().workflows()!! metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = processingId metadata[BluePrintConstants.PROPERTY_BLUEPRINT_VALID] = valid - save(metadata, archiveFile) + save(metadata, archiveFile, workflows) return processingId } @@ -87,7 +89,7 @@ abstract class BlueprintCatalogServiceImpl( override suspend fun deleteFromDatabase(name: String, version: String) = delete(name, version) - abstract suspend fun save(metadata: MutableMap<String, String>, archiveFile: File) + abstract suspend fun save(metadata: MutableMap<String, String>, archiveFile: File, workflows: Map<String, Workflow>) abstract suspend fun get(name: String, version: String, extract: Boolean): Path? abstract suspend fun delete(name: String, version: String) } diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintProcessorCatalogServiceImpl.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintProcessorCatalogServiceImpl.kt index 9c007da31..c45a28a07 100755 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintProcessorCatalogServiceImpl.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/primary/service/BlueprintProcessorCatalogServiceImpl.kt @@ -29,6 +29,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode +import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow import org.onap.ccsdk.cds.controllerblueprints.core.deCompress import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintValidatorService @@ -120,7 +121,7 @@ class BlueprintProcessorCatalogServiceImpl( } } - override suspend fun save(metadata: MutableMap<String, String>, archiveFile: File) { + override suspend fun save(metadata: MutableMap<String, String>, archiveFile: File, workflows: Map<String, Workflow>) { val artifactName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME] val artifactVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION] @@ -152,9 +153,8 @@ class BlueprintProcessorCatalogServiceImpl( blueprintModel.artifactVersion = artifactVersion blueprintModel.updatedBy = metadata[BluePrintConstants.METADATA_TEMPLATE_AUTHOR]!! blueprintModel.tags = metadata[BluePrintConstants.METADATA_TEMPLATE_TAGS]!! - val description = - if (null != metadata[BluePrintConstants.METADATA_TEMPLATE_DESCRIPTION]) metadata[BluePrintConstants.METADATA_TEMPLATE_DESCRIPTION] else "" - blueprintModel.artifactDescription = description + blueprintModel.artifactDescription = "Controller Blueprint for $artifactName:$artifactVersion" + blueprintModel.workflows = workflows val blueprintModelContent = BlueprintModelContent() blueprintModelContent.id = metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] diff --git a/ms/blueprintsprocessor/modules/commons/db-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImplTest.kt b/ms/blueprintsprocessor/modules/commons/db-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImplTest.kt index b7b1f78cf..5f1d09578 100644 --- a/ms/blueprintsprocessor/modules/commons/db-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImplTest.kt +++ b/ms/blueprintsprocessor/modules/commons/db-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/db/BlueprintProcessorCatalogServiceImplTest.kt @@ -102,10 +102,11 @@ class BlueprintProcessorCatalogServiceImplTest { runBlocking { val file = normalizedFile("./target/blueprints/generated-cba.zip") assertTrue(file.exists(), "couldnt get file ${file.absolutePath}") - val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + val ctx = bluePrintRuntimeService.bluePrintContext() + val metadata = ctx.metadata!! metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = blueprintId - blueprintsProcessorCatalogService.save(metadata, file) + blueprintsProcessorCatalogService.save(metadata, file, ctx.workflows()!!) } } @@ -114,10 +115,12 @@ class BlueprintProcessorCatalogServiceImplTest { runBlocking { val file = normalizedFile("./target/blueprints/generated-cba.zip") assertTrue(file.exists(), "couldnt get file ${file.absolutePath}") - val metadata = bluePrintRuntimeService.bluePrintContext().metadata!! + + val ctx = bluePrintRuntimeService.bluePrintContext() + val metadata = ctx.metadata!! metadata[BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID] = blueprintId - blueprintsProcessorCatalogService.save(metadata, file) + blueprintsProcessorCatalogService.save(metadata, file, ctx.workflows()!!) blueprintsProcessorCatalogService.get("baseconfiguration", "1.0.0", true) } diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/RestLoggerService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/RestLoggerService.kt index 611c0855d..b1d8abd16 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/RestLoggerService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/RestLoggerService.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.reactor.ReactorContext import kotlinx.coroutines.reactor.asCoroutineContext import kotlinx.coroutines.withContext import org.apache.http.message.BasicHeader +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.ONAP_INVOCATION_ID import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.ONAP_ORIGINATOR_ID @@ -107,8 +108,12 @@ class RestLoggerService { } } -/** Used in Rest controller API methods to populate MDC context to nested coroutines from reactor web filter context. */ +/** + * Used in Rest controller API methods to populate MDC context to nested coroutines from reactor web filter context. + * @param executionServiceInput (Optional) Used to override values populated in the MDC Context + */ suspend fun <T> mdcWebCoroutineScope( + executionServiceInput: ExecutionServiceInput? = null, block: suspend CoroutineScope.() -> T ) = coroutineScope { val reactorContext = this.coroutineContext[ReactorContext] @@ -118,12 +123,20 @@ suspend fun <T> mdcWebCoroutineScope( !reactorContext.context.isEmpty && reactorContext.context.hasKey(MDCContext) ) { - val mdcContext = reactorContext.context.get<MDCContext>(MDCContext) + val mdcContext = if (executionServiceInput != null) { + // MDC Context with overridden request ID + MDC.put("RequestID", executionServiceInput.commonHeader.requestId) + MDCContext(MDC.getCopyOfContextMap()) + } else { + // Default MDC Context + reactorContext.context.get(MDCContext) + } if (mdcContext != null) newCoroutineContext(this.coroutineContext + reactorContext + mdcContext) else newCoroutineContext(this.coroutineContext + reactorContext) } else this.coroutineContext + // Execute the block with new and old context withContext(newContext) { block() diff --git a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BluePrintManagementGRPCHandler.kt b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BluePrintManagementGRPCHandler.kt index 54f8dbc9d..0dc0941c2 100644 --- a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BluePrintManagementGRPCHandler.kt +++ b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BluePrintManagementGRPCHandler.kt @@ -29,10 +29,12 @@ import org.onap.ccsdk.cds.controllerblueprints.common.api.Status import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException import org.onap.ccsdk.cds.controllerblueprints.core.asJsonString +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType import org.onap.ccsdk.cds.controllerblueprints.core.emptyTONull import org.onap.ccsdk.cds.controllerblueprints.core.utils.currentTimestamp import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintBootstrapInput import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintDownloadInput +import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintGetWorkflowsInput import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementOutput import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementServiceGrpc import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintRemoveInput @@ -241,6 +243,33 @@ open class BluePrintManagementGRPCHandler( } } + @PreAuthorize("hasRole('USER')") + override fun getWorkflows( + request: BluePrintGetWorkflowsInput, + responseObserver: StreamObserver<BluePrintManagementOutput> + ) { + runBlocking { + val blueprintName = request.blueprintName + val blueprintVersion = request.blueprintVersion + val blueprint = "blueprint $blueprintName:$blueprintVersion" + + log.info("request(${request.commonHeader.requestId}): Received getWorkflows $blueprint") + try { + val workflowsSetJson = bluePrintModelHandler.getWorkflowNamesFromRepository(blueprintName, blueprintVersion) + responseObserver.onNext(successStatus(request.commonHeader, mapOf("workflows" to workflowsSetJson).asJsonType().toString())) + } catch (e: Exception) { + responseObserver.onNext( + failStatus( + request.commonHeader, + "request(${request.commonHeader.requestId}): Failed to get workflows for ($blueprint)", e + ) + ) + } finally { + responseObserver.onCompleted() + } + } + } + private fun outputWithFileBytes(header: CommonHeader, byteArray: ByteArray): BluePrintManagementOutput = BluePrintManagementOutput.newBuilder() .setCommonHeader(header) @@ -258,6 +287,7 @@ open class BluePrintManagementGRPCHandler( private fun successStatus(header: CommonHeader, propertyContent: String? = null): BluePrintManagementOutput { // Populate Response Payload val propertiesBuilder = BluePrintManagementOutput.newBuilder().propertiesBuilder + // propertyContent is expected to have a string which contains a JSON map propertyContent?.let { JsonFormat.parser().merge(propertyContent, propertiesBuilder) } diff --git a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt index 66d4b0e16..777a21457 100644 --- a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/BlueprintModelController.kt @@ -393,10 +393,7 @@ open class BlueprintModelController(private val bluePrintModelHandler: BluePrint @PostMapping( path = arrayOf("/workflow-spec"), - produces = arrayOf( - MediaType - .APPLICATION_JSON_VALUE - ), + produces = arrayOf(MediaType.APPLICATION_JSON_VALUE), consumes = arrayOf(MediaType.APPLICATION_JSON_VALUE) ) @ApiOperation( @@ -418,9 +415,7 @@ open class BlueprintModelController(private val bluePrintModelHandler: BluePrint } @GetMapping( - path = arrayOf( - "/workflows/blueprint-name/{name}/version/{version}" - ), + path = arrayOf("/workflows/blueprint-name/{name}/version/{version}"), produces = arrayOf(MediaType.APPLICATION_JSON_VALUE) ) @ApiOperation( diff --git a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt index 7bbaa8c59..a5fcd322c 100644 --- a/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt +++ b/ms/blueprintsprocessor/modules/inbounds/designer-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/designer/api/handler/BluePrintModelHandler.kt @@ -39,6 +39,7 @@ import org.onap.ccsdk.cds.controllerblueprints.core.data.DataType import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition import org.onap.ccsdk.cds.controllerblueprints.core.deleteNBDir import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintEnhancerService import org.onap.ccsdk.cds.controllerblueprints.core.logger @@ -107,42 +108,78 @@ open class BluePrintModelHandler( } @Throws(BluePrintException::class) - open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest): - WorkFlowSpecResponse { - val basePath = blueprintsProcessorCatalogService.getFromDatabase( - req - .blueprintName, - req.version - ) - log.info("blueprint base path $basePath") + private suspend fun getBlueprintCtxByNameAndVersion(blueprintName: String, version: String): BluePrintContext { + val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, version) + log.info("blueprint base path $basePath for blueprint: $blueprintName version:$version") + return BluePrintMetadataUtils.getBluePrintContext(basePath.toString()) + } + + @Throws(BluePrintException::class) + /** + * Try to get workflows cached from the BLUEPRINT_MODEL table first, + * failing that, load the CBA from the filesystem and extract workflows. + * The second case is possible during update scenario - current CDS DB already contains desired CBAs and reupload + * is not feasible. + */ + open suspend fun getWorkflowsFromRepository(name: String, version: String): Map<String, Workflow> { + var workflowsFromCache: Map<String, Workflow> + try { + workflowsFromCache = blueprintModelRepository.findByArtifactNameAndArtifactVersion(name, version)?.workflows!! + if (workflowsFromCache.isEmpty()) { + log.info("findByArtifactNameAndArtifactVersion did not return list of workflows for blueprintName:($name) version:($version), falling back to loading CBA from filesystem.") + workflowsFromCache = getBlueprintCtxByNameAndVersion(name, version).workflows()!! + // TODO: does it make sense to update the BLUEPRINT_MODEL workflows in this case or just wait for CBA reupload? + } + } catch (e: Exception) { + throw BluePrintException("Failed to get workflows from DB cache or by reading CBA", e) + } + return workflowsFromCache + } + + // lookup workflows list from field. + open suspend fun getWorkflowNamesFromRepository(name: String, version: String): Set<String> { + return getWorkflowsFromRepository(name, version).keys + } - val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString()) - val workFlow = blueprintContext.workflowByName(req.workflowName) + @Throws(BluePrintException::class) + open suspend fun prepareWorkFlowSpec(req: WorkFlowSpecRequest): WorkFlowSpecResponse { + val basePath = blueprintsProcessorCatalogService.getFromDatabase(req.blueprintName, req.version) + log.info("blueprint base path $basePath") - val wfRes = WorkFlowSpecResponse() - wfRes.blueprintName = req.blueprintName - wfRes.version = req.version + val blueprintContext = BluePrintMetadataUtils.getBluePrintContext(basePath.toString()) + val workFlow = blueprintContext.workflowByName(req.workflowName) - val workFlowData = WorkFlowData() - workFlowData.workFlowName = req.workflowName - workFlowData.inputs = workFlow.inputs - workFlowData.outputs = workFlow.outputs - wfRes.workFlowData = workFlowData + val wfRes = WorkFlowSpecResponse() + wfRes.blueprintName = req.blueprintName + wfRes.version = req.version - if (workFlow.inputs != null) { - for ((k, v) in workFlow.inputs!!) { - addPropertyInfo(k, v, blueprintContext, wfRes) - } + val workFlowData = WorkFlowData() + workFlowData.workFlowName = req.workflowName + workFlowData.inputs = workFlow.inputs + workFlowData.outputs = workFlow.outputs + + if (workFlow.inputs != null) { + for ((k, v) in workFlow.inputs!!) { + addPropertyInfo(v, blueprintContext, wfRes) } + } - if (workFlow.outputs != null) { - for ((k, v) in workFlow.outputs!!) { - addPropertyInfo(k, v, blueprintContext, wfRes) - } + if (workFlow.outputs != null) { + for ((k, v) in workFlow.outputs!!) { + addPropertyInfo(k, v, blueprintContext, wfRes) } + } - return wfRes + wfRes.workFlowData = workFlowData + return wfRes + } + + private fun addPropertyInfo(prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) { + addDataType(prop.type, ctx, res) + if (prop.entrySchema != null && prop.entrySchema!!.type != null) { + addDataType(prop.entrySchema!!.type, ctx, res) } + } private fun addPropertyInfo(propName: String, prop: PropertyDefinition, ctx: BluePrintContext, res: WorkFlowSpecResponse) { updatePropertyInfo(propName, prop, ctx, res) @@ -192,23 +229,19 @@ open class BluePrintModelHandler( } } + // wrap CBA workflows list in WorkflowsResponse object @Throws(BluePrintException::class) open suspend fun getWorkflowNames(name: String, version: String): WorkFlowsResponse { - val basePath = blueprintsProcessorCatalogService.getFromDatabase( - name, version + var workflows = getWorkflowsFromRepository(name, version) + if (workflows == null) throw httpProcessorException( + ErrorCatalogCodes.RESOURCE_NOT_FOUND, DesignerApiDomains.DESIGNER_API, + "Failed to find workflows list for blueprint: ($name) version: ($version) from filesystem." ) - log.info("blueprint base path $basePath") var res = WorkFlowsResponse() res.blueprintName = name res.version = version - - val blueprintContext = BluePrintMetadataUtils.getBluePrintContext( - basePath.toString() - ) - if (blueprintContext.workflows() != null) { - res.workflows = blueprintContext.workflows()!!.keys - } + res.workflows = workflows.keys.toMutableSet() return res } @@ -242,6 +275,7 @@ open class BluePrintModelHandler( try { return upload(filePart, false) } catch (e: IOException) { + log.error("saveBlueprintModel fails ${e.message}", e) throw httpProcessorException( ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API, "Error in Save CBA: ${e.message}", e.errorCauseOrDefault() @@ -527,13 +561,15 @@ open class BluePrintModelHandler( val enhancedByteArray = enrichBlueprintFileSource(filePart) return upload(enhancedByteArray, true) } catch (e: BluePrintProcessorException) { - e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT) val errorMsg = "Error while enhancing and uploading the CBA package." + log.error(errorMsg, e) + e.http(ErrorCatalogCodes.IO_FILE_INTERRUPT) throw e.updateErrorMessage( DesignerApiDomains.DESIGNER_API, errorMsg, "Wrong CBA file provided, please verify the source CBA." ) } catch (e: Exception) { + log.error("Error enriching/uploading CBA", e) throw httpProcessorException( ErrorCatalogCodes.IO_FILE_INTERRUPT, DesignerApiDomains.DESIGNER_API, "EnrichBlueprint: ${e.message}", e.errorCauseOrDefault() diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt index 15c27a43b..d2b7ac368 100644 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/ResourceController.kt @@ -26,6 +26,8 @@ import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.R import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes +import org.onap.ccsdk.cds.error.catalog.core.ErrorPayload +import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.security.access.prepost.PreAuthorize @@ -110,6 +112,60 @@ open class ResourceController(private var resourceResolutionDBService: ResourceR } @RequestMapping( + path = ["/occurrences"], + method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE] + ) + @ApiOperation( + value = "Get the map of resolved resources with 'occurrence' as the keys to the resolved resources ", + notes = "With optional 'occurrence' options, subset of stored resolved resources can be retrieved " + + "using the blueprint name, blueprint version, artifact name and the resolution-key.", + response = ResourceResolution::class, + responseContainer = "List", + produces = MediaType.APPLICATION_JSON_VALUE + ) + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun getOccurrences( + @ApiParam(value = "Name of the CBA.", required = true) + @RequestParam(value = "bpName", required = true) bpName: String, + @ApiParam(value = "Version of the CBA.", required = true) + @RequestParam(value = "bpVersion", required = true) bpVersion: String, + @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true) + @RequestParam(value = "artifactName", required = true, defaultValue = "") artifactName: String, + @ApiParam(value = "Resolution Key associated with the resolution.", required = true) + @RequestParam(value = "resolutionKey", required = true, defaultValue = "") resolutionKey: String, + @ApiParam(value = "Number of earlier N occurrences of the resolutions.", required = false) + @RequestParam(value = "firstN", required = false) firstN: Int?, + @ApiParam(value = "Number of latest N occurrences of the resolutions.", required = false) + @RequestParam(value = "lastN", required = false) lastN: Int?, + @ApiParam(value = "For Range option - 'begin' is the start occurrence of range of the resolutions.", required = false) + @RequestParam(value = "begin", required = false) begin: Int?, + @ApiParam(value = "For Range option - 'end' is the end occurrence of the range of the resolutions.", required = false) + @RequestParam(value = "end", required = false) end: Int? + ): ResponseEntity<Map<Int, List<ResourceResolution>>> = runBlocking { + when { + artifactName.isEmpty() -> "'artifactName' must not be empty" + resolutionKey.isEmpty() -> "'resolutionKey' must not be empty" + // Optional options - validate if provided + (firstN != null && lastN != null) -> "Retrieve occurrences using either 'firstN' OR 'lastN' option" + ((firstN != null || lastN != null) && (begin != null || end != null)) -> "Retrieve occurrences using either 'firstN' OR 'lastN' OR 'begin' and 'end' option." + ((begin != null && end == null) || (begin == null && end != null)) -> " Retrieving occurrences within range - please provide both 'begin' and 'end' option" + else -> null + }?.let { throw httpProcessorException(ErrorCatalogCodes.REQUEST_NOT_FOUND, ResourceApiDomains.RESOURCE_API, it) } + + when { + firstN != null -> + resourceResolutionDBService.findFirstNOccurrences(bpName, bpVersion, artifactName, resolutionKey, firstN) + lastN != null -> + resourceResolutionDBService.findLastNOccurrences(bpName, bpVersion, artifactName, resolutionKey, lastN) + begin != null && end != null -> + resourceResolutionDBService.findOccurrencesWithinRange(bpName, bpVersion, artifactName, resolutionKey, begin, end) + else -> + resourceResolutionDBService.readWithResolutionKey(bpName, bpVersion, artifactName, resolutionKey).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder()) + }.let { result -> ResponseEntity.ok().body(result) } + } + + @RequestMapping( method = [RequestMethod.DELETE], produces = [MediaType.APPLICATION_JSON_VALUE] ) @ApiOperation( @@ -161,9 +217,14 @@ open class ResourceController(private var resourceResolutionDBService: ResourceR @ApiParam(value = "Name of the resource to retrieve", required = true) @RequestParam(value = "name", required = true) name: String ): - ResponseEntity<ResourceResolution> = runBlocking { + ResponseEntity<out Any>? = runBlocking { - ResponseEntity.ok() - .body(resourceResolutionDBService.readValue(bpName, bpVersion, artifactName, resolutionKey, name)) + var result: ResourceResolution? = resourceResolutionDBService.readValue(bpName, bpVersion, artifactName, resolutionKey, name) + if (result != null) { + ResponseEntity.ok().body(result) + } else { + var errorPayload = ErrorPayload(HttpStatus.NOT_FOUND.value(), ErrorCatalogCodes.GENERIC_FAILURE, "No records found") + ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorPayload) + } } } diff --git a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt index 2d32d0e1d..2840f8016 100644 --- a/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/resource-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/resource/api/TemplateController.kt @@ -169,6 +169,60 @@ open class TemplateController(private val templateResolutionService: TemplateRes ResponseEntity.ok().body(resultStored) } + @RequestMapping( + path = ["/occurrences"], + method = [RequestMethod.GET], produces = [MediaType.APPLICATION_JSON_VALUE] + ) + @ApiOperation( + value = "Get the map of resolved templates with 'occurrence' as the keys to the resolved templates ", + notes = "With optional 'occurrence' options, subset of stored resolved templates can be retrieved " + + "using the blueprint name, blueprint version, artifact name and the resolution-key.", + response = TemplateResolution::class, + responseContainer = "List", + produces = MediaType.APPLICATION_JSON_VALUE + ) + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun getOccurrences( + @ApiParam(value = "Name of the CBA.", required = true) + @RequestParam(value = "bpName", required = true) bpName: String, + @ApiParam(value = "Version of the CBA.", required = true) + @RequestParam(value = "bpVersion", required = true) bpVersion: String, + @ApiParam(value = "Artifact name for which to retrieve a resolved resource.", required = true) + @RequestParam(value = "artifactName", required = true, defaultValue = "") artifactName: String, + @ApiParam(value = "Resolution Key associated with the resolution.", required = true) + @RequestParam(value = "resolutionKey", required = true, defaultValue = "") resolutionKey: String, + @ApiParam(value = "Number of earlier N occurrences of the templates.", required = false) + @RequestParam(value = "firstN", required = false) firstN: Int?, + @ApiParam(value = "Number of latest N occurrences of the templates.", required = false) + @RequestParam(value = "lastN", required = false) lastN: Int?, + @ApiParam(value = "For Range option - 'begin' is the start occurrence of range of the templates.", required = false) + @RequestParam(value = "begin", required = false) begin: Int?, + @ApiParam(value = "For Range option - 'end' is the end occurrence of the range of the templates.", required = false) + @RequestParam(value = "end", required = false) end: Int? + ): ResponseEntity<Map<Int, List<TemplateResolution>>> = runBlocking { + when { + artifactName.isEmpty() -> "'artifactName' must not be empty" + resolutionKey.isEmpty() -> "'resolutionKey' must not be empty" + // Optional options - validate if provided + (firstN != null && lastN != null) -> "Retrieve occurrences using either 'firstN' OR 'lastN' option" + ((firstN != null || lastN != null) && (begin != null || end != null)) -> "Retrieve occurrences using either 'firstN' OR 'lastN' OR 'begin' and 'end' option." + ((begin != null && end == null) || (begin == null && end != null)) -> " Retrieving occurrences within range - please provide both 'begin' and 'end' option" + else -> null + }?.let { throw httpProcessorException(ErrorCatalogCodes.REQUEST_NOT_FOUND, ResourceApiDomains.RESOURCE_API, it) } + + when { + firstN != null -> + templateResolutionService.findFirstNOccurrences(bpName, bpVersion, artifactName, resolutionKey, firstN) + lastN != null -> + templateResolutionService.findLastNOccurrences(bpName, bpVersion, artifactName, resolutionKey, lastN) + begin != null && end != null -> + templateResolutionService.findOccurrencesWithinRange(bpName, bpVersion, artifactName, resolutionKey, begin, end) + else -> + templateResolutionService.readWithResolutionKey(bpName, bpVersion, artifactName, resolutionKey).groupBy(TemplateResolution::occurrence).toSortedMap(reverseOrder()) + }.let { result -> ResponseEntity.ok().body(result) } + } + @PostMapping( "/{bpName}/{bpVersion}/{artifactName}/{resourceType}/{resourceId}", produces = [MediaType.APPLICATION_JSON_VALUE] diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt index bb7ecc6ad..9e0a7ee71 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceController.kt @@ -91,8 +91,7 @@ open class ExecutionServiceController { suspend fun process( @ApiParam(value = "ExecutionServiceInput payload.", required = true) @RequestBody executionServiceInput: ExecutionServiceInput - ): ResponseEntity<ExecutionServiceOutput> = mdcWebCoroutineScope { - + ): ResponseEntity<ExecutionServiceOutput> = mdcWebCoroutineScope(executionServiceInput) { if (executionServiceInput.actionIdentifiers.mode == ACTION_MODE_ASYNC) { throw httpProcessorException( ErrorCatalogCodes.GENERIC_FAILURE, |