3c2b72be2a3beb6502cfb10ffcbf306c856cddc5
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / servlets / ServiceServlet.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 package org.openecomp.sdc.be.servlets;
21
22 import static org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR;
23
24 import com.fasterxml.jackson.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.ObjectMapper;
26 import com.google.gson.reflect.TypeToken;
27 import com.jcabi.aspects.Loggable;
28 import fj.data.Either;
29 import io.swagger.v3.oas.annotations.Operation;
30 import io.swagger.v3.oas.annotations.Parameter;
31 import io.swagger.v3.oas.annotations.media.ArraySchema;
32 import io.swagger.v3.oas.annotations.media.Content;
33 import io.swagger.v3.oas.annotations.media.Schema;
34 import io.swagger.v3.oas.annotations.responses.ApiResponse;
35 import io.swagger.v3.oas.annotations.servers.Server;
36 import io.swagger.v3.oas.annotations.tags.Tag;
37 import java.io.File;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.lang.reflect.Type;
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import javax.inject.Inject;
46 import javax.servlet.ServletContext;
47 import javax.servlet.http.HttpServletRequest;
48 import javax.ws.rs.Consumes;
49 import javax.ws.rs.DELETE;
50 import javax.ws.rs.GET;
51 import javax.ws.rs.HeaderParam;
52 import javax.ws.rs.POST;
53 import javax.ws.rs.PUT;
54 import javax.ws.rs.Path;
55 import javax.ws.rs.PathParam;
56 import javax.ws.rs.Produces;
57 import javax.ws.rs.QueryParam;
58 import javax.ws.rs.core.Context;
59 import javax.ws.rs.core.MediaType;
60 import javax.ws.rs.core.Response;
61 import org.apache.http.HttpStatus;
62 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
63 import org.glassfish.jersey.media.multipart.FormDataParam;
64 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
65 import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
66 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
67 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
68 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
69 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
70 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
71 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
72 import org.openecomp.sdc.be.config.BeEcompErrorManager;
73 import org.openecomp.sdc.be.dao.api.ActionStatus;
74 import org.openecomp.sdc.be.datamodel.ServiceRelations;
75 import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
76 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
77 import org.openecomp.sdc.be.impl.ComponentsUtils;
78 import org.openecomp.sdc.be.impl.ServletUtils;
79 import org.openecomp.sdc.be.model.Component;
80 import org.openecomp.sdc.be.model.DistributionStatusEnum;
81 import org.openecomp.sdc.be.model.GroupInstanceProperty;
82 import org.openecomp.sdc.be.model.Resource;
83 import org.openecomp.sdc.be.model.Service;
84 import org.openecomp.sdc.be.model.UploadServiceInfo;
85 import org.openecomp.sdc.be.model.User;
86 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
87 import org.openecomp.sdc.be.resources.data.auditing.model.DistributionData;
88 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
89 import org.openecomp.sdc.be.servlets.ServiceUploadServlet.ServiceAuthorityTypeEnum;
90 import org.openecomp.sdc.common.api.Constants;
91 import org.openecomp.sdc.common.datastructure.Wrapper;
92 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
93 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
94 import org.openecomp.sdc.common.log.enums.StatusCode;
95 import org.openecomp.sdc.common.log.wrappers.Logger;
96 import org.openecomp.sdc.common.util.Multitenancy;
97 import org.openecomp.sdc.common.zip.exception.ZipException;
98 import org.openecomp.sdc.exception.ResponseFormat;
99 import org.springframework.stereotype.Controller;
100 import org.keycloak.representations.AccessToken;
101 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
102 @Path("/v1/catalog")
103 @Server(url = "/sdc2/rest")
104 @Controller
105 public class ServiceServlet extends AbstractValidationsServlet {
106
107     private static final Logger log = Logger.getLogger(ServiceServlet.class);
108     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(ServiceServlet.class.getName());
109     private static final String START_HANDLE_REQUEST_OF = "Start handle request of {}";
110     private static final String MODIFIER_ID_IS = "modifier id is {}";
111     private final ElementBusinessLogic elementBusinessLogic;
112     private final ServiceBusinessLogic serviceBusinessLogic;
113
114     @Inject
115     public ServiceServlet(ComponentInstanceBusinessLogic componentInstanceBL, ComponentsUtils componentsUtils,
116                           ServletUtils servletUtils, ResourceImportManager resourceImportManager, ServiceBusinessLogic serviceBusinessLogic,
117                           ResourceBusinessLogic resourceBusinessLogic, ElementBusinessLogic elementBusinessLogic) {
118         super(componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
119         this.serviceBusinessLogic = serviceBusinessLogic;
120         this.elementBusinessLogic = elementBusinessLogic;
121     }
122
123     @POST
124     @Path("/services")
125     @Tag(name = "SDCE-2 APIs")
126     @Consumes(MediaType.APPLICATION_JSON)
127     @Produces(MediaType.APPLICATION_JSON)
128     @Operation(description = "Create Service", method = "POST", summary = "Returns created service", responses = {
129         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
130         @ApiResponse(responseCode = "201", description = "Service created"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
131         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
132         @ApiResponse(responseCode = "409", description = "Service already exist"),
133             @ApiResponse(responseCode = "401", description = "Unauthorized Tenant")})
134     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
135     public Response createService(@Parameter(description = "Service object to be created", required = true) String data,
136                                   @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
137         String url = request.getMethod() + " " + request.getRequestURI();
138         log.debug(START_HANDLE_REQUEST_OF, url);
139         User modifier = new User(userId);
140         log.debug(MODIFIER_ID_IS, userId);
141         loggerSupportability.log(LoggerSupportabilityActions.CREATE_SERVICE, StatusCode.STARTED, "Starting to create a service by user {} ", userId);
142         validateNotEmptyBody(data);
143         Either<Service, ResponseFormat> convertResponse = parseToService(data, modifier);
144         if (convertResponse.isRight()) {
145             throw new ByResponseFormatComponentException(convertResponse.right().value());
146         }
147         Multitenancy keyaccess = new Multitenancy();
148         Service service = convertResponse.left().value();
149         if (keyaccess.multiTenancyCheck()) {
150             AccessToken.Access realmAccess = keyaccess.getAccessToken(request).getRealmAccess();
151             Set<String> realmroles = realmAccess.getRoles();
152             boolean match = realmroles.contains(service.getTenant());
153             if (match) {
154                 Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.createService(service, modifier);
155                 if (actionResponse.isRight()) {
156                     log.debug("Failed to create service");
157                     throw new ByResponseFormatComponentException(actionResponse.right().value());
158                 }
159                 loggerSupportability.log(LoggerSupportabilityActions.CREATE_SERVICE, service.getComponentMetadataForSupportLog(), StatusCode.COMPLETE,
160                         "Service {} has been created by user {} ", service.getName(), userId);
161                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.left().value());
162             } else {
163                 log.debug("Unauthorized Tenant");
164                 return Response.status(401, "Unauthorized Tenant").build();
165             }
166         } else {
167             Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.createService(service, modifier);
168             if (actionResponse.isRight()) {
169                 log.debug("Failed to create service");
170                 throw new ByResponseFormatComponentException(actionResponse.right().value());
171             }
172             loggerSupportability.log(LoggerSupportabilityActions.CREATE_SERVICE, service.getComponentMetadataForSupportLog(), StatusCode.COMPLETE,
173                     "Service {} has been created by user {} ", service.getName(), userId);
174             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.left().value());
175         }
176     }
177
178     public Either<Service, ResponseFormat> parseToService(String serviceJson, User user) {
179         return getComponentsUtils()
180             .convertJsonToObjectUsingObjectMapper(serviceJson, user, Service.class, AuditingActionEnum.CREATE_RESOURCE, ComponentTypeEnum.SERVICE);
181     }
182
183     @GET
184     @Path("/services/validate-name/{serviceName}")
185     @Tag(name = "SDCE-2 APIs")
186     @Consumes(MediaType.APPLICATION_JSON)
187     @Produces(MediaType.APPLICATION_JSON)
188     @Operation(description = "validate service name", method = "GET", summary = "checks if the chosen service name is available ", responses = {
189         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
190         @ApiResponse(responseCode = "200", description = "Service found"), @ApiResponse(responseCode = "403", description = "Restricted operation")})
191     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
192     public Response validateServiceName(@PathParam("serviceName") final String serviceName, @Context final HttpServletRequest request,
193                                         @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
194         String url = request.getMethod() + " " + request.getRequestURI();
195         log.debug(START_HANDLE_REQUEST_OF, url);
196         log.debug(MODIFIER_ID_IS, userId);
197         try {
198             Either<Map<String, Boolean>, ResponseFormat> actionResponse = serviceBusinessLogic.validateServiceNameExists(serviceName, userId);
199             if (actionResponse.isRight()) {
200                 log.debug("failed to get validate service name");
201                 return buildErrorResponse(actionResponse.right().value());
202             }
203             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
204         } catch (Exception e) {
205             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Service Name");
206             log.debug("validate service name failed with exception", e);
207             throw e;
208         }
209     }
210
211     @GET
212     @Path("/audit-records/{componentType}/{componentUniqueId}")
213     @Tag(name = "SDCE-2 APIs")
214     @Consumes(MediaType.APPLICATION_JSON)
215     @Produces(MediaType.APPLICATION_JSON)
216     @Operation(description = "get component audit records", method = "GET", summary = "get audit records for a service or a resource", responses = {
217         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
218         @ApiResponse(responseCode = "200", description = "Service found"), @ApiResponse(responseCode = "403", description = "Restricted operation")})
219     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
220     public Response getComponentAuditRecords(@PathParam("componentType") final String componentType,
221                                              @PathParam("componentUniqueId") final String componentUniqueId,
222                                              @Context final HttpServletRequest request,
223                                              @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
224         init();
225         ServletContext context = request.getSession().getServletContext();
226         String url = request.getMethod() + " " + request.getRequestURI();
227         log.debug(START_HANDLE_REQUEST_OF, url);
228         log.debug(MODIFIER_ID_IS, userId);
229         Wrapper<Response> responseWrapper = new Wrapper<>();
230         Wrapper<String> uuidWrapper = new Wrapper<>();
231         Wrapper<String> versionWrapper = new Wrapper<>();
232         Wrapper<User> userWrapper = new Wrapper<>();
233         try {
234             validateUserExist(responseWrapper, userWrapper, userId);
235             if (responseWrapper.isEmpty()) {
236                 fillUUIDAndVersion(responseWrapper, uuidWrapper, versionWrapper, userWrapper.getInnerElement(), validateComponentType(componentType),
237                     componentUniqueId, context);
238             }
239             if (responseWrapper.isEmpty()) {
240                 Either<List<Map<String, Object>>, ResponseFormat> eitherServiceAudit = serviceBusinessLogic
241                     .getComponentAuditRecords(versionWrapper.getInnerElement(), uuidWrapper.getInnerElement(), userId);
242                 if (eitherServiceAudit.isRight()) {
243                     Response errorResponse = buildErrorResponse(eitherServiceAudit.right().value());
244                     responseWrapper.setInnerElement(errorResponse);
245                 } else {
246                     List<Map<String, Object>> auditRecords = eitherServiceAudit.left().value();
247                     Response okResponse = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), auditRecords);
248                     responseWrapper.setInnerElement(okResponse);
249                 }
250             }
251         } catch (Exception e) {
252             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Service Name");
253             log.debug("get Service Audit Records failed with exception", e);
254             throw e;
255         }
256         return responseWrapper.getInnerElement();
257     }
258
259     private void fillUUIDAndVersion(Wrapper<Response> responseWrapper, Wrapper<String> uuidWrapper, Wrapper<String> versionWrapper, User user,
260                                     final ComponentTypeEnum componentTypeEnum, final String componentUniqueId, ServletContext context) {
261         if (componentTypeEnum == ComponentTypeEnum.RESOURCE) {
262             Either<Resource, ResponseFormat> eitherResource = getResourceBL(context).getResource(componentUniqueId, user);
263             if (eitherResource.isLeft()) {
264                 uuidWrapper.setInnerElement(eitherResource.left().value().getUUID());
265                 versionWrapper.setInnerElement(eitherResource.left().value().getVersion());
266             } else {
267                 responseWrapper.setInnerElement(buildErrorResponse(eitherResource.right().value()));
268             }
269         } else {
270             Either<Service, ResponseFormat> eitherService = getServiceBL(context).getService(componentUniqueId, user);
271             if (eitherService.isLeft()) {
272                 uuidWrapper.setInnerElement(eitherService.left().value().getUUID());
273                 versionWrapper.setInnerElement(eitherService.left().value().getVersion());
274             } else {
275                 responseWrapper.setInnerElement(buildErrorResponse(eitherService.right().value()));
276             }
277         }
278     }
279
280     @DELETE
281     @Path("/services/{serviceId}")
282     @Tag(name = "SDCE-2 APIs")
283     @Operation(description = "Delete Service", method = "DELETE", summary = "Return no content", responses = {
284         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
285         @ApiResponse(responseCode = "204", description = "Service deleted"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
286         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
287         @ApiResponse(responseCode = "404", description = "Service not found")})
288     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
289     public Response deleteService(@PathParam("serviceId") final String serviceId,
290                                   @Parameter(description = "Optional parameter to determine the delete action: " +
291                                       "DELETE, which will permanently delete theService from the system or " +
292                                       "MARK_AS_DELETE, which will logically mark the service as deleted. Default action is to MARK_AS_DELETE")
293                                   @QueryParam("deleteAction") final Action deleteAction,
294                                   @Context final HttpServletRequest request) {
295         ServletContext context = request.getSession().getServletContext();
296         String url = request.getMethod() + " " + request.getRequestURI();
297         log.debug(START_HANDLE_REQUEST_OF, url);
298         // get modifier id
299         String userId = request.getHeader(Constants.USER_ID_HEADER);
300         User modifier = new User(userId);
301         log.debug(MODIFIER_ID_IS, userId);
302         try {
303             String serviceIdLower = serviceId.toLowerCase();
304             loggerSupportability
305                 .log(LoggerSupportabilityActions.DELETE_SERVICE, StatusCode.STARTED, "Starting to delete service {} by user {} ", serviceIdLower,
306                     userId);
307             ServiceBusinessLogic businessLogic = getServiceBL(context);
308             ResponseFormat actionResponse;
309             if (Action.DELETE.equals(deleteAction)) {
310                 businessLogic.deleteServiceAllVersions(serviceIdLower, modifier);
311                 actionResponse = componentsUtils.getResponseFormat(ActionStatus.NO_CONTENT);
312             } else {
313                 actionResponse = businessLogic.markServiceForDeletion(serviceIdLower, modifier);
314             }
315             if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
316                 log.debug("failed to delete service");
317                 return buildErrorResponse(actionResponse);
318             }
319             loggerSupportability
320                 .log(LoggerSupportabilityActions.DELETE_SERVICE, StatusCode.COMPLETE, "Ended deleting service {} by user {}", serviceIdLower, userId);
321             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
322         } catch (Exception e) {
323             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Service");
324             log.debug("delete service failed with exception", e);
325             throw e;
326         }
327     }
328
329     @DELETE
330     @Path("/services/{serviceName}/{version}")
331     @Tag(name = "SDCE-2 APIs")
332     @Operation(description = "Delete Service By Name And Version", method = "DELETE", summary = "Returns no content", responses = {
333         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Resource.class)))),
334         @ApiResponse(responseCode = "204", description = "Service deleted"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
335         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
336         @ApiResponse(responseCode = "404", description = "Service not found")})
337     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
338     public Response deleteServiceByNameAndVersion(@PathParam("serviceName") final String serviceName, @PathParam("version") final String version,
339                                                   @Context final HttpServletRequest request) {
340         User modifier = getUser(request);
341         try {
342             ResponseFormat actionResponse = serviceBusinessLogic.deleteServiceByNameAndVersion(serviceName, version, modifier);
343             if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
344                 log.debug("failed to delete service");
345                 return buildErrorResponse(actionResponse);
346             }
347             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
348         } catch (Exception e) {
349             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Service");
350             log.debug("delete service failed with exception", e);
351             throw e;
352         }
353     }
354
355     private User getUser(HttpServletRequest request) {
356         String url = request.getMethod() + " " + request.getRequestURI();
357         log.debug(START_HANDLE_REQUEST_OF, url);
358         // get modifier id
359         String userId = request.getHeader(Constants.USER_ID_HEADER);
360         User modifier = new User(userId);
361         log.debug(MODIFIER_ID_IS, userId);
362         return modifier;
363     }
364
365     @PUT
366     @Path("/services/{serviceId}/metadata")
367     @Tag(name = "SDCE-2 APIs")
368     @Consumes(MediaType.APPLICATION_JSON)
369     @Produces(MediaType.APPLICATION_JSON)
370     @Operation(description = "Update Service Metadata", method = "PUT", summary = "Returns updated service", responses = {
371         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
372         @ApiResponse(responseCode = "200", description = "Service Updated"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
373         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
374     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
375     public Response updateServiceMetadata(@PathParam("serviceId") final String serviceId,
376                                           @Parameter(description = "Service object to be Updated", required = true) String data,
377                                           @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId)
378         throws IOException {
379         String url = request.getMethod() + " " + request.getRequestURI();
380         log.debug(START_HANDLE_REQUEST_OF, url);
381         User modifier = new User(userId);
382         log.debug(MODIFIER_ID_IS, userId);
383         try {
384             String serviceIdLower = serviceId.toLowerCase();
385             Either<Service, ResponseFormat> convertResponse = parseToService(data, modifier);
386             if (convertResponse.isRight()) {
387                 log.debug("failed to parse service");
388                 return buildErrorResponse(convertResponse.right().value());
389             }
390             Service updatedService = convertResponse.left().value();
391             Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.updateServiceMetadata(serviceIdLower, updatedService, modifier);
392             if (actionResponse.isRight()) {
393                 log.debug("failed to update service");
394                 return buildErrorResponse(actionResponse.right().value());
395             }
396             Service service = actionResponse.left().value();
397             Object result = RepresentationUtils.toRepresentation(service);
398             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
399         } catch (Exception e) {
400             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Service Metadata");
401             log.debug("update service metadata failed with exception", e);
402             throw e;
403         }
404     }
405
406     /**
407      * updates group instance property values Note, than in case of group instance updated successfully, related resourceInstance and containing
408      * component modification time will be updated
409      *
410      * @param serviceId
411      * @param componentInstanceId
412      * @param groupInstanceId
413      * @param data
414      * @param request
415      * @param userId
416      * @return
417      */
418     @PUT
419     @Path("/{containerComponentType}/{serviceId}/resourceInstance/{componentInstanceId}/groupInstance/{groupInstanceId}")
420     @Tag(name = "SDCE-2 APIs")
421     @Consumes(MediaType.APPLICATION_JSON)
422     @Produces(MediaType.APPLICATION_JSON)
423     @Operation(description = "Update Group Instance Property Values", method = "PUT", summary = "Returns updated group instance", responses = {
424         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
425         @ApiResponse(responseCode = "200", description = "Group Instance Property Values Updated"),
426         @ApiResponse(responseCode = "403", description = "Restricted operation"),
427         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content")})
428     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
429     public Response updateGroupInstancePropertyValues(@PathParam("serviceId") final String serviceId,
430                                                       @PathParam("componentInstanceId") final String componentInstanceId,
431                                                       @PathParam("groupInstanceId") final String groupInstanceId,
432                                                       @Parameter(description = "Group instance object to be Updated", required = true) String data,
433                                                       @Context final HttpServletRequest request,
434                                                       @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws JsonProcessingException {
435         String url = request.getMethod() + " " + request.getRequestURI();
436         log.debug(START_HANDLE_REQUEST_OF, url);
437         User modifier = new User(userId);
438         log.debug(MODIFIER_ID_IS, userId);
439         Either<List<GroupInstanceProperty>, ResponseFormat> actionResponse = null;
440         try {
441             List<GroupInstanceProperty> updatedProperties;
442             Type listType = new TypeToken<ArrayList<GroupInstanceProperty>>() {
443             }.getType();
444             ArrayList<GroupInstanceProperty> newProperties = gson.fromJson(data, listType);
445             if (newProperties == null) {
446                 actionResponse = Either.right(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
447             }
448             if (actionResponse == null) {
449                 log.debug("Start handle update group instance property values request. Received group instance is {}", groupInstanceId);
450                 actionResponse = serviceBusinessLogic
451                     .updateGroupInstancePropertyValues(modifier, serviceId, componentInstanceId, groupInstanceId, newProperties);
452                 if (actionResponse.isRight()) {
453                     actionResponse = Either.right(actionResponse.right().value());
454                 }
455             }
456             if (actionResponse.isLeft()) {
457                 updatedProperties = actionResponse.left().value();
458                 ObjectMapper mapper = new ObjectMapper();
459                 String result = mapper.writeValueAsString(updatedProperties);
460                 return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
461             } else {
462                 return buildErrorResponse(actionResponse.right().value());
463             }
464         } catch (Exception e) {
465             log.error(BUSINESS_PROCESS_ERROR, this.getClass().getName(), "Exception occured during update Group Instance property values.", e);
466             throw e;
467         }
468     }
469
470     @GET
471     @Path("/services/{serviceId}")
472     @Tag(name = "SDCE-2 APIs")
473     @Consumes(MediaType.APPLICATION_JSON)
474     @Produces(MediaType.APPLICATION_JSON)
475     @Operation(description = "Retrieve Service", method = "GET", summary = "Returns service according to serviceId", responses = {
476         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
477         @ApiResponse(responseCode = "200", description = "Service found"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
478         @ApiResponse(responseCode = "404", description = "Service not found")})
479     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
480     public Response getServiceById(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request,
481                                    @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
482         String url = request.getMethod() + " " + request.getRequestURI();
483         log.debug(START_HANDLE_REQUEST_OF, url);
484         // get modifier id
485         User modifier = new User(userId);
486         log.debug(MODIFIER_ID_IS, userId);
487         try {
488             String serviceIdLower = serviceId.toLowerCase();
489             log.debug("get service with id {}", serviceId);
490             Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.getService(serviceIdLower, modifier);
491             if (actionResponse.isRight()) {
492                 log.debug("failed to get service");
493                 return buildErrorResponse(actionResponse.right().value());
494             }
495             Service service = actionResponse.left().value();
496             Object result = RepresentationUtils.toRepresentation(service);
497             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
498         } catch (Exception e) {
499             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Service");
500             log.debug("get service failed with exception", e);
501             throw e;
502         }
503     }
504
505     @GET
506     @Path("/services/serviceName/{serviceName}/serviceVersion/{serviceVersion}")
507     @Tag(name = "SDCE-2 APIs")
508     @Consumes(MediaType.APPLICATION_JSON)
509     @Produces(MediaType.APPLICATION_JSON)
510     @Operation(description = "Retrieve Service", method = "GET", summary = "Returns service according to name and version", responses = {
511         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Service.class)))),
512         @ApiResponse(responseCode = "200", description = "Service found"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
513         @ApiResponse(responseCode = "404", description = "Service not found")})
514     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
515     public Response getServiceByNameAndVersion(@PathParam("serviceName") final String serviceName,
516                                                @PathParam("serviceVersion") final String serviceVersion, @Context final HttpServletRequest request,
517                                                @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
518         // get modifier id
519         log.debug(MODIFIER_ID_IS, userId);
520         try {
521             Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.getServiceByNameAndVersion(serviceName, serviceVersion, userId);
522             if (actionResponse.isRight()) {
523                 return buildErrorResponse(actionResponse.right().value());
524             }
525             Service service = actionResponse.left().value();
526             Object result = RepresentationUtils.toRepresentation(service);
527             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
528         } catch (Exception e) {
529             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Service by name and version");
530             log.debug("get service failed with exception", e);
531             throw e;
532         }
533     }
534
535     @POST
536     @Path("/services/{serviceId}/distribution/{env}/activate")
537     @Tag(name = "SDCE-5 APIs")
538     @Consumes(MediaType.APPLICATION_JSON)
539     @Produces(MediaType.APPLICATION_JSON)
540     @Operation(description = "Activate distribution", method = "POST", summary = "activate distribution", responses = {
541         @ApiResponse(responseCode = "200", description = "OK"),
542         @ApiResponse(responseCode = "409", description = "Service cannot be distributed due to missing deployment artifacts"),
543         @ApiResponse(responseCode = "404", description = "Requested service was not found"),
544         @ApiResponse(responseCode = "500", description = "Internal Server Error. Please try again later.")})
545     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
546     public Response activateDistribution(@PathParam("serviceId") final String serviceId, @PathParam("env") final String env,
547                                          @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId)
548         throws IOException {
549         String url = request.getMethod() + " " + request.getRequestURI();
550         log.debug(START_HANDLE_REQUEST_OF, url);
551         User modifier = new User(userId);
552         log.debug(MODIFIER_ID_IS, userId);
553         Either<Service, ResponseFormat> distResponse = serviceBusinessLogic.activateDistribution(serviceId, env, modifier, request);
554         if (distResponse.isRight()) {
555             log.debug("failed to activate service distribution");
556             return buildErrorResponse(distResponse.right().value());
557         }
558         Service service = distResponse.left().value();
559         Object result = null;
560         try {
561             result = RepresentationUtils.toRepresentation(service);
562         } catch (IOException e) {
563             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Activate Distribution");
564             log.debug("activate distribution failed with exception", e);
565             throw e;
566         }
567         return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
568     }
569
570     @POST
571     @Path("/services/{serviceId}/distribution/{did}/markDeployed")
572     @Tag(name = "SDCE-5 APIs")
573     @Consumes(MediaType.APPLICATION_JSON)
574     @Produces(MediaType.APPLICATION_JSON)
575     @Operation(description = "Mark distribution as deployed", method = "POST", summary = "relevant audit record will be created", responses = {
576         @ApiResponse(responseCode = "200", description = "Service was marked as deployed"),
577         @ApiResponse(responseCode = "409", description = "Restricted operation"),
578         @ApiResponse(responseCode = "403", description = "Service is not available"),
579         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
580         @ApiResponse(responseCode = "404", description = "Requested service was not found"),
581         @ApiResponse(responseCode = "500", description = "Internal Server Error. Please try again later.")})
582     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
583     public Response markDistributionAsDeployed(@PathParam("serviceId") final String serviceId, @PathParam("did") final String did,
584                                                @Context final HttpServletRequest request,
585                                                @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
586         String url = request.getMethod() + " " + request.getRequestURI();
587         log.debug(START_HANDLE_REQUEST_OF, url);
588         User modifier = new User(userId);
589         log.debug(MODIFIER_ID_IS, userId);
590         try {
591             Either<Service, ResponseFormat> distResponse = serviceBusinessLogic.markDistributionAsDeployed(serviceId, did, modifier);
592             if (distResponse.isRight()) {
593                 log.debug("failed to mark distribution as deployed");
594                 return buildErrorResponse(distResponse.right().value());
595             }
596             Service service = distResponse.left().value();
597             Object result = RepresentationUtils.toRepresentation(service);
598             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
599         } catch (Exception e) {
600             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Mark Distribution As Deployed");
601             log.debug("mark distribution as deployed failed with exception", e);
602             throw e;
603         }
604     }
605
606     @POST
607     @Path("/services/{serviceId}/tempUrlToBeDeleted")
608     @Tag(name = "SDCE-2 APIs")
609     @Consumes(MediaType.APPLICATION_JSON)
610     @Produces(MediaType.APPLICATION_JSON)
611     @Operation(responses = {@ApiResponse(responseCode = "200", description = "OK"),
612         @ApiResponse(responseCode = "500", description = "Internal Server Error. Please try again later.")})
613     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
614     public Response tempUrlToBeDeleted(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request,
615                                        @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
616         String url = request.getMethod() + " " + request.getRequestURI();
617         log.debug(START_HANDLE_REQUEST_OF, url);
618         User modifier = new User(userId);
619         log.debug(MODIFIER_ID_IS, userId);
620         try {
621             Service service = (serviceBusinessLogic.getService(serviceId, modifier)).left().value();
622             Either<Service, ResponseFormat> res = serviceBusinessLogic
623                 .updateDistributionStatusForActivation(service, modifier, DistributionStatusEnum.DISTRIBUTED);
624             if (res.isRight()) {
625                 buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
626             }
627             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), null);
628         } catch (Exception e) {
629             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("tempUrlToBeDeleted");
630             log.debug("failed with exception", e);
631             throw e;
632         }
633     }
634
635     @GET
636     @Path("/services/{serviceId}/linksMap")
637     @Tag(name = "SDCE-2 APIs")
638     @Consumes(MediaType.APPLICATION_JSON)
639     @Produces(MediaType.APPLICATION_JSON)
640     @Operation(description = "Retrieve Service component relations map", method = "GET", summary = "Returns service components relations", responses = {
641         @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = ServiceRelations.class)))),
642         @ApiResponse(responseCode = "200", description = "Service found"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
643         @ApiResponse(responseCode = "404", description = "Service not found")})
644     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
645     public Response getServiceComponentRelationMap(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request,
646                                                    @HeaderParam(value = Constants.USER_ID_HEADER) String userId) throws IOException {
647         String url = request.getMethod() + " " + request.getRequestURI();
648         log.debug(START_HANDLE_REQUEST_OF, url);
649         // get modifier id
650         User modifier = new User(userId);
651         log.debug(MODIFIER_ID_IS, userId);
652         try {
653             String serviceIdLower = serviceId.toLowerCase();
654             log.debug("get service components relations with id {}", serviceId);
655             Either<ServiceRelations, ResponseFormat> actionResponse = serviceBusinessLogic.getServiceComponentsRelations(serviceIdLower, modifier);
656             if (actionResponse.isRight()) {
657                 log.debug("failed to get service relations data");
658                 return buildErrorResponse(actionResponse.right().value());
659             }
660             ServiceRelations serviceRelations = actionResponse.left().value();
661             Object result = RepresentationUtils.toRepresentation(serviceRelations);
662             return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
663         } catch (Exception e) {
664             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Service");
665             log.debug("get service relations data failed with exception", e);
666             throw e;
667         }
668     }
669
670     @POST
671     @Path("/services/importService")
672     @Tag(name = "SDCE-2 APIs")
673     @Consumes(MediaType.APPLICATION_JSON)
674     @Produces(MediaType.APPLICATION_JSON)
675     @Operation(description = "Import Service", method = "POST", summary = "Returns imported service", responses = {
676         @ApiResponse(responseCode = "201", description = "Service created"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
677         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
678         @ApiResponse(responseCode = "409", description = "Service already exist")})
679     public Response importNsService(@Parameter(description = "Service object to be imported", required = true) String data,
680                                     @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
681         userId = (userId != null) ? userId : request.getHeader(Constants.USER_ID_HEADER);
682         initSpringFromContext();
683         String url = request.getMethod() + " " + request.getRequestURI();
684         log.debug(START_HANDLE_REQUEST_OF, url);
685         log.debug(MODIFIER_ID_IS, userId);
686         try {
687             final Wrapper<Response> responseWrapper = performUIImport(data, request, userId);
688             return responseWrapper.getInnerElement();
689         } catch (IOException | ZipException e) {
690             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Import Service");
691             log.debug("import service failed with exception", e);
692             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
693         }
694     }
695
696     private Wrapper<Response> performUIImport(String data, final HttpServletRequest request,
697                                               String userId) throws FileNotFoundException, ZipException {
698         Wrapper<Response> responseWrapper = new Wrapper<>();
699         Wrapper<User> userWrapper = new Wrapper<>();
700         Wrapper<UploadServiceInfo> uploadServiceInfoWrapper = new Wrapper<>();
701         Wrapper<String> yamlStringWrapper = new Wrapper<>();
702         ServiceAuthorityTypeEnum serviceAuthorityTypeEnum = ServiceAuthorityTypeEnum.USER_TYPE_UI;
703         commonServiceGeneralValidations(responseWrapper, userWrapper, uploadServiceInfoWrapper, serviceAuthorityTypeEnum, userId, data);
704         specificServiceAuthorityValidations(responseWrapper, uploadServiceInfoWrapper, yamlStringWrapper, request,
705             data, serviceAuthorityTypeEnum);
706         if (responseWrapper.isEmpty()) {
707             handleImportService(responseWrapper, userWrapper.getInnerElement(), uploadServiceInfoWrapper.getInnerElement());
708         }
709         return responseWrapper;
710     }
711
712     /**
713      * import ReplaceService
714      *
715      * @param userId
716      * @param requestId
717      * @param instanceIdHeader
718      * @param accept
719      * @param authorization
720      * @param request
721      * @param file
722      * @param contentDispositionHeader
723      * @param serviceInfoJsonString
724      * @param uuid
725      * @return
726      */
727     @POST
728     @Path("/services/serviceUUID/{uuid}/importReplaceService")
729     @Tag(name = "SDCE-2 APIs")
730     @Produces(MediaType.APPLICATION_JSON)
731     @Operation(description = "Import Service", method = "POST", summary = "Returns imported service", responses = {
732         @ApiResponse(responseCode = "201", description = "Service created"), @ApiResponse(responseCode = "403", description = "Restricted operation"),
733         @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
734         @ApiResponse(responseCode = "409", description = "Service already exist")})
735     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
736     public Response importReplaceService(
737         @Parameter(description = "The user id", required = true) @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
738         @Parameter(description = "X-ECOMP-RequestID header", required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId,
739         @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam(value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader,
740         @Parameter(description = "Determines the format of the body of the response", required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept,
741         @Parameter(description = "The username and password", required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization,
742         @Context final HttpServletRequest request, @Parameter(description = "FileInputStream") @FormDataParam("serviceZip") File file,
743         @Parameter(description = "ContentDisposition") @FormDataParam("serviceZip") FormDataContentDisposition contentDispositionHeader,
744         @Parameter(description = "serviceMetadata") @FormDataParam("serviceZipMetadata") String serviceInfoJsonString,
745         @Parameter(description = "The requested asset uuid", required = true) @PathParam("uuid") final String uuid) {
746         initSpringFromContext();
747         String requestURI = request.getRequestURI();
748         String url = request.getMethod() + " " + requestURI;
749         log.debug("importReplaceService,Start handle request of {}", url);
750         // get modifier id
751         User modifier = new User(userId);
752         log.debug("importReplaceService,modifier id is {}", userId);
753         log.debug("importReplaceService,get file:{},fileName:{}", file, file.getName());
754         ResponseFormat responseFormat = null;
755         AuditingActionEnum auditingActionEnum = AuditingActionEnum.Import_Replace_Service;
756         String assetType = "services";
757         Either<List<? extends Component>, ResponseFormat> assetTypeData = elementBusinessLogic
758             .getCatalogComponentsByUuidAndAssetType(assetType, uuid);
759         if (assetTypeData.isRight() || assetTypeData.left().value().size() != 1) {
760             log.debug("getServiceAbstractStatus: Service Fetching Failed");
761             throw new ByResponseFormatComponentException(assetTypeData.right().value());
762         }
763         log.debug("getServiceAbstractStatus: Service Fetching Success");
764         Service oldService = (Service) assetTypeData.left().value().get(0);
765         ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
766         ResourceCommonInfo resourceCommonInfo = new ResourceCommonInfo(componentType.getValue());
767         DistributionData distributionData = new DistributionData(instanceIdHeader, requestURI);
768         // Mandatory
769         if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
770             log.debug("importReplaceService: Missing X-ECOMP-InstanceID header");
771             responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
772             getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, distributionData, resourceCommonInfo, requestId, null);
773             return buildErrorResponse(responseFormat);
774         }
775         try {
776             Wrapper<Response> responseWrapper = new Wrapper<>();
777             // file import
778             Wrapper<User> userWrapper = new Wrapper<>();
779             Wrapper<UploadServiceInfo> uploadServiceInfoWrapper = new Wrapper<>();
780             Wrapper<String> yamlStringWrapper = new Wrapper<>();
781             ServiceUploadServlet.ServiceAuthorityTypeEnum serviceAuthorityEnum = ServiceUploadServlet.ServiceAuthorityTypeEnum.CSAR_TYPE_BE;
782             // PayLoad Validations
783             commonServiceGeneralValidations(responseWrapper, userWrapper, uploadServiceInfoWrapper, serviceAuthorityEnum, userId,
784                 serviceInfoJsonString);
785             fillServicePayload(responseWrapper, uploadServiceInfoWrapper, yamlStringWrapper, modifier, serviceInfoJsonString, serviceAuthorityEnum,
786                 file);
787             specificServiceAuthorityValidations(responseWrapper, uploadServiceInfoWrapper, yamlStringWrapper, request,
788                 serviceInfoJsonString, serviceAuthorityEnum);
789             log.debug("importReplaceService:get payload:{}", uploadServiceInfoWrapper.getInnerElement().getPayloadData());
790             ServiceMetadataDataDefinition serviceMetadataDataDefinition = (ServiceMetadataDataDefinition) oldService.getComponentMetadataDefinition()
791                 .getMetadataDataDefinition();
792             uploadServiceInfoWrapper.getInnerElement().setServiceVendorModelNumber(serviceMetadataDataDefinition.getServiceVendorModelNumber());
793             uploadServiceInfoWrapper.getInnerElement().setDescription(oldService.getDescription());
794             uploadServiceInfoWrapper.getInnerElement().setCategories(oldService.getCategories());
795             uploadServiceInfoWrapper.getInnerElement().setIcon(oldService.getIcon());
796             uploadServiceInfoWrapper.getInnerElement().setProjectCode(oldService.getProjectCode());
797             if (responseWrapper.isEmpty()) {
798                 log.debug("importReplaceService:start handleImportService");
799                 handleImportService(responseWrapper, userWrapper.getInnerElement(), uploadServiceInfoWrapper.getInnerElement());
800             }
801             return responseWrapper.getInnerElement();
802         } catch (final ZipException e) {
803             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Import Service");
804             log.debug("import service failed with exception", e);
805             return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
806         }
807     }
808
809     public enum Action {DELETE, MARK_AS_DELETE}
810 }