catalog-be servlets refactoring
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / TypesUploadServlet.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.reflect.TypeToken;
24 import com.jcabi.aspects.Loggable;
25 import fj.data.Either;
26 import io.swagger.annotations.*;
27 import javax.inject.Inject;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.glassfish.jersey.media.multipart.FormDataParam;
30 import org.openecomp.sdc.be.components.impl.*;
31 import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData;
32 import org.openecomp.sdc.be.config.BeEcompErrorManager;
33 import org.openecomp.sdc.be.dao.api.ActionStatus;
34 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
35 import org.openecomp.sdc.be.impl.ComponentsUtils;
36 import org.openecomp.sdc.be.impl.ServletUtils;
37 import org.openecomp.sdc.be.model.DataTypeDefinition;
38 import org.openecomp.sdc.be.model.GroupTypeDefinition;
39 import org.openecomp.sdc.be.model.PolicyTypeDefinition;
40 import org.openecomp.sdc.be.model.RelationshipTypeDefinition;
41 import org.openecomp.sdc.be.model.User;
42 import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata;
43 import org.openecomp.sdc.be.user.UserBusinessLogic;
44 import org.openecomp.sdc.common.api.Constants;
45 import org.openecomp.sdc.common.datastructure.FunctionalInterfaces.ConsumerTwoParam;
46 import org.openecomp.sdc.common.datastructure.Wrapper;
47 import org.openecomp.sdc.common.log.wrappers.Logger;
48 import org.openecomp.sdc.exception.ResponseFormat;
49 import org.springframework.stereotype.Controller;
50
51 import javax.servlet.http.HttpServletRequest;
52 import javax.ws.rs.*;
53 import javax.ws.rs.core.Context;
54 import javax.ws.rs.core.MediaType;
55 import javax.ws.rs.core.Response;
56 import java.io.File;
57 import java.io.IOException;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.function.Supplier;
62 import java.util.stream.Collectors;
63
64 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
65 @Path("/v1/catalog/uploadType")
66 @Consumes(MediaType.MULTIPART_FORM_DATA)
67 @Produces(MediaType.APPLICATION_JSON)
68 @Api(value = "Catalog Types Upload")
69 @Controller
70 public class TypesUploadServlet extends AbstractValidationsServlet {
71     private static final Logger log = Logger.getLogger(TypesUploadServlet.class);
72     public static final String CREATE = "Create ";
73
74     private final CapabilityTypeImportManager capabilityTypeImportManager;
75     private final InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager;
76     private final CategoriesImportManager categoriesImportManager;
77     private final DataTypeImportManager dataTypeImportManager;
78     private final GroupTypeImportManager groupTypeImportManager;
79     private final PolicyTypeImportManager policyTypeImportManager;
80     private final RelationshipTypeImportManager relationshipTypeImportManager;
81
82     @Inject
83     public TypesUploadServlet(UserBusinessLogic userBusinessLogic,
84         ComponentInstanceBusinessLogic componentInstanceBL,
85         ComponentsUtils componentsUtils, ServletUtils servletUtils,
86         ResourceImportManager resourceImportManager,
87         CapabilityTypeImportManager capabilityTypeImportManager,
88         InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager,
89         CategoriesImportManager categoriesImportManager,
90         DataTypeImportManager dataTypeImportManager,
91         GroupTypeImportManager groupTypeImportManager,
92         PolicyTypeImportManager policyTypeImportManager,
93         RelationshipTypeImportManager relationshipTypeImportManager) {
94         super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
95         this.capabilityTypeImportManager = capabilityTypeImportManager;
96         this.interfaceLifecycleTypeImportManager = interfaceLifecycleTypeImportManager;
97         this.categoriesImportManager = categoriesImportManager;
98         this.dataTypeImportManager = dataTypeImportManager;
99         this.groupTypeImportManager = groupTypeImportManager;
100         this.policyTypeImportManager = policyTypeImportManager;
101         this.relationshipTypeImportManager = relationshipTypeImportManager;
102     }
103
104     @POST
105     @Path("/capability")
106     @ApiOperation(value = "Create Capability Type from yaml", httpMethod = "POST", notes = "Returns created Capability Type", response = Response.class)
107     @ApiResponses(value = { @ApiResponse(code = 201, message = "Capability Type created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
108     @ApiResponse(code = 409, message = "Capability Type already exist") })
109     public Response uploadCapabilityType(@ApiParam("FileInputStream") @FormDataParam("capabilityTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
110         ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload));
111         return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.CapabilityType.name());
112     }
113
114     @POST
115     @Path("/relationship")
116     @ApiOperation(value = "Create Relationship Type from yaml", httpMethod = "POST",
117             notes = "Returns created Relationship Type", response = Response.class)
118     @ApiResponses(value = {@ApiResponse(code = 201, message = "Relationship Type created"),
119             @ApiResponse(code = 403, message = "Restricted operation"),
120             @ApiResponse(code = 400, message = "Invalid content / Missing content"),
121             @ApiResponse(code = 409, message = "Relationship Type already exist")})
122     public Response uploadRelationshipType(@ApiParam("FileInputStream") @FormDataParam("relationshipTypeZip") File file,
123                                            @Context final HttpServletRequest request,
124                                            @HeaderParam("USER_ID") String creator) {
125         return uploadElementTypeServletLogic(this::createRelationshipTypes, file, request, creator,
126                 NodeTypeEnum.RelationshipType.getName());
127     }
128
129     @POST
130     @Path("/interfaceLifecycle")
131     @ApiOperation(value = "Create Interface Lyfecycle Type from yaml", httpMethod = "POST", notes = "Returns created Interface Lifecycle Type", response = Response.class)
132     @ApiResponses(value = { @ApiResponse(code = 201, message = "Interface Lifecycle Type created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
133     @ApiResponse(code = 409, message = "Interface Lifecycle Type already exist") })
134     public Response uploadInterfaceLifecycleType(@ApiParam("FileInputStream") @FormDataParam("interfaceLifecycleTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
135         ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload));
136         return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "Interface Types");
137     }
138
139     @POST
140     @Path("/categories")
141     @ApiOperation(value = "Create Categories from yaml", httpMethod = "POST", notes = "Returns created categories", response = Response.class)
142     @ApiResponses(value = { @ApiResponse(code = 201, message = "Categories created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
143     @ApiResponse(code = 409, message = "Category already exist") })
144     public Response uploadCategories(@ApiParam("FileInputStream") @FormDataParam("categoriesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
145         ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> categoriesImportManager.createCategories(ymlPayload));
146         return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "categories");
147     }
148
149     @POST
150     @Path("/datatypes")
151     @ApiOperation(value = "Create Categories from yaml", httpMethod = "POST", notes = "Returns created data types", response = Response.class)
152     @ApiResponses(value = { @ApiResponse(code = 201, message = "Data types created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
153     @ApiResponse(code = 409, message = "Data types already exist") })
154     public Response uploadDataTypes(@ApiParam("FileInputStream") @FormDataParam("dataTypesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
155         ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = this::createDataTypes;
156         return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.DataType.getName());
157     }
158
159     @POST
160     @Path("/grouptypes")
161     @ApiOperation(value = "Create GroupTypes from yaml", httpMethod = "POST", notes = "Returns created group types", response = Response.class)
162     @ApiResponses(value = { @ApiResponse(code = 201, message = "group types created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
163     @ApiResponse(code = 409, message = "group types already exist") })
164     public Response uploadGroupTypes(@ApiParam("toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
165                                      @ApiParam("FileInputStream") @FormDataParam("groupTypesZip") File file,
166                                      @Context final HttpServletRequest request,
167                                      @HeaderParam("USER_ID") String creator) {
168         Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
169         return uploadTypesWithMetaData(this::createGroupTypes, typesMetadata, file, request, creator, NodeTypeEnum.GroupType.getName());
170     }
171
172     @POST
173     @Path("/policytypes")
174     @ApiOperation(value = "Create PolicyTypes from yaml", httpMethod = "POST", notes = "Returns created policy types", response = Response.class)
175     @ApiResponses(value = { @ApiResponse(code = 201, message = "policy types created"),
176                             @ApiResponse(code = 403, message = "Restricted operation"),
177                             @ApiResponse(code = 400, message = "Invalid content / Missing content"),
178     @ApiResponse(code = 409, message = "policy types already exist") })
179     public Response uploadPolicyTypes(
180             @ApiParam("toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
181             @ApiParam("FileInputStream") @FormDataParam("policyTypesZip") File file,
182             @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
183         Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
184         return uploadTypesWithMetaData(this::createPolicyTypes, typesMetadata, file, request, creator, NodeTypeEnum.PolicyType.getName());
185     }
186
187     private Map<String, ToscaTypeMetadata> getTypesMetadata(String toscaTypesMetaData) {
188         return gson.fromJson(toscaTypesMetaData, new TypeToken<Map<String, ToscaTypeMetadata>>(){}.getType());
189     }
190
191     private Response uploadElementTypeServletLogic(ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod, File file, final HttpServletRequest request, String creator, String elementTypeName) {
192         init();
193         String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
194         try {
195             Wrapper<String> yamlStringWrapper = new Wrapper<>();
196
197             String url = request.getMethod() + " " + request.getRequestURI();
198             log.debug("Start handle request of {}", url);
199             Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
200             if (responseWrapper.isEmpty()) {
201                 fillZipContents(yamlStringWrapper, file);
202             }
203             if (responseWrapper.isEmpty()) {
204                 createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement());
205             }
206             return responseWrapper.getInnerElement();
207         } catch (Exception e) {
208             log.debug("create {} failed with exception:", elementTypeName, e);
209             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
210             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
211         }
212     }
213
214     private Wrapper<Response> doUploadTypeValidations(final HttpServletRequest request, String userId, File file) {
215         Wrapper<Response> responseWrapper = new Wrapper<>();
216         Wrapper<User> userWrapper = new Wrapper<>();
217
218         String url = request.getMethod() + " " + request.getRequestURI();
219         log.debug("Start handle request of {}", url);
220
221         validateUserExist(responseWrapper, userWrapper, userId);
222
223         if (responseWrapper.isEmpty()) {
224             validateUserRole(responseWrapper, userWrapper.getInnerElement());
225         }
226
227         if (responseWrapper.isEmpty()) {
228             validateDataNotNull(responseWrapper, file);
229         }
230         return responseWrapper;
231     }
232
233     private Response uploadTypesWithMetaData(ConsumerTwoParam<Wrapper<Response>, ToscaTypeImportData> createElementsMethod, Map<String, ToscaTypeMetadata> typesMetaData, File file, final HttpServletRequest request, String creator, String elementTypeName) {
234         init();
235         String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
236         Wrapper<String> yamlStringWrapper = new Wrapper<>();
237         try {
238             Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
239             if (responseWrapper.isEmpty()) {
240                 fillZipContents(yamlStringWrapper, file);
241             }
242             if (responseWrapper.isEmpty()) {
243                 ToscaTypeImportData toscaTypeImportData = new ToscaTypeImportData(yamlStringWrapper.getInnerElement(), typesMetaData);
244                 createElementsMethod.accept(responseWrapper, toscaTypeImportData);
245             }
246             return responseWrapper.getInnerElement();
247         } catch (Exception e) {
248             log.debug("create {} failed with exception:", elementTypeName, e);
249             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
250             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
251         }
252     }
253
254     private <T> void createElementsType(Wrapper<Response> responseWrapper, Supplier<Either<T, ResponseFormat>> elementsCreater) {
255         Either<T, ResponseFormat> eitherResult = elementsCreater.get();
256         if (eitherResult.isRight()) {
257             Response response = buildErrorResponse(eitherResult.right().value());
258             responseWrapper.setInnerElement(response);
259         } else {
260             try {
261                 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), RepresentationUtils.toRepresentation(eitherResult.left().value()));
262                 responseWrapper.setInnerElement(response);
263             } catch (Exception e) {
264                 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
265                 responseWrapper.setInnerElement(response);
266                 log.error("#createElementsType - json serialization failed with error: ", e);
267             }
268         }
269     }
270
271     // data types
272     private void createDataTypes(Wrapper<Response> responseWrapper, String dataTypesYml) {
273         final Supplier<Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> dataTypeImportManager.createDataTypes(dataTypesYml);
274         buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.DATA_TYPE_ALREADY_EXIST, NodeTypeEnum.DataType.name());
275     }
276
277     // group types
278     private void createGroupTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
279         final Supplier<Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> groupTypeImportManager.createGroupTypes(toscaTypeImportData);
280         buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.GROUP_TYPE_ALREADY_EXIST, NodeTypeEnum.GroupType.name());
281     }
282
283     // policy types
284     private void createPolicyTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
285         final Supplier<Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> policyTypeImportManager.createPolicyTypes(toscaTypeImportData);
286         buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.POLICY_TYPE_ALREADY_EXIST, NodeTypeEnum.PolicyType.name());
287     }
288
289     // data types
290     private <ElementTypeDefinition> void buildStatusForElementTypeCreate(Wrapper<Response> responseWrapper, Supplier<Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml, ActionStatus alreadyExistStatus, String elementTypeName) {
291         Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> eitherResult = generateElementTypeFromYml.get();
292
293         if (eitherResult.isRight()) {
294             Response response = buildErrorResponse(eitherResult.right().value());
295             responseWrapper.setInnerElement(response);
296         } else {
297             Object representation;
298             try {
299                 List<ImmutablePair<ElementTypeDefinition, Boolean>> list = eitherResult.left().value();
300                 ActionStatus status = ActionStatus.OK;
301                 if (list != null) {
302
303                     // Group result by the right value - true or false.
304                     // I.e., get the number of data types which are new and
305                     // which are old.
306                     Map<Boolean, List<ImmutablePair<ElementTypeDefinition, Boolean>>> collect = list.stream().collect(Collectors.groupingBy(ImmutablePair<ElementTypeDefinition, Boolean>::getRight));
307                     if (collect != null) {
308                         Set<Boolean> keySet = collect.keySet();
309                         if (keySet.size() == 1) {
310                             Boolean isNew = keySet.iterator().next();
311                             if (isNew.booleanValue()) {
312                                 // all data types created at the first time
313                                 status = ActionStatus.CREATED;
314                             } else {
315                                 // All data types already exists
316
317                                 status = alreadyExistStatus;
318                             }
319                         }
320                     }
321                 }
322                 representation = RepresentationUtils.toRepresentation(eitherResult.left().value());
323
324                 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(status), representation);
325                 responseWrapper.setInnerElement(response);
326
327             } catch (IOException e) {
328                 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
329                 log.debug("failed to convert {} to json", elementTypeName, e);
330                 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
331                 responseWrapper.setInnerElement(response);
332             }
333         }
334     }
335     // relationship types
336     private void createRelationshipTypes(Wrapper<Response> responseWrapper, String relationshipTypesYml) {
337         final Supplier<Either<List<ImmutablePair<RelationshipTypeDefinition, Boolean>>, ResponseFormat>>
338                 generateElementTypeFromYml =
339                 () -> relationshipTypeImportManager.createRelationshipTypes(relationshipTypesYml);
340         buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml,
341                 ActionStatus.RELATIONSHIP_TYPE_ALREADY_EXIST, NodeTypeEnum.RelationshipType.name());
342     }
343
344 }