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