Refactoring BP Code with ErrorCatalog
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / inbounds / designer-api / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / designer / api / BluePrintManagementGRPCHandler.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 Bell Canada.
4  * Modifications Copyright © 2019 IBM.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 package org.onap.ccsdk.cds.blueprintsprocessor.designer.api
20
21 import com.google.protobuf.ByteString
22 import com.google.protobuf.util.JsonFormat
23 import io.grpc.stub.StreamObserver
24 import kotlinx.coroutines.runBlocking
25 import org.onap.ccsdk.cds.blueprintsprocessor.designer.api.handler.BluePrintModelHandler
26 import org.onap.ccsdk.cds.controllerblueprints.common.api.CommonHeader
27 import org.onap.ccsdk.cds.controllerblueprints.common.api.Status
28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
29 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
30 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonString
31 import org.onap.ccsdk.cds.controllerblueprints.core.emptyTONull
32 import org.onap.ccsdk.cds.controllerblueprints.core.utils.currentTimestamp
33 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintBootstrapInput
34 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintDownloadInput
35 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementOutput
36 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintManagementServiceGrpc
37 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintRemoveInput
38 import org.onap.ccsdk.cds.controllerblueprints.management.api.BluePrintUploadInput
39 import org.onap.ccsdk.cds.controllerblueprints.management.api.DownloadAction
40 import org.onap.ccsdk.cds.controllerblueprints.management.api.FileChunk
41 import org.onap.ccsdk.cds.controllerblueprints.management.api.RemoveAction
42 import org.onap.ccsdk.cds.controllerblueprints.management.api.UploadAction
43 import org.onap.ccsdk.cds.error.catalog.core.ErrorCatalogCodes
44 import org.onap.ccsdk.cds.error.catalog.core.GrpcErrorCodes
45 import org.onap.ccsdk.cds.error.catalog.core.utils.errorMessageOrDefault
46 import org.onap.ccsdk.cds.error.catalog.services.ErrorCatalogService
47 import org.slf4j.LoggerFactory
48 import org.springframework.security.access.prepost.PreAuthorize
49 import org.springframework.stereotype.Service
50
51 // TODO("Convert to coroutines handler")
52 @Service
53 open class BluePrintManagementGRPCHandler(
54     private val bluePrintModelHandler: BluePrintModelHandler,
55     private val errorCatalogService: ErrorCatalogService
56 ) :
57     BluePrintManagementServiceGrpc.BluePrintManagementServiceImplBase() {
58
59     private val log = LoggerFactory.getLogger(BluePrintManagementGRPCHandler::class.java)
60
61     @PreAuthorize("hasRole('USER')")
62     override fun uploadBlueprint(
63         request: BluePrintUploadInput,
64         responseObserver: StreamObserver<BluePrintManagementOutput>
65     ) {
66
67         runBlocking {
68             // TODO("catch if request id is missing")
69             log.info("request(${request.commonHeader.requestId})")
70             try {
71                 /** Get the file byte array */
72                 val byteArray = request.fileChunk.chunk.toByteArray()
73                 /** Get the Upload Action */
74                 val uploadAction = request.actionIdentifiers?.actionName.emptyTONull()
75                     ?: UploadAction.DRAFT.toString()
76
77                 when (uploadAction) {
78                     UploadAction.DRAFT.toString() -> {
79                         val blueprintModel = bluePrintModelHandler.upload(byteArray, false)
80                         responseObserver.onNext(successStatus(request.commonHeader, blueprintModel.asJsonString()))
81                     }
82                     UploadAction.PUBLISH.toString() -> {
83                         val blueprintModel = bluePrintModelHandler.upload(byteArray, true)
84                         responseObserver.onNext(successStatus(request.commonHeader, blueprintModel.asJsonString()))
85                     }
86                     UploadAction.VALIDATE.toString() -> {
87                         // TODO("Not Implemented")
88                         responseObserver.onNext(
89                             failStatus(
90                                 request.commonHeader,
91                                 "Upload action($uploadAction) not implemented",
92                                 BluePrintProcessorException("Not Implemented")
93                             )
94                         )
95                     }
96                     UploadAction.ENRICH.toString() -> {
97                         val enrichedByteArray = bluePrintModelHandler.enrichBlueprintFileSource(byteArray)
98                         responseObserver.onNext(outputWithFileBytes(request.commonHeader, enrichedByteArray))
99                     }
100                     else -> {
101                         responseObserver.onNext(
102                             failStatus(
103                                 request.commonHeader,
104                                 "Upload action($uploadAction) not implemented",
105                                 BluePrintProcessorException("Not implemented")
106                             )
107                         )
108                     }
109                 }
110             } catch (e: Exception) {
111                 responseObserver.onNext(
112                     failStatus(
113                         request.commonHeader,
114                         "request(${request.commonHeader.requestId}): Failed to upload CBA", e
115                     )
116                 )
117             } finally {
118                 responseObserver.onCompleted()
119             }
120         }
121     }
122
123     @PreAuthorize("hasRole('USER')")
124     override fun downloadBlueprint(
125         request: BluePrintDownloadInput,
126         responseObserver: StreamObserver<BluePrintManagementOutput>
127     ) {
128         runBlocking {
129             val blueprintName = request.actionIdentifiers.blueprintName
130             val blueprintVersion = request.actionIdentifiers.blueprintVersion
131             val blueprint = "blueprint $blueprintName:$blueprintVersion"
132
133             /** Get the Search Action */
134             val searchAction = request.actionIdentifiers?.actionName.emptyTONull()
135                 ?: DownloadAction.SEARCH.toString()
136
137             log.info("request(${request.commonHeader.requestId}): Received download $blueprint")
138             try {
139                 when (searchAction) {
140                     DownloadAction.SEARCH.toString() -> {
141                         val downloadByteArray = bluePrintModelHandler.download(blueprintName, blueprintVersion)
142                         responseObserver.onNext(outputWithFileBytes(request.commonHeader, downloadByteArray))
143                     }
144                     else -> {
145                         responseObserver.onNext(
146                             failStatus(
147                                 request.commonHeader,
148                                 "Search action($searchAction) not implemented",
149                                 BluePrintProcessorException("Not implemented")
150                             )
151                         )
152                     }
153                 }
154             } catch (e: Exception) {
155                 responseObserver.onNext(
156                     failStatus(
157                         request.commonHeader,
158                         "request(${request.commonHeader.requestId}): Failed to delete $blueprint", e
159                     )
160                 )
161             } finally {
162                 responseObserver.onCompleted()
163             }
164         }
165     }
166
167     @PreAuthorize("hasRole('USER')")
168     override fun removeBlueprint(
169         request: BluePrintRemoveInput,
170         responseObserver:
171         StreamObserver<BluePrintManagementOutput>
172     ) {
173
174         runBlocking {
175             val blueprintName = request.actionIdentifiers.blueprintName
176             val blueprintVersion = request.actionIdentifiers.blueprintVersion
177             val blueprint = "blueprint $blueprintName:$blueprintVersion"
178
179             log.info("request(${request.commonHeader.requestId}): Received delete $blueprint")
180
181             /** Get the Remove Action */
182             val removeAction = request.actionIdentifiers?.actionName.emptyTONull()
183                 ?: RemoveAction.DEFAULT.toString()
184
185             try {
186                 when (removeAction) {
187                     RemoveAction.DEFAULT.toString() -> {
188                         bluePrintModelHandler.deleteBlueprintModel(blueprintName, blueprintVersion)
189                         responseObserver.onNext(successStatus(request.commonHeader))
190                     }
191                     else -> {
192                         responseObserver.onNext(
193                             failStatus(
194                                 request.commonHeader,
195                                 "Remove action($removeAction) not implemented",
196                                 BluePrintProcessorException("Not implemented")
197                             )
198                         )
199                     }
200                 }
201             } catch (e: Exception) {
202                 responseObserver.onNext(
203                     failStatus(
204                         request.commonHeader,
205                         "request(${request.commonHeader.requestId}): Failed to delete $blueprint", e
206                     )
207                 )
208             } finally {
209                 responseObserver.onCompleted()
210             }
211         }
212     }
213
214     override fun bootstrapBlueprint(
215         request: BluePrintBootstrapInput,
216         responseObserver: StreamObserver<BluePrintManagementOutput>
217     ) {
218         runBlocking {
219             try {
220                 log.info("request(${request.commonHeader.requestId}): Received bootstrap request")
221                 val bootstrapRequest = BootstrapRequest().apply {
222                     loadModelType = request.loadModelType
223                     loadResourceDictionary = request.loadResourceDictionary
224                     loadCBA = request.loadCBA
225                 }
226                 /** Perform bootstrap of Model Types, Resource Definitions and CBA */
227                 bluePrintModelHandler.bootstrapBlueprint(bootstrapRequest)
228                 responseObserver.onNext(successStatus(request.commonHeader))
229             } catch (e: Exception) {
230                 responseObserver.onNext(
231                     failStatus(
232                         request.commonHeader,
233                         "request(${request.commonHeader.requestId}): Failed to bootstrap", e
234                     )
235                 )
236             } finally {
237                 responseObserver.onCompleted()
238             }
239         }
240     }
241
242     private fun outputWithFileBytes(header: CommonHeader, byteArray: ByteArray): BluePrintManagementOutput =
243         BluePrintManagementOutput.newBuilder()
244             .setCommonHeader(header)
245             .setFileChunk(FileChunk.newBuilder().setChunk(ByteString.copyFrom(byteArray)))
246             .setStatus(
247                 Status.newBuilder()
248                     .setTimestamp(currentTimestamp())
249                     .setMessage(BluePrintConstants.STATUS_SUCCESS)
250                     .setCode(200)
251                     .build()
252             )
253             .build()
254
255     private fun successStatus(header: CommonHeader, propertyContent: String? = null): BluePrintManagementOutput {
256         // Populate Response Payload
257         val propertiesBuilder = BluePrintManagementOutput.newBuilder().propertiesBuilder
258         propertyContent?.let {
259             JsonFormat.parser().merge(propertyContent, propertiesBuilder)
260         }
261         return BluePrintManagementOutput.newBuilder()
262             .setCommonHeader(header)
263             .setProperties(propertiesBuilder.build())
264             .setStatus(
265                 Status.newBuilder()
266                     .setTimestamp(currentTimestamp())
267                     .setMessage(BluePrintConstants.STATUS_SUCCESS)
268                     .setCode(200)
269                     .build()
270             )
271             .build()
272     }
273
274     private fun failStatus(header: CommonHeader, message: String, e: Exception): BluePrintManagementOutput {
275         log.error(message, e)
276         return if (e is BluePrintProcessorException) onErrorCatalog(header, message, e) else onError(header, message, e)
277     }
278
279     private fun onError(header: CommonHeader, message: String, error: Exception): BluePrintManagementOutput {
280         val code = GrpcErrorCodes.code(ErrorCatalogCodes.GENERIC_FAILURE)
281         return BluePrintManagementOutput.newBuilder()
282                 .setCommonHeader(header)
283                 .setStatus(
284                         Status.newBuilder()
285                                 .setTimestamp(currentTimestamp())
286                                 .setMessage(BluePrintConstants.STATUS_FAILURE)
287                                 .setErrorMessage("Error : $message \n Details: ${error.errorMessageOrDefault()}")
288                                 .setCode(code)
289                                 .build()
290                 )
291                 .build()
292     }
293
294     private fun onErrorCatalog(header: CommonHeader, message: String, error: BluePrintProcessorException):
295             BluePrintManagementOutput {
296         val err = if (error.protocol == "") {
297             error.grpc(ErrorCatalogCodes.GENERIC_FAILURE)
298         } else {
299             error.convertToGrpc()
300         }
301         val errorPayload = errorCatalogService.errorPayload(err.addErrorPayloadMessage(message))
302         return BluePrintManagementOutput.newBuilder()
303                 .setCommonHeader(header)
304                 .setStatus(
305                         Status.newBuilder()
306                                 .setTimestamp(currentTimestamp())
307                                 .setMessage(BluePrintConstants.STATUS_FAILURE)
308                                 .setErrorMessage("Error : ${errorPayload.message}")
309                                 .setCode(errorPayload.code)
310                                 .build()
311                 )
312                 .build()
313     }
314 }