c291522ca7dbc6578d9a2d71b85e74ae38d8fc5a
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / ComponentPropertyServlet.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 package org.openecomp.sdc.be.servlets;
17
18 import com.jcabi.aspects.Loggable;
19 import fj.data.Either;
20 import io.swagger.v3.oas.annotations.Operation;
21 import io.swagger.v3.oas.annotations.Parameter;
22 import io.swagger.v3.oas.annotations.media.ArraySchema;
23 import io.swagger.v3.oas.annotations.media.Content;
24 import io.swagger.v3.oas.annotations.media.Schema;
25 import io.swagger.v3.oas.annotations.responses.ApiResponse;
26 import io.swagger.v3.oas.annotations.servers.Server;
27 import io.swagger.v3.oas.annotations.tags.Tag;
28 import java.util.List;
29 import java.util.Map;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.ws.rs.Consumes;
34 import javax.ws.rs.DELETE;
35 import javax.ws.rs.GET;
36 import javax.ws.rs.HeaderParam;
37 import javax.ws.rs.POST;
38 import javax.ws.rs.PUT;
39 import javax.ws.rs.Path;
40 import javax.ws.rs.PathParam;
41 import javax.ws.rs.Produces;
42 import javax.ws.rs.core.Context;
43 import javax.ws.rs.core.MediaType;
44 import javax.ws.rs.core.Response;
45 import org.openecomp.sdc.be.components.impl.PropertyBusinessLogic;
46 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
47 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
48 import org.openecomp.sdc.be.config.BeEcompErrorManager;
49 import org.openecomp.sdc.be.dao.api.ActionStatus;
50 import org.openecomp.sdc.be.datamodel.utils.PropertyValueConstraintValidationUtil;
51 import org.openecomp.sdc.be.impl.ComponentsUtils;
52 import org.openecomp.sdc.be.model.PropertyDefinition;
53 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
54 import org.openecomp.sdc.be.resources.data.EntryData;
55 import org.openecomp.sdc.common.api.Constants;
56 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
57 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
58 import org.openecomp.sdc.common.log.enums.StatusCode;
59 import org.openecomp.sdc.exception.ResponseFormat;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
64 @Path("/v1/catalog")
65 @Tag(name = "SDCE-2 APIs")
66 @Server(url = "/sdc2/rest")
67 @Singleton
68 public class ComponentPropertyServlet extends BeGenericServlet {
69
70     private static final Logger log = LoggerFactory.getLogger(ComponentPropertyServlet.class);
71     private static final String CREATE_PROPERTY = "Create Property";
72     private static final String DEBUG_MESSAGE = "Start handle request of {} modifier id is {}";
73     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(ComponentPropertyServlet.class.getName());
74     private final PropertyBusinessLogic propertyBusinessLogic;
75     private final ApplicationDataTypeCache applicationDataTypeCache;
76
77     @Inject
78     public ComponentPropertyServlet(ComponentsUtils componentsUtils,
79                                     ApplicationDataTypeCache applicationDataTypeCache, PropertyBusinessLogic propertyBusinessLogic) {
80         super(componentsUtils);
81         this.applicationDataTypeCache = applicationDataTypeCache;
82         this.propertyBusinessLogic = propertyBusinessLogic;
83     }
84
85     @POST
86     @Path("services/{serviceId}/properties")
87     @Consumes(MediaType.APPLICATION_JSON)
88     @Produces(MediaType.APPLICATION_JSON)
89     @Operation(description = "Create Service Property", method = "POST", summary = "Returns created service property", responses = {
90         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
91         @ApiResponse(responseCode = "201", description = "Service property created"),
92         @ApiResponse(responseCode = "403", description = "Restricted operation"),
93         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
94         @ApiResponse(responseCode = "409", description = "Service property already exist")})
95     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
96     public Response createPropertyInService(
97         @Parameter(description = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
98         @Parameter(description = "Service property to be created", required = true) String data, @Context final HttpServletRequest request,
99         @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
100         return createProperty(serviceId, data, request, userId);
101     }
102
103     @POST
104     @Path("resources/{resourceId}/properties")
105     @Consumes(MediaType.APPLICATION_JSON)
106     @Produces(MediaType.APPLICATION_JSON)
107     @Operation(description = "Create Resource Property", method = "POST", summary = "Returns created service property", responses = {
108         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
109         @ApiResponse(responseCode = "201", description = "Resource property created"),
110         @ApiResponse(responseCode = "403", description = "Restricted operation"),
111         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
112         @ApiResponse(responseCode = "409", description = "Resource property already exist")})
113     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
114     public Response createPropertyInResource(
115         @Parameter(description = "Resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
116         @Parameter(description = "Resource property to be created", required = true) String data, @Context final HttpServletRequest request,
117         @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
118         return createProperty(resourceId, data, request, userId);
119     }
120
121     @GET
122     @Path("services/{serviceId}/properties/{propertyId}")
123     @Consumes(MediaType.APPLICATION_JSON)
124     @Produces(MediaType.APPLICATION_JSON)
125     @Operation(description = "Get Service Property", method = "GET", summary = "Returns property of service", responses = {
126         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
127         @ApiResponse(responseCode = "200", description = "property"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
128         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
129         @ApiResponse(responseCode = "404", description = "Service property not found")})
130     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
131     public Response getPropertyInService(
132         @Parameter(description = "service id of property", required = true) @PathParam("serviceId") final String serviceId,
133         @Parameter(description = "property id to get", required = true) @PathParam("propertyId") final String propertyId,
134         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
135         return getProperty(serviceId, propertyId, request, userId);
136     }
137
138     @GET
139     @Path("resources/{resourceId}/properties/{propertyId}")
140     @Consumes(MediaType.APPLICATION_JSON)
141     @Produces(MediaType.APPLICATION_JSON)
142     @Operation(description = "Get Resource Property", method = "GET", summary = "Returns property of resource", responses = {
143         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
144         @ApiResponse(responseCode = "200", description = "property"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
145         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
146         @ApiResponse(responseCode = "404", description = "Resource property not found")})
147     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
148     public Response getPropertyInResource(
149         @Parameter(description = "resource id of property", required = true) @PathParam("resourceId") final String resourceId,
150         @Parameter(description = "property id to get", required = true) @PathParam("propertyId") final String propertyId,
151         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
152         return getProperty(resourceId, propertyId, request, userId);
153     }
154
155     @GET
156     @Path("services/{serviceId}/properties")
157     @Consumes(MediaType.APPLICATION_JSON)
158     @Produces(MediaType.APPLICATION_JSON)
159     @Operation(description = "Get Service Property", method = "GET", summary = "Returns property list of service", responses = {
160         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
161         @ApiResponse(responseCode = "200", description = "property"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
162         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
163         @ApiResponse(responseCode = "404", description = "Service property not found")})
164     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
165     public Response getPropertyListInService(
166         @Parameter(description = "service id of property", required = true) @PathParam("serviceId") final String serviceId,
167         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
168         return getPropertyList(serviceId, request, userId);
169     }
170
171     @GET
172     @Path("resources/{resourceId}/properties")
173     @Consumes(MediaType.APPLICATION_JSON)
174     @Produces(MediaType.APPLICATION_JSON)
175     @Operation(description = "Get Resource Property", method = "GET", summary = "Returns property list of resource", responses = {
176         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
177         @ApiResponse(responseCode = "200", description = "property"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
178         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
179         @ApiResponse(responseCode = "404", description = "Resource property not found")})
180     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
181     public Response getPropertyListInResource(
182         @Parameter(description = "resource id of property", required = true) @PathParam("resourceId") final String resourceId,
183         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
184         return getPropertyList(resourceId, request, userId);
185     }
186
187     @DELETE
188     @Path("services/{serviceId}/properties/{propertyId}")
189     @Consumes(MediaType.APPLICATION_JSON)
190     @Produces(MediaType.APPLICATION_JSON)
191     @Operation(description = "Delete Service Property", method = "DELETE", summary = "Returns deleted property", responses = {
192         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
193         @ApiResponse(responseCode = "204", description = "deleted property"),
194         @ApiResponse(responseCode = "403", description = "Restricted operation"),
195         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
196         @ApiResponse(responseCode = "404", description = "Service property not found")})
197     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
198     public Response deletePropertyInService(
199         @Parameter(description = "service id of property", required = true) @PathParam("serviceId") final String serviceId,
200         @Parameter(description = "Property id to delete", required = true) @PathParam("propertyId") final String propertyId,
201         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
202         return deleteProperty(serviceId, propertyId, request, userId);
203     }
204
205     @DELETE
206     @Path("resources/{resourceId}/properties/{propertyId}")
207     @Consumes(MediaType.APPLICATION_JSON)
208     @Produces(MediaType.APPLICATION_JSON)
209     @Operation(description = "Delete Resource Property", method = "DELETE", summary = "Returns deleted property", responses = {
210         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
211         @ApiResponse(responseCode = "204", description = "deleted property"),
212         @ApiResponse(responseCode = "403", description = "Restricted operation"),
213         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
214         @ApiResponse(responseCode = "404", description = "Resource property not found")})
215     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
216     public Response deletePropertyInResource(
217         @Parameter(description = "resource id of property", required = true) @PathParam("resourceId") final String resourceId,
218         @Parameter(description = "Property id to delete", required = true) @PathParam("propertyId") final String propertyId,
219         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
220         return deleteProperty(resourceId, propertyId, request, userId);
221     }
222
223     @PUT
224     @Path("services/{serviceId}/properties")
225     @Consumes(MediaType.APPLICATION_JSON)
226     @Produces(MediaType.APPLICATION_JSON)
227     @Operation(description = "Update Service Property", method = "PUT", summary = "Returns updated property", responses = {
228         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
229         @ApiResponse(responseCode = "200", description = "Service property updated"),
230         @ApiResponse(responseCode = "403", description = "Restricted operation"),
231         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
232     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
233     public Response updatePropertyInService(
234         @Parameter(description = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
235         @Parameter(description = "Service property to update", required = true) String data, @Context final HttpServletRequest request,
236         @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
237         return updateProperty(serviceId, data, request, userId);
238     }
239
240     @PUT
241     @Path("resources/{resourceId}/properties")
242     @Consumes(MediaType.APPLICATION_JSON)
243     @Produces(MediaType.APPLICATION_JSON)
244     @Operation(description = "Update Resource Property", method = "PUT", summary = "Returns updated property", responses = {
245         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
246         @ApiResponse(responseCode = "200", description = "Resource property updated"),
247         @ApiResponse(responseCode = "403", description = "Restricted operation"),
248         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
249     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
250     public Response updatePropertyInResource(
251         @Parameter(description = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
252         @Parameter(description = "Resource property to update", required = true) String data, @Context final HttpServletRequest request,
253         @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
254         return updateProperty(resourceId, data, request, userId);
255     }
256
257     private Response createProperty(String componentId, String data, HttpServletRequest request, String userId) {
258         String url = request.getMethod() + " " + request.getRequestURI();
259         log.debug("Start handle request of {} modifier id is {} data is {}", url, userId, data);
260         loggerSupportability.log(LoggerSupportabilityActions.CREATE_PROPERTIES, StatusCode.STARTED, "CREATE_PROPERTIES by user {} ", userId);
261         try {
262             Either<Map<String, PropertyDefinition>, ActionStatus> propertyDefinition = getPropertyModel(componentId, data);
263             if (propertyDefinition.isRight()) {
264                 return buildErrorResponse(getComponentsUtils().getResponseFormat(propertyDefinition.right().value()));
265             }
266             Map<String, PropertyDefinition> properties = propertyDefinition.left().value();
267             if (properties == null || properties.size() != 1) {
268                 log.info("Property content is invalid - {}", data);
269                 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
270             }
271
272             //Validate value and Constraint of property and Fetch all data types from cache
273             Either<Boolean, ResponseFormat> constraintValidatorResponse = new PropertyValueConstraintValidationUtil()
274                 .validatePropertyConstraints(properties.values(), applicationDataTypeCache,
275                     propertyBusinessLogic.getComponentModelByComponentId(componentId));
276             if (constraintValidatorResponse.isRight()) {
277                 log.error("Failed validation value and constraint of property: {}", constraintValidatorResponse.right().value());
278                 return buildErrorResponse(constraintValidatorResponse.right().value());
279             }
280
281             Map.Entry<String, PropertyDefinition> entry = properties.entrySet().iterator().next();
282             PropertyDefinition newPropertyDefinition = entry.getValue();
283             newPropertyDefinition.setParentUniqueId(componentId);
284             newPropertyDefinition.setUserCreated(true);
285             Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyEither =
286                 propertyBusinessLogic.addPropertyToComponent(componentId, newPropertyDefinition, userId);
287             if (addPropertyEither.isRight()) {
288                 return buildErrorResponse(addPropertyEither.right().value());
289             }
290             loggerSupportability.log(LoggerSupportabilityActions.CREATE_PROPERTIES, StatusCode.COMPLETE, "CREATE_PROPERTIES by user {} ", userId);
291             return buildOkResponse(newPropertyDefinition);
292         } catch (Exception e) {
293             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE_PROPERTY);
294             log.debug("create property failed with exception", e);
295             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
296         }
297     }
298
299     private Response updateProperty(String componentId, String data, HttpServletRequest request, String userId) {
300         String url = request.getMethod() + " " + request.getRequestURI();
301         log.debug("Start handle request of {}", url);
302         loggerSupportability.log(LoggerSupportabilityActions.UPDATE_PROPERTIES, StatusCode.STARTED, "UPDATE_PROPERTIES by user {} ", userId);
303         log.debug("modifier id is {}", userId);
304
305         try {
306             // convert json to PropertyDefinition
307             Either<Map<String, PropertyDefinition>, ActionStatus> propertiesListEither = getPropertiesListForUpdate(data);
308             if (propertiesListEither.isRight()) {
309                 return buildErrorResponse(getComponentsUtils().getResponseFormat(propertiesListEither.right().value()));
310             }
311             Map<String, PropertyDefinition> properties = propertiesListEither.left().value();
312             if (properties == null) {
313                 log.info("Property content is invalid - {}", data);
314                 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
315             }
316             //Validate value and Constraint of property and Fetch all data types from cache
317             Either<Boolean, ResponseFormat> constraintValidatorResponse = new PropertyValueConstraintValidationUtil()
318                 .validatePropertyConstraints(properties.values(), applicationDataTypeCache,
319                     propertyBusinessLogic.getComponentModelByComponentId(componentId));
320             if (constraintValidatorResponse.isRight()) {
321                 log.error("Failed validation value and constraint of property: {}", constraintValidatorResponse.right().value());
322                 return buildErrorResponse(constraintValidatorResponse.right().value());
323             }
324             // update property
325             for (PropertyDefinition propertyDefinition : properties.values()) {
326                 Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = propertyBusinessLogic
327                     .updateComponentProperty(componentId, propertyDefinition.getUniqueId(), propertyDefinition, userId);
328                 if (status.isRight()) {
329                     log.info("Failed to update Property. Reason - {}", status.right().value());
330                     return buildErrorResponse(status.right().value());
331                 }
332                 log.debug("Property id {} updated successfully ", status.left().value().getValue().getUniqueId());
333             }
334             loggerSupportability.log(LoggerSupportabilityActions.UPDATE_PROPERTIES, StatusCode.COMPLETE, "UPDATE_PROPERTIES by user {} ", userId);
335             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
336         } catch (Exception e) {
337             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Property");
338             log.debug("update property failed with exception", e);
339             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
340         }
341     }
342
343     private Response getProperty(String componentId, String propertyId, HttpServletRequest request, String userId) {
344         String url = request.getMethod() + " " + request.getRequestURI();
345         log.debug(DEBUG_MESSAGE, url, userId);
346         try {
347             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> retrievedPropertyEither = propertyBusinessLogic
348                 .getComponentProperty(componentId, propertyId, userId);
349             if (retrievedPropertyEither.isRight()) {
350                 return buildErrorResponse(retrievedPropertyEither.right().value());
351             }
352             return buildOkResponse(retrievedPropertyEither.left().value());
353         } catch (Exception e) {
354             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE_PROPERTY);
355             log.debug("get property failed with exception", e);
356             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
357         }
358     }
359
360     private Response getPropertyList(String componentId, HttpServletRequest request, String userId) {
361         String url = request.getMethod() + " " + request.getRequestURI();
362         log.debug(DEBUG_MESSAGE, url, userId);
363         try {
364             Either<List<PropertyDefinition>, ResponseFormat> propertiesListEither = propertyBusinessLogic.getPropertiesList(componentId, userId);
365             if (propertiesListEither.isRight()) {
366                 return buildErrorResponse(propertiesListEither.right().value());
367             }
368             return buildOkResponse(propertiesListEither.left().value());
369         } catch (Exception e) {
370             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE_PROPERTY);
371             log.debug("get property failed with exception", e);
372             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
373         }
374     }
375
376     private Response deleteProperty(String componentId, String propertyId, HttpServletRequest request, String userId) {
377         String url = request.getMethod() + " " + request.getRequestURI();
378         log.debug(DEBUG_MESSAGE, url, userId);
379         try {
380             // delete the property
381             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> status = propertyBusinessLogic
382                 .deletePropertyFromComponent(componentId, propertyId, userId);
383             if (status.isRight()) {
384                 log.debug("Failed to delete Property. Reason - ", status.right().value());
385                 return buildErrorResponse(status.right().value());
386             }
387             Map.Entry<String, PropertyDefinition> property = status.left().value();
388             log.debug("Property {} deleted successfully with id {}", property.getKey(), property.getValue().getUniqueId());
389             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), propertyToJson(property));
390         } catch (Exception e) {
391             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Property");
392             log.debug("delete property failed with exception", e);
393             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
394         }
395     }
396 }