Fix: Run both sonar and clm scans in parallel
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / resource-resolution / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / resource / resolution / processor / DatabaseResourceAssignmentProcessor.kt
1 /*
2  *  Copyright © 2018 IBM.
3  *  Modifications Copyright © 2017-2018 AT&T Intellectual Property, Bell Canada.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor
19
20 import com.fasterxml.jackson.databind.JsonNode
21 import org.onap.ccsdk.cds.blueprintsprocessor.db.BluePrintDBLibGenericService
22 import org.onap.ccsdk.cds.blueprintsprocessor.db.PrimaryDBLibGenericService
23 import org.onap.ccsdk.cds.blueprintsprocessor.db.primary.BluePrintDBLibPropertyService
24 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.DatabaseResourceSource
25 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR
26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
27 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ExecutionServiceDomains
28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
29 import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
30 import org.onap.ccsdk.cds.controllerblueprints.core.isNotEmpty
31 import org.onap.ccsdk.cds.controllerblueprints.core.nullToEmpty
32 import org.onap.ccsdk.cds.controllerblueprints.core.updateErrorMessage
33 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
34 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.KeyIdentifier
35 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
36 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.factory.ResourceSourceMappingFactory
37 import org.slf4j.LoggerFactory
38 import org.springframework.beans.factory.config.ConfigurableBeanFactory
39 import org.springframework.context.annotation.Scope
40 import org.springframework.stereotype.Service
41 import java.util.HashMap
42
43 /**
44  * DatabaseResourceAssignmentProcessor
45  *
46  * @author Kapil Singal
47  */
48 @Service("${PREFIX_RESOURCE_RESOLUTION_PROCESSOR}source-db")
49 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
50 open class DatabaseResourceAssignmentProcessor(
51     private val bluePrintDBLibPropertyService: BluePrintDBLibPropertyService,
52     private val primaryDBLibGenericService: PrimaryDBLibGenericService
53 ) : ResourceAssignmentProcessor() {
54
55     private val logger = LoggerFactory.getLogger(DatabaseResourceAssignmentProcessor::class.java)
56
57     override fun getName(): String {
58         return "${PREFIX_RESOURCE_RESOLUTION_PROCESSOR}source-db"
59     }
60
61     override suspend fun processNB(resourceAssignment: ResourceAssignment) {
62         try {
63             validate(resourceAssignment)
64             // Check if It has Input
65             if (!setFromInput(resourceAssignment)) {
66                 setValueFromDB(resourceAssignment)
67             }
68             // Check the value has populated for mandatory case
69             ResourceAssignmentUtils.assertTemplateKeyValueNotNull(resourceAssignment)
70         } catch (e: BluePrintProcessorException) {
71             val errorMsg = "Failed to process Database resource resolution in template key ($resourceAssignment) assignments."
72             throw e.updateErrorMessage(
73                 ExecutionServiceDomains.RESOURCE_RESOLUTION, errorMsg,
74                 "Wrong resource definition or DB resolution failed."
75             )
76         } catch (e: Exception) {
77             ResourceAssignmentUtils.setFailedResourceDataValue(resourceAssignment, e.message)
78             throw BluePrintProcessorException("Failed in template key ($resourceAssignment) assignments with: ${e.message}", e)
79         }
80     }
81
82     open fun setValueFromDB(resourceAssignment: ResourceAssignment) {
83         val dName = resourceAssignment.dictionaryName!!
84         val dSource = resourceAssignment.dictionarySource!!
85         val resourceDefinition = resourceDefinition(dName)
86
87         /** Check Resource Assignment has the source definitions, If not get from Resource Definition **/
88         val resourceSource = resourceAssignment.dictionarySourceDefinition
89             ?: resourceDefinition?.sources?.get(dSource)
90             ?: throw BluePrintProcessorException("couldn't get resource definition $dName source($dSource)")
91         val resourceSourceProperties = checkNotNull(resourceSource.properties) {
92             "failed to get source properties for $dName "
93         }
94         val sourceProperties =
95             JacksonUtils.getInstanceFromMap(resourceSourceProperties, DatabaseResourceSource::class.java)
96
97         val sql = checkNotNull(sourceProperties.query) {
98             "failed to get request query for $dName under $dSource properties"
99         }
100         val inputKeyMapping = checkNotNull(sourceProperties.inputKeyMapping) {
101             "failed to get input-key-mappings for $dName under $dSource properties"
102         }
103
104         val resolvedInputKeyMapping = resolveInputKeyMappingVariables(
105             inputKeyMapping,
106             resourceAssignment.templatingConstants
107         ).toMutableMap()
108         logger.info("\nResolved Input Key mappings: \n$resolvedInputKeyMapping")
109
110         resolvedInputKeyMapping.map { KeyIdentifier(it.key, it.value) }.let {
111             resourceAssignment.keyIdentifiers.addAll(it)
112         }
113
114         logger.info(
115             "DatabaseResource ($dSource) dictionary information: " +
116                 "Query:($sql), input-key-mapping:($inputKeyMapping), output-key-mapping:(${sourceProperties.outputKeyMapping})"
117         )
118         val jdbcTemplate = blueprintDBLibService(sourceProperties, dSource)
119
120         val rows = jdbcTemplate.query(sql, populateNamedParameter(resolvedInputKeyMapping))
121         if (rows.isEmpty()) {
122             logger.warn("Emptyset from dictionary-source($dSource) for dictionary name ($dName) the query ($sql).")
123         }
124         logger.debug("Query returned ${rows.size} values")
125         populateResource(resourceAssignment, sourceProperties, rows)
126     }
127
128     open fun blueprintDBLibService(sourceProperties: DatabaseResourceSource, selector: String): BluePrintDBLibGenericService {
129         return if (isNotEmpty(sourceProperties.endpointSelector)) {
130             val dbPropertiesJson = raRuntimeService.resolveDSLExpression(sourceProperties.endpointSelector!!)
131             bluePrintDBLibPropertyService.JdbcTemplate(dbPropertiesJson)
132         } else {
133             bluePrintDBLibPropertyService.JdbcTemplate(selector)
134         }
135     }
136
137     @Throws(BluePrintProcessorException::class)
138     open fun validate(resourceAssignment: ResourceAssignment) {
139         checkNotEmpty(resourceAssignment.name) { "resource assignment template key is not defined" }
140         checkNotEmpty(resourceAssignment.dictionaryName) {
141             "resource assignment dictionary name is not defined for template key (${resourceAssignment.name})"
142         }
143         check(resourceAssignment.dictionarySource in getListOfDBSources()) {
144             "resource assignment source ${resourceAssignment.dictionarySource} is not registered in \"resourceSourceMappings\""
145         }
146     }
147
148     // placeholder to get the list of DB sources.
149     open fun getListOfDBSources(): Array<String> {
150         return ResourceSourceMappingFactory.getRegisterSourceMapping()
151             .resourceSourceMappings.filterValues { it == "source-db" }.keys.toTypedArray()
152     }
153
154     open fun populateNamedParameter(inputKeyMapping: Map<String, JsonNode>): Map<String, Any> {
155         val namedParameters = HashMap<String, Any>()
156         inputKeyMapping.forEach {
157             val expressionValue = it.value.textValue()
158             logger.trace("Reference dictionary key (${it.key}) resulted in value ($expressionValue)")
159             namedParameters[it.key] = expressionValue
160         }
161         if (namedParameters.isNotEmpty()) {
162             logger.info("Parameter information : ($namedParameters)")
163         }
164         return namedParameters
165     }
166
167     @Throws(BluePrintProcessorException::class)
168     open fun populateResource(
169         resourceAssignment: ResourceAssignment,
170         sourceProperties: DatabaseResourceSource,
171         rows: List<Map<String, Any>>
172     ) {
173         val dName = resourceAssignment.dictionaryName
174         val dSource = resourceAssignment.dictionarySource
175         val type = nullToEmpty(resourceAssignment.property?.type)
176
177         val outputKeyMapping = checkNotNull(sourceProperties.outputKeyMapping) {
178             "failed to get output-key-mappings for $dName under $dSource properties"
179         }
180         logger.info("Response processing type ($type)")
181
182         val responseNode = checkNotNull(JacksonUtils.getJsonNode(rows)) {
183             "Failed to get database query result into Json node."
184         }
185
186         val parsedResponseNode = ResourceAssignmentUtils.parseResponseNode(
187             responseNode, resourceAssignment,
188             raRuntimeService, outputKeyMapping
189         )
190         ResourceAssignmentUtils.setResourceDataValue(resourceAssignment, raRuntimeService, parsedResponseNode)
191     }
192
193     override suspend fun recoverNB(runtimeException: RuntimeException, resourceAssignment: ResourceAssignment) {
194         addError(runtimeException.message!!)
195     }
196 }