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