Preparing the request payload for calling SO macro flow.
[externalapi/nbi.git] / src / main / java / org / onap / nbi / apis / serviceorder / workflow / SOTaskProcessor.java
1 /**
2  * Copyright (c) 2018 Orange
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11  * specific language governing permissions and limitations under the License.
12  */
13
14 package org.onap.nbi.apis.serviceorder.workflow;
15
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Date;
20 import java.util.List;
21 import java.util.Optional;
22 import org.onap.nbi.apis.serviceorder.model.ActionType;
23 import org.onap.nbi.apis.serviceorder.model.ServiceOrder;
24 import org.onap.nbi.apis.serviceorder.model.ServiceOrderItem;
25 import org.onap.nbi.apis.serviceorder.model.StateType;
26 import org.onap.nbi.apis.serviceorder.model.consumer.CreateE2EServiceInstanceResponse;
27 import org.onap.nbi.apis.serviceorder.model.consumer.CreateMacroServiceInstanceResponse;
28 import org.onap.nbi.apis.serviceorder.model.consumer.CreateServiceInstanceResponse;
29 import org.onap.nbi.apis.serviceorder.model.orchestrator.ExecutionTask;
30 import org.onap.nbi.apis.serviceorder.model.orchestrator.ServiceOrderInfo;
31 import org.onap.nbi.apis.serviceorder.repositories.ExecutionTaskRepository;
32 import org.onap.nbi.apis.serviceorder.service.ServiceOrderService;
33 import org.onap.nbi.apis.serviceorder.utils.E2EServiceUtils;
34 import org.onap.nbi.apis.serviceorder.utils.JsonEntityConverter;
35 import org.onap.nbi.apis.serviceorder.utils.MacroServiceUtils;
36 import org.onap.nbi.exceptions.TechnicalException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.springframework.beans.factory.annotation.Autowired;
40 import org.springframework.beans.factory.annotation.Value;
41 import org.springframework.http.HttpStatus;
42 import org.springframework.http.ResponseEntity;
43 import org.springframework.stereotype.Service;
44 import org.springframework.util.CollectionUtils;
45
46 @Service
47 public class SOTaskProcessor {
48
49     @Autowired
50     private ServiceOrderService serviceOrderService;
51
52     @Autowired
53     private ExecutionTaskRepository executionTaskRepository;
54
55     @Autowired
56     private PostSoProcessor postSoProcessor;
57
58     @Autowired
59     private SOGetStatusManager sOGetStatusManager;
60
61     @Value("${scheduler.pollingDurationInMins}")
62     private float pollingDurationInMins;
63
64     private static final Logger LOGGER = LoggerFactory.getLogger(SOTaskProcessor.class);
65
66     /**
67      * Run the ServiceOrchestrator processing for a serviceOrderItem which with any sub relations
68      */
69     public void processOrderItem(ExecutionTask executionTask) {
70
71         executionTask.setLastAttemptDate(new Date());
72         executionTaskRepository.save(executionTask);
73
74         ServiceOrderInfo serviceOrderInfo = getServiceOrderInfo(executionTask);
75
76         Optional<ServiceOrder> optionalServiceOrder =
77                 serviceOrderService.findServiceOrderById(serviceOrderInfo.getServiceOrderId());
78         if (!optionalServiceOrder.isPresent()) {
79             throw new TechnicalException(
80                     "Unable to retrieve service order for id " + serviceOrderInfo.getServiceOrderId());
81         }
82         ServiceOrder serviceOrder = optionalServiceOrder.get();
83         ServiceOrderItem serviceOrderItem = getServiceOrderItem(executionTask, serviceOrder);
84         boolean e2eService =
85                 E2EServiceUtils.isE2EService(serviceOrderInfo.getServiceOrderItemInfos().get(serviceOrderItem.getId()));
86         boolean macroService = MacroServiceUtils
87                 .isMacroService(serviceOrderInfo.getServiceOrderItemInfos().get(serviceOrderItem.getId()));
88
89         if (shouldPostSo(serviceOrderItem)) {
90             if (e2eService) {
91                 ResponseEntity<CreateE2EServiceInstanceResponse> response =
92                         postSoProcessor.postE2EServiceOrderItem(serviceOrderInfo, serviceOrderItem, serviceOrder);
93                 updateE2EServiceOrderItem(response, serviceOrderItem, serviceOrder);
94             } else if (macroService) {
95               LOGGER.info("Mode type macro");
96               // call SO macro flow.(EXTAPI-368)
97               ResponseEntity<CreateMacroServiceInstanceResponse> response = postSoProcessor
98                   .postMacroServiceOrderItem(serviceOrderInfo, serviceOrderItem);
99               updateMacroServiceOrderItem(response, serviceOrderItem, serviceOrder);
100             } else {
101
102                 ResponseEntity<CreateServiceInstanceResponse> response =
103                         postSoProcessor.postServiceOrderItem(serviceOrderInfo, serviceOrderItem);
104                 updateServiceOrderItem(response, serviceOrderItem, serviceOrder);
105             }
106         }
107
108         boolean shouldStopPolling = shouldStopPolling(executionTask);
109         if (!shouldStopPolling && StateType.FAILED != serviceOrderItem.getState()) {
110             // TODO lancer en asynchrone
111             sOGetStatusManager.pollRequestStatus(serviceOrder, serviceOrderItem, e2eService);
112
113             if (serviceOrderItem.getState().equals(StateType.COMPLETED)) {
114                 updateSuccessTask(executionTask);
115             }
116         } else if (shouldStopPolling && StateType.FAILED != serviceOrderItem.getState()) {
117             serviceOrderService.addOrderItemMessage(serviceOrder, serviceOrderItem, "504");
118             updateFailedTask(executionTask, serviceOrder);
119         } else {
120             updateFailedTask(executionTask, serviceOrder);
121         }
122
123         updateServiceOrder(serviceOrder);
124     }
125
126     private boolean shouldPostSo(ServiceOrderItem serviceOrderItem) {
127         return StateType.ACKNOWLEDGED == serviceOrderItem.getState()
128                 || StateType.INPROGRESS_MODIFY_ITEM_TO_CREATE == serviceOrderItem.getState();
129     }
130
131     private ServiceOrderItem getServiceOrderItem(ExecutionTask executionTask, ServiceOrder serviceOrder) {
132         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
133             if (item.getId().equals(executionTask.getOrderItemId())) {
134                 return item;
135             }
136         }
137         throw new TechnicalException(
138                 "Unable to retrieve serviceOrderItem for executionTaskId " + executionTask.getInternalId());
139     }
140
141     private ServiceOrderInfo getServiceOrderInfo(ExecutionTask executionTask) {
142         String serviceOrderInfoJson = executionTask.getServiceOrderInfoJson();
143         ServiceOrderInfo serviceOrderInfo = null;
144         try {
145             serviceOrderInfo = JsonEntityConverter.convertJsonToServiceOrderInfo(serviceOrderInfoJson);
146         } catch (IOException e) {
147             LOGGER.error("Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId(),
148                     e);
149             throw new TechnicalException(
150                     "Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId());
151         }
152         return serviceOrderInfo;
153     }
154
155     private void updateServiceOrder(ServiceOrder serviceOrder) {
156         boolean atLeastOneCompleted = false;
157         boolean atLeastOneNotFinished = false;
158         boolean atLeastOneFailed = false;
159
160         for (ServiceOrderItem serviceOrderItem : serviceOrder.getOrderItem()) {
161             switch (serviceOrderItem.getState()) {
162                 case COMPLETED:
163                     atLeastOneCompleted = true;
164                     break;
165                 case INPROGRESS:
166                 case ACKNOWLEDGED:
167                     atLeastOneNotFinished = true;
168                     break;
169                 case FAILED:
170                     atLeastOneFailed = true;
171                     break;
172                 default:
173                     break;
174
175             }
176         }
177         if (atLeastOneNotFinished) {
178             serviceOrderService.updateOrderState(serviceOrder, StateType.INPROGRESS);
179         } else {
180             StateType finalState;
181             if (atLeastOneFailed) {
182                 if (!atLeastOneCompleted) {
183                     finalState = StateType.FAILED;
184                 } else {
185                     finalState = StateType.PARTIAL;
186                 }
187             } else {
188                 finalState = StateType.COMPLETED;
189             }
190             serviceOrderService.updateOrderState(serviceOrder, finalState);
191         }
192     }
193
194     /**
195      * Update ServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
196      */
197     private void updateServiceOrderItem(ResponseEntity<CreateServiceInstanceResponse> response,
198             ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
199
200         if (response == null || !response.getStatusCode().is2xxSuccessful()) {
201             LOGGER.warn("response ko for serviceOrderItem.id=" + orderItem.getId());
202             serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
203             buildOrderMessageIfNeeded(orderItem, serviceOrder, response);
204         } else {
205             CreateServiceInstanceResponse createServiceInstanceResponse = response.getBody();
206             if (createServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
207                 orderItem.getService().setId(createServiceInstanceResponse.getRequestReferences().getInstanceId());
208                 orderItem.setRequestId(createServiceInstanceResponse.getRequestReferences().getRequestId());
209             }
210
211             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
212                     || response.getBody().getRequestReferences() == null) {
213                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
214                 LOGGER.warn("order item {} failed , status {} , response {}", orderItem.getId(),
215                         response.getStatusCode(), response.getBody());
216             } else {
217                 updateOrderItemToInProgress(serviceOrder, orderItem);
218             }
219         }
220     }
221
222     /**
223      * Update ServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
224      */
225     private void updateMacroServiceOrderItem(ResponseEntity<CreateMacroServiceInstanceResponse> response,
226         ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
227
228         if (response == null || !response.getStatusCode().is2xxSuccessful()) {
229             LOGGER.warn("response ko for serviceOrderItem.id=" + orderItem.getId());
230             serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
231             buildOrderMessageIfNeeded(orderItem, serviceOrder, response);
232         } else {
233             CreateMacroServiceInstanceResponse createMacroServiceInstanceResponse = response.getBody();
234             if (createMacroServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
235                 orderItem.getService().setId(createMacroServiceInstanceResponse.getRequestReferences().getInstanceId());
236                 orderItem.setRequestId(createMacroServiceInstanceResponse.getRequestReferences().getRequestId());
237             }
238
239             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
240                         || response.getBody().getRequestReferences() == null) {
241                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
242                 LOGGER
243                     .warn("order item {} failed , status {} , response {}", orderItem.getId(), response.getStatusCode(),
244                         response.getBody());
245             } else {
246                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.INPROGRESS);
247             }
248         }
249     }
250     
251     private void updateOrderItemToInProgress(ServiceOrder serviceOrder, ServiceOrderItem serviceOrderItem) {
252         if (serviceOrderItem.getAction() != ActionType.MODIFY) {
253             serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem, StateType.INPROGRESS);
254         } else {
255             if (StateType.ACKNOWLEDGED == serviceOrderItem.getState()) {
256                 serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem,
257                         StateType.INPROGRESS_MODIFY_REQUEST_DELETE_SEND);
258             } else {
259                 serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem,
260                         StateType.INPROGRESS_MODIFY_REQUEST_CREATE_SEND);
261             }
262         }
263     }
264
265     private void buildOrderMessageIfNeeded(ServiceOrderItem serviceOrderItem, ServiceOrder serviceOrder,
266             ResponseEntity<?> response) {
267         if (response != null) {
268             if (response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
269                 serviceOrderService.addOrderMessage(serviceOrder, "502");
270             } else if (response.getStatusCode() == HttpStatus.BAD_REQUEST) {
271                 ResponseEntity<?> messageError = response;
272                 if (messageError.getBody().toString().toLowerCase().contains("serviceinstance already exists")) {
273                     serviceOrderService.addOrderItemMessage(serviceOrder, serviceOrderItem, "105");
274                 } else {
275                     serviceOrderService.addOrderItemMessageRequestSo(serviceOrder, serviceOrderItem,
276                             messageError.getBody().toString());
277                 }
278             }
279         }
280     }
281
282     /**
283      * Update E2EServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
284      */
285     private void updateE2EServiceOrderItem(ResponseEntity<CreateE2EServiceInstanceResponse> response,
286             ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
287
288         if (response == null || !response.getStatusCode().is2xxSuccessful()) {
289             LOGGER.warn("response ko for serviceOrderItem.id=" + orderItem.getId());
290             serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
291         } else {
292             CreateE2EServiceInstanceResponse createE2EServiceInstanceResponse = response.getBody();
293             if (createE2EServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
294                 orderItem.getService().setId(createE2EServiceInstanceResponse.getService().getServiceId());
295                 orderItem.setRequestId(createE2EServiceInstanceResponse.getService().getOperationId());
296             }
297
298             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
299                     || response.getBody().getService().getOperationId() == null
300                     || response.getBody().getService().getServiceId() == null) {
301                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
302                 LOGGER.warn("order item {} failed , status {} , response {}", orderItem.getId(),
303                         response.getStatusCode(), response.getBody());
304             } else {
305                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.INPROGRESS);
306             }
307         }
308     }
309
310     /**
311      * Update an executionTask in database when it's process with a success
312      */
313     private void updateSuccessTask(ExecutionTask executionTask) {
314         executionTaskRepository.deleteById(executionTask.getInternalId());
315         executionTaskRepository.updateReliedTaskAfterDelete(executionTask.getInternalId());
316
317     }
318
319     /**
320      * @param executionTask
321      * @param serviceOrder
322      */
323     private void updateFailedTask(ExecutionTask executionTask, ServiceOrder serviceOrder) {
324         List<ExecutionTask> executionTasksToDelete = findExecutionTasksRecursively(executionTask);
325         for (ExecutionTask taskId : executionTasksToDelete) {
326             executionTaskRepository.delete(taskId);
327             LOGGER.warn("task {} with orderitem id {} deleted cause orderitem id {} failed ", taskId.getInternalId(),
328                     taskId.getOrderItemId(), executionTask.getOrderItemId());
329         }
330         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
331             for (ExecutionTask taskToDelete : executionTasksToDelete) {
332                 if (taskToDelete.getOrderItemId().equals(item.getId())) {
333                     serviceOrderService.updateOrderItemState(serviceOrder, item, StateType.FAILED);
334                     LOGGER.warn("task {} with orderitem id {} failed cause orderitem id {} failed ",
335                             taskToDelete.getInternalId(), taskToDelete.getOrderItemId(),
336                             executionTask.getOrderItemId());
337
338                 }
339             }
340         }
341     }
342
343     /**
344      * @param executionTask
345      * @return
346      */
347     private List<ExecutionTask> findExecutionTasksRecursively(ExecutionTask executionTask) {
348
349         List<ExecutionTask> executionTasks = new ArrayList<>();
350
351         List<ExecutionTask> tasksReliedToAnOrderItemId =
352                 executionTaskRepository.findTasksReliedToAnOrderItemId(executionTask.getInternalId());
353
354         if (CollectionUtils.isEmpty(tasksReliedToAnOrderItemId)) {
355             return Arrays.asList(executionTask);
356         } else {
357             for (ExecutionTask task : tasksReliedToAnOrderItemId) {
358                 executionTasks.addAll(findExecutionTasksRecursively(task));
359             }
360         }
361         executionTasks.add(executionTask);
362         return executionTasks;
363     }
364
365     private boolean shouldStopPolling(ExecutionTask executionTask) {
366         long createTimeinMillis = executionTask.getCreateDate().getTime();
367         long lastAttemptTimeInMillis = executionTask.getLastAttemptDate().getTime();
368         long differenceInMillis = lastAttemptTimeInMillis - createTimeinMillis;
369         float pollingDurationInMillis = pollingDurationInMins * 60000;
370         LOGGER.debug("Task {} with orderitem id {}: Task create date: {} Task last attempt date: {}",
371                 executionTask.getInternalId(), executionTask.getOrderItemId(), createTimeinMillis,
372                 lastAttemptTimeInMillis);
373         LOGGER.debug("Difference {} and Polling Duration {}", differenceInMillis, pollingDurationInMins);
374         return (differenceInMillis > pollingDurationInMillis);
375     }
376 }