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