2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.sdc.be.servlets;
23 import com.google.gson.reflect.TypeToken;
24 import com.jcabi.aspects.Loggable;
25 import fj.data.Either;
26 import io.swagger.v3.oas.annotations.OpenAPIDefinition;
27 import io.swagger.v3.oas.annotations.Operation;
28 import io.swagger.v3.oas.annotations.Parameter;
29 import io.swagger.v3.oas.annotations.info.Info;
30 import io.swagger.v3.oas.annotations.media.ArraySchema;
31 import io.swagger.v3.oas.annotations.media.Content;
32 import io.swagger.v3.oas.annotations.media.Schema;
33 import io.swagger.v3.oas.annotations.responses.ApiResponse;
34 import io.swagger.v3.oas.annotations.responses.ApiResponses;
35 import org.apache.commons.lang3.tuple.ImmutablePair;
36 import org.glassfish.jersey.media.multipart.FormDataParam;
37 import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager;
38 import org.openecomp.sdc.be.components.impl.CategoriesImportManager;
39 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
40 import org.openecomp.sdc.be.components.impl.DataTypeImportManager;
41 import org.openecomp.sdc.be.components.impl.GroupTypeImportManager;
42 import org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager;
43 import org.openecomp.sdc.be.components.impl.PolicyTypeImportManager;
44 import org.openecomp.sdc.be.components.impl.RelationshipTypeImportManager;
45 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
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.components.impl.model.ToscaTypeImportData;
49 import org.openecomp.sdc.be.config.BeEcompErrorManager;
50 import org.openecomp.sdc.be.dao.api.ActionStatus;
51 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
52 import org.openecomp.sdc.be.impl.ComponentsUtils;
53 import org.openecomp.sdc.be.impl.ServletUtils;
54 import org.openecomp.sdc.be.model.DataTypeDefinition;
55 import org.openecomp.sdc.be.model.GroupTypeDefinition;
56 import org.openecomp.sdc.be.model.PolicyTypeDefinition;
57 import org.openecomp.sdc.be.model.RelationshipTypeDefinition;
58 import org.openecomp.sdc.be.model.User;
59 import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata;
60 import org.openecomp.sdc.be.user.UserBusinessLogic;
61 import org.openecomp.sdc.common.api.Constants;
62 import org.openecomp.sdc.common.datastructure.FunctionalInterfaces.ConsumerTwoParam;
63 import org.openecomp.sdc.common.datastructure.Wrapper;
64 import org.openecomp.sdc.common.log.wrappers.Logger;
65 import org.openecomp.sdc.exception.ResponseFormat;
66 import org.springframework.stereotype.Controller;
68 import javax.inject.Inject;
69 import javax.servlet.http.HttpServletRequest;
70 import javax.ws.rs.Consumes;
71 import javax.ws.rs.HeaderParam;
72 import javax.ws.rs.POST;
73 import javax.ws.rs.Path;
74 import javax.ws.rs.Produces;
75 import javax.ws.rs.core.Context;
76 import javax.ws.rs.core.MediaType;
77 import javax.ws.rs.core.Response;
79 import java.io.IOException;
80 import java.util.List;
83 import java.util.function.Supplier;
84 import java.util.stream.Collectors;
86 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
87 @Path("/v1/catalog/uploadType")
88 @Consumes(MediaType.MULTIPART_FORM_DATA)
89 @Produces(MediaType.APPLICATION_JSON)
90 @OpenAPIDefinition(info = @Info(title = "Catalog Types Upload"))
92 public class TypesUploadServlet extends AbstractValidationsServlet {
93 private static final Logger log = Logger.getLogger(TypesUploadServlet.class);
94 public static final String CREATE = "Create ";
96 private final CapabilityTypeImportManager capabilityTypeImportManager;
97 private final InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager;
98 private final CategoriesImportManager categoriesImportManager;
99 private final DataTypeImportManager dataTypeImportManager;
100 private final GroupTypeImportManager groupTypeImportManager;
101 private final PolicyTypeImportManager policyTypeImportManager;
102 private final RelationshipTypeImportManager relationshipTypeImportManager;
105 public TypesUploadServlet(UserBusinessLogic userBusinessLogic,
106 ComponentInstanceBusinessLogic componentInstanceBL,
107 ComponentsUtils componentsUtils, ServletUtils servletUtils,
108 ResourceImportManager resourceImportManager,
109 CapabilityTypeImportManager capabilityTypeImportManager,
110 InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager,
111 CategoriesImportManager categoriesImportManager,
112 DataTypeImportManager dataTypeImportManager,
113 GroupTypeImportManager groupTypeImportManager,
114 PolicyTypeImportManager policyTypeImportManager,
115 RelationshipTypeImportManager relationshipTypeImportManager) {
116 super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
117 this.capabilityTypeImportManager = capabilityTypeImportManager;
118 this.interfaceLifecycleTypeImportManager = interfaceLifecycleTypeImportManager;
119 this.categoriesImportManager = categoriesImportManager;
120 this.dataTypeImportManager = dataTypeImportManager;
121 this.groupTypeImportManager = groupTypeImportManager;
122 this.policyTypeImportManager = policyTypeImportManager;
123 this.relationshipTypeImportManager = relationshipTypeImportManager;
128 @Operation(description = "Create Capability Type from yaml", method = "POST",
129 summary = "Returns created Capability Type", responses = @ApiResponse(
130 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
131 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Capability Type created"),
132 @ApiResponse(responseCode = "403", description = "Restricted operation"),
133 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
134 @ApiResponse(responseCode = "409", description = "Capability Type already exist")})
135 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
136 public Response uploadCapabilityType(@Parameter(description = "FileInputStream") @FormDataParam("capabilityTypeZip") File file,
137 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
138 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod =
139 (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
140 () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload));
141 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator,
142 NodeTypeEnum.CapabilityType.name());
146 @Path("/relationship")
147 @Operation(description = "Create Relationship Type from yaml", method = "POST",
148 summary = "Returns created Relationship Type", responses = @ApiResponse(
149 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
150 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Relationship Type created"),
151 @ApiResponse(responseCode = "403", description = "Restricted operation"),
152 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
153 @ApiResponse(responseCode = "409", description = "Relationship Type already exist")})
154 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
155 public Response uploadRelationshipType(@Parameter(description = "FileInputStream") @FormDataParam("relationshipTypeZip") File file,
156 @Context final HttpServletRequest request,
157 @HeaderParam("USER_ID") String creator) {
158 return uploadElementTypeServletLogic(this::createRelationshipTypes, file, request, creator,
159 NodeTypeEnum.RelationshipType.getName());
163 @Path("/interfaceLifecycle")
164 @Operation(description = "Create Interface Lyfecycle Type from yaml", method = "POST",
165 summary = "Returns created Interface Lifecycle Type", responses = @ApiResponse(
166 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
167 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Interface Lifecycle Type created"),
168 @ApiResponse(responseCode = "403", description = "Restricted operation"),
169 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
170 @ApiResponse(responseCode = "409", description = "Interface Lifecycle Type already exist")})
171 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
172 public Response uploadInterfaceLifecycleType(
173 @Parameter(description = "FileInputStream") @FormDataParam("interfaceLifecycleTypeZip") File file,
174 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
175 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod =
176 (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
177 () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload));
178 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "Interface Types");
183 @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created categories",
184 responses = @ApiResponse(
185 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
186 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Categories created"),
187 @ApiResponse(responseCode = "403", description = "Restricted operation"),
188 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
189 @ApiResponse(responseCode = "409", description = "Category already exist")})
190 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
191 public Response uploadCategories(@Parameter(description = "FileInputStream") @FormDataParam("categoriesZip") File file,
192 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
193 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod =
194 (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
195 () -> categoriesImportManager.createCategories(ymlPayload));
196 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "categories");
201 @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created data types",
202 responses = @ApiResponse(
203 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
204 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Data types created"),
205 @ApiResponse(responseCode = "403", description = "Restricted operation"),
206 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
207 @ApiResponse(responseCode = "409", description = "Data types already exist")})
208 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
209 public Response uploadDataTypes(@Parameter(description = "FileInputStream") @FormDataParam("dataTypesZip") File file,
210 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
211 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = this::createDataTypes;
212 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator,
213 NodeTypeEnum.DataType.getName());
218 @Operation(description = "Create GroupTypes from yaml", method = "POST", summary = "Returns created group types",
219 responses = @ApiResponse(
220 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
221 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "group types created"),
222 @ApiResponse(responseCode = "403", description = "Restricted operation"),
223 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
224 @ApiResponse(responseCode = "409", description = "group types already exist")})
225 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
226 public Response uploadGroupTypes(
227 @Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
228 @Parameter(description = "FileInputStream") @FormDataParam("groupTypesZip") File file,
229 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
230 Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
231 return uploadTypesWithMetaData(this::createGroupTypes, typesMetadata, file, request, creator,
232 NodeTypeEnum.GroupType.getName());
236 @Path("/policytypes")
237 @Operation(description = "Create PolicyTypes from yaml", method = "POST", summary = "Returns created policy types",
238 responses = @ApiResponse(
239 content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))))
240 @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "policy types created"),
241 @ApiResponse(responseCode = "403", description = "Restricted operation"),
242 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
243 @ApiResponse(responseCode = "409", description = "policy types already exist")})
244 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
245 public Response uploadPolicyTypes(
246 @Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
247 @Parameter(description = "FileInputStream") @FormDataParam("policyTypesZip") File file,
248 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
249 Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
250 return uploadTypesWithMetaData(this::createPolicyTypes, typesMetadata, file, request, creator,
251 NodeTypeEnum.PolicyType.getName());
254 private Map<String, ToscaTypeMetadata> getTypesMetadata(String toscaTypesMetaData) {
255 return gson.fromJson(toscaTypesMetaData, new TypeToken<Map<String, ToscaTypeMetadata>>(){}.getType());
258 private Response uploadElementTypeServletLogic(ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod, File file, final HttpServletRequest request, String creator, String elementTypeName) {
260 String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
262 Wrapper<String> yamlStringWrapper = new Wrapper<>();
264 String url = request.getMethod() + " " + request.getRequestURI();
265 log.debug("Start handle request of {}", url);
266 Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
267 if (responseWrapper.isEmpty()) {
268 fillZipContents(yamlStringWrapper, file);
270 if (responseWrapper.isEmpty()) {
271 createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement());
273 return responseWrapper.getInnerElement();
274 } catch (Exception e) {
275 log.debug("create {} failed with exception:", elementTypeName, e);
276 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
277 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
281 private Wrapper<Response> doUploadTypeValidations(final HttpServletRequest request, String userId, File file) {
282 Wrapper<Response> responseWrapper = new Wrapper<>();
283 Wrapper<User> userWrapper = new Wrapper<>();
285 String url = request.getMethod() + " " + request.getRequestURI();
286 log.debug("Start handle request of {}", url);
288 validateUserExist(responseWrapper, userWrapper, userId);
290 if (responseWrapper.isEmpty()) {
291 validateUserRole(responseWrapper, userWrapper.getInnerElement());
294 if (responseWrapper.isEmpty()) {
295 validateDataNotNull(responseWrapper, file);
297 return responseWrapper;
300 private Response uploadTypesWithMetaData(ConsumerTwoParam<Wrapper<Response>, ToscaTypeImportData> createElementsMethod, Map<String, ToscaTypeMetadata> typesMetaData, File file, final HttpServletRequest request, String creator, String elementTypeName) {
302 String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
303 Wrapper<String> yamlStringWrapper = new Wrapper<>();
305 Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
306 if (responseWrapper.isEmpty()) {
307 fillZipContents(yamlStringWrapper, file);
309 if (responseWrapper.isEmpty()) {
310 ToscaTypeImportData toscaTypeImportData = new ToscaTypeImportData(yamlStringWrapper.getInnerElement(), typesMetaData);
311 createElementsMethod.accept(responseWrapper, toscaTypeImportData);
313 return responseWrapper.getInnerElement();
314 } catch (Exception e) {
315 log.debug("create {} failed with exception:", elementTypeName, e);
316 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
317 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
321 private <T> void createElementsType(Wrapper<Response> responseWrapper, Supplier<Either<T, ResponseFormat>> elementsCreater) {
322 Either<T, ResponseFormat> eitherResult = elementsCreater.get();
323 if (eitherResult.isRight()) {
324 Response response = buildErrorResponse(eitherResult.right().value());
325 responseWrapper.setInnerElement(response);
328 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), RepresentationUtils.toRepresentation(eitherResult.left().value()));
329 responseWrapper.setInnerElement(response);
330 } catch (Exception e) {
331 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
332 responseWrapper.setInnerElement(response);
333 log.error("#createElementsType - json serialization failed with error: ", e);
339 private void createDataTypes(Wrapper<Response> responseWrapper, String dataTypesYml) {
340 final Supplier<Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml =
341 () -> dataTypeImportManager.createDataTypes(dataTypesYml);
342 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml,
343 ActionStatus.DATA_TYPE_ALREADY_EXIST, NodeTypeEnum.DataType.name());
347 private void createGroupTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
348 final Supplier<Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml =
349 () -> groupTypeImportManager.createGroupTypes(toscaTypeImportData);
350 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml,
351 ActionStatus.GROUP_TYPE_ALREADY_EXIST, NodeTypeEnum.GroupType.name());
355 private void createPolicyTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
356 final Supplier<Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml =
357 () -> policyTypeImportManager.createPolicyTypes(toscaTypeImportData);
358 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml,
359 ActionStatus.POLICY_TYPE_ALREADY_EXIST, NodeTypeEnum.PolicyType.name());
363 private <ElementTypeDefinition> void buildStatusForElementTypeCreate(Wrapper<Response> responseWrapper, Supplier<Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml, ActionStatus alreadyExistStatus, String elementTypeName) {
364 Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> eitherResult = generateElementTypeFromYml.get();
366 if (eitherResult.isRight()) {
367 Response response = buildErrorResponse(eitherResult.right().value());
368 responseWrapper.setInnerElement(response);
370 Object representation;
372 List<ImmutablePair<ElementTypeDefinition, Boolean>> list = eitherResult.left().value();
373 ActionStatus status = ActionStatus.OK;
376 // Group result by the right value - true or false.
377 // I.e., get the number of data types which are new and
379 Map<Boolean, List<ImmutablePair<ElementTypeDefinition, Boolean>>> collect = list.stream().collect(Collectors.groupingBy(ImmutablePair<ElementTypeDefinition, Boolean>::getRight));
380 if (collect != null) {
381 Set<Boolean> keySet = collect.keySet();
382 if (keySet.size() == 1) {
383 Boolean isNew = keySet.iterator().next();
385 // all data types created at the first time
386 status = ActionStatus.CREATED;
388 // All data types already exists
390 status = alreadyExistStatus;
395 representation = RepresentationUtils.toRepresentation(eitherResult.left().value());
397 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(status), representation);
398 responseWrapper.setInnerElement(response);
400 } catch (IOException e) {
401 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
402 log.debug("failed to convert {} to json", elementTypeName, e);
403 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
404 responseWrapper.setInnerElement(response);
408 // relationship types
409 private void createRelationshipTypes(Wrapper<Response> responseWrapper, String relationshipTypesYml) {
410 final Supplier<Either<List<ImmutablePair<RelationshipTypeDefinition, Boolean>>, ResponseFormat>>
411 generateElementTypeFromYml =
412 () -> relationshipTypeImportManager.createRelationshipTypes(relationshipTypesYml);
413 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml,
414 ActionStatus.RELATIONSHIP_TYPE_ALREADY_EXIST, NodeTypeEnum.RelationshipType.name());