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