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