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=========================================================
20 package org.openecomp.sdc.be.servlets;
22 import com.google.gson.reflect.TypeToken;
23 import com.jcabi.aspects.Loggable;
24 import fj.data.Either;
25 import io.swagger.v3.oas.annotations.Operation;
26 import io.swagger.v3.oas.annotations.Parameter;
27 import io.swagger.v3.oas.annotations.media.ArraySchema;
28 import io.swagger.v3.oas.annotations.media.Content;
29 import io.swagger.v3.oas.annotations.media.Schema;
30 import io.swagger.v3.oas.annotations.responses.ApiResponse;
31 import io.swagger.v3.oas.annotations.servers.Server;
32 import io.swagger.v3.oas.annotations.servers.Servers;
33 import io.swagger.v3.oas.annotations.tags.Tag;
34 import io.swagger.v3.oas.annotations.tags.Tags;
36 import java.io.IOException;
37 import java.util.List;
40 import java.util.function.Supplier;
41 import java.util.stream.Collectors;
42 import javax.inject.Inject;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.ws.rs.Consumes;
45 import javax.ws.rs.HeaderParam;
46 import javax.ws.rs.POST;
47 import javax.ws.rs.Path;
48 import javax.ws.rs.Produces;
49 import javax.ws.rs.core.Context;
50 import javax.ws.rs.core.MediaType;
51 import javax.ws.rs.core.Response;
52 import org.apache.commons.lang3.tuple.ImmutablePair;
53 import org.glassfish.jersey.media.multipart.FormDataParam;
54 import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager;
55 import org.openecomp.sdc.be.components.impl.CategoriesImportManager;
56 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
57 import org.openecomp.sdc.be.components.impl.DataTypeImportManager;
58 import org.openecomp.sdc.be.components.impl.GroupTypeImportManager;
59 import org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager;
60 import org.openecomp.sdc.be.components.impl.PolicyTypeImportManager;
61 import org.openecomp.sdc.be.components.impl.RelationshipTypeImportManager;
62 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
63 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
64 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
65 import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData;
66 import org.openecomp.sdc.be.config.BeEcompErrorManager;
67 import org.openecomp.sdc.be.dao.api.ActionStatus;
68 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
69 import org.openecomp.sdc.be.impl.ComponentsUtils;
70 import org.openecomp.sdc.be.impl.ServletUtils;
71 import org.openecomp.sdc.be.model.DataTypeDefinition;
72 import org.openecomp.sdc.be.model.GroupTypeDefinition;
73 import org.openecomp.sdc.be.model.PolicyTypeDefinition;
74 import org.openecomp.sdc.be.model.RelationshipTypeDefinition;
75 import org.openecomp.sdc.be.model.User;
76 import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata;
77 import org.openecomp.sdc.be.user.UserBusinessLogic;
78 import org.openecomp.sdc.common.api.Constants;
79 import org.openecomp.sdc.common.datastructure.FunctionalInterfaces.ConsumerThreeParam;
80 import org.openecomp.sdc.common.datastructure.FunctionalInterfaces.ConsumerTwoParam;
81 import org.openecomp.sdc.common.datastructure.Wrapper;
82 import org.openecomp.sdc.common.log.wrappers.Logger;
83 import org.openecomp.sdc.exception.ResponseFormat;
84 import org.springframework.stereotype.Controller;
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 @Tags({@Tag(name = "SDCE-2 APIs")})
91 @Servers({@Server(url = "/sdc2/rest")})
93 public class TypesUploadServlet extends AbstractValidationsServlet {
95 public static final String CREATE = "Create ";
96 private static final Logger log = Logger.getLogger(TypesUploadServlet.class);
97 private final CapabilityTypeImportManager capabilityTypeImportManager;
98 private final InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager;
99 private final CategoriesImportManager categoriesImportManager;
100 private final DataTypeImportManager dataTypeImportManager;
101 private final GroupTypeImportManager groupTypeImportManager;
102 private final PolicyTypeImportManager policyTypeImportManager;
103 private final RelationshipTypeImportManager relationshipTypeImportManager;
106 public TypesUploadServlet(UserBusinessLogic userBusinessLogic, ComponentInstanceBusinessLogic componentInstanceBL,
107 ComponentsUtils componentsUtils, ServletUtils servletUtils, ResourceImportManager resourceImportManager,
108 CapabilityTypeImportManager capabilityTypeImportManager,
109 InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager,
110 CategoriesImportManager categoriesImportManager, DataTypeImportManager dataTypeImportManager,
111 GroupTypeImportManager groupTypeImportManager, PolicyTypeImportManager policyTypeImportManager,
112 RelationshipTypeImportManager relationshipTypeImportManager) {
113 super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
114 this.capabilityTypeImportManager = capabilityTypeImportManager;
115 this.interfaceLifecycleTypeImportManager = interfaceLifecycleTypeImportManager;
116 this.categoriesImportManager = categoriesImportManager;
117 this.dataTypeImportManager = dataTypeImportManager;
118 this.groupTypeImportManager = groupTypeImportManager;
119 this.policyTypeImportManager = policyTypeImportManager;
120 this.relationshipTypeImportManager = relationshipTypeImportManager;
125 @Operation(description = "Create Capability Type from yaml", method = "POST", summary = "Returns created Capability Type", responses = {
126 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
127 @ApiResponse(responseCode = "201", description = "Capability Type created"),
128 @ApiResponse(responseCode = "403", description = "Restricted operation"),
129 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
130 @ApiResponse(responseCode = "409", description = "Capability Type already exist")})
131 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
132 public Response uploadCapabilityType(@Parameter(description = "FileInputStream") @FormDataParam("capabilityTypeZip") File file,
133 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
134 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
135 () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload));
136 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.CapabilityType.name());
140 @Path("/relationship")
141 @Operation(description = "Create Relationship Type from yaml", method = "POST", summary = "Returns created Relationship Type", responses = {
142 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
143 @ApiResponse(responseCode = "201", description = "Relationship Type created"),
144 @ApiResponse(responseCode = "403", description = "Restricted operation"),
145 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
146 @ApiResponse(responseCode = "409", description = "Relationship Type already exist")})
147 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
148 public Response uploadRelationshipType(@Parameter(description = "FileInputStream") @FormDataParam("relationshipTypeZip") File file,
149 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator,
150 @Parameter(description = "model") @FormDataParam("model") String modelName) {
151 return uploadElementTypeServletLogic(this::createRelationshipTypes, file, request, creator, NodeTypeEnum.RelationshipType.getName(), modelName);
155 @Path("/interfaceLifecycle")
156 @Operation(description = "Create Interface Lyfecycle Type from yaml", method = "POST", summary = "Returns created Interface Lifecycle Type", responses = {
157 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
158 @ApiResponse(responseCode = "201", description = "Interface Lifecycle Type created"),
159 @ApiResponse(responseCode = "403", description = "Restricted operation"),
160 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
161 @ApiResponse(responseCode = "409", description = "Interface Lifecycle Type already exist")})
162 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
163 public Response uploadInterfaceLifecycleType(@Parameter(description = "FileInputStream") @FormDataParam("interfaceLifecycleTypeZip") File file,
164 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
165 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
166 () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload));
167 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "Interface Types");
172 @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created categories", responses = {
173 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
174 @ApiResponse(responseCode = "201", description = "Categories created"),
175 @ApiResponse(responseCode = "403", description = "Restricted operation"),
176 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
177 @ApiResponse(responseCode = "409", description = "Category already exist")})
178 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
179 public Response uploadCategories(@Parameter(description = "FileInputStream") @FormDataParam("categoriesZip") File file,
180 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
181 ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper,
182 () -> categoriesImportManager.createCategories(ymlPayload));
183 return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "categories");
188 @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created data types", responses = {
189 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
190 @ApiResponse(responseCode = "201", description = "Data types created"),
191 @ApiResponse(responseCode = "403", description = "Restricted operation"),
192 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
193 @ApiResponse(responseCode = "409", description = "Data types already exist")})
194 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
195 public Response uploadDataTypes(@Parameter(description = "FileInputStream") @FormDataParam("dataTypesZip") File file,
196 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator,
197 @Parameter(description = "model") @FormDataParam("model") String modelName) {
198 return uploadElementTypeServletLogic(this::createDataTypes, file, request, creator, NodeTypeEnum.DataType.getName(), modelName);
203 @Operation(description = "Create GroupTypes from yaml", method = "POST", summary = "Returns created group types", responses = {
204 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
205 @ApiResponse(responseCode = "201", description = "group types created"),
206 @ApiResponse(responseCode = "403", description = "Restricted operation"),
207 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
208 @ApiResponse(responseCode = "409", description = "group types already exist")})
209 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
210 public Response uploadGroupTypes(@Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
211 @Parameter(description = "FileInputStream") @FormDataParam("groupTypesZip") File file,
212 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
213 Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
214 return uploadTypesWithMetaData(this::createGroupTypes, typesMetadata, file, request, creator, NodeTypeEnum.GroupType.getName());
218 @Path("/policytypes")
219 @Operation(description = "Create PolicyTypes from yaml", method = "POST", summary = "Returns created policy types", responses = {
220 @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
221 @ApiResponse(responseCode = "201", description = "policy types created"),
222 @ApiResponse(responseCode = "403", description = "Restricted operation"),
223 @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
224 @ApiResponse(responseCode = "409", description = "policy types already exist")})
225 @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
226 public Response uploadPolicyTypes(@Parameter(description = "toscaTypeMetadata") @FormDataParam("toscaTypeMetadata") String toscaTypesMetaData,
227 @Parameter(description = "FileInputStream") @FormDataParam("policyTypesZip") File file,
228 @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
229 Map<String, ToscaTypeMetadata> typesMetadata = getTypesMetadata(toscaTypesMetaData);
230 return uploadTypesWithMetaData(this::createPolicyTypes, typesMetadata, file, request, creator, NodeTypeEnum.PolicyType.getName());
233 private Map<String, ToscaTypeMetadata> getTypesMetadata(String toscaTypesMetaData) {
234 return gson.fromJson(toscaTypesMetaData, new TypeToken<Map<String, ToscaTypeMetadata>>() {
238 private Response uploadElementTypeServletLogic(ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod, File file,
239 final HttpServletRequest request, String creator, String elementTypeName) {
241 String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
243 Wrapper<String> yamlStringWrapper = new Wrapper<>();
244 String url = request.getMethod() + " " + request.getRequestURI();
245 log.debug("Start handle request of {}", url);
246 Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
247 if (responseWrapper.isEmpty()) {
248 fillZipContents(yamlStringWrapper, file);
250 if (responseWrapper.isEmpty()) {
251 createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement());
253 return responseWrapper.getInnerElement();
254 } catch (Exception e) {
255 log.debug("create {} failed with exception:", elementTypeName, e);
256 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
257 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
261 private Response uploadElementTypeServletLogic(final ConsumerThreeParam<Wrapper<Response>, String, String> createElementsMethod,
262 final File file, final HttpServletRequest request, final String creator, final String elementTypeName, final String modelName) {
264 final String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
266 final Wrapper<String> yamlStringWrapper = new Wrapper<>();
267 final String url = request.getMethod() + " " + request.getRequestURI();
268 log.debug("Start handle request of {}", url);
269 final Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
270 if (responseWrapper.isEmpty()) {
271 fillZipContents(yamlStringWrapper, file);
273 if (responseWrapper.isEmpty()) {
274 createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement(), modelName);
276 return responseWrapper.getInnerElement();
277 } catch (final Exception e) {
278 log.debug("create {} failed with exception:", elementTypeName, e);
279 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
280 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
284 private Wrapper<Response> doUploadTypeValidations(final HttpServletRequest request, String userId, File file) {
285 Wrapper<Response> responseWrapper = new Wrapper<>();
286 Wrapper<User> userWrapper = new Wrapper<>();
287 String url = request.getMethod() + " " + request.getRequestURI();
288 log.debug("Start handle request of {}", url);
289 validateUserExist(responseWrapper, userWrapper, userId);
290 if (responseWrapper.isEmpty()) {
291 validateUserRole(responseWrapper, userWrapper.getInnerElement());
293 if (responseWrapper.isEmpty()) {
294 validateDataNotNull(responseWrapper, file);
296 return responseWrapper;
299 private Response uploadTypesWithMetaData(ConsumerTwoParam<Wrapper<Response>, ToscaTypeImportData> createElementsMethod,
300 Map<String, ToscaTypeMetadata> typesMetaData, File file, final HttpServletRequest request,
301 String creator, String elementTypeName) {
303 String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
304 Wrapper<String> yamlStringWrapper = new Wrapper<>();
306 Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file);
307 if (responseWrapper.isEmpty()) {
308 fillZipContents(yamlStringWrapper, file);
310 if (responseWrapper.isEmpty()) {
311 ToscaTypeImportData toscaTypeImportData = new ToscaTypeImportData(yamlStringWrapper.getInnerElement(), typesMetaData);
312 createElementsMethod.accept(responseWrapper, toscaTypeImportData);
314 return responseWrapper.getInnerElement();
315 } catch (Exception e) {
316 log.debug("create {} failed with exception:", elementTypeName, e);
317 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
318 return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
322 private <T> void createElementsType(Wrapper<Response> responseWrapper, Supplier<Either<T, ResponseFormat>> elementsCreater) {
323 Either<T, ResponseFormat> eitherResult = elementsCreater.get();
324 if (eitherResult.isRight()) {
325 Response response = buildErrorResponse(eitherResult.right().value());
326 responseWrapper.setInnerElement(response);
329 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED),
330 RepresentationUtils.toRepresentation(eitherResult.left().value()));
331 responseWrapper.setInnerElement(response);
332 } catch (Exception e) {
333 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
334 responseWrapper.setInnerElement(response);
335 log.error("#createElementsType - json serialization failed with error: ", e);
341 private void createDataTypes(Wrapper<Response> responseWrapper, String dataTypesYml, final String modelName) {
342 final Supplier<Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> dataTypeImportManager
343 .createDataTypes(dataTypesYml, modelName);
344 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.DATA_TYPE_ALREADY_EXIST,
345 NodeTypeEnum.DataType.name());
349 private void createGroupTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
350 final Supplier<Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> groupTypeImportManager
351 .createGroupTypes(toscaTypeImportData);
352 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.GROUP_TYPE_ALREADY_EXIST,
353 NodeTypeEnum.GroupType.name());
357 private void createPolicyTypes(Wrapper<Response> responseWrapper, ToscaTypeImportData toscaTypeImportData) {
358 final Supplier<Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> policyTypeImportManager
359 .createPolicyTypes(toscaTypeImportData);
360 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.POLICY_TYPE_ALREADY_EXIST,
361 NodeTypeEnum.PolicyType.name());
365 private <ElementTypeDefinition> void buildStatusForElementTypeCreate(Wrapper<Response> responseWrapper,
366 Supplier<Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml,
367 ActionStatus alreadyExistStatus, String elementTypeName) {
368 Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> eitherResult = generateElementTypeFromYml.get();
369 if (eitherResult.isRight()) {
370 Response response = buildErrorResponse(eitherResult.right().value());
371 responseWrapper.setInnerElement(response);
373 Object representation;
375 List<ImmutablePair<ElementTypeDefinition, Boolean>> list = eitherResult.left().value();
376 ActionStatus status = ActionStatus.OK;
378 // Group result by the right value - true or false.
380 // I.e., get the number of data types which are new and
383 Map<Boolean, List<ImmutablePair<ElementTypeDefinition, Boolean>>> collect = list.stream()
384 .collect(Collectors.groupingBy(ImmutablePair<ElementTypeDefinition, Boolean>::getRight));
385 if (collect != null) {
386 Set<Boolean> keySet = collect.keySet();
387 if (keySet.size() == 1) {
388 Boolean isNew = keySet.iterator().next();
390 // all data types created at the first time
391 status = ActionStatus.CREATED;
393 // All data types already exists
394 status = alreadyExistStatus;
399 representation = RepresentationUtils.toRepresentation(eitherResult.left().value());
400 Response response = buildOkResponse(getComponentsUtils().getResponseFormat(status), representation);
401 responseWrapper.setInnerElement(response);
402 } catch (IOException e) {
403 BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName);
404 log.debug("failed to convert {} to json", elementTypeName, e);
405 Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
406 responseWrapper.setInnerElement(response);
411 // relationship types
412 private void createRelationshipTypes(final Wrapper<Response> responseWrapper, final String relationshipTypesYml, final String modelName) {
413 final Supplier<Either<List<ImmutablePair<RelationshipTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> relationshipTypeImportManager
414 .createRelationshipTypes(relationshipTypesYml, modelName);
415 buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.RELATIONSHIP_TYPE_ALREADY_EXIST,
416 NodeTypeEnum.RelationshipType.name());