6f4eb2ec54c4a0cefba17c1bc291225f3cad5ebd
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / ServiceConsumptionServlet.java
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.openecomp.sdc.be.servlets;
17
18 import static org.openecomp.sdc.be.tosca.InterfacesOperationsConverter.SELF;
19
20 import com.google.gson.Gson;
21 import com.google.gson.JsonParseException;
22 import com.jcabi.aspects.Loggable;
23 import fj.data.Either;
24 import io.swagger.v3.oas.annotations.Parameter;
25 import io.swagger.v3.oas.annotations.media.ArraySchema;
26 import io.swagger.v3.oas.annotations.media.Content;
27 import io.swagger.v3.oas.annotations.media.Schema;
28 import io.swagger.v3.oas.annotations.responses.ApiResponse;
29 import io.swagger.v3.oas.annotations.servers.Server;
30 import io.swagger.v3.oas.annotations.servers.Servers;
31 import io.swagger.v3.oas.annotations.tags.Tag;
32 import io.swagger.v3.oas.annotations.tags.Tags;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 import java.util.stream.Collectors;
40 import javax.inject.Inject;
41 import javax.inject.Singleton;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.ws.rs.Consumes;
44 import javax.ws.rs.GET;
45 import javax.ws.rs.HeaderParam;
46 import javax.ws.rs.POST;
47 import javax.ws.rs.Path;
48 import javax.ws.rs.PathParam;
49 import javax.ws.rs.Produces;
50 import javax.ws.rs.core.Context;
51 import javax.ws.rs.core.MediaType;
52 import javax.ws.rs.core.Response;
53 import org.apache.commons.lang3.StringUtils;
54 import org.json.simple.JSONArray;
55 import org.json.simple.parser.JSONParser;
56 import org.json.simple.parser.ParseException;
57 import org.openecomp.sdc.be.components.impl.InterfaceOperationBusinessLogic;
58 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
59 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
60 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
61 import org.openecomp.sdc.be.config.BeEcompErrorManager;
62 import org.openecomp.sdc.be.dao.api.ActionStatus;
63 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
64 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
65 import org.openecomp.sdc.be.impl.ComponentsUtils;
66 import org.openecomp.sdc.be.model.Operation;
67 import org.openecomp.sdc.be.model.OperationInput;
68 import org.openecomp.sdc.be.model.User;
69 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
70 import org.openecomp.sdc.be.types.ServiceConsumptionData;
71 import org.openecomp.sdc.be.types.ServiceConsumptionSource;
72 import org.openecomp.sdc.common.api.Constants;
73 import org.openecomp.sdc.exception.ResponseFormat;
74 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
79 @Path("/v1/catalog")
80 @Tags({@Tag(name = "SDCE-2 APIs")})
81 @Servers({@Server(url = "/sdc2/rest")})
82 @Singleton
83 public class ServiceConsumptionServlet extends BeGenericServlet {
84
85     private static final Logger log = LoggerFactory.getLogger(ServiceConsumptionServlet.class);
86     private final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic;
87     private final ServiceBusinessLogic serviceBusinessLogic;
88
89     @Inject
90     public ServiceConsumptionServlet(ComponentsUtils componentsUtils,
91                                      InterfaceOperationBusinessLogic interfaceOperationBusinessLogic, ServiceBusinessLogic serviceBusinessLogic) {
92         super(componentsUtils);
93         this.interfaceOperationBusinessLogic = interfaceOperationBusinessLogic;
94         this.serviceBusinessLogic = serviceBusinessLogic;
95     }
96
97     @POST
98     @Path("/services/{serviceId}/consumption/{serviceInstanceId}")
99     @Consumes(MediaType.APPLICATION_JSON)
100     @Produces(MediaType.APPLICATION_JSON)
101     @io.swagger.v3.oas.annotations.Operation(description = "Service consumption on operation", method = "POST", summary = "Returns consumption data", responses = {
102         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
103         @ApiResponse(responseCode = "201", description = "Service property created"),
104         @ApiResponse(responseCode = "403", description = "Restricted operation"),
105         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
106         @ApiResponse(responseCode = "409", description = "Service property already exist")})
107     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
108     public Response addInputToServiceOperation(@PathParam("serviceId") final String serviceId,
109                                                @PathParam("serviceInstanceId") final String serviceInstanceId,
110                                                @Parameter(description = "Service Consumption Data", required = true) String data,
111                                                @Context final HttpServletRequest request,
112                                                @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
113         String url = request.getMethod() + " " + request.getRequestURI();
114         log.debug("Start handle request of {} modifier id is {} data is {}", url, userId, data);
115         User modifier = new User();
116         modifier.setUserId(userId);
117         try {
118             Either<Map<String, List<ServiceConsumptionData>>, ResponseFormat> dataFromJson = getServiceConsumptionData(data, modifier);
119             if (dataFromJson.isRight()) {
120                 return buildErrorResponse(dataFromJson.right().value());
121             }
122             Map<String, List<ServiceConsumptionData>> serviceConsumptionDataMap = dataFromJson.left().value();
123             for (Entry<String, List<ServiceConsumptionData>> consumptionEntry : serviceConsumptionDataMap.entrySet()) {
124                 List<ServiceConsumptionData> consumptionList = consumptionEntry.getValue();
125                 Either<List<Operation>, ResponseFormat> operationEither = serviceBusinessLogic
126                     .addServiceConsumptionData(serviceId, serviceInstanceId, consumptionEntry.getKey(), consumptionList, userId);
127                 if (operationEither.isRight()) {
128                     return buildErrorResponse(operationEither.right().value());
129                 }
130             }
131             return buildOkResponse(serviceConsumptionDataMap);
132         } catch (Exception e) {
133             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Operation Inputs");
134             log.debug("Create Operation Inputs failed with exception", e);
135             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
136             return buildErrorResponse(responseFormat);
137         }
138     }
139
140     @GET
141     @Path("/services/{serviceId}/consumption/{serviceInstanceId}/interfaces/{interfaceId}/operations/{operationId}/inputs")
142     @Consumes(MediaType.APPLICATION_JSON)
143     @Produces(MediaType.APPLICATION_JSON)
144     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
145     public Response getInputsListOfOperation(@PathParam("serviceId") final String serviceId,
146                                              @PathParam("serviceInstanceId") final String serviceInstanceId,
147                                              @PathParam("interfaceId") final String interfaceId, @PathParam("operationId") final String operationId,
148                                              @Context final HttpServletRequest request,
149                                              @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
150         String url = request.getMethod() + " " + request.getRequestURI();
151         log.debug("Start handle request of {} modifier id is {}", url, userId);
152         User user = new User();
153         user.setUserId(userId);
154         try {
155             Either<List<OperationInputDefinition>, ResponseFormat> inputsEither = interfaceOperationBusinessLogic
156                 .getInputsListForOperation(serviceId, serviceInstanceId, interfaceId, operationId, user);
157             if (inputsEither.isRight()) {
158                 return buildErrorResponse(inputsEither.right().value());
159             }
160             List<OperationInputDefinition> inputs = inputsEither.left().value();
161             return buildOkResponse(updateOperationInputListForUi(inputs, interfaceOperationBusinessLogic));
162         } catch (Exception e) {
163             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Operation Inputs");
164             log.debug("Get Operation Inputs failed with exception", e);
165             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
166             return buildErrorResponse(responseFormat);
167         }
168     }
169
170     private List<OperationInput> updateOperationInputListForUi(List<OperationInputDefinition> inputsList,
171                                                                InterfaceOperationBusinessLogic interfaceOperationBL) {
172         List<OperationInput> operationInputs = new ArrayList<>();
173         for (OperationInputDefinition input : inputsList) {
174             String value = input.getValue();
175             // Additional UI mapping needed for other sources
176             if (StringUtils.isNotBlank(value) && !ServiceConsumptionSource.STATIC.getSource().equals(input.getSource())) {
177                 uiMappingForOtherSources(value, input);
178             }
179             // Add Constraint for UI
180             OperationInput operationInput = new OperationInput(input);
181             operationInput.setConstraints(interfaceOperationBL.setInputConstraint(input));
182             operationInputs.add(operationInput);
183         }
184         return operationInputs;
185     }
186
187     private void uiMappingForOtherSources(String value, OperationInputDefinition input) {
188         try {
189             Map<String, Object> valueAsMap = (new Gson()).fromJson(value, Map.class);
190             String toscaFunction = valueAsMap.keySet().iterator().next();
191             Object consumptionValueName = valueAsMap.values().iterator().next();
192             if (consumptionValueName instanceof List) {
193                 List<Object> toscaFunctionList = (List<Object>) consumptionValueName;
194                 String consumptionInputValue = null;
195                 if (ToscaFunctions.GET_PROPERTY.getFunctionName().equals(toscaFunction)) {
196                     String propertyValue = toscaFunctionList.stream().map(Object::toString).filter(val -> !val.equals(SELF))
197                         .collect(Collectors.joining("_"));
198                     consumptionInputValue = String.valueOf(propertyValue);
199                 } else if (ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName().equals(toscaFunction)) {
200                     //Return full output name
201                     consumptionInputValue = toscaFunctionList.get(1) + "." + toscaFunctionList.get(2) + "." + toscaFunctionList.get(3);
202                 }
203                 input.setValue(consumptionInputValue);
204             } else {
205                 input.setValue(String.valueOf(consumptionValueName));
206             }
207         } catch (JsonParseException ex) {
208             log.info("This means it is static value for which no changes are needed");
209         }
210     }
211
212     private Either<Map<String, List<ServiceConsumptionData>>, ResponseFormat> getServiceConsumptionData(String data, User user) {
213         JSONParser parser = new JSONParser();
214         Map<String, List<ServiceConsumptionData>> serviceConsumptionDataMap = new HashMap<>();
215         try {
216             JSONArray operationsArray = (JSONArray) parser.parse(data);
217             Iterator iterator = operationsArray.iterator();
218             while (iterator.hasNext()) {
219                 Map next = (Map) iterator.next();
220                 Entry consumptionEntry = (Entry) next.entrySet().iterator().next();
221                 String operationId = (String) consumptionEntry.getKey();
222                 Object value = consumptionEntry.getValue();
223                 JSONArray inputsArray = (JSONArray) parser.parse(value.toString());
224                 serviceConsumptionDataMap.putIfAbsent(operationId, new ArrayList<>());
225                 for (Object consumptionObject : inputsArray) {
226                     Either<ServiceConsumptionData, ResponseFormat> serviceDataEither = getComponentsUtils()
227                         .convertJsonToObjectUsingObjectMapper(consumptionObject.toString(), user, ServiceConsumptionData.class,
228                             AuditingActionEnum.CREATE_RESOURCE, ComponentTypeEnum.SERVICE);
229                     if (serviceDataEither.isRight()) {
230                         return Either.right(serviceDataEither.right().value());
231                     }
232                     serviceConsumptionDataMap.get(operationId).add(serviceDataEither.left().value());
233                 }
234             }
235         } catch (ParseException e) {
236             log.info("Conetnt is invalid - {}", data);
237             return Either.right(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
238         }
239         return Either.left(serviceConsumptionDataMap);
240     }
241 }