catalog-be servlets refactoring
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / BeGenericServlet.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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.openecomp.sdc.be.servlets;
22
23 import com.fasterxml.jackson.databind.DeserializationFeature;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.module.SimpleModule;
26 import com.google.gson.Gson;
27 import com.google.gson.GsonBuilder;
28 import com.google.gson.reflect.TypeToken;
29 import fj.data.Either;
30 import java.lang.reflect.Type;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.Objects;
37 import java.util.Set;
38 import java.util.function.Supplier;
39 import javax.servlet.ServletContext;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.ws.rs.core.Context;
42 import javax.ws.rs.core.Response;
43 import javax.ws.rs.core.Response.ResponseBuilder;
44 import org.json.simple.JSONArray;
45 import org.json.simple.JSONObject;
46 import org.json.simple.parser.JSONParser;
47 import org.json.simple.parser.ParseException;
48 import org.openecomp.sdc.be.components.impl.BaseBusinessLogic;
49 import org.openecomp.sdc.be.components.impl.InputsBusinessLogic;
50 import org.openecomp.sdc.be.components.impl.PolicyBusinessLogic;
51 import org.openecomp.sdc.be.config.BeEcompErrorManager;
52 import org.openecomp.sdc.be.dao.api.ActionStatus;
53 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
54 import org.openecomp.sdc.be.datatypes.enums.DeclarationTypeEnum;
55 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
56 import org.openecomp.sdc.be.impl.ComponentsUtils;
57 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
58 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
59 import org.openecomp.sdc.be.model.PropertyConstraint;
60 import org.openecomp.sdc.be.model.PropertyDefinition;
61 import org.openecomp.sdc.be.model.User;
62 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
63 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserializer;
64 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
65 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
66 import org.openecomp.sdc.be.user.UserBusinessLogic;
67 import org.openecomp.sdc.common.api.Constants;
68 import org.openecomp.sdc.common.datastructure.Wrapper;
69 import org.openecomp.sdc.common.log.wrappers.Logger;
70 import org.openecomp.sdc.common.servlets.BasicServlet;
71 import org.openecomp.sdc.exception.ResponseFormat;
72 import org.springframework.web.context.WebApplicationContext;
73
74 public class BeGenericServlet extends BasicServlet {
75
76     public BeGenericServlet(UserBusinessLogic userAdminManager,
77         ComponentsUtils componentsUtils) {
78         this.userAdminManager = userAdminManager;
79         this.componentsUtils = componentsUtils;
80     }
81
82     @Context
83     protected HttpServletRequest servletRequest;
84
85     private static final Logger log = Logger.getLogger(BeGenericServlet.class);
86
87     private static final String PROPERTY_NAME_REGEX = "[\\w,\\d,_]+";
88
89     private UserBusinessLogic userAdminManager;
90     protected ComponentsUtils componentsUtils;
91
92     /******************** New error response mechanism
93      * @param requestErrorWrapper **************/
94
95     protected Response buildErrorResponse(ResponseFormat requestErrorWrapper) {
96         return Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
97     }
98
99     protected Response buildGeneralErrorResponse() {
100         return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
101     }
102
103     protected Response buildOkResponse(Object entity) {
104         return buildOkResponseStatic(entity);
105     }
106
107     private static Response buildOkResponseStatic(Object entity) {
108         return Response.status(Response.Status.OK)
109             .entity(entity)
110             .build();
111     }
112
113     protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity) {
114         return buildOkResponse(errorResponseWrapper, entity, null);
115     }
116
117     protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity, Map<String, String> additionalHeaders) {
118         int status = errorResponseWrapper.getStatus();
119         ResponseBuilder responseBuilder = Response.status(status);
120         if (entity != null) {
121             if (log.isTraceEnabled())
122                 log.trace("returned entity is {}", entity.toString());
123             responseBuilder = responseBuilder.entity(entity);
124         }
125         if (additionalHeaders != null) {
126             for (Entry<String, String> additionalHeader : additionalHeaders.entrySet()) {
127                 String headerName = additionalHeader.getKey();
128                 String headerValue = additionalHeader.getValue();
129                 if (log.isTraceEnabled())
130                     log.trace("Adding header {} with value {} to the response", headerName, headerValue);
131                 responseBuilder.header(headerName, headerValue);
132             }
133         }
134         return responseBuilder.build();
135     }
136
137     /*******************************************************************************************************/
138     protected Either<User, ResponseFormat> getUser(final HttpServletRequest request, String userId) {
139         Either<User, ActionStatus> eitherCreator = userAdminManager.getUser(userId, false);
140         if (eitherCreator.isRight()) {
141             log.info("createResource method - user is not listed. userId= {}", userId);
142             ResponseFormat errorResponse = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_INFORMATION);
143             User user = new User("", "", userId, "", null, null);
144
145             getComponentsUtils().auditResource(errorResponse, user, "", AuditingActionEnum.CHECKOUT_RESOURCE);
146             return Either.right(errorResponse);
147         }
148         return Either.left(eitherCreator.left().value());
149
150     }
151
152     <T> T getClassFromWebAppContext(ServletContext context, Supplier<Class<T>> businessLogicClassGen) {
153         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
154         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
155         return webApplicationContext.getBean(businessLogicClassGen.get());
156     }
157
158     protected ComponentsUtils getComponentsUtils() {
159         return componentsUtils;
160     }
161
162     /**
163      * Used to support Unit Test.<br>
164      * Header Params are not supported in Unit Tests
165      *
166      * @return
167      */
168     String initHeaderParam(String headerValue, HttpServletRequest request, String headerName) {
169         String retValue;
170         if (headerValue != null) {
171             retValue = headerValue;
172         } else {
173             retValue = request.getHeader(headerName);
174         }
175         return retValue;
176     }
177
178     protected String getContentDispositionValue(String artifactFileName) {
179         return new StringBuilder().append("attachment; filename=\"").append(artifactFileName).append("\"").toString();
180     }
181
182     <T> void convertJsonToObjectOfClass(String json, Wrapper<T> policyWrapper, Class<T> clazz, Wrapper<Response> errorWrapper) {
183         T object = null;
184         ObjectMapper mapper = new ObjectMapper()
185             .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
186             .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
187         try {
188             log.trace("Starting to convert json to object. Json=\n{}", json);
189
190             SimpleModule module = new SimpleModule("customDeserializationModule");
191             module.addDeserializer(PropertyConstraint.class, new PropertyConstraintJacksonDeserializer());
192             mapper.registerModule(module);
193
194             object = mapper.readValue(json, clazz);
195             if (object != null) {
196                 policyWrapper.setInnerElement(object);
197             } else {
198                 BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
199                 log.debug("The object of class {} is null after converting from json. ", clazz);
200                 errorWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT)));
201             }
202         } catch (Exception e) {
203             BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
204             log.debug("The exception {} occured upon json to object convertation. Json=\n{}", e, json);
205             errorWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT)));
206         }
207     }
208
209     protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertyModel(String componentId, String data) {
210         JSONParser parser = new JSONParser();
211         JSONObject root;
212         try {
213             Map<String, PropertyDefinition> properties = new HashMap<>();
214             root = (JSONObject) parser.parse(data);
215
216             Set entrySet = root.entrySet();
217             Iterator iterator = entrySet.iterator();
218             while (iterator.hasNext()) {
219                 Entry next = (Entry) iterator.next();
220                 String propertyName = (String) next.getKey();
221
222                 if(!isPropertyNameValid(propertyName)) {
223                     return Either.right(ActionStatus.INVALID_PROPERTY_NAME);
224                 }
225
226                 JSONObject value = (JSONObject) next.getValue();
227                 Either<PropertyDefinition, ActionStatus> propertyDefinitionEither =
228                         getPropertyDefinitionFromJson(componentId, propertyName, value);
229
230                 if(propertyDefinitionEither.isRight()) {
231                     return Either.right(propertyDefinitionEither.right().value());
232                 }
233
234                 properties.put(propertyName, propertyDefinitionEither.left().value());
235             }
236
237             return Either.left(properties);
238         } catch (ParseException e) {
239             log.info("Property conetnt is invalid - {}", data);
240             return Either.right(ActionStatus.INVALID_CONTENT);
241         }
242     }
243
244     protected boolean isPropertyNameValid(String propertyName) {
245         return Objects.nonNull(propertyName)
246                        && propertyName.matches(PROPERTY_NAME_REGEX);
247
248     }
249
250     private Either<PropertyDefinition, ActionStatus> getPropertyDefinitionFromJson(String componentId, String propertyName, JSONObject value) {
251         String jsonString = value.toJSONString();
252         Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, PropertyDefinition.class);
253         if (convertJsonToObject.isRight()) {
254             return Either.right(convertJsonToObject.right().value());
255         }
256         PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
257         String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName);
258         propertyDefinition.setUniqueId(uniqueId);
259
260         return Either.left(propertyDefinition);
261     }
262
263     protected Either<Map<String, PropertyDefinition>, ActionStatus> getPropertiesListForUpdate(String data) {
264
265         Map<String, PropertyDefinition> properties = new HashMap<>();
266         JSONParser parser = new JSONParser();
267         JSONArray jsonArray;
268
269         try {
270             jsonArray = (JSONArray) parser.parse(data);
271             for (Object jsonElement : jsonArray) {
272                 String propertyAsString = jsonElement.toString();
273                 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(propertyAsString, PropertyDefinition.class);
274
275                 if (convertJsonToObject.isRight()) {
276                     return Either.right(convertJsonToObject.right().value());
277                 }
278
279                 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
280                 properties.put(propertyDefinition.getName(), propertyDefinition);
281             }
282
283             return Either.left(properties);
284         } catch (Exception e) {
285             log.info("Property content is invalid - {}", data);
286             return Either.right(ActionStatus.INVALID_CONTENT);
287         }
288
289     }
290
291
292     protected String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
293         JSONObject root = new JSONObject();
294         String propertyName = property.getKey();
295         PropertyDefinition propertyDefinition = property.getValue();
296         JSONObject propertyDefinitionO = getPropertyDefinitionJSONObject(propertyDefinition);
297         root.put(propertyName, propertyDefinitionO);
298         propertyDefinition.getType();
299         return root.toString();
300     }
301
302     private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition propertyDefinition) {
303
304         Either<String, ActionStatus> either = convertObjectToJson(propertyDefinition);
305         if (either.isRight()) {
306             return new JSONObject();
307         }
308         String value = either.left().value();
309         try {
310             JSONObject root = (JSONObject) new JSONParser().parse(value);
311             return root;
312         } catch (ParseException e) {
313             log.info("failed to convert input to json");
314             log.error("failed to convert to json", e);
315             return new JSONObject();
316         }
317
318     }
319
320     protected  <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
321         T t = null;
322         Type constraintType = new TypeToken<PropertyConstraint>() {
323         }.getType();
324         Gson
325             gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintDeserialiser()).create();
326         try {
327             log.trace("convert json to object. json=\n {}", data);
328             t = gson.fromJson(data, clazz);
329             if (t == null) {
330                 log.info("object is null after converting from json");
331                 return Either.right(ActionStatus.INVALID_CONTENT);
332             }
333         } catch (Exception e) {
334             // INVALID JSON
335             log.info("failed to convert from json");
336             log.error("failed to convert from json", e);
337             return Either.right(ActionStatus.INVALID_CONTENT);
338         }
339         return Either.left(t);
340     }
341
342     private Either<String, ActionStatus> convertObjectToJson(PropertyDefinition propertyDefinition) {
343         Type constraintType = new TypeToken<PropertyConstraint>() {
344         }.getType();
345         Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyOperation.PropertyConstraintSerialiser()).create();
346         try {
347             log.trace("convert object to json. propertyDefinition= {}", propertyDefinition);
348             String json = gson.toJson(propertyDefinition);
349             if (json == null) {
350                 log.info("object is null after converting to json");
351                 return Either.right(ActionStatus.INVALID_CONTENT);
352             }
353             return Either.left(json);
354         } catch (Exception e) {
355             // INVALID JSON
356             log.info("failed to convert to json");
357             log.debug("failed to convert fto json", e);
358             return Either.right(ActionStatus.INVALID_CONTENT);
359         }
360
361     }
362
363     protected InputsBusinessLogic getInputBL(ServletContext context) {
364         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
365         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
366         return webApplicationContext.getBean(InputsBusinessLogic.class);
367     }
368
369     protected PolicyBusinessLogic getPolicyBL(ServletContext context) {
370         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
371         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
372         return webApplicationContext.getBean(PolicyBusinessLogic.class);
373     }
374
375     protected Either<ComponentInstInputsMap, ResponseFormat> parseToComponentInstanceMap(String componentJson, User user, ComponentTypeEnum componentType) {
376         return getComponentsUtils().convertJsonToObjectUsingObjectMapper(componentJson, user, ComponentInstInputsMap.class, AuditingActionEnum.CREATE_RESOURCE, componentType);
377     }
378
379     protected Response declareProperties(String userId, String componentId, String componentType,
380             String componentInstInputsMapObj, DeclarationTypeEnum typeEnum, HttpServletRequest request) {
381         ServletContext context = request.getSession().getServletContext();
382         String url = request.getMethod() + " " + request.getRequestURI();
383         log.debug("(get) Start handle request of {}", url);
384         Response response = null;
385
386         try {
387             BaseBusinessLogic businessLogic = getBlForPropertyDeclaration(typeEnum, context);
388
389             // get modifier id
390             User modifier = new User();
391             modifier.setUserId(userId);
392             log.debug("modifier id is {}", userId);
393             ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
394             Either<ComponentInstInputsMap, ResponseFormat> componentInstInputsMapRes = parseToComponentInstanceMap(componentInstInputsMapObj, modifier, componentTypeEnum);
395             if (componentInstInputsMapRes.isRight()) {
396                 log.debug("failed to parse componentInstInputsMap");
397                 response = buildErrorResponse(componentInstInputsMapRes.right().value());
398                 return response;
399             }
400
401             Either<List<ToscaDataDefinition>, ResponseFormat> propertiesAfterDeclaration = businessLogic
402                                                                                .declareProperties(userId, componentId,
403                                                                                        componentTypeEnum,
404                                                                                        componentInstInputsMapRes.left().value());
405             if (propertiesAfterDeclaration.isRight()) {
406                 log.debug("failed to create inputs  for service: {}", componentId);
407                 return buildErrorResponse(propertiesAfterDeclaration.right().value());
408             }
409             Object properties = RepresentationUtils.toRepresentation(propertiesAfterDeclaration.left().value());
410             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
411
412         } catch (Exception e) {
413             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create inputs for service with id: " + componentId);
414             log.debug("Properties declaration failed with exception", e);
415             response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
416             return response;
417         }
418     }
419
420     public BaseBusinessLogic getBlForPropertyDeclaration(DeclarationTypeEnum typeEnum,
421                                                           ServletContext context) {
422         if(typeEnum.equals(DeclarationTypeEnum.POLICY)) {
423             return getPolicyBL(context);
424         }
425
426         return getInputBL(context);
427     }
428 }