CDS max-occurrence feature
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / resource-resolution / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / resource / resolution / db / ResourceResolutionDBService.kt
1 /*
2  * Copyright (C) 2019 Bell Canada.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db
17
18 import kotlinx.coroutines.Dispatchers
19 import kotlinx.coroutines.withContext
20 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
21 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
22 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
23 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
24 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
25 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
26 import org.slf4j.LoggerFactory
27 import org.springframework.dao.EmptyResultDataAccessException
28 import org.springframework.stereotype.Service
29 import java.lang.IllegalArgumentException
30 import java.util.UUID
31
32 @Service
33 class ResourceResolutionDBService(private val resourceResolutionRepository: ResourceResolutionRepository) {
34
35     private val log = LoggerFactory.getLogger(ResourceResolutionDBService::class.toString())
36
37     suspend fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
38         bluePrintRuntimeService: BluePrintRuntimeService<*>,
39         key: String,
40         occurrence: Int,
41         artifactPrefix: String
42     ): List<ResourceResolution> {
43         return try {
44             val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
45
46             val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
47             val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
48
49             resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKeyAndOccurrence(
50                 blueprintName,
51                 blueprintVersion,
52                 artifactPrefix,
53                 key,
54                 occurrence
55             )
56         } catch (e: EmptyResultDataAccessException) {
57             emptyList()
58         }
59     }
60
61     suspend fun findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
62         bluePrintRuntimeService: BluePrintRuntimeService<*>,
63         resourceId: String,
64         resourceType: String,
65         occurrence: Int,
66         artifactPrefix: String
67     ): List<ResourceResolution> {
68         return try {
69
70             val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
71
72             val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
73             val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
74
75             resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceIdAndResourceTypeAndOccurrence(
76                 blueprintName,
77                 blueprintVersion,
78                 artifactPrefix,
79                 resourceId,
80                 resourceType,
81                 occurrence
82             )
83         } catch (e: EmptyResultDataAccessException) {
84             emptyList()
85         }
86     }
87
88     suspend fun readValue(
89         blueprintName: String,
90         blueprintVersion: String,
91         artifactPrefix: String,
92         resolutionKey: String,
93         name: String
94     ): ResourceResolution? = withContext(Dispatchers.IO) {
95
96         resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactNameAndName(
97             resolutionKey,
98             blueprintName,
99             blueprintVersion,
100             artifactPrefix,
101             name
102         )
103     }
104
105     suspend fun readWithResolutionKey(
106         blueprintName: String,
107         blueprintVersion: String,
108         artifactPrefix: String,
109         resolutionKey: String
110     ): List<ResourceResolution> = withContext(Dispatchers.IO) {
111
112         resourceResolutionRepository.findByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
113             resolutionKey,
114             blueprintName,
115             blueprintVersion,
116             artifactPrefix
117         )
118     }
119
120     /**
121      * This returns the resolutions of first N 'occurrences'.
122      *
123      * @param blueprintName
124      * @param blueprintVersion
125      * @param artifactPrefix
126      * @param resolutionKey
127      * @param firstN
128      */
129     suspend fun findFirstNOccurrences(
130         blueprintName: String,
131         blueprintVersion: String,
132         artifactPrefix: String,
133         resolutionKey: String,
134         firstN: Int
135     ): Map<Int, List<ResourceResolution>> = withContext(Dispatchers.IO) {
136
137         resourceResolutionRepository.findFirstNOccurrences(
138             resolutionKey,
139             blueprintName,
140             blueprintVersion,
141             artifactPrefix,
142             firstN
143         ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder())
144     }
145
146     /**
147      * This returns the resolutions of last N 'occurrences'.
148      *
149      * @param blueprintName
150      * @param blueprintVersion
151      * @param artifactPrefix
152      * @param resolutionKey
153      * @param lastN
154      */
155     suspend fun findLastNOccurrences(
156         blueprintName: String,
157         blueprintVersion: String,
158         artifactPrefix: String,
159         resolutionKey: String,
160         lastN: Int
161     ): Map<Int, List<ResourceResolution>> = withContext(Dispatchers.IO) {
162
163         resourceResolutionRepository.findLastNOccurrences(
164             resolutionKey,
165             blueprintName,
166             blueprintVersion,
167             artifactPrefix,
168             lastN
169         ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder())
170     }
171
172     /**
173      * This returns the resolutions of last N 'occurrences'.
174      *
175      * @param blueprintName
176      * @param blueprintVersion
177      * @param artifactPrefix
178      * @param resourceId
179      * @param resourceType
180      * @param lastN
181      */
182     suspend fun findLastNOccurrences(
183         blueprintName: String,
184         blueprintVersion: String,
185         artifactPrefix: String,
186         resourceId: String,
187         resourceType: String,
188         lastN: Int
189     ): Map<Int, List<ResourceResolution>> = withContext(Dispatchers.IO) {
190
191         resourceResolutionRepository.findLastNOccurrences(
192             resourceId,
193             resourceType,
194             blueprintName,
195             blueprintVersion,
196             artifactPrefix,
197             lastN
198         ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder())
199     }
200
201     /**
202      * This returns the resolutions with 'occurrence' value between begin and end.
203      *
204      * @param blueprintName
205      * @param blueprintVersion
206      * @param artifactPrefix
207      * @param resolutionKey
208      * @param begin
209      * @param end
210      */
211     suspend fun findOccurrencesWithinRange(
212         blueprintName: String,
213         blueprintVersion: String,
214         artifactPrefix: String,
215         resolutionKey: String,
216         begin: Int,
217         end: Int
218     ): Map<Int, List<ResourceResolution>> = withContext(Dispatchers.IO) {
219
220         resourceResolutionRepository.findOccurrencesWithinRange(
221             resolutionKey,
222             blueprintName,
223             blueprintVersion,
224             artifactPrefix,
225             begin,
226             end
227         ).groupBy(ResourceResolution::occurrence).toSortedMap(reverseOrder())
228     }
229
230     suspend fun readWithResourceIdAndResourceType(
231         blueprintName: String,
232         blueprintVersion: String,
233         resourceId: String,
234         resourceType: String
235     ): List<ResourceResolution> =
236         withContext(Dispatchers.IO) {
237
238             resourceResolutionRepository.findByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType(
239                 blueprintName,
240                 blueprintVersion,
241                 resourceId,
242                 resourceType
243             )
244         }
245
246     suspend fun write(
247         properties: Map<String, Any>,
248         bluePrintRuntimeService: BluePrintRuntimeService<*>,
249         artifactPrefix: String,
250         resourceAssignment: ResourceAssignment
251     ): ResourceResolution = withContext(Dispatchers.IO) {
252
253         val metadata = bluePrintRuntimeService.bluePrintContext().metadata!!
254
255         val blueprintVersion = metadata[BluePrintConstants.METADATA_TEMPLATE_VERSION]!!
256         val blueprintName = metadata[BluePrintConstants.METADATA_TEMPLATE_NAME]!!
257
258         val resolutionKey = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] as String
259         val resourceId = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] as String
260         val resourceType = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] as String
261         val occurrence = properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] as Int
262
263         write(
264             blueprintName,
265             blueprintVersion,
266             resolutionKey,
267             resourceId,
268             resourceType,
269             artifactPrefix,
270             resourceAssignment,
271             occurrence
272         )
273     }
274
275     suspend fun write(
276         blueprintName: String,
277         blueprintVersion: String,
278         resolutionKey: String,
279         resourceId: String,
280         resourceType: String,
281         artifactPrefix: String,
282         resourceAssignment: ResourceAssignment,
283         occurrence: Int = 0
284     ): ResourceResolution = withContext(Dispatchers.IO) {
285
286         val resourceResolution = ResourceResolution()
287         resourceResolution.id = UUID.randomUUID().toString()
288         resourceResolution.artifactName = artifactPrefix
289         resourceResolution.occurrence = occurrence
290         resourceResolution.blueprintVersion = blueprintVersion
291         resourceResolution.blueprintName = blueprintName
292         resourceResolution.resolutionKey = resolutionKey
293         resourceResolution.resourceType = resourceType
294         resourceResolution.resourceId = resourceId
295         resourceResolution.value = resourceAssignment.property?.value?.let {
296             if (BluePrintConstants.STATUS_SUCCESS == resourceAssignment.status)
297                 JacksonUtils.getValue(it).toString()
298             else ""
299         } ?: ""
300         resourceResolution.name = resourceAssignment.name
301         resourceResolution.dictionaryName = resourceAssignment.dictionaryName
302         resourceResolution.dictionaryVersion = resourceAssignment.version
303         resourceResolution.dictionarySource = resourceAssignment.dictionarySource
304         resourceResolution.status = resourceAssignment.status ?: BluePrintConstants.STATUS_FAILURE
305
306         try {
307             resourceResolutionRepository.saveAndFlush(resourceResolution)
308         } catch (ex: Exception) {
309             throw BluePrintException("Failed to store resource resolution result.", ex)
310         }
311     }
312
313     /**
314      * This method to deletes resources associated to a specific resolution-key
315      *
316      * @param blueprintName name of the CBA
317      * @param blueprintVersion version of the CBA
318      * @param artifactName name of the artifact
319      * @param resolutionKey value of the resolution-key
320      * @param lastNOccurrences number of occurrences to delete starting from the last,
321      * all occurrences will be deleted when null
322      *
323      * @return number of deleted rows
324      */
325     fun deleteResources(
326         blueprintName: String,
327         blueprintVersion: String,
328         artifactName: String,
329         resolutionKey: String,
330         lastNOccurrences: Int?
331     ): Int = lastNOccurrences?.let {
332         if (lastNOccurrences < 0) {
333             throw IllegalArgumentException("last N occurrences must be a positive integer")
334         }
335         resourceResolutionRepository.deleteLastNOccurrences(
336             blueprintName,
337             blueprintVersion,
338             artifactName,
339             resolutionKey,
340             it
341         )
342     } ?: resourceResolutionRepository.deleteByBlueprintNameAndBlueprintVersionAndArtifactNameAndResolutionKey(
343         blueprintName,
344         blueprintVersion,
345         artifactName,
346         resolutionKey
347     )
348
349     /**
350      * This method to deletes resources associated to a specific resourceType and resourceId
351      *
352      * @param blueprintName name of the CBA
353      * @param blueprintVersion version of the CBA
354      * @param artifactName name of the artifact
355      * @param resourceType value of the resourceType
356      * @param resourceId value of the resourceId
357      * @param lastNOccurrences number of occurrences to delete starting from the last,
358      * all occurrences will be deleted when null
359      *
360      * @return number of deleted rows
361      */
362     fun deleteResources(
363         blueprintName: String,
364         blueprintVersion: String,
365         artifactName: String,
366         resourceType: String,
367         resourceId: String,
368         lastNOccurrences: Int?
369     ): Int = lastNOccurrences?.let {
370         if (lastNOccurrences < 0) {
371             throw IllegalArgumentException("last N occurrences must be a positive integer")
372         }
373         resourceResolutionRepository.deleteLastNOccurrences(
374             blueprintName,
375             blueprintVersion,
376             artifactName,
377             resourceType,
378             resourceId,
379             it
380         )
381     } ?: resourceResolutionRepository.deleteByBlueprintNameAndBlueprintVersionAndArtifactNameAndResourceTypeAndResourceId(
382         blueprintName,
383         blueprintVersion,
384         artifactName,
385         resourceType,
386         resourceId
387     )
388
389     suspend fun deleteResourceResolutionList(listResourceResolution: List<ResourceResolution>) = withContext(Dispatchers.IO) {
390         try {
391             resourceResolutionRepository.deleteInBatch(listResourceResolution)
392         } catch (ex: Exception) {
393             throw BluePrintException("Failed to batch delete resource resolution", ex)
394         }
395     }
396
397     /**
398      * This method returns the (highest occurrence + 1) of resource resolutions if present in DB, returns 1 otherwise.
399      * The 'occurrence' is used to persist new resource resolution in the DB.
400      *
401      * @param resolutionKey
402      * @param blueprintName
403      * @param blueprintVersion
404      * @param artifactPrefix
405      */
406     suspend fun findNextOccurrenceByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
407         resolutionKey: String,
408         blueprintName: String,
409         blueprintVersion: String,
410         artifactPrefix: String
411     ) = withContext(Dispatchers.IO) {
412         val maxOccurrence = resourceResolutionRepository.findMaxOccurrenceByResolutionKeyAndBlueprintNameAndBlueprintVersionAndArtifactName(
413             resolutionKey,
414             blueprintName,
415             blueprintVersion,
416             artifactPrefix
417         )
418         maxOccurrence?.inc() ?: 1
419     }
420
421     /**
422      * This method returns the (highest occurrence + 1) of resource resolutions if present in DB, returns 1 otherwise.
423      * The 'occurrence' is used to persist new resource resolution in the DB.
424      *
425      * @param blueprintName
426      * @param blueprintVersion
427      * @param resourceId
428      * @param resourceType
429      */
430     suspend fun findNextOccurrenceByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType(
431         blueprintName: String,
432         blueprintVersion: String,
433         resourceId: String,
434         resourceType: String
435     ) = withContext(Dispatchers.IO) {
436         val maxOccurrence = resourceResolutionRepository.findMaxOccurrenceByBlueprintNameAndBlueprintVersionAndResourceIdAndResourceType(
437             blueprintName,
438             blueprintVersion,
439             resourceId,
440             resourceType
441         )
442         maxOccurrence?.inc() ?: 1
443     }
444 }