Fix: Run both sonar and clm scans in parallel
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / inbounds / configs-api / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / configs / api / ResourceConfigSnapshotController.kt
1 /*
2  * Copyright © 2021 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
17 package org.onap.ccsdk.cds.blueprintsprocessor.configs.api
18
19 import com.fasterxml.jackson.databind.JsonNode
20 import io.swagger.annotations.Api
21 import io.swagger.annotations.ApiOperation
22 import io.swagger.annotations.ApiParam
23 import kotlinx.coroutines.runBlocking
24 import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot
25 import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshotService
26 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.mdcWebCoroutineScope
27 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
28 import org.onap.ccsdk.cds.controllerblueprints.core.httpProcessorException
29 import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes
30 import org.onap.ccsdk.cds.error.catalog.core.utils.errorCauseOrDefault
31 import org.springframework.http.MediaType
32 import org.springframework.http.ResponseEntity
33 import org.springframework.security.access.prepost.PreAuthorize
34 import org.springframework.web.bind.annotation.DeleteMapping
35 import org.springframework.web.bind.annotation.PathVariable
36 import org.springframework.web.bind.annotation.PostMapping
37 import org.springframework.web.bind.annotation.RequestBody
38 import org.springframework.web.bind.annotation.RequestMapping
39 import org.springframework.web.bind.annotation.RequestMethod
40 import org.springframework.web.bind.annotation.RequestParam
41 import org.springframework.web.bind.annotation.ResponseBody
42 import org.springframework.web.bind.annotation.RestController
43 import java.util.Optional
44
45 /**
46  * Exposes Resource Configuration Snapshot API to store and retrieve stored resource configurations.
47  *
48  * @author Serge Simard
49  * @version 1.0
50  */
51 @RestController
52 @RequestMapping("/api/v1/configs")
53 @Api(
54     value = "Resource configuration",
55     description = "Interaction with stored configurations"
56 )
57 open class ResourceConfigSnapshotController(private val resourceConfigSnapshotService: ResourceConfigSnapshotService) {
58
59     private val JSON_MIME_TYPE = "application/json"
60
61     @RequestMapping(
62         path = ["/health-check"],
63         method = [RequestMethod.GET],
64         produces = [MediaType.APPLICATION_JSON_VALUE]
65     )
66     @ResponseBody
67     @ApiOperation(value = "Health Check", hidden = true)
68     fun ressCfgSnapshotControllerHealthCheck(): JsonNode = runBlocking {
69         "Success".asJsonPrimitive()
70     }
71
72     @RequestMapping(
73         method = [RequestMethod.GET],
74         produces = [MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE]
75     )
76     @ApiOperation(
77         value = "Retrieve a resource configuration snapshot",
78         notes = "Retrieve a config snapshot, identified by its Resource Id and Type. " +
79             "An extra 'format' parameter can be passed to tell what content-type is expected."
80     )
81     @ResponseBody
82     @PreAuthorize("hasRole('USER')")
83     fun get(
84         @ApiParam(value = "Resource Type associated of the resource configuration snapshot", required = false, example = "\"PNF\"")
85         @RequestParam(value = "resourceType", required = true) resourceType: String,
86
87         @ApiParam(value = "Resource Id associated of the resource configuration snapshot", required = false, example = "\"1\"")
88         @RequestParam(value = "resourceId", required = true) resourceId: String,
89
90         @ApiParam(value = "Status of the snapshot being retrieved", defaultValue = "RUNNING", required = false)
91         @RequestParam(value = "status", required = false, defaultValue = "RUNNING") status: String,
92
93         @ApiParam(
94             value = "Expected format of the snapshot being retrieved", defaultValue = MediaType.TEXT_PLAIN_VALUE,
95             required = false
96         )
97         @RequestParam(value = "format", required = false, defaultValue = MediaType.TEXT_PLAIN_VALUE) format: String
98     ):
99
100         ResponseEntity<String> = runBlocking {
101
102             var configSnapshot = ""
103
104             if (resourceType.isNotEmpty() && resourceId.isNotEmpty()) {
105                 try {
106                     configSnapshot = resourceConfigSnapshotService.findByResourceIdAndResourceTypeAndStatus(
107                         resourceId,
108                         resourceType, ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())
109                     )
110                 } catch (ex: NoSuchElementException) {
111                     throw httpProcessorException(
112                         ErrorCatalogCodes.RESOURCE_NOT_FOUND, ConfigsApiDomains.CONFIGS_API,
113                         "Could not find configuration snapshot entry for type $resourceType and Id $resourceId",
114                         ex.errorCauseOrDefault()
115                     )
116                 } catch (ex: Exception) {
117                     throw httpProcessorException(
118                         ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
119                         "Could not find configuration snapshot entry for type $resourceType and Id $resourceId",
120                         ex.errorCauseOrDefault()
121                     )
122                 }
123             } else {
124                 throw httpProcessorException(
125                     ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
126                     "Missing param. You must specify resource-id and resource-type."
127                 )
128             }
129
130             var expectedContentType = format
131             if (expectedContentType.indexOf('/') < 0) {
132                 expectedContentType = "application/$expectedContentType"
133             }
134             val expectedMediaType: MediaType = MediaType.valueOf(expectedContentType)
135
136             ResponseEntity.ok().contentType(expectedMediaType).body(configSnapshot)
137         }
138
139     @PostMapping(
140         "/{resourceType}/{resourceId}/{status}",
141         produces = [MediaType.APPLICATION_JSON_VALUE]
142     )
143     @ApiOperation(
144         value = "Store a resource configuration snapshot identified by resourceId, resourceType, status",
145         notes = "Store a resource configuration snapshot, identified by its resourceId and resourceType, " +
146             "and optionally its status, either RUNNING or CANDIDATE.",
147         response = ResourceConfigSnapshot::class
148     )
149     @ResponseBody
150     @PreAuthorize("hasRole('USER')")
151     fun postWithResourceIdAndResourceType(
152         @ApiParam(value = "Resource Type associated with the resolution", required = false, example = "\"PNF\"")
153         @PathVariable(value = "resourceType", required = true) resourceType: String,
154         @ApiParam(value = "Resource Id associated with the resolution", required = false, example = "\"1\"")
155         @PathVariable(value = "resourceId", required = true) resourceId: String,
156         @ApiParam(value = "Status of the snapshot being retrieved", defaultValue = "RUNNING", required = true)
157         @PathVariable(value = "status", required = true) status: String,
158         @ApiParam(value = "Config snapshot to store", required = true, example = "\"config_snapshot\"")
159         @RequestBody snapshot: String
160     ): ResponseEntity<ResourceConfigSnapshot> = runBlocking {
161
162         val resultStored =
163             resourceConfigSnapshotService.write(
164                 snapshot, resourceId, resourceType,
165                 ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())
166             )
167
168         ResponseEntity.ok().body(resultStored)
169     }
170
171     @DeleteMapping(
172         "/{resourceType}/{resourceId}/{status}",
173         "/{resourceType}/{resourceId}"
174     )
175     @ApiOperation(
176         value = "Delete a resource configuration snapshot identified by resourceId, resourceType, status.",
177         notes = "Delete a resource configuration snapshot, identified by its resourceId and resourceType, " +
178             "and optionally its status, either RUNNING or CANDIDATE."
179     )
180     @ResponseBody
181     @PreAuthorize("hasRole('USER')")
182     suspend fun deleteWithResourceIdAndResourceType(
183         @ApiParam(value = "Resource Type associated with the resolution.", required = true)
184         @PathVariable(value = "resourceType", required = true) resourceType: String,
185         @ApiParam(value = "Resource Id associated with the resolution.", required = true)
186         @PathVariable(value = "resourceId", required = true) resourceId: String,
187         @ApiParam(value = "Status of the snapshot being deleted.", required = false)
188         @PathVariable(value = "status", required = false) status: Optional<String>
189     ) = mdcWebCoroutineScope {
190
191         if (resourceId.isBlank() || resourceType.isBlank())
192             throw httpProcessorException(
193                 ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
194                 "You must specify path variables resource-id and resource-type."
195             )
196
197         try {
198             if (status.isPresent)
199                 resourceConfigSnapshotService.deleteByResourceIdAndResourceTypeAndStatus(
200                     resourceId, resourceType,
201                     ResourceConfigSnapshot.Status.valueOf(status.get().toUpperCase())
202                 )
203             else {
204                 resourceConfigSnapshotService.deleteByResourceIdAndResourceTypeAndStatus(
205                     resourceId, resourceType,
206                     ResourceConfigSnapshot.Status.RUNNING
207                 )
208                 resourceConfigSnapshotService.deleteByResourceIdAndResourceTypeAndStatus(
209                     resourceId, resourceType,
210                     ResourceConfigSnapshot.Status.CANDIDATE
211                 )
212             }
213         } catch (ex: Exception) {
214             throw httpProcessorException(
215                 ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
216                 "Could not delete configuration snapshot entry for type $resourceType and Id $resourceId",
217                 ex.errorCauseOrDefault()
218             )
219         }
220     }
221
222     @RequestMapping(
223         path = ["/allByID"],
224         method = [RequestMethod.GET],
225         produces = [MediaType.APPLICATION_JSON_VALUE]
226     )
227     @ApiOperation(
228         value = "Retrieve all resource configuration snapshots identified by a given resource_id",
229         notes = "Retrieve all config snapshots, identified by its Resource Id, ordered by most recently created/modified date. "
230     )
231     @ResponseBody
232     @PreAuthorize("hasRole('USER')")
233     fun getAllByID(
234         @ApiParam(value = "Resource Id associated of the resource configuration snapshots", required = false, example = "\"1\"")
235         @RequestParam(value = "resourceId", required = true) resourceId: String,
236         @ApiParam(value = "Status of the snapshot being retrieved", defaultValue = "ANY", required = false)
237         @RequestParam(value = "status", required = false, defaultValue = "ANY") status: String
238     ): ResponseEntity<List<ResourceConfigSnapshot>?> = runBlocking {
239         var configSnapshots: List<ResourceConfigSnapshot>?
240
241         try {
242             if (status == "ANY") {
243                 configSnapshots = resourceConfigSnapshotService.findAllByResourceId(resourceId)
244             } else {
245                 configSnapshots = resourceConfigSnapshotService.findAllByResourceIdForStatus(
246                     resourceId, ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())
247                 )
248             }
249         } catch (ex: NoSuchElementException) {
250             throw httpProcessorException(
251                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, ConfigsApiDomains.CONFIGS_API,
252                 "Could not find configuration snapshot entry for ID $resourceId",
253                 ex.errorCauseOrDefault()
254             )
255         } catch (ex: Exception) {
256             throw httpProcessorException(
257                 ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
258                 "Unexpected error while finding configuration snapshot entries for ID $resourceId",
259                 ex.errorCauseOrDefault()
260             )
261         }
262
263         val expectedMediaType: MediaType = MediaType.valueOf(JSON_MIME_TYPE)
264         ResponseEntity.ok().contentType(expectedMediaType).body(configSnapshots)
265     }
266
267     @RequestMapping(
268         path = ["allByType"],
269         method = [RequestMethod.GET],
270         produces = [MediaType.APPLICATION_JSON_VALUE]
271     )
272     @ApiOperation(
273         value = "Retrieve all resource configuration snapshots for a given resource type",
274         notes = "Retrieve all config snapshots matching a specified Resource Type, ordered by most recently created/modified date. "
275     )
276     @ResponseBody
277     @PreAuthorize("hasRole('USER')")
278     fun getAllByType(
279         @ApiParam(value = "Resource Type associated of the resource configuration snapshot", required = false, example = "\"PNF\"")
280         @RequestParam(value = "resourceType", required = true) resourceType: String,
281         @ApiParam(value = "Status of the snapshot being retrieved", defaultValue = "ANY", required = false)
282         @RequestParam(value = "status", required = false, defaultValue = "ANY") status: String
283     ): ResponseEntity<List<ResourceConfigSnapshot>?> = runBlocking {
284         var configSnapshots: List<ResourceConfigSnapshot>?
285
286         try {
287             if (status == "ANY") {
288                 configSnapshots = resourceConfigSnapshotService.findAllByResourceType(resourceType)
289             } else {
290                 configSnapshots = resourceConfigSnapshotService.findAllByResourceTypeForStatus(
291                     resourceType, ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())
292                 )
293             }
294         } catch (ex: NoSuchElementException) {
295             throw httpProcessorException(
296                 ErrorCatalogCodes.RESOURCE_NOT_FOUND, ConfigsApiDomains.CONFIGS_API,
297                 "Could not find configuration snapshot entry for ID $resourceType",
298                 ex.errorCauseOrDefault()
299             )
300         } catch (ex: Exception) {
301             throw httpProcessorException(
302                 ErrorCatalogCodes.INVALID_REQUEST_FORMAT, ConfigsApiDomains.CONFIGS_API,
303                 "Unexpected error while finding configuration snapshot entries for type $resourceType",
304                 ex.errorCauseOrDefault()
305             )
306         }
307
308         val expectedMediaType: MediaType = MediaType.valueOf(JSON_MIME_TYPE)
309         ResponseEntity.ok().contentType(expectedMediaType).body(configSnapshots)
310     }
311 }