Support setting interfaces on instances
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / ComponentInterfaceOperationServlet.java
1 /*
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  *  Copyright (C) 2021 Nordix Foundation. 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  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 package org.openecomp.sdc.be.servlets;
23
24 import static org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum.RESOURCE;
25
26 import fj.data.Either;
27 import io.swagger.v3.oas.annotations.Operation;
28 import io.swagger.v3.oas.annotations.Parameter;
29 import io.swagger.v3.oas.annotations.media.ArraySchema;
30 import io.swagger.v3.oas.annotations.media.Content;
31 import io.swagger.v3.oas.annotations.media.Schema;
32 import io.swagger.v3.oas.annotations.responses.ApiResponse;
33 import io.swagger.v3.oas.annotations.servers.Server;
34 import io.swagger.v3.oas.annotations.tags.Tag;
35 import java.io.IOException;
36 import java.util.Optional;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.ws.rs.Consumes;
39 import javax.ws.rs.HeaderParam;
40 import javax.ws.rs.POST;
41 import javax.ws.rs.PUT;
42 import javax.ws.rs.Path;
43 import javax.ws.rs.PathParam;
44 import javax.ws.rs.Produces;
45 import javax.ws.rs.core.Context;
46 import javax.ws.rs.core.MediaType;
47 import javax.ws.rs.core.Response;
48 import org.apache.commons.io.IOUtils;
49 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
50 import org.openecomp.sdc.be.components.impl.ComponentInterfaceOperationBusinessLogic;
51 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
52 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
53 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
54 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
55 import org.openecomp.sdc.be.config.BeEcompErrorManager;
56 import org.openecomp.sdc.be.dao.api.ActionStatus;
57 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
58 import org.openecomp.sdc.be.impl.ComponentsUtils;
59 import org.openecomp.sdc.be.impl.ServletUtils;
60 import org.openecomp.sdc.be.model.Component;
61 import org.openecomp.sdc.be.model.ComponentInstance;
62 import org.openecomp.sdc.be.model.InterfaceDefinition;
63 import org.openecomp.sdc.be.model.User;
64 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
65 import org.openecomp.sdc.be.ui.model.UiComponentDataTransfer;
66 import org.openecomp.sdc.common.api.Constants;
67 import org.openecomp.sdc.common.datastructure.Wrapper;
68 import org.openecomp.sdc.common.util.ValidationUtils;
69 import org.openecomp.sdc.exception.ResponseFormat;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.beans.factory.annotation.Autowired;
73 import org.springframework.stereotype.Controller;
74
75 @Path("/v1/catalog")
76 @Tag(name = "SDCE-2 APIs")
77 @Consumes(MediaType.APPLICATION_JSON)
78 @Produces(MediaType.APPLICATION_JSON)
79 @Server(url = "/sdc2/rest")
80 @Controller
81 public class ComponentInterfaceOperationServlet extends AbstractValidationsServlet {
82
83     private static final Logger LOGGER = LoggerFactory.getLogger(ComponentInterfaceOperationServlet.class);
84     private static final String START_HANDLE_REQUEST_OF = "Start handle {} request of {}";
85     private static final String MODIFIER_ID_IS = "modifier id is {}";
86     private static final String FAILED_TO_UPDATE_INTERFACE_OPERATION = "failed to update Interface Operation on component instance {}";
87     private static final String UPDATE_INTERFACE_OPERATION = "Update Interface Operation on Component Instance";
88     private static final String FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR = "Failed to update Interface Operation with an error";
89     private static final String INTERFACE_OPERATION_CONTENT_INVALID = "Interface Operation content is invalid - {}";
90     private static final String UNSUPPORTED_COMPONENT_TYPE = "Unsupported component type {}";
91     private static final String INTERFACE_OPERATION_SUCCESSFULLY_UPDATED =
92         "Interface Operation successfully updated on component instance with id {}";
93     private final ComponentInterfaceOperationBusinessLogic componentInterfaceOperationBusinessLogic;
94
95     @Autowired
96     public ComponentInterfaceOperationServlet(final ComponentInstanceBusinessLogic componentInstanceBL,
97                                               final ComponentsUtils componentsUtils, final ServletUtils servletUtils,
98                                               final ResourceImportManager resourceImportManager,
99                                               final ComponentInterfaceOperationBusinessLogic componentInterfaceOperationBusinessLogic) {
100         super(componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
101         this.componentInterfaceOperationBusinessLogic = componentInterfaceOperationBusinessLogic;
102     }
103
104     @PUT
105     @Path("/{componentType}/{componentId}/componentInstance/{componentInstanceId}/interfaceOperation")
106     @Consumes(MediaType.APPLICATION_JSON)
107     @Produces(MediaType.APPLICATION_JSON)
108     @Operation(description = "Update Interface Operation", method = "PUT", summary = "Update Interface Operation on ComponentInstance", responses = {
109         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
110         @ApiResponse(responseCode = "201", description = "Update Interface Operation"),
111         @ApiResponse(responseCode = "403", description = "Restricted operation"),
112         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
113     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
114     public Response updateComponentInstanceInterfaceOperation(
115         @Parameter(description = "valid values: resources / services", schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME,
116             ComponentTypeEnum.SERVICE_PARAM_NAME})) @PathParam("componentType") String componentType,
117         @Parameter(description = "Component Id") @PathParam("componentId") String componentId,
118         @Parameter(description = "Component Instance Id") @PathParam("componentInstanceId") String componentInstanceId,
119         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
120         LOGGER.debug(START_HANDLE_REQUEST_OF, request.getMethod(), request.getRequestURI());
121         userId = ValidationUtils.sanitizeInputString(userId);
122         componentType = ValidationUtils.sanitizeInputString(componentType);
123         componentInstanceId = ValidationUtils.sanitizeInputString(componentInstanceId);
124         LOGGER.debug(MODIFIER_ID_IS, userId);
125         final User userModifier = componentInterfaceOperationBusinessLogic.validateUser(userId);
126         final ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
127         if (componentTypeEnum == null) {
128             LOGGER.debug(UNSUPPORTED_COMPONENT_TYPE, componentType);
129             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, componentType));
130         }
131         final byte[] bytes = IOUtils.toByteArray(request.getInputStream());
132         if (bytes == null || bytes.length == 0) {
133             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, "content is empty");
134             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
135         }
136         final String data = new String(bytes);
137         final Optional<InterfaceDefinition> mappedInterfaceOperationData = getMappedInterfaceData(data, userModifier, componentTypeEnum);
138         if (mappedInterfaceOperationData.isEmpty()) {
139             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, data);
140             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
141         }
142         final Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
143         try {
144             final Optional<ComponentInstance> actionResponse = componentInterfaceOperationBusinessLogic.updateComponentInstanceInterfaceOperation(
145                 componentId, componentInstanceId, mappedInterfaceOperationData.get(), componentTypeEnum, errorWrapper, true);
146             if (actionResponse.isEmpty()) {
147                 LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION, componentInstanceId);
148                 return buildErrorResponse(errorWrapper.getInnerElement());
149             } else {
150                 LOGGER.debug(INTERFACE_OPERATION_SUCCESSFULLY_UPDATED, componentInstanceId);
151                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.get());
152             }
153         } catch (final Exception e) {
154             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(UPDATE_INTERFACE_OPERATION);
155             LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR, e);
156             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
157         }
158     }
159
160     @PUT
161     @Path("/resources/{componentId}/interfaceOperation")
162     @Consumes(MediaType.APPLICATION_JSON)
163     @Produces(MediaType.APPLICATION_JSON)
164     @Operation(description = "Update Interface Operation", method = "PUT", summary = "Update Interface Operation on ComponentInstance", responses = {
165         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
166         @ApiResponse(responseCode = "201", description = "Update Interface Operation"),
167         @ApiResponse(responseCode = "403", description = "Restricted operation"),
168         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
169     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
170     public Response updateResourceInterfaceOperation(
171         @Parameter(description = "Component Id") @PathParam("componentId") String componentId,
172         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
173         LOGGER.debug(START_HANDLE_REQUEST_OF, request.getMethod(), request.getRequestURI());
174         LOGGER.debug(MODIFIER_ID_IS, userId);
175         final User userModifier = componentInterfaceOperationBusinessLogic.validateUser(userId);
176         final byte[] bytes = IOUtils.toByteArray(request.getInputStream());
177         if (bytes == null || bytes.length == 0) {
178             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID);
179             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
180         }
181         final ComponentTypeEnum componentType = RESOURCE;
182         final String data = new String(bytes);
183         final Optional<InterfaceDefinition> mappedInterfaceOperationData = getMappedInterfaceData(data, userModifier, componentType);
184         if (mappedInterfaceOperationData.isEmpty()) {
185             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, data);
186             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
187         }
188         final Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
189         try {
190             final Optional<Component> actionResponse = componentInterfaceOperationBusinessLogic
191                 .updateResourceInterfaceOperation(componentId, userId, mappedInterfaceOperationData.get(), componentType,
192                     errorWrapper, true);
193             if (actionResponse.isEmpty()) {
194                 LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION, componentId);
195                 return buildErrorResponse(errorWrapper.getInnerElement());
196             } else {
197                 LOGGER.debug(INTERFACE_OPERATION_SUCCESSFULLY_UPDATED, componentId);
198                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.get());
199             }
200         } catch (final ComponentException e) {
201             //let it be handled by org.openecomp.sdc.be.servlets.exception.ComponentExceptionMapper
202             throw e;
203         } catch (final Exception e) {
204             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(UPDATE_INTERFACE_OPERATION);
205             LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR, e);
206             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
207         }
208     }
209
210     @POST
211     @Path("/{componentType}/{componentId}/componentInstance/{componentInstanceId}/interfaceOperation")
212     @Consumes(MediaType.APPLICATION_JSON)
213     @Produces(MediaType.APPLICATION_JSON)
214     @Operation(description = "Create Interface Operation", method = "POST", summary = "Create Interface Operation on ComponentInstance", responses = {
215         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
216         @ApiResponse(responseCode = "201", description = "Create Interface Operation"),
217         @ApiResponse(responseCode = "403", description = "Restricted operation"),
218         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
219     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
220     public Response createComponentInstanceInterfaceOperation(
221         @Parameter(description = "valid values: resources / services", schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME,
222             ComponentTypeEnum.SERVICE_PARAM_NAME})) @PathParam("componentType") String componentType,
223         @Parameter(description = "Component Id") @PathParam("componentId") String componentId,
224         @Parameter(description = "Component Instance Id") @PathParam("componentInstanceId") String componentInstanceId,
225         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
226         LOGGER.debug(START_HANDLE_REQUEST_OF, request.getMethod(), request.getRequestURI());
227         userId = ValidationUtils.sanitizeInputString(userId);
228         componentType = ValidationUtils.sanitizeInputString(componentType);
229         componentInstanceId = ValidationUtils.sanitizeInputString(componentInstanceId);
230         LOGGER.debug(MODIFIER_ID_IS, userId);
231         final User userModifier = componentInterfaceOperationBusinessLogic.validateUser(userId);
232         final ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
233         if (componentTypeEnum == null) {
234             LOGGER.debug(UNSUPPORTED_COMPONENT_TYPE, componentType);
235             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, componentType));
236         }
237         final byte[] bytes = IOUtils.toByteArray(request.getInputStream());
238         if (bytes == null || bytes.length == 0) {
239             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, "content is empty");
240             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
241         }
242         final String data = new String(bytes);
243         final Optional<InterfaceDefinition> mappedInterfaceOperationData = getMappedInterfaceData(data, userModifier, componentTypeEnum);
244         if (mappedInterfaceOperationData.isEmpty()) {
245             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, data);
246             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
247         }
248         final Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
249         try {
250             final Optional<ComponentInstance> actionResponse = componentInterfaceOperationBusinessLogic.createComponentInstanceInterfaceOperation(
251                 componentId, componentInstanceId, mappedInterfaceOperationData.get(), componentTypeEnum, errorWrapper, true);
252             if (actionResponse.isEmpty()) {
253                 LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION, componentInstanceId);
254                 return buildErrorResponse(errorWrapper.getInnerElement());
255             } else {
256                 LOGGER.debug(INTERFACE_OPERATION_SUCCESSFULLY_UPDATED, componentInstanceId);
257                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.get());
258             }
259         } catch (final Exception e) {
260             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(UPDATE_INTERFACE_OPERATION);
261             LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR, e);
262             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
263         }
264     }
265
266     @POST
267     @Path("/{componentType}/{componentId}/resource/interfaceOperation")
268     @Consumes(MediaType.APPLICATION_JSON)
269     @Produces(MediaType.APPLICATION_JSON)
270     @Operation(description = "Create Interface Operation", method = "POST", summary = "Create Interface Operation on ComponentInstance", responses = {
271         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
272         @ApiResponse(responseCode = "201", description = "Create Interface Operation"),
273         @ApiResponse(responseCode = "403", description = "Restricted operation"),
274         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
275     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
276     public Response createInterfaceOperationInResource(
277         @Parameter(description = "valid values: resources", schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME}))
278         @PathParam("componentType") final String componentType,
279         @Parameter(description = "Component Id") @PathParam("componentId") String componentId,
280         @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
281         LOGGER.debug(START_HANDLE_REQUEST_OF, request.getMethod(), request.getRequestURI());
282         LOGGER.debug(MODIFIER_ID_IS, userId);
283         final User userModifier = componentInterfaceOperationBusinessLogic.validateUser(userId);
284         final ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
285         if (componentTypeEnum == null) {
286             LOGGER.debug(UNSUPPORTED_COMPONENT_TYPE, componentType);
287             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, componentType));
288         }
289         final byte[] bytes = IOUtils.toByteArray(request.getInputStream());
290         if (bytes == null || bytes.length == 0) {
291             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID);
292             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
293         }
294         final String data = new String(bytes);
295         final Optional<InterfaceDefinition> mappedInterfaceOperationData = getMappedInterfaceData(data, userModifier, componentTypeEnum);
296         if (mappedInterfaceOperationData.isEmpty()) {
297             LOGGER.error(INTERFACE_OPERATION_CONTENT_INVALID, data);
298             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
299         }
300         final Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
301         try {
302             final Optional<Component> actionResponse = componentInterfaceOperationBusinessLogic.createInterfaceOperationInResource(
303                 componentId, mappedInterfaceOperationData.get(), componentTypeEnum, errorWrapper, true);
304             if (actionResponse.isEmpty()) {
305                 LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION, componentId);
306                 return buildErrorResponse(errorWrapper.getInnerElement());
307             } else {
308                 LOGGER.debug(INTERFACE_OPERATION_SUCCESSFULLY_UPDATED, componentId);
309                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.get());
310             }
311         } catch (final Exception e) {
312             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(UPDATE_INTERFACE_OPERATION);
313             LOGGER.error(FAILED_TO_UPDATE_INTERFACE_OPERATION_WITH_ERROR, e);
314             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
315         }
316     }
317
318     private Optional<InterfaceDefinition> getMappedInterfaceData(final String inputJson, final User user, final ComponentTypeEnum componentTypeEnum) {
319         final Either<UiComponentDataTransfer, ResponseFormat> uiComponentEither = getComponentsUtils()
320             .convertJsonToObjectUsingObjectMapper(inputJson, user, UiComponentDataTransfer.class, AuditingActionEnum.UPDATE_RESOURCE_METADATA,
321                 componentTypeEnum);
322         return uiComponentEither.left().value().getInterfaces().values().stream().findFirst();
323     }
324 }