Sync Integ to Master
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / PropertyServlet.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.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.reflect.TypeToken;
26 import com.jcabi.aspects.Loggable;
27 import fj.data.Either;
28 import io.swagger.annotations.*;
29 import org.json.simple.JSONObject;
30 import org.json.simple.parser.JSONParser;
31 import org.json.simple.parser.ParseException;
32 import org.openecomp.sdc.be.components.impl.PropertyBusinessLogic;
33 import org.openecomp.sdc.be.config.BeEcompErrorManager;
34 import org.openecomp.sdc.be.dao.api.ActionStatus;
35 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
36 import org.openecomp.sdc.be.model.PropertyConstraint;
37 import org.openecomp.sdc.be.model.PropertyDefinition;
38 import org.openecomp.sdc.be.model.User;
39 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
40 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintSerialiser;
41 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
42 import org.openecomp.sdc.be.resources.data.EntryData;
43 import org.openecomp.sdc.common.api.Constants;
44 import org.openecomp.sdc.common.config.EcompErrorName;
45 import org.openecomp.sdc.exception.ResponseFormat;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.web.context.WebApplicationContext;
49
50 import javax.inject.Singleton;
51 import javax.servlet.ServletContext;
52 import javax.servlet.http.HttpServletRequest;
53 import javax.ws.rs.*;
54 import javax.ws.rs.core.Context;
55 import javax.ws.rs.core.MediaType;
56 import javax.ws.rs.core.Response;
57 import java.lang.reflect.Type;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.Map;
61 import java.util.Map.Entry;
62 import java.util.Set;
63 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
64 @Path("/v1/catalog")
65 @Api(value = "Resource Property Servlet", description = "Resource Property Servlet")
66 @Singleton
67 public class PropertyServlet extends BeGenericServlet {
68
69     private static final Logger log = LoggerFactory.getLogger(PropertyServlet.class);
70
71     @POST
72     @Path("resources/{resourceId}/properties")
73     @Consumes(MediaType.APPLICATION_JSON)
74     @Produces(MediaType.APPLICATION_JSON)
75     @ApiOperation(value = "Create Resource Property", httpMethod = "POST", notes = "Returns created resource property", response = Response.class)
76     @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource property created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
77             @ApiResponse(code = 409, message = "Resource property already exist") })
78     public Response createProperty(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId, @ApiParam(value = "Resource property to be created", required = true) String data,
79             @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
80
81         ServletContext context = request.getSession().getServletContext();
82
83         String url = request.getMethod() + " " + request.getRequestURI();
84         log.debug("Start handle request of {} modifier id is {} data is {}", url, userId, data);
85
86         try {
87             // convert json to PropertyDefinition
88             Either<Map<String, PropertyDefinition>, ActionStatus> either = getPropertyModel(resourceId, data);
89             if (either.isRight()) {
90                 ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(either.right().value());
91                 return buildErrorResponse(responseFormat);
92             }
93             Map<String, PropertyDefinition> properties = either.left().value();
94             if (properties == null || properties.size() != 1) {
95                 log.info("Property conetnt is invalid - {}", data);
96                 ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
97                 return buildErrorResponse(responseFormat);
98             }
99             Entry<String, PropertyDefinition> entry = properties.entrySet().iterator().next();
100             String propertyName = entry.getKey();
101             PropertyDefinition newPropertyDefinition = entry.getValue();
102
103             // create the new property
104             PropertyBusinessLogic businessLogic = getPropertyBL(context);
105             Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = businessLogic.createProperty(resourceId, propertyName, newPropertyDefinition, userId);
106             if (status.isRight()) {
107                 log.info("Failed to create Property. Reason - ", status.right().value());
108                 return buildErrorResponse(status.right().value());
109             }
110             EntryData<String, PropertyDefinition> property = status.left().value();
111             String name = property.getKey();
112             PropertyDefinition propertyDefinition = property.getValue();
113
114             log.debug("Property {} created successfully with id {}", name, propertyDefinition.getUniqueId());
115             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
116             return buildOkResponse(responseFormat, propertyToJson(property));
117
118         } catch (Exception e) {
119             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Property");
120             log.debug("create property failed with exception", e);
121             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
122             return buildErrorResponse(responseFormat);
123
124         }
125     }
126
127     @GET
128     @Path("resources/{resourceId}/properties/{propertyId}")
129     @Consumes(MediaType.APPLICATION_JSON)
130     @Produces(MediaType.APPLICATION_JSON)
131     @ApiOperation(value = "Create Resource Property", httpMethod = "GET", notes = "Returns property of resource", response = Response.class)
132     @ApiResponses(value = { @ApiResponse(code = 200, message = "property"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
133             @ApiResponse(code = 404, message = "Resource property not found") })
134     public Response getProperty(@ApiParam(value = "resource id of property", required = true) @PathParam("resourceId") final String resourceId, @ApiParam(value = "proerty id to get", required = true) @PathParam("propertyId") final String propertyId,
135             @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
136
137         ServletContext context = request.getSession().getServletContext();
138
139         String url = request.getMethod() + " " + request.getRequestURI();
140         log.debug("Start handle request of {}, modifier id is {}", url, userId);
141
142         try {
143
144             //
145             PropertyBusinessLogic businessLogic = getPropertyBL(context);
146             Either<Entry<String, PropertyDefinition>, ResponseFormat> status = businessLogic.getProperty(resourceId, propertyId, userId);
147
148             if (status.isRight()) {
149                 log.info("Failed to get Property. Reason - ", status.right().value());
150                 return buildErrorResponse(status.right().value());
151             }
152             Entry<String, PropertyDefinition> property = status.left().value();
153             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
154             return buildOkResponse(responseFormat, propertyToJson(property));
155         } catch (Exception e) {
156             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Property");
157             log.debug("get property failed with exception", e);
158             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
159             return buildErrorResponse(responseFormat);
160
161         }
162     }
163
164     @DELETE
165     @Path("resources/{resourceId}/properties/{propertyId}")
166     @Consumes(MediaType.APPLICATION_JSON)
167     @Produces(MediaType.APPLICATION_JSON)
168     @ApiOperation(value = "Create Resource Property", httpMethod = "DELETE", notes = "Returns deleted property", response = Response.class)
169     @ApiResponses(value = { @ApiResponse(code = 204, message = "deleted property"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
170             @ApiResponse(code = 404, message = "Resource property not found") })
171     public Response deleteProperty(@ApiParam(value = "resource id of property", required = true) @PathParam("resourceId") final String resourceId,
172             @ApiParam(value = "Property id to delete", required = true) @PathParam("propertyId") final String propertyId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
173
174         ServletContext context = request.getSession().getServletContext();
175
176         String url = request.getMethod() + " " + request.getRequestURI();
177         log.debug("Start handle request of {} modifier id is {}", url, userId);
178
179         try {
180
181             // delete the property
182             PropertyBusinessLogic businessLogic = getPropertyBL(context);
183             Either<Entry<String, PropertyDefinition>, ResponseFormat> status = businessLogic.deleteProperty(resourceId, propertyId, userId);
184             if (status.isRight()) {
185                 log.debug("Failed to delete Property. Reason - ", status.right().value());
186                 return buildErrorResponse(status.right().value());
187             }
188             Entry<String, PropertyDefinition> property = status.left().value();
189             String name = property.getKey();
190             PropertyDefinition propertyDefinition = property.getValue();
191
192             log.debug("Property {} deleted successfully with id {}", name, propertyDefinition.getUniqueId());
193             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
194             return buildOkResponse(responseFormat, propertyToJson(property));
195
196         } catch (Exception e) {
197             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Property");
198             log.debug("delete property failed with exception", e);
199             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
200             return buildErrorResponse(responseFormat);
201
202         }
203     }
204
205     @PUT
206     @Path("resources/{resourceId}/properties/{propertyId}")
207     @Consumes(MediaType.APPLICATION_JSON)
208     @Produces(MediaType.APPLICATION_JSON)
209     @ApiOperation(value = "Update Resource Property", httpMethod = "PUT", notes = "Returns updated property", response = Response.class)
210     @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource property updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
211     public Response updateProperty(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
212             @ApiParam(value = "proerty id to update", required = true) @PathParam("propertyId") final String propertyId, @ApiParam(value = "Resource property to update", required = true) String data, @Context final HttpServletRequest request,
213             @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
214
215         ServletContext context = request.getSession().getServletContext();
216
217         String url = request.getMethod() + " " + request.getRequestURI();
218         log.debug("Start handle request of {}", url);
219
220         // get modifier id
221         User modifier = new User();
222         modifier.setUserId(userId);
223         log.debug("modifier id is {}", userId);
224
225         try {
226             // convert json to PropertyDefinition
227             Either<Map<String, PropertyDefinition>, ActionStatus> either = getPropertyModel(resourceId, data);
228             if (either.isRight()) {
229                 ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(either.right().value());
230                 return buildErrorResponse(responseFormat);
231             }
232             Map<String, PropertyDefinition> properties = either.left().value();
233             if (properties == null || properties.size() != 1) {
234                 log.info("Property conetnt is invalid - {}", data);
235                 ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
236                 return buildErrorResponse(responseFormat);
237             }
238             Entry<String, PropertyDefinition> entry = properties.entrySet().iterator().next();
239             PropertyDefinition newPropertyDefinition = entry.getValue();
240
241             // update property
242             PropertyBusinessLogic businessLogic = getPropertyBL(context);
243             Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = businessLogic.updateProperty(resourceId, propertyId, newPropertyDefinition, userId);
244             if (status.isRight()) {
245                 log.info("Failed to update Property. Reason - ", status.right().value());
246                 return buildErrorResponse(status.right().value());
247             }
248             EntryData<String, PropertyDefinition> property = status.left().value();
249             PropertyDefinition propertyDefinition = property.getValue();
250
251             log.debug("Property id {} updated successfully ", propertyDefinition.getUniqueId());
252             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
253             return buildOkResponse(responseFormat, propertyToJson(property));
254
255         } catch (Exception e) {
256             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Property");
257             log.debug("update property failed with exception", e);
258             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
259             return buildErrorResponse(responseFormat);
260
261         }
262     }
263
264     private Either<Map<String, PropertyDefinition>, ActionStatus> getPropertyModel(String resourceId, String data) {
265         JSONParser parser = new JSONParser();
266         JSONObject root;
267         try {
268             Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
269             root = (JSONObject) parser.parse(data);
270
271             Set entrySet = root.entrySet();
272             Iterator iterator = entrySet.iterator();
273             while (iterator.hasNext()) {
274                 Entry next = (Entry) iterator.next();
275                 String propertyName = (String) next.getKey();
276                 JSONObject value = (JSONObject) next.getValue();
277                 String jsonString = value.toJSONString();
278                 Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, PropertyDefinition.class);
279                 if (convertJsonToObject.isRight()) {
280                     return Either.right(convertJsonToObject.right().value());
281                 }
282                 PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
283                 String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(resourceId, (String) propertyName);
284                 propertyDefinition.setUniqueId(uniqueId);
285                 properties.put(propertyName, propertyDefinition);
286             }
287
288             return Either.left(properties);
289         } catch (ParseException e) {
290             log.info("Property conetnt is invalid - {}", data);
291             return Either.right(ActionStatus.INVALID_CONTENT);
292         }
293     }
294
295     private String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
296         JSONObject root = new JSONObject();
297         String propertyName = property.getKey();
298         PropertyDefinition propertyDefinition = property.getValue();
299         JSONObject propertyDefinitionO = getPropertyDefinitionJSONObject(propertyDefinition);
300         root.put(propertyName, propertyDefinitionO);
301         propertyDefinition.getType();
302         return root.toString();
303     }
304
305     private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition propertyDefinition) {
306
307         Either<String, ActionStatus> either = convertObjectToJson(propertyDefinition);
308         if (either.isRight()) {
309             return new JSONObject();
310         }
311         String value = either.left().value();
312         try {
313             JSONObject root = (JSONObject) new JSONParser().parse(value);
314             return root;
315         } catch (ParseException e) {
316             log.info("failed to convert input to json");
317             log.debug("failed to convert to json", e);
318             return new JSONObject();
319         }
320
321     }
322
323     private <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
324         T t = null;
325         Type constraintType = new TypeToken<PropertyConstraint>() {
326         }.getType();
327         Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
328         try {
329             log.trace("convert json to object. json=\n {}", data);
330             t = gson.fromJson(data, clazz);
331             if (t == null) {
332                 log.info("object is null after converting from json");
333                 return Either.right(ActionStatus.INVALID_CONTENT);
334             }
335         } catch (Exception e) {
336             // INVALID JSON
337             log.info("failed to convert from json");
338             log.debug("failed to convert from json", e);
339             return Either.right(ActionStatus.INVALID_CONTENT);
340         }
341         return Either.left(t);
342     }
343
344     private <T> Either<String, ActionStatus> convertObjectToJson(PropertyDefinition propertyDefinition) {
345         Type constraintType = new TypeToken<PropertyConstraint>() {
346         }.getType();
347         Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintSerialiser()).create();
348         try {
349             log.trace("convert object to json. propertyDefinition= {}", propertyDefinition);
350             String json = gson.toJson(propertyDefinition);
351             if (json == null) {
352                 log.info("object is null after converting to json");
353                 return Either.right(ActionStatus.INVALID_CONTENT);
354             }
355             return Either.left(json);
356         } catch (Exception e) {
357             // INVALID JSON
358             log.info("failed to convert to json");
359             log.debug("failed to convert fto json", e);
360             return Either.right(ActionStatus.INVALID_CONTENT);
361         }
362
363     }
364
365     private PropertyBusinessLogic getPropertyBL(ServletContext context) {
366         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
367         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
368         PropertyBusinessLogic propertytBl = webApplicationContext.getBean(PropertyBusinessLogic.class);
369         return propertytBl;
370     }
371
372 }