add junit coverage for SvnfmService
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / SDNCAdapterRestV1.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (c) 2019 Samsung
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.so.bpmn.common.scripts
24
25 import org.onap.so.logger.LoggingAnchor
26 import org.onap.so.client.HttpClientFactory
27 import org.onap.so.logger.ErrorCode
28
29 import java.text.SimpleDateFormat
30 import javax.ws.rs.core.Response
31 import java.net.URLEncoder
32
33 import org.apache.commons.codec.binary.Base64
34 import org.apache.commons.lang3.*
35 import org.camunda.bpm.engine.delegate.BpmnError
36 import org.camunda.bpm.engine.delegate.DelegateExecution
37
38 import groovy.json.*
39
40 import org.json.JSONObject
41
42 import org.onap.so.bpmn.core.UrnPropertiesReader
43 import org.onap.so.bpmn.core.WorkflowException
44 import org.onap.so.bpmn.core.domain.RollbackData
45 import org.onap.so.bpmn.core.json.JsonUtils
46 import org.onap.so.client.HttpClient
47 import org.onap.so.logger.MessageEnum
48 import org.slf4j.Logger
49 import org.slf4j.LoggerFactory
50 import org.onap.so.utils.TargetEntity
51
52
53
54
55 class SDNCAdapterRestV1 extends AbstractServiceTaskProcessor {
56     private static final Logger logger = LoggerFactory.getLogger( SDNCAdapterRestV1.class);
57
58
59         ExceptionUtil exceptionUtil = new ExceptionUtil()
60         JsonUtils jsonUtil = new JsonUtils()
61
62         /**
63          * Processes the incoming request.
64          */
65         public void preProcessRequest (DelegateExecution execution) {
66                 def method = getClass().getSimpleName() + '.preProcessRequest(' +
67                         'execution=' + execution.getId() +
68                         ')'
69                 logger.trace('Entered ' + method)
70
71                 def prefix="SDNCREST_"
72                 execution.setVariable("prefix", prefix)
73                 setSuccessIndicator(execution, false)
74
75                 try {
76                         // Determine the request type and log the request
77
78                         String request = validateRequest(execution, "mso-request-id")
79                         String requestType = jsonUtil.getJsonRootProperty(request)
80                         execution.setVariable(prefix + 'requestType', requestType)
81                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'requestType = ' + requestType)
82
83                         // Determine the SDNCAdapter endpoint
84
85                         String sdncAdapterEndpoint = UrnPropertiesReader.getVariable("mso.adapters.sdnc.rest.endpoint", execution)
86
87                         if (sdncAdapterEndpoint == null || sdncAdapterEndpoint.isEmpty()) {
88                                 String msg = getProcessKey(execution) + ': mso:adapters:sdnc:rest:endpoint URN mapping is not defined'
89                                 logger.debug(msg)
90                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
91                                                 ErrorCode.UnknownError.getValue());
92                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
93                         }
94
95                         while (sdncAdapterEndpoint.endsWith('/')) {
96                                 sdncAdapterEndpoint = sdncAdapterEndpoint.substring(0, sdncAdapterEndpoint.length()-1)
97                         }
98
99                         String sdncAdapterMethod = null
100                         String sdncAdapterUrl = null
101                         String sdncAdapterRequest = request
102
103                         if ('SDNCServiceRequest'.equals(requestType)) {
104                                 // Get the sdncRequestId from the request
105
106                                 String sdncRequestId = jsonUtil.getJsonValue(request, requestType + ".sdncRequestId")
107
108                                 if (sdncRequestId == null || sdncRequestId.isEmpty()) {
109                                         String msg = getProcessKey(execution) + ': no sdncRequestId in ' + requestType
110                                         logger.debug(msg)
111                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
112                                                         ErrorCode.UnknownError.getValue());
113                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
114                                 }
115
116                                 execution.setVariable('SDNCAResponse_CORRELATOR', sdncRequestId)
117                                 logger.debug(getProcessKey(execution) + ': SDNCAResponse_CORRELATOR = ' + sdncRequestId)
118
119                                 // Get the bpNotificationUrl from the request (just to make sure it's there)
120
121                                 String bpNotificationUrl = jsonUtil.getJsonValue(request, requestType + ".bpNotificationUrl")
122
123                                 if (bpNotificationUrl == null || bpNotificationUrl.isEmpty()) {
124                                         String msg = getProcessKey(execution) + ': no bpNotificationUrl in ' + requestType
125                                         logger.debug(msg)
126                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
127                                                         ErrorCode.UnknownError.getValue());
128                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
129                                 }
130
131                                 sdncAdapterMethod = 'POST'
132                                 sdncAdapterUrl = sdncAdapterEndpoint
133
134                                 RollbackData rollbackData = new RollbackData()
135                                 rollbackData.setRequestId(sdncRequestId)
136                                 rollbackData.getAdditionalData().put("service", jsonUtil.getJsonValue(request, requestType + ".sdncService"))
137                                 rollbackData.getAdditionalData().put("operation", jsonUtil.getJsonValue(request, requestType + ".sdncOperation"))
138                                 execution.setVariable("RollbackData", rollbackData)
139
140                         } else {
141                                 String msg = getProcessKey(execution) + ': Unsupported request type: ' + requestType
142                                 logger.debug(msg)
143                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
144                                                 ErrorCode.UnknownError.getValue());
145                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
146                         }
147
148                         execution.setVariable(prefix + 'sdncAdapterMethod', sdncAdapterMethod)
149                         execution.setVariable(prefix + 'sdncAdapterUrl', sdncAdapterUrl)
150                         execution.setVariable(prefix + 'sdncAdapterRequest', sdncAdapterRequest)
151
152                         // Get the Basic Auth credentials for the SDNCAdapter (yes... we ARE using the PO adapters credentials)
153
154                         String basicAuthValue = UrnPropertiesReader.getVariable("mso.adapters.po.auth", execution)
155
156                         if (basicAuthValue == null || basicAuthValue.isEmpty()) {
157                                 logger.debug(getProcessKey(execution) + ": mso:adapters:po:auth URN mapping is not defined")
158                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(),
159                                                 getProcessKey(execution) + ": mso:adapters:po:auth URN mapping is not defined", "BPMN",
160                                                 ErrorCode.UnknownError.getValue());
161                         } else {
162                                 try {
163                                         def encodedString = utils.getBasicAuth(basicAuthValue, UrnPropertiesReader.getVariable("mso.msoKey", execution))
164                                         execution.setVariable(prefix + 'basicAuthHeaderValue', encodedString)
165                                 } catch (IOException ex) {
166                                         logger.debug(getProcessKey(execution) + ": Unable to encode BasicAuth credentials for SDNCAdapter")
167                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(),
168                                                         getProcessKey(execution) + ": Unable to encode BasicAuth credentials for SDNCAdapter",
169                                                         "BPMN", ErrorCode.UnknownError.getValue(), ex);
170                                 }
171                         }
172
173                         // Set the timeout value, e.g. PT5M. It may be specified in the request as the
174                         // bpTimeout value.  If it's not in the request, use the URN mapping value.
175
176                         String timeout = jsonUtil.getJsonValue(request, requestType + ".bpTimeout")
177
178                         // in addition to null/empty, also need to verify that the timer value is a valid duration "P[n]T[n]H|M|S"
179                         String timerRegex = "PT[0-9]+[HMS]";
180                         if (timeout == null || timeout.isEmpty() || !timeout.matches(timerRegex)) {
181                                 logger.debug(getProcessKey(execution) + ': preProcessRequest(): null/empty/invalid bpTimeout value. Using "mso.adapters.sdnc.timeout"')
182                                 timeout = UrnPropertiesReader.getVariable("mso.adapters.sdnc.timeout", execution)
183                         }
184
185                         // the timeout could still be null at this point if the config parm is missing/undefined
186                         // forced to log (so OPs can fix the config) and temporarily use a hard coded value of 10 seconds
187                         if (timeout == null) {
188                                 msoLogger.warnSimple('preProcessRequest()', 'property "mso.adapters.sdnc.timeout" is missing/undefined. Using "PT10S"')
189                                 timeout = "PT10S"
190                         }
191
192                         execution.setVariable(prefix + 'timeout', timeout)
193                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'timeout = ' + timeout)
194                 } catch (BpmnError e) {
195                         throw e
196                 } catch (Exception e) {
197                         String msg = 'Caught exception in ' + method + ": " + e
198                         logger.debug(msg)
199                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
200                                         ErrorCode.UnknownError.getValue());
201                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
202                 }
203         }
204
205         /**
206          * Sends the request to the SDNC adapter.
207          */
208         public void sendRequestToSDNCAdapter(DelegateExecution execution) {
209                 def method = getClass().getSimpleName() + '.sendRequestToSDNCAdapter(' +
210                         'execution=' + execution.getId() +
211                         ')'
212                 logger.trace('Entered ' + method)
213
214                 String prefix = execution.getVariable('prefix')
215
216                 try {
217                         String sdncAdapterMethod = execution.getVariable(prefix + 'sdncAdapterMethod')
218                         logger.debug("SDNC Method is: " + sdncAdapterMethod)
219                         String sdncAdapterUrl = execution.getVariable(prefix + 'sdncAdapterUrl')
220                         logger.debug("SDNC Url is: " + sdncAdapterUrl)
221                         String sdncAdapterRequest = execution.getVariable(prefix + 'sdncAdapterRequest')
222                         logger.debug("SDNC Rest Request is: " + sdncAdapterRequest)
223
224                         URL url = new URL(sdncAdapterUrl);
225
226                         HttpClient httpClient = new HttpClientFactory().newJsonClient(url, TargetEntity.SDNC_ADAPTER)
227                         httpClient.addAdditionalHeader("X-ONAP-RequestID", execution.getVariable("mso-request-id"))
228                         httpClient.addAdditionalHeader("X-ONAP-InvocationID", UUID.randomUUID().toString())
229                         httpClient.addAdditionalHeader("X-ONAP-PartnerName", "SO-SDNCAdapter")
230                         httpClient.addAdditionalHeader("mso-request-id", execution.getVariable("mso-request-id"))
231                         httpClient.addAdditionalHeader("mso-service-instance-id", execution.getVariable("mso-service-instance-id"))
232                         httpClient.addAdditionalHeader("Authorization", execution.getVariable(prefix + "basicAuthHeaderValue"))
233
234                         Response response
235
236                         if ("GET".equals(sdncAdapterMethod)) {
237                                 response = httpClient.get()
238                         } else if ("PUT".equals(sdncAdapterMethod)) {
239                                 response = httpClient.put(sdncAdapterRequest)
240                         } else if ("POST".equals(sdncAdapterMethod)) {
241                                 response = httpClient.post(sdncAdapterRequest)
242                         } else if ("DELETE".equals(sdncAdapterMethod)) {
243                                 response = httpClient.delete(sdncAdapterRequest)
244                         } else {
245                                 String msg = 'Unsupported HTTP method "' + sdncAdapterMethod + '" in ' + method + ": " + e
246                                 logger.debug(msg)
247                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
248                                                 ErrorCode.UnknownError.getValue());
249                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
250                         }
251
252                         execution.setVariable(prefix + "sdncAdapterStatusCode", response.getStatus())
253                         if(response.hasEntity()){
254                                 execution.setVariable(prefix + "sdncAdapterResponse", response.readEntity(String.class))
255                         }
256                 } catch (BpmnError e) {
257                         throw e
258                 } catch (Exception e) {
259                         String msg = 'Caught exception in ' + method + ": " + e
260                         logger.debug(msg, e)
261                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
262                                         ErrorCode.UnknownError.getValue());
263                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
264                 }
265         }
266
267         /**
268          * Processes a callback.
269          */
270         public void processCallback(DelegateExecution execution){
271                 def method = getClass().getSimpleName() + '.processCallback(' +
272                         'execution=' + execution.getId() +
273                         ')'
274                 logger.trace('Entered ' + method)
275
276                 String prefix = execution.getVariable('prefix')
277                 String callback = execution.getVariable('SDNCAResponse_MESSAGE')
278                 logger.debug("Incoming SDNC Rest Callback is: " + callback)
279
280                 try {
281                         int callbackNumber = 1
282                         while (execution.getVariable(prefix + 'callback' + callbackNumber) != null) {
283                                 ++callbackNumber
284                         }
285
286                         execution.setVariable(prefix + 'callback' + callbackNumber, callback)
287                         execution.removeVariable('SDNCAResponse_MESSAGE')
288
289                         String responseType = jsonUtil.getJsonRootProperty(callback)
290
291                         // Get the ackFinalIndicator and make sure it's either Y or N.  Default to Y.
292                         String ackFinalIndicator = jsonUtil.getJsonValue(callback, responseType + ".ackFinalIndicator")
293
294                         if (!'N'.equals(ackFinalIndicator)) {
295                                 ackFinalIndicator = 'Y'
296                         }
297
298                         execution.setVariable(prefix + "ackFinalIndicator", ackFinalIndicator)
299
300                         if (responseType.endsWith('Error')) {
301                                 sdncAdapterBuildWorkflowException(execution, callback)
302                         }
303                 } catch (Exception e) {
304                         callback = callback == null || String.valueOf(callback).isEmpty() ? "NONE" : callback
305                         String msg = "Received error from SDNCAdapter: " + callback
306                         logger.debug(getProcessKey(execution) + ': ' + msg)
307                         exceptionUtil.buildWorkflowException(execution, 5300, msg)
308                 }
309         }
310
311         /**
312          * Tries to parse the response as XML to extract the information to create
313          * a WorkflowException.  If the response cannot be parsed, a more generic
314          * WorkflowException is created.
315          */
316         public void sdncAdapterBuildWorkflowException(DelegateExecution execution, String response) {
317                 try {
318                         String responseType = jsonUtil.getJsonRootProperty(response)
319                         String responseCode = jsonUtil.getJsonValue(response, responseType + ".responseCode")
320                         String responseMessage = jsonUtil.getJsonValue(response, responseType + ".responseMessage")
321
322                         String info = ""
323
324                         if (responseCode != null && !responseCode.isEmpty()) {
325                                  info += " responseCode='" + responseCode + "'"
326                         }
327
328                         if (responseMessage != null && !responseMessage.isEmpty()) {
329                                  info += " responseMessage='" + responseMessage + "'"
330                         }
331
332                         // Note: the mapping function handles a null or empty responseCode
333                         int mappedResponseCode = Integer.parseInt(exceptionUtil.MapSDNCResponseCodeToErrorCode(responseCode));
334                         exceptionUtil.buildWorkflowException(execution, mappedResponseCode, "Received " + responseType +
335                                 " from SDNCAdapter:" + info)
336                 } catch (Exception e) {
337                         response = response == null || String.valueOf(response).isEmpty() ? "NONE" : response
338                         exceptionUtil.buildWorkflowException(execution, 5300, "Received error from SDNCAdapter: " + response)
339                 }
340         }
341
342         /**
343          * Gets the last callback request from the execution, or null if there was no callback.
344          */
345         public String getLastCallback(DelegateExecution execution) {
346                 def method = getClass().getSimpleName() + '.getLastCallback(' +
347                         'execution=' + execution.getId() +
348                         ')'
349                 logger.trace('Entered ' + method)
350
351                 String prefix = execution.getVariable('prefix')
352
353                 try {
354                         int callbackNumber = 1
355                         String callback = null
356
357                         while (true) {
358                                 String thisCallback = (String) execution.getVariable(prefix + 'callback' + callbackNumber)
359
360                                 if (thisCallback == null) {
361                                         break
362                                 }
363
364                                 callback = thisCallback
365                                 ++callbackNumber
366                         }
367
368                         return callback
369                 } catch (Exception e) {
370                         String msg = 'Caught exception in ' + method + ": " + e
371                         logger.debug(msg)
372                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
373                                         ErrorCode.UnknownError.getValue());
374                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
375                 }
376         }
377
378         /**
379          * Sets the timeout value to wait for the next notification.
380          */
381         public void setTimeoutValue(DelegateExecution execution) {
382                 def method = getClass().getSimpleName() + '.setTimeoutValue(' +
383                         'execution=' + execution.getId() +
384                         ')'
385                 logger.trace('Entered ' + method)
386
387                 String prefix = execution.getVariable('prefix')
388
389                 try {
390                         def timeoutValue = UrnPropertiesReader.getVariable("mso.adapters.sdnc.timeout", execution)
391
392                         if (execution.getVariable(prefix + 'callback1') != null) {
393                                 // Waiting for subsequent notifications
394                         }
395                 } catch (Exception e) {
396                         String msg = 'Caught exception in ' + method + ": " + e
397                         logger.debug(msg)
398                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
399                                         ErrorCode.UnknownError.getValue());
400                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
401                 }
402         }
403         
404         public Logger getLogger() {
405                 return logger;
406         }
407 }