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